From 9e53263b95e3d70cffca6d560478a61d01055ae0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micka=C3=ABl=20Schoentgen?=
Date: Fri, 26 Jul 2019 17:38:38 +0200
Subject: [PATCH 01/39] Remove duplicate code to import MSS
In fact, the old code was a copy of the `mss.mss()` factory.
---
vidgear/gears/screengear.py | 14 +++-----------
1 file changed, 3 insertions(+), 11 deletions(-)
diff --git a/vidgear/gears/screengear.py b/vidgear/gears/screengear.py
index ff5c0b182..d03dbc7ad 100644
--- a/vidgear/gears/screengear.py
+++ b/vidgear/gears/screengear.py
@@ -77,17 +77,9 @@ def __init__(self, monitor = 1, colorspace = None, logging = False, **options):
#intialize threaded queue mode
self.threaded_queue_mode = True
try:
- # try import necessary system specific mss library
- import platform
- if platform.system() == 'Linux':
- from mss.linux import MSS as mss
- elif platform.system() == 'Windows':
- from mss.windows import MSS as mss
- elif platform.system() == 'Darwin':
- from mss.darwin import MSS as mss
- else:
- from mss import mss
- #import mss error handler
+ # import mss factory
+ from mss import mss
+ # import mss error handler
from mss.exception import ScreenShotError
except ImportError as error:
# otherwise raise import error
From d416e05c8b8b44e19df8a8638a5d21937c1c8ecc Mon Sep 17 00:00:00 2001
From: Abhishek Thakur
Date: Tue, 30 Jul 2019 12:41:34 +0530
Subject: [PATCH 02/39] [Enhancement] Adding OSX environment support for Travis
Cli tests (#42)
[Enhancement] Adding OSX environment support for Travis Cli tests
- Dropped Python 2.7 Support from CLI Tests
- Reformatted travis.yml from scratch
- Added native support for MacOS environment
- Added necessary system-specific dependencies
- Added Static binaries FFmpeg for MacOS
- Fixed Redundant URL
- Fixed Bugs
- Updated Docs
---
.travis.yml | 118 +++++++++++++-----
appveyor.yml | 7 --
scripts/bash/prepare_dataset.sh | 50 ++++++--
.../test_benchmark_Videowriter.py | 6 +-
.../test_benchmark_playback.py | 13 +-
vidgear/tests/test_helper.py | 6 +-
.../tests/videocapture_tests/test_camgear.py | 5 +-
.../writer_tests/test_compression_mode.py | 6 +-
.../writer_tests/test_non_compression_mode.py | 6 +-
9 files changed, 157 insertions(+), 60 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index c880132d4..f01ef753f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,41 +1,101 @@
language: python
-before_install:
- - "sudo add-apt-repository ppa:jonathonf/ffmpeg-4 -y"
- - "sudo apt-get update -q"
- - "sudo apt-get install ffmpeg unzip wget -y"
- - "sudo apt-get install dos2unix -y"
- - "dos2unix scripts/bash/prepare_dataset.sh"
- - "chmod +x scripts/bash/prepare_dataset.sh"
- - "dos2unix scripts/bash/install_opencv.sh"
- - "chmod +x scripts/bash/install_opencv.sh"
+matrix:
+ include:
+ - os: osx
+ python: "3.5"
+ language: generic
+ osx_image: xcode11
+ env: PYTHON=35
+ - os: osx
+ python: "3.6"
+ language: generic
+ osx_image: xcode11
+ env: PYTHON=36
+ - os: osx
+ python: "3.7"
+ language: generic
+ osx_image: xcode11
+ env: PYTHON=37
+ - os: linux
+ python: "3.5"
+ language: python
+ cache: pip
+ - os: linux
+ python: "3.6"
+ language: python
+ cache: pip
+ - os: linux
+ dist: xenial
+ python: "3.7"
+ language: python
+ cache: pip
+
+
+
+before_install:
+ - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
+ brew install swig;
+ brew install ffmpeg;
+ brew install unzip;
+ curl -LO https://raw.githubusercontent.com/GiovanniBussi/macports-ci/master/macports-ci;
+ source ./macports-ci install;
+ yes | sudo port install python$PYTHON;
+ yes | sudo port install py$PYTHON-pip;
+ sudo port select --set python3 python$PYTHON;
+ sudo port select --set pip pip$PYTHON;
+ python3 --version;
+ pip --version;
+ export PATH=$PATH:$(python3 -c "import site; print(site.USER_BASE)")/bin;
+ chmod +x scripts/bash/prepare_dataset.sh;
+ fi
+ - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
+ sudo add-apt-repository ppa:jonathonf/ffmpeg-4 -y;
+ sudo apt-get update -q;
+ sudo apt-get install ffmpeg unzip wget -y;
+ sudo apt-get install dos2unix -y;
+ dos2unix scripts/bash/prepare_dataset.sh;
+ chmod +x scripts/bash/prepare_dataset.sh;
+ dos2unix scripts/bash/install_opencv.sh;
+ chmod +x scripts/bash/install_opencv.sh;
+ fi
branches:
only:
- testing
-cache: pip
-
install:
- - "pip install --upgrade pip wheel"
- - "pip install --upgrade numpy"
- - "pip install ."
- - "pip uninstall opencv-contrib-python -y"
- - "pip install six"
- - "pip install --upgrade pytest"
- - "pip install --upgrade youtube-dl"
-
-dist: xenial
-
-python:
- - "2.7"
- - "3.5"
- - "3.6"
- - "3.7"
+ - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
+ pip install --upgrade pip wheel;
+ pip install --upgrade numpy;
+ pip install .;
+ pip uninstall opencv-contrib-python -y;
+ pip install six;
+ pip install --upgrade pytest;
+ pip install --upgrade youtube-dl;
+ fi
+ - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
+ pip install --upgrade --user pip wheel;
+ pip install --upgrade --user numpy;
+ pip install --user .;
+ pip install --user six;
+ pip install --upgrade --user pytest;
+ pip install --upgrade --user youtube-dl;
+ fi
before_script:
- - bash scripts/bash/install_opencv.sh
- - bash scripts/bash/prepare_dataset.sh
+ - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
+ bash scripts/bash/prepare_dataset.sh;
+ fi
+ - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
+ bash scripts/bash/install_opencv.sh;
+ bash scripts/bash/prepare_dataset.sh;
+ fi
script:
- - python -m pytest -sv
+ - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
+ travis_wait pytest -sv;
+ fi
+ - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
+ python -m pytest -sv;
+ fi
\ No newline at end of file
diff --git a/appveyor.yml b/appveyor.yml
index 96860c425..9b0ba0607 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,12 +1,5 @@
environment:
matrix:
- - PYTHON: "C:\\Python27"
- PYTHON_VERSION: "2.7.x"
- PYTHON_ARCH: "32"
-
- - PYTHON: "C:\\Python27-x64"
- PYTHON_VERSION: "2.7.x"
- PYTHON_ARCH: "64"
- PYTHON: "C:\\Python35"
PYTHON_VERSION: "3.5.x"
diff --git a/scripts/bash/prepare_dataset.sh b/scripts/bash/prepare_dataset.sh
index e5961fe5e..fd31ae0d9 100644
--- a/scripts/bash/prepare_dataset.sh
+++ b/scripts/bash/prepare_dataset.sh
@@ -12,17 +12,37 @@
#The above copyright notice and this permission notice shall be included in all
#copies or substantial portions of the Software.
+
+# Creating necessary directories
mkdir -p $HOME/Downloads
mkdir -p $HOME/Downloads/{FFmpeg_static,Test_videos}
-cd $HOME/Downloads/FFmpeg_static
-
-OS_TYPE=$(uname)
+# Acknowledging machine architecture
MACHINE_BIT=$(uname -m)
+# Acknowledging machine OS type
+case $(uname | tr '[:upper:]' '[:lower:]') in
+linux*)
+ OS_NAME=linux
+ ;;
+darwin*)
+ OS_NAME=osx
+ ;;
+msys*)
+ OS_NAME=windows
+ ;;
+*)
+ OS_NAME=notset
+ ;;
+esac
+
+
#Download and Configure FFmpeg Static
-if [ $OS_TYPE = "Linux" ]; then
-
+cd $HOME/Downloads/FFmpeg_static
+
+if [ $OS_NAME = "linux" ]; then
+
+ echo "Downloading Linux Static FFmpeg Binaries..."
if [ $MACHINE_BIT = "x86_64" ]; then
curl https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz -o ffmpeg-release-amd64-static.tar.xz
tar -xJf ffmpeg-release-amd64-static.tar.xz
@@ -35,8 +55,9 @@ if [ $OS_TYPE = "Linux" ]; then
mv ffmpeg* ffmpeg
fi
-else
+elif [ $OS_NAME = "windows" ]; then
+ echo "Downloading Windows Static FFmpeg Binaries..."
if [ $MACHINE_BIT = "x86_64" ]; then
curl https://ffmpeg.zeranoe.com/builds/win64/static/ffmpeg-latest-win64-static.zip -o ffmpeg-latest-win64-static.zip
unzip -qq ffmpeg-latest-win64-static.zip
@@ -48,14 +69,25 @@ else
rm ffmpeg-latest-win32-static.zip
mv ffmpeg-latest-win32-static ffmpeg
fi
+
+else
+
+ echo "Downloading MacOS64 Static FFmpeg Binary..."
+ curl -LO https://ffmpeg.zeranoe.com/builds/macos64/static/ffmpeg-latest-macos64-static.zip
+ unzip -qq ffmpeg-latest-macos64-static.zip
+ rm ffmpeg-latest-macos64-static.zip
+ mv ffmpeg-latest-macos64-static ffmpeg
+
fi
+# Downloading Test Data
cd $HOME/Downloads/Test_videos
-# Download Test-Data
+
+echo "Downloading Test-Data..."
curl http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4 -o BigBuckBunny.mp4
-curl https://sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4 -o BigBuckBunny_4sec.mp4
+curl https://raw.githubusercontent.com/abhiTronix/Imbakup/master/Images/big_buck_bunny_720p_1mb.mp4 -o BigBuckBunny_4sec.mp4
curl http://jell.yfish.us/media/jellyfish-20-mbps-hd-hevc-10bit.mkv -o 20_mbps_hd_hevc_10bit.mkv
curl http://jell.yfish.us/media/jellyfish-50-mbps-hd-h264.mkv -o 50_mbps_hd_h264.mkv
curl http://jell.yfish.us/media/jellyfish-90-mbps-hd-hevc-10bit.mkv -o 90_mbps_hd_hevc_10bit.mkv
curl http://jell.yfish.us/media/jellyfish-120-mbps-4k-uhd-h264.mkv -o 120_mbps_4k_uhd_h264.mkv
-
+echo "Done Downloading Test-Data!"
\ No newline at end of file
diff --git a/vidgear/tests/benchmark_tests/test_benchmark_Videowriter.py b/vidgear/tests/benchmark_tests/test_benchmark_Videowriter.py
index 9de97aeac..6d2b4af43 100644
--- a/vidgear/tests/benchmark_tests/test_benchmark_Videowriter.py
+++ b/vidgear/tests/benchmark_tests/test_benchmark_Videowriter.py
@@ -23,7 +23,7 @@
===============================================
"""
-import os
+import os, platform
import pytest
from vidgear.gears import WriteGear
from vidgear.gears import VideoGear
@@ -45,8 +45,10 @@ def return_static_ffmpeg():
return FFmpeg static path
"""
path = ''
- if os.name == 'nt':
+ if platform.system() == 'Windows':
path += os.path.join(os.environ['USERPROFILE'],'Downloads/FFmpeg_static/ffmpeg/bin/ffmpeg.exe')
+ elif platform.system() == 'Darwin':
+ path += os.path.join(os.environ['HOME'],'Downloads/FFmpeg_static/ffmpeg/bin/ffmpeg')
else:
path += os.path.join(os.environ['HOME'],'Downloads/FFmpeg_static/ffmpeg/ffmpeg')
return os.path.abspath(path)
diff --git a/vidgear/tests/benchmark_tests/test_benchmark_playback.py b/vidgear/tests/benchmark_tests/test_benchmark_playback.py
index 19adb2e74..482f296b6 100644
--- a/vidgear/tests/benchmark_tests/test_benchmark_playback.py
+++ b/vidgear/tests/benchmark_tests/test_benchmark_playback.py
@@ -23,7 +23,7 @@
===============================================
"""
-import os
+import os, platform
import pytest
from vidgear.gears import CamGear
from .fps import FPS
@@ -64,7 +64,10 @@ def test_benchmark(level):
"""
Benchmarking low to extreme 4k video playback capabilities of VidGear
"""
- try:
- playback(level)
- except Exception as e:
- print(e)
+ if platform.system() != 'Darwin':
+ try:
+ playback(level)
+ except Exception as e:
+ print(e)
+ else:
+ print("Skipping this test for macOS!")
diff --git a/vidgear/tests/test_helper.py b/vidgear/tests/test_helper.py
index dcbbf090f..c5071f5a9 100644
--- a/vidgear/tests/test_helper.py
+++ b/vidgear/tests/test_helper.py
@@ -23,7 +23,7 @@
===============================================
"""
-import os, pytest, tempfile, shutil
+import os, pytest, tempfile, shutil, platform
from vidgear.gears.helper import download_ffmpeg_binaries
from vidgear.gears.helper import validate_ffmpeg
@@ -36,8 +36,10 @@ def return_static_ffmpeg():
return FFmpeg static path
"""
path = ''
- if os.name == 'nt':
+ if platform.system() == 'Windows':
path += os.path.join(os.environ['USERPROFILE'],'Downloads/FFmpeg_static/ffmpeg/bin/ffmpeg.exe')
+ elif platform.system() == 'Darwin':
+ path += os.path.join(os.environ['HOME'],'Downloads/FFmpeg_static/ffmpeg/bin/ffmpeg')
else:
path += os.path.join(os.environ['HOME'],'Downloads/FFmpeg_static/ffmpeg/ffmpeg')
return os.path.abspath(path)
diff --git a/vidgear/tests/videocapture_tests/test_camgear.py b/vidgear/tests/videocapture_tests/test_camgear.py
index 21ffb5497..30de1a98a 100644
--- a/vidgear/tests/videocapture_tests/test_camgear.py
+++ b/vidgear/tests/videocapture_tests/test_camgear.py
@@ -25,6 +25,7 @@
import youtube_dl
import cv2
+import platform
import os, time
import pytest
import numpy as np
@@ -99,7 +100,7 @@ def test_youtube_playback():
"""
Testing Youtube Video Playback capabilities of VidGear
"""
- if os.name != 'nt':
+ if not platform.system() in ['Windows', 'Darwin']:
Url = 'https://youtu.be/YqeW9_5kURI'
result = True
errored = False #keep watch if youtube streaming not successful
@@ -129,7 +130,7 @@ def test_youtube_playback():
print('YouTube playback Test is skipped due to above error!')
else:
- print('YouTube playback Test is skipped due to bug with Appveyor on Windows builds!')
+ print('YouTube playback Test is skipped due to bug with opencv-python library builds on windows and macOS!')
diff --git a/vidgear/tests/writer_tests/test_compression_mode.py b/vidgear/tests/writer_tests/test_compression_mode.py
index 65bd33d51..0a6047b73 100644
--- a/vidgear/tests/writer_tests/test_compression_mode.py
+++ b/vidgear/tests/writer_tests/test_compression_mode.py
@@ -31,7 +31,7 @@
import pytest
import cv2
import tempfile
-import os
+import os, platform
import subprocess, re
@@ -41,8 +41,10 @@ def return_static_ffmpeg():
return FFmpeg static path
"""
path = ''
- if os.name == 'nt':
+ if platform.system() == 'Windows':
path += os.path.join(os.environ['USERPROFILE'],'Downloads/FFmpeg_static/ffmpeg/bin/ffmpeg.exe')
+ elif platform.system() == 'Darwin':
+ path += os.path.join(os.environ['HOME'],'Downloads/FFmpeg_static/ffmpeg/bin/ffmpeg')
else:
path += os.path.join(os.environ['HOME'],'Downloads/FFmpeg_static/ffmpeg/ffmpeg')
return os.path.abspath(path)
diff --git a/vidgear/tests/writer_tests/test_non_compression_mode.py b/vidgear/tests/writer_tests/test_non_compression_mode.py
index 47c529ffb..2853032bb 100644
--- a/vidgear/tests/writer_tests/test_non_compression_mode.py
+++ b/vidgear/tests/writer_tests/test_non_compression_mode.py
@@ -28,7 +28,7 @@
from vidgear.gears.helper import check_output
from six import string_types
-import os
+import os, platform
import pytest
import cv2
import tempfile
@@ -40,8 +40,10 @@ def return_static_ffmpeg():
return FFmpeg static path
"""
path = ''
- if os.name == 'nt':
+ if platform.system() == 'Windows':
path += os.path.join(os.environ['USERPROFILE'],'Downloads/FFmpeg_static/ffmpeg/bin/ffmpeg.exe')
+ elif platform.system() == 'Darwin':
+ path += os.path.join(os.environ['HOME'],'Downloads/FFmpeg_static/ffmpeg/bin/ffmpeg')
else:
path += os.path.join(os.environ['HOME'],'Downloads/FFmpeg_static/ffmpeg/ffmpeg')
return os.path.abspath(path)
From 5d175e6f2e76946cdfa6d275e7498c2acf890156 Mon Sep 17 00:00:00 2001
From: Abhishek
Date: Thu, 1 Aug 2019 21:30:35 +0530
Subject: [PATCH 03/39] Enhancement: Introducing Multi-Server Compatibility and
`PUB/SUB` messaging pattern support in NetGear
- Implemented Robust Multi-Server Compatibility support in NetGear.
- Implemented new Publish/Subscribe (`zmq.PUB/zmq.SUB`) patterns for seamless Live Streaming.
- Upgraded NetGear ability to handle any number of servers.
- Optimized overall real-time performance of NetGear.
- Updated ScreenGear to use Threaded Queue Mode by default.
- Removed redundant `THREADED_QUEUE_MODE` flag support from ScreenGear.
- Fixed various bugs related to these implementations.
---
changelog.md | 25 ++++
setup.py | 4 +-
vidgear/gears/helper.py | 2 +-
vidgear/gears/netgear.py | 225 ++++++++++++++++++++++++------------
vidgear/gears/screengear.py | 36 +++---
vidgear/version.py | 2 +-
6 files changed, 199 insertions(+), 95 deletions(-)
diff --git a/changelog.md b/changelog.md
index c7bfe5b5a..64eb14df8 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,30 @@
# CHANGELOG
+## VidGear 0.1.6-dev
+
+### New Features:
+ * Added VidGear's official native support for MacOS environment.
+
+### Updates/Improvements:
+ * Updated Travis CLI Tests with support for macOS environment
+ * Reformatted & implemented necessary changes and dependencies in `travis.yml`.
+
+### Breaking Updates / Improvements / Changes
+ * `Python 2.7` legacy support removed from CLI tests.
+
+### Fixes
+ * Fixed unreliable dataset video file URL(rehosted file on github.com)
+ * Remove duplicate code to import MSS(@BoboTiG) from NetGear
+ * Fixed various macOS environment bugs
+
+### Pull requests(PR) involved:
+ * PR #39
+ * PR #42
+
+:warning: PyPi Release does NOT contain Tests and Scripts!
+
+
+
## VidGear v0.1.5
### New Features:
diff --git a/setup.py b/setup.py
index f23bb497a..40d1b9a97 100644
--- a/setup.py
+++ b/setup.py
@@ -53,7 +53,7 @@ def test_opencv():
setup(
name='vidgear',
packages=['vidgear','vidgear.gears'],
- version='0.1.5',
+ version='0.1.6-dev',
description='Powerful python Video Processing library built with Multi-Threaded Gears(a.k.a APIs) each with a unique set of trailblazing features.',
license='MIT License',
author='abhiTronix',
@@ -64,7 +64,7 @@ def test_opencv():
long_description_content_type="text/markdown",
author_email='abhi.una12@gmail.com',
url='https://github.com/abhiTronix/vidgear',
- download_url='https://github.com/abhiTronix/vidgear/tarball/0.1.5',
+ download_url='https://github.com/abhiTronix/vidgear/tarball/0.1.6-dev',
keywords=['opencv', 'multithreading', 'FFmpeg', 'picamera', 'mss', 'pyzmq', 'pafy', 'Video Processing', 'Video Stablization', 'Computer Vision'],
classifiers=[
'Development Status :: 5 - Production/Stable',
diff --git a/vidgear/gears/helper.py b/vidgear/gears/helper.py
index 39a9e0ab0..17cfbc28b 100644
--- a/vidgear/gears/helper.py
+++ b/vidgear/gears/helper.py
@@ -28,6 +28,7 @@
# import the neccesary packages
import os, sys
import cv2
+import numpy as np
from pkg_resources import parse_version
@@ -70,7 +71,6 @@ def dict2Args(param_dict):
return args
-
def get_valid_ffmpeg_path(custom_ffmpeg = '', is_windows = False, ffmpeg_download_path = '', logging = False):
"""
Validate the FFmpeg path/binaries and returns valid FFmpeg file executable location(also downloads static binaries on windows)
diff --git a/vidgear/gears/netgear.py b/vidgear/gears/netgear.py
index c4367330a..424315e2d 100644
--- a/vidgear/gears/netgear.py
+++ b/vidgear/gears/netgear.py
@@ -28,7 +28,7 @@
from pkg_resources import parse_version
import numpy as np
import time
-
+import random
try:
@@ -112,14 +112,14 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
#log and enable threaded queue mode
if logging:
- print('Threaded Mode is enabled by default for NetGear.')
+ print('[LOG]: Threaded Mode is enabled by default for NetGear.')
#import deque
from collections import deque
#define deque and assign it to global var
self.queue = deque(maxlen=96) #max len 96 to check overflow
- #define valid messaging pattern `0`: zmq.PAIR and `1`:(zmq.REQ,zmq.REP)
- valid_messaging_patterns = {0:(zmq.PAIR,zmq.PAIR), 1:(zmq.REQ,zmq.REP)}
+ #define valid messaging pattern `0`: zmq.PAIR, `1`:(zmq.REQ,zmq.REP), and `1`:(zmq.SUB,zmq.PUB)
+ valid_messaging_patterns = {0:(zmq.PAIR,zmq.PAIR), 1:(zmq.REQ,zmq.REP), 2:(zmq.PUB,zmq.SUB)}
# initialize messaging pattern
msg_pattern = None
@@ -134,28 +134,38 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
msg_pattern = valid_messaging_patterns[self.pattern]
if logging:
#log it
- print('Wrong pattern, Defaulting to `zmq.PAIR`! Kindly refer Docs for more Information.')
+ print('[LOG]: Wrong pattern, Defaulting to `zmq.PAIR`! Kindly refer Docs for more Information.')
#check whether user-defined messaging protocol is valid
- if not (protocol is None) and protocol in ['tcp', 'upd', 'pgm', 'inproc', 'ipc']:
+ if protocol in ['tcp', 'upd', 'pgm', 'inproc', 'ipc']:
pass
else:
# else default to `tcp` protocol
protocol = 'tcp'
if logging:
#log it
- print('Protocol is not valid or provided. Defaulting to `tcp` protocol! Kindly refer Docs for more Information.')
+ print('[LOG]: Protocol is not valid or provided. Defaulting to `tcp` protocol! Kindly refer Docs for more Information.')
+ #generate random device id
+ self.id = ''.join(random.choice('0123456789ABCDEF') for i in range(5))
self.msg_flag = 0 #handles connection flags
self.msg_copy = False #handles whether to copy data
self.msg_track = False #handles whether to track packets
+
+ self.multiserver_mode = False
+ recv_filter = ''
+
try:
#reformat dict
- options = {k.strip(): v for k,v in options.items()}
+ options = {k.lower().strip(): v for k,v in options.items()}
# apply attributes to source if specified and valid
for key, value in options.items():
- if key == 'flag' and isinstance(value, str):
+ if key == 'multiserver_mode' and isinstance(value, bool) and self.pattern == 2:
+ self.multiserver_mode = value
+ elif key == 'filter' and isinstance(value, str):
+ recv_filter = value
+ elif key == 'flag' and isinstance(value, str):
self.msg_flag = getattr(zmq, value)
elif key == 'copy' and isinstance(value, bool):
self.msg_copy = value
@@ -166,9 +176,8 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
except Exception as e:
# Catch if any error occurred
if logging:
- print(e)
-
-
+ print('[Exception]: '+ e)
+
# enable logging if specified
self.logging = logging
@@ -186,28 +195,40 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
#check whether `receive_mode` is enabled by user
if receive_mode:
+
# if does than define connection address and port
if address is None: #define address
- address = '*'
- if port is None: #define port
+ address = 'localhost' if self.multiserver_mode else '*'
+
+ if self.multiserver_mode:
+ if port is None or not isinstance(port, (tuple, list)):
+ raise ValueError('Incorrect port value! Kindly provide a list/tuple of ports at Receiver-end while Multi-Server mode is enabled. For more information refer VidGear docs.')
+ else:
+ print('[LOG]: Enabling Multi-Server Mode at PORTS: {}!'.format(port))
+ self.port_buffer = []
+ else:
port = '5555'
- if logging:
- import random
- #generate and log random device id for server-side debugging
- self.id = ''.join(random.choice('0123456789ABCDEF') for i in range(5))
- print('This device ID is {}.'.format(self.id))
-
try:
# initialize and define thread-safe messaging socket
self.msg_socket = self.msg_context.socket(msg_pattern[1])
- # bind socket to given protocol, address and port
- self.msg_socket.bind(protocol+'://' + str(address) + ':' + str(port))
- # define socket receive timeout
- self.msg_socket.setsockopt(zmq.LINGER, 0)
- if logging:
- #log it on success
- print('Successfully Binded to address: {}.'.format(protocol+'://' + str(address) + ':' + str(port)))
+
+ if self.multiserver_mode:
+ for pt in port:
+ # connect socket to given protocol, address and port
+ self.msg_socket.connect(protocol+'://' + str(address) + ':' + str(pt))
+ self.msg_socket.setsockopt(zmq.LINGER, 0)
+ # define socket options
+ self.msg_socket.setsockopt_string(zmq.SUBSCRIBE, recv_filter)
+ else:
+ # bind socket to given protocol, address and port
+ self.msg_socket.bind(protocol+'://' + str(address) + ':' + str(port))
+ # define socket options
+ if self.pattern == 2:
+ self.msg_socket.setsockopt_string(zmq.SUBSCRIBE,'')
+ else:
+ self.msg_socket.setsockopt(zmq.LINGER, 0)
+
except Exception as e:
# otherwise raise value error
raise ValueError('Failed to bind address: {} and pattern: {}! Kindly recheck all parameters.'.format((protocol+'://' + str(address) + ':' + str(port)), pattern))
@@ -216,39 +237,57 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
self.thread = Thread(target=self.update, args=())
self.thread.daemon = True
self.thread.start()
+
if logging:
- #log it
- print('Multi-threaded Receive Mode is enabled Successfully!')
+ #log it
+ print('[LOG]: Successfully Binded to address: {}.'.format(protocol+'://' + str(address) + ':' + str(port)))
+ print('[LOG]: Multi-threaded Receive Mode is enabled Successfully!')
+ print('[LOG]: This device Unique ID is {}.'.format(self.id))
+ print('[LOG]: Receive Mode is activated successfully!')
else:
+
#otherwise default to `Send Mode
- # if does than define connection address and port`
if address is None: #define address
- address = 'localhost'
- if port is None: #define port
- port = '5555'
+ address = '*' if self.multiserver_mode else 'localhost'
+
+ if self.multiserver_mode:
+ if port is None:
+ raise ValueError('Incorrect port value! Kindly provide a unique & valid port value at Server-end while Multi-Server mode is enabled. For more information refer VidGear docs.')
+ else:
+ print('[LOG]: Enabling Multi-Server Mode at PORT: {} on this device!'.format(port))
+ self.port = port
+ else:
+ port = 5555 #define port
+
try:
# initialize and define thread-safe messaging socket
self.msg_socket = self.msg_context.socket(msg_pattern[0])
- if self.pattern == 1:
- # if pattern is 1, define additional flags
- self.msg_socket.REQ_RELAXED = True
- self.msg_socket.REQ_CORRELATE = True
- # bind socket to given protocol, address and port
- self.msg_socket.connect(protocol+'://' + str(address) + ':' + str(port))
- # define socket receive timeout
- self.msg_socket.setsockopt(zmq.LINGER, 0)
- if logging:
- #log it
- print('Successfully connected to address: {}.'.format(protocol+'://' + str(address) + ':' + str(port)))
+
+ if self.multiserver_mode:
+ # connect socket to given protocol, address and port
+ self.msg_socket.bind(protocol+'://' + str(address) + ':' + str(port))
+ else:
+ if self.pattern == 1:
+ # if pattern is 1, define additional flags
+ self.msg_socket.REQ_RELAXED = True
+ self.msg_socket.REQ_CORRELATE = True
+
+ # connect socket to given protocol, address and port
+ self.msg_socket.connect(protocol+'://' + str(address) + ':' + str(port))
+
+ # define socket options
+ self.msg_socket.setsockopt(zmq.LINGER, 0)
+
except Exception as e:
# otherwise raise value error
raise ValueError('Failed to connect address: {} and pattern: {}! Kindly recheck all parameters.'.format((protocol+'://' + str(address) + ':' + str(port)), pattern))
if logging:
#log it
- print('Send Mode is successfully activated and ready to send data!')
-
+ print('[LOG]: Successfully connected to address: {}.'.format(protocol+'://' + str(address) + ':' + str(port)))
+ print('[LOG]: This device Unique ID is {}.'.format(self.id))
+ print('[LOG]: Send Mode is successfully activated and ready to send data!')
def update(self):
@@ -259,6 +298,7 @@ def update(self):
frame = None
# keep looping infinitely until the thread is terminated
while not self.exit_loop:
+
# check if global termination_flag is enabled
if self.terminate:
# check whether there is still frames in queue before breaking out
@@ -274,21 +314,40 @@ def update(self):
#stop iterating if overflowing occurs
time.sleep(0.000001)
continue
-
# extract json data out of socket
msg_json = self.msg_socket.recv_json(flags=self.msg_flag)
+
# check if terminate_flag` is enabled in json
if msg_json['terminate_flag']:
- if self.pattern == 1:
- # if pattern is 1, then send back server the info about termination
- self.msg_socket.send_string('Termination received on port: {} !'.format(self.id))
- #assign values to global termination flag
- self.terminate = msg_json['terminate_flag']
+ if self.multiserver_mode:
+ self.port_buffer.remove(msg_json['port'])
+ if not self.port_buffer:
+ print('Termination signal received from all Servers!!!')
+ self.terminate = True
+ continue
+ else:
+ if self.pattern == 1:
+ # if pattern is 1, then send back server the info about termination
+ self.msg_socket.send_string('Termination signal received from server!')
+ #assign values to global termination flag
+ self.terminate = msg_json['terminate_flag']
+ continue
+
+ try:
+ assert int(msg_json['pattern']) == self.pattern
+ except (AssertionError) as e:
+ raise ValueError("Messaging pattern on the Server-end & Client-end must a valid pairs! Kindly refer VidGear docs.")
+ self.terminate = True
continue
+
+
# extract array from socket
msg_data = self.msg_socket.recv(flags=self.msg_flag, copy=self.msg_copy, track=self.msg_track)
- # send confirmation message to server for debugging
- self.msg_socket.send_string('Data received on port: {} !'.format(self.id))
+
+ if self.pattern != 2:
+ # send confirmation message to server for debugging
+ self.msg_socket.send_string('Data received on device: {} !'.format(self.id))
+
# recover frame from array buffer
frame_buffer = np.frombuffer(msg_data, dtype=msg_json['dtype'])
# reshape frame
@@ -297,9 +356,14 @@ def update(self):
if msg_json['message']:
print(msg_json['message'])
- # append recovered frame to queue
- self.queue.append(frame)
-
+ if self.multiserver_mode:
+ if not msg_json['port'] in self.port_buffer:
+ self.port_buffer.append(msg_json['port'])
+ # append recovered unique port and frame to queue
+ self.queue.append((msg_json['port'],frame))
+ else:
+ # append recovered frame to queue
+ self.queue.append(frame)
# finally properly close the socket
self.msg_socket.close()
@@ -353,22 +417,36 @@ def send(self, frame, message = None):
else:
#otherwise make it contiguous
frame = np.ascontiguousarray(frame, dtype=frame.dtype)
- # prepare the json dict and assign values
- msg_dict = dict(terminate_flag = exit_flag,
- message = message if not(message is None) else '',
- dtype = str(frame.dtype),
- shape = frame.shape)
+
+ if self.multiserver_mode:
+ # prepare the json dict and assign values with unique port
+ msg_dict = dict(terminate_flag = exit_flag,
+ port = self.port,
+ pattern = str(self.pattern),
+ message = message if not(message is None) else '',
+ dtype = str(frame.dtype),
+ shape = frame.shape)
+ else:
+ # prepare the json dict and assign values
+ msg_dict = dict(terminate_flag = exit_flag,
+ message = message if not(message is None) else '',
+ pattern = self.pattern,
+ dtype = str(frame.dtype),
+ shape = frame.shape)
+
# send the json dict
self.msg_socket.send_json(msg_dict, self.msg_flag|self.zmq.SNDMORE)
# send the frame array with correct flags
self.msg_socket.send(frame, flags = self.msg_flag, copy=self.msg_copy, track=self.msg_track)
# wait for confirmation
- if self.logging:
- # log confirmation
- print(self.msg_socket.recv())
- else:
- # otherwise be quiet
- self.msg_socket.recv()
+
+ if self.pattern != 2:
+ if self.logging:
+ # log confirmation
+ print(self.msg_socket.recv())
+ else:
+ # otherwise be quiet
+ self.msg_socket.recv()
@@ -378,7 +456,7 @@ def close(self):
"""
if self.logging:
#log it
- print('\n Terminating various {} Processes \n'.format('Receive Mode' if self.receive_mode else 'Send Mode'))
+ print(' \n[LOG]: Terminating various {} Processes \n'.format('Receive Mode' if self.receive_mode else 'Send Mode'))
# whether `receive_mode` is enabled or not
if self.receive_mode:
# indicate that process should be terminated
@@ -391,12 +469,17 @@ def close(self):
# wait until stream resources are released (producer thread might be still grabbing frame)
if self.thread is not None:
self.thread.join()
+ self.thread = None
#properly handle thread exit
else:
# otherwise indicate that the thread should be terminated
self.terminate = True
- # send termination flag to client
- term_dict = dict(terminate_flag = True)
+ if self.multiserver_mode:
+ # send termination flag to client
+ term_dict = dict(terminate_flag = True, port = self.port)
+ else:
+ # send termination flag to client
+ term_dict = dict(terminate_flag = True)
self.msg_socket.send_json(term_dict)
# properly close the socket
self.msg_socket.close()
\ No newline at end of file
diff --git a/vidgear/gears/screengear.py b/vidgear/gears/screengear.py
index d03dbc7ad..c9a23e54c 100644
--- a/vidgear/gears/screengear.py
+++ b/vidgear/gears/screengear.py
@@ -74,8 +74,10 @@ class ScreenGear:
"""
def __init__(self, monitor = 1, colorspace = None, logging = False, **options):
- #intialize threaded queue mode
+
+ #intialize threaded queue mode by default
self.threaded_queue_mode = True
+
try:
# import mss factory
from mss import mss
@@ -92,26 +94,18 @@ def __init__(self, monitor = 1, colorspace = None, logging = False, **options):
monitor_instance = self.mss_object.monitors[monitor]
else:
raise ValueError("`monitor` value cannot be negative, Read Docs!")
- # Initiate User-Defined Threaded Queue Mode
- if options:
- if "THREADED_QUEUE_MODE" in options:
- if isinstance(options["THREADED_QUEUE_MODE"],bool):
- self.threaded_queue_mode = options["THREADED_QUEUE_MODE"] #assigsn special parameter to global variable
- del options["THREADED_QUEUE_MODE"] #clean
- #reformat option dict
+
+ # Initialize Queue
self.queue = None
- #intialize deque
- if self.threaded_queue_mode:
- #import deque
- from collections import deque
- #define deque and assign it to global var
- self.queue = deque(maxlen=96) #max len 96 to check overflow
- #log it
- if logging:
- print('Enabling Threaded Queue Mode!')
- else:
- #otherwise disable it
- self.threaded_queue_mode = False
+
+ #import deque
+ from collections import deque
+ #define deque and assign it to global var
+ self.queue = deque(maxlen=96) #max len 96 to check overflow
+ #log it
+ if logging:
+ print('Enabling Threaded Queue Mode by default for ScreenGear!')
+
#intiate screen dimension handler
screen_dims = {}
#initializing colorspace variable
@@ -126,6 +120,7 @@ def __init__(self, monitor = 1, colorspace = None, logging = False, **options):
# Catch if any error occurred
if logging:
print(e)
+
# intialize mss capture instance
self.mss_capture_instance = None
try:
@@ -144,6 +139,7 @@ def __init__(self, monitor = 1, colorspace = None, logging = False, **options):
raise ValueError("ScreenShotError caught: Wrong dimensions passed to python-mss, Kindly Refer Docs!")
if logging:
print(self.mss_object.get_error_details())
+
# enable logging if specified
self.logging = logging
# thread initialization
diff --git a/vidgear/version.py b/vidgear/version.py
index 9f090905f..35bf2e99a 100644
--- a/vidgear/version.py
+++ b/vidgear/version.py
@@ -1 +1 @@
-__version__ = '0.15.0'
\ No newline at end of file
+__version__ = '0.1.6-dev'
\ No newline at end of file
From 225e040af6cb6aa5a21e378cfb83b946ec512e0d Mon Sep 17 00:00:00 2001
From: Abhishek
Date: Sun, 4 Aug 2019 13:15:05 +0530
Subject: [PATCH 04/39] Code Documentation Updates - Updated Netgear Docs with
new changes - Updated each API Logging format
---
vidgear/gears/camgear.py | 12 +-
vidgear/gears/helper.py | 16 +--
vidgear/gears/netgear.py | 104 +++++++++++-------
vidgear/gears/pigear.py | 6 +-
vidgear/gears/screengear.py | 6 +-
vidgear/gears/stabilizer.py | 2 +-
vidgear/gears/videogear.py | 2 +-
vidgear/gears/writegear.py | 18 +--
vidgear/tests/test_helper.py | 4 +-
.../tests/videocapture_tests/test_camgear.py | 10 +-
.../writer_tests/test_compression_mode.py | 2 +-
.../writer_tests/test_non_compression_mode.py | 2 +-
12 files changed, 104 insertions(+), 80 deletions(-)
diff --git a/vidgear/gears/camgear.py b/vidgear/gears/camgear.py
index 5a1c4c3a0..6473fabc9 100644
--- a/vidgear/gears/camgear.py
+++ b/vidgear/gears/camgear.py
@@ -133,9 +133,9 @@ def __init__(self, source = 0, y_tube = False, backend = 0, colorspace = None, l
if _source is None:
_source = source_object.getbest("any", ftypestrict=False)
if logging:
- print('URL: {}'.format(url))
- print('Title: {}'.format(source_object.title))
- print('Extension: {}'.format(_source.extension))
+ print('[LOG]: URL: {}'.format(url))
+ print('[LOG]: Title: {}'.format(source_object.title))
+ print('[LOG]: Extension: {}'.format(_source.extension))
source = _source.url
except Exception as e:
if logging:
@@ -162,7 +162,7 @@ def __init__(self, source = 0, y_tube = False, backend = 0, colorspace = None, l
self.queue = deque(maxlen=96) #max len 96 to check overflow
#log it
if logging:
- print('Enabling Threaded Queue Mode for the current video source!')
+ print('[LOG]: Enabling Threaded Queue Mode for the current video source!')
else:
#otherwise disable it
self.threaded_queue_mode = False
@@ -289,13 +289,13 @@ def update(self):
else:
self.color_space = None
if self.logging:
- print('Colorspace value {} is not a valid Colorspace!'.format(self.color_space))
+ print('[LOG]: Colorspace value {} is not a valid Colorspace!'.format(self.color_space))
except Exception as e:
# Catch if any error occurred
self.color_space = None
if self.logging:
print(e)
- print('Input Colorspace is not a valid Colorspace!')
+ print('[LOG]: Input Colorspace is not a valid Colorspace!')
if not(color_frame is None):
self.frame = color_frame
diff --git a/vidgear/gears/helper.py b/vidgear/gears/helper.py
index 17cfbc28b..21604d534 100644
--- a/vidgear/gears/helper.py
+++ b/vidgear/gears/helper.py
@@ -93,7 +93,7 @@ def get_valid_ffmpeg_path(custom_ffmpeg = '', is_windows = False, ffmpeg_downloa
ffmpeg_download_path = tempfile.gettempdir()
if logging:
- print('FFmpeg Windows Download Path: {}'.format(ffmpeg_download_path))
+ print('[LOG]: FFmpeg Windows Download Path: {}'.format(ffmpeg_download_path))
#download Binaries
_path = download_ffmpeg_binaries(path = ffmpeg_download_path, os_windows = is_windows)
@@ -104,7 +104,7 @@ def get_valid_ffmpeg_path(custom_ffmpeg = '', is_windows = False, ffmpeg_downloa
#log if any error occured
if logging:
print(e)
- print('Error downloading FFmpeg binaries, Check your network and Try again!')
+ print('[LOG]: Error downloading FFmpeg binaries, Check your network and Try again!')
return False
if os.path.isfile(final_path):
#check if valid FFmpeg file exist
@@ -115,7 +115,7 @@ def get_valid_ffmpeg_path(custom_ffmpeg = '', is_windows = False, ffmpeg_downloa
else:
#else return False
if logging:
- print('No valid FFmpeg executables found at Custom FFmpeg path!')
+ print('[LOG]: No valid FFmpeg executables found at Custom FFmpeg path!')
return False
else:
#otherwise perform test for Unix
@@ -130,14 +130,14 @@ def get_valid_ffmpeg_path(custom_ffmpeg = '', is_windows = False, ffmpeg_downloa
else:
#else return False
if logging:
- print('No valid FFmpeg executables found at Custom FFmpeg path!')
+ print('[LOG]: No valid FFmpeg executables found at Custom FFmpeg path!')
return False
else:
#otherwise assign ffmpeg binaries from system
final_path += "ffmpeg"
if logging:
- print('Final FFmpeg Path: {}'.format(final_path))
+ print('[LOG]: Final FFmpeg Path: {}'.format(final_path))
# Final Auto-Validation for FFmeg Binaries. returns final path if test is passed
if validate_ffmpeg(final_path, logging = logging):
@@ -210,13 +210,13 @@ def validate_ffmpeg(path, logging = False):
version = firstline.split(b' ')[2].strip()
if logging:
#log if test are passed
- print('FFmpeg validity Test Passed!')
- print('Found valid FFmpeg Version: `{}` installed on this system'.format(version))
+ print('[LOG]: FFmpeg validity Test Passed!')
+ print('[LOG]: Found valid FFmpeg Version: `{}` installed on this system'.format(version))
except Exception as e:
#log if test are failed
if logging:
print(e)
- print('FFmpeg validity Test Failed!')
+ print('[LOG]: FFmpeg validity Test Failed!')
return False
return True
diff --git a/vidgear/gears/netgear.py b/vidgear/gears/netgear.py
index 424315e2d..19e9212db 100644
--- a/vidgear/gears/netgear.py
+++ b/vidgear/gears/netgear.py
@@ -51,20 +51,23 @@ class NetGear:
This is achieved by implementing a high-level wrapper around PyZmQ python library that contains python bindings for ZeroMQ - a
high-performance asynchronous distributed messaging library that aim to be used in distributed or concurrent applications.
It provides a message queue, but unlike message-oriented middleware, a ZeroMQ system can run without a dedicated message broker.
- Furthermore, NetGear currently supports two ZeroMQ messaging patterns: i.e zmq.PAIR and zmq.REQ and zmq.REP and the supported
- protocol are: 'tcp', 'upd', 'pgm', 'inproc', 'ipc'.
+ Furthermore, NetGear currently supports three ZeroMQ messaging patterns: i.e zmq.PAIR, zmq.REQ and zmq.REP,and zmq.PUB,zmq.SUB whereas
+ supported protocol are: 'tcp', 'upd', 'pgm', 'inproc', 'ipc'.
- Threaded Queue Mode => Sequentially adds and releases frames to/from deque and handles overflow of this queue. It utilizes
- deques that support thread-safe, memory efficient appends and pops from either side of the deque with approximately the
- same O(1) performance in either direction.
+ Multi-Server Mode: This mode in NetGear API can robustly handle multiple servers at once through exclusive Publish/Subscribe (zmq.PUB/zmq.SUB)
+ messaging pattern for seamless Live Streaming across various device at the same time. Each device new server on network is
+ identied using its unique port address. Also, when all the connected servers on the network get disconnected, the client
+ itself automatically exits too. This mode can be activated through`multiserver_mode` option boolean attribute during
+ netgear initialization easily.
:param address(string): sets the valid network address of the server/client. Network addresses unique identifiers across the
network. Its default value of this parameter is based on mode it is working, 'localhost' for Send Mode
and `*` for Receive Mode.
- :param port(string): sets the valid network port of the server/client. A network port is a number that identifies one side
+ :param port(string/dict/list): sets the valid network port of the server/client. A network port is a number that identifies one side
of a connection between two devices on network. It is used determine to which process or application
- a message should be delivered. Its default value is `5555` .
+ a message should be delivered. In Multi-Server Mode a unique port number must required at each server, and a
+ list/tuple of port addresses of each connected server is required at clients end.
:param protocol(string): sets the valid messaging protocol between server and client. A network protocol is a set of established rules
that dictates how to format, transmit and receive data so computer network devices - from servers and
@@ -81,6 +84,9 @@ class NetGear:
interleaved or distributed to both the servers. socket zmq.REQ will block
on send unless it has successfully received a reply back and socket zmq.REP
will block on recv unless it has received a request.
+ 2. zmq.PUB,zmq.SUB -> Publish/Subscribe is another classic pattern where senders of messages, called publishers,
+ do not program the messages to be sent directly to specific receivers, called subscribers.
+ Messages are published without the knowledge of what or if any subscriber of that knowledge exists.
Its default value is `0`(i.e zmq.PAIR).
:param (boolean) receive_mode: set this flag to select the Netgear's Mode of operation. This basically activates `Receive Mode`(if True) and `Send Mode`(if False).
@@ -112,7 +118,7 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
#log and enable threaded queue mode
if logging:
- print('[LOG]: Threaded Mode is enabled by default for NetGear.')
+ print('[LOG]:Threaded Mode is enabled by default for NetGear.')
#import deque
from collections import deque
#define deque and assign it to global var
@@ -134,7 +140,7 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
msg_pattern = valid_messaging_patterns[self.pattern]
if logging:
#log it
- print('[LOG]: Wrong pattern, Defaulting to `zmq.PAIR`! Kindly refer Docs for more Information.')
+ print('[LOG]: Wrong pattern value, Defaulting to `zmq.PAIR`! Kindly refer Docs for more Information.')
#check whether user-defined messaging protocol is valid
if protocol in ['tcp', 'upd', 'pgm', 'inproc', 'ipc']:
@@ -144,7 +150,7 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
protocol = 'tcp'
if logging:
#log it
- print('[LOG]: Protocol is not valid or provided. Defaulting to `tcp` protocol! Kindly refer Docs for more Information.')
+ print('[LOG]:protocol is not valid or provided. Defaulting to `tcp` protocol!')
#generate random device id
self.id = ''.join(random.choice('0123456789ABCDEF') for i in range(5))
@@ -153,13 +159,13 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
self.msg_copy = False #handles whether to copy data
self.msg_track = False #handles whether to track packets
- self.multiserver_mode = False
- recv_filter = ''
+ self.multiserver_mode = False #handles multiserver_mode state
+ recv_filter = '' #user-defined filter to allow specific port/servers only in multiserver_mode
try:
#reformat dict
options = {k.lower().strip(): v for k,v in options.items()}
- # apply attributes to source if specified and valid
+ # assign values to global variables if specified and valid
for key, value in options.items():
if key == 'multiserver_mode' and isinstance(value, bool) and self.pattern == 2:
self.multiserver_mode = value
@@ -196,17 +202,23 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
#check whether `receive_mode` is enabled by user
if receive_mode:
- # if does than define connection address and port
+ # if does than define connection address
if address is None: #define address
address = 'localhost' if self.multiserver_mode else '*'
+ #check if multiserver_mode is enabled
if self.multiserver_mode:
+ # check if unique server port address list/tuple is assigned or not in multiserver_mode
if port is None or not isinstance(port, (tuple, list)):
+ # raise error if not
raise ValueError('Incorrect port value! Kindly provide a list/tuple of ports at Receiver-end while Multi-Server mode is enabled. For more information refer VidGear docs.')
else:
+ #otherwise log it
print('[LOG]: Enabling Multi-Server Mode at PORTS: {}!'.format(port))
+ #create port address buffer for keeping track of incoming server's port
self.port_buffer = []
else:
+ # otherwise assign local port address
port = '5555'
try:
@@ -214,23 +226,24 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
self.msg_socket = self.msg_context.socket(msg_pattern[1])
if self.multiserver_mode:
+ #if multiserver_mode is enabled assign port addresses to zmq socket
for pt in port:
- # connect socket to given protocol, address and port
+ # connect socket to given server protocol, address and ports
self.msg_socket.connect(protocol+'://' + str(address) + ':' + str(pt))
self.msg_socket.setsockopt(zmq.LINGER, 0)
# define socket options
self.msg_socket.setsockopt_string(zmq.SUBSCRIBE, recv_filter)
else:
- # bind socket to given protocol, address and port
+ # otherwise bind socket to given protocol, address and port normally
self.msg_socket.bind(protocol+'://' + str(address) + ':' + str(port))
- # define socket options
+ # define exclusive socket options for patterns
if self.pattern == 2:
self.msg_socket.setsockopt_string(zmq.SUBSCRIBE,'')
else:
self.msg_socket.setsockopt(zmq.LINGER, 0)
except Exception as e:
- # otherwise raise value error
+ # otherwise raise value error if errored
raise ValueError('Failed to bind address: {} and pattern: {}! Kindly recheck all parameters.'.format((protocol+'://' + str(address) + ':' + str(port)), pattern))
# initialize and start threading instance
@@ -239,33 +252,38 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
self.thread.start()
if logging:
- #log it
- print('[LOG]: Successfully Binded to address: {}.'.format(protocol+'://' + str(address) + ':' + str(port)))
- print('[LOG]: Multi-threaded Receive Mode is enabled Successfully!')
- print('[LOG]: This device Unique ID is {}.'.format(self.id))
- print('[LOG]: Receive Mode is activated successfully!')
+ #log progress
+ print('[LOG]: Successfully Binded to address: {}.'.format(protocol+'://' + str(address) + ':' + str(port)))
+ print('[LOG]: Multi-threaded Receive Mode is enabled Successfully!')
+ print('[LOG]: This device Unique ID is {}.'.format(self.id))
+ print('[LOG]: Receive Mode is activated successfully!')
else:
- #otherwise default to `Send Mode
+ #otherwise default to `Send Mode`
if address is None: #define address
address = '*' if self.multiserver_mode else 'localhost'
+ #check if multiserver_mode is enabled
if self.multiserver_mode:
+ # check if unique server port address is assigned or not in multiserver_mode
if port is None:
+ #raise error is not
raise ValueError('Incorrect port value! Kindly provide a unique & valid port value at Server-end while Multi-Server mode is enabled. For more information refer VidGear docs.')
else:
+ #otherwise log it
print('[LOG]: Enabling Multi-Server Mode at PORT: {} on this device!'.format(port))
+ #assign value to global variable
self.port = port
else:
- port = 5555 #define port
+ port = 5555 #define port normally
try:
# initialize and define thread-safe messaging socket
self.msg_socket = self.msg_context.socket(msg_pattern[0])
if self.multiserver_mode:
- # connect socket to given protocol, address and port
+ # connect socket to protocol, address and a unique port if multiserver_mode is activated
self.msg_socket.bind(protocol+'://' + str(address) + ':' + str(port))
else:
if self.pattern == 1:
@@ -284,10 +302,10 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
raise ValueError('Failed to connect address: {} and pattern: {}! Kindly recheck all parameters.'.format((protocol+'://' + str(address) + ':' + str(port)), pattern))
if logging:
- #log it
- print('[LOG]: Successfully connected to address: {}.'.format(protocol+'://' + str(address) + ':' + str(port)))
- print('[LOG]: This device Unique ID is {}.'.format(self.id))
- print('[LOG]: Send Mode is successfully activated and ready to send data!')
+ #log progress
+ print('[LOG]: Successfully connected to address: {}.'.format(protocol+'://' + str(address) + ':' + str(port)))
+ print('[LOG]: This device Unique ID is {}.'.format(self.id))
+ print('[LOG]: Send Mode is successfully activated and ready to send data!')
def update(self):
@@ -317,30 +335,34 @@ def update(self):
# extract json data out of socket
msg_json = self.msg_socket.recv_json(flags=self.msg_flag)
- # check if terminate_flag` is enabled in json
+ # check if terminate_flag` received
if msg_json['terminate_flag']:
+ #if multiserver_mode is enabled
if self.multiserver_mode:
+ # check from which ports signal is received
self.port_buffer.remove(msg_json['port'])
+ #if termination signal received from all servers then exit client.
if not self.port_buffer:
print('Termination signal received from all Servers!!!')
- self.terminate = True
+ self.terminate = True #termination
continue
else:
if self.pattern == 1:
# if pattern is 1, then send back server the info about termination
self.msg_socket.send_string('Termination signal received from server!')
- #assign values to global termination flag
+ #termination
self.terminate = msg_json['terminate_flag']
continue
try:
+ #check if pattern is same at both server's and client's end.
assert int(msg_json['pattern']) == self.pattern
- except (AssertionError) as e:
+ except AssertionError as e:
+ #otherwise raise error and exit
raise ValueError("Messaging pattern on the Server-end & Client-end must a valid pairs! Kindly refer VidGear docs.")
self.terminate = True
continue
-
# extract array from socket
msg_data = self.msg_socket.recv(flags=self.msg_flag, copy=self.msg_copy, track=self.msg_track)
@@ -418,8 +440,9 @@ def send(self, frame, message = None):
#otherwise make it contiguous
frame = np.ascontiguousarray(frame, dtype=frame.dtype)
+ #check if multiserver_mode is activated
if self.multiserver_mode:
- # prepare the json dict and assign values with unique port
+ # prepare the exclusive json dict and assign values with unique port
msg_dict = dict(terminate_flag = exit_flag,
port = self.port,
pattern = str(self.pattern),
@@ -427,7 +450,7 @@ def send(self, frame, message = None):
dtype = str(frame.dtype),
shape = frame.shape)
else:
- # prepare the json dict and assign values
+ # otherwise prepare normal json dict and assign values
msg_dict = dict(terminate_flag = exit_flag,
message = message if not(message is None) else '',
pattern = self.pattern,
@@ -472,13 +495,14 @@ def close(self):
self.thread = None
#properly handle thread exit
else:
- # otherwise indicate that the thread should be terminated
+ # otherwise in `send_mode`, inform client(s) that the termination is reached
self.terminate = True
+ #check if multiserver_mode
if self.multiserver_mode:
- # send termination flag to client
+ # send termination flag to client with its unique port
term_dict = dict(terminate_flag = True, port = self.port)
else:
- # send termination flag to client
+ # otherwise send termination flag to client
term_dict = dict(terminate_flag = True)
self.msg_socket.send_json(term_dict)
# properly close the socket
diff --git a/vidgear/gears/pigear.py b/vidgear/gears/pigear.py
index 393569253..694c5a268 100644
--- a/vidgear/gears/pigear.py
+++ b/vidgear/gears/pigear.py
@@ -160,7 +160,7 @@ def update(self):
# preparation for the next frame
if stream is None:
if self.logging:
- print('The Camera Module is not working Properly!')
+ print('[LOG]: The Camera Module is not working Properly!')
self.terminate = True
if self.terminate:
break
@@ -177,14 +177,14 @@ def update(self):
else:
self.color_space = None
if self.logging:
- print('Colorspace value {} is not a valid Colorspace!'.format(self.color_space))
+ print('[LOG]: Colorspace value {} is not a valid Colorspace!'.format(self.color_space))
except Exception as e:
# Catch if any error occurred
self.color_space = None
if self.logging:
print(e)
- print('Input Colorspace is not a valid Colorspace!')
+ print('[LOG]: Input Colorspace is not a valid Colorspace!')
if not(color_frame is None):
self.frame = color_frame
diff --git a/vidgear/gears/screengear.py b/vidgear/gears/screengear.py
index c9a23e54c..bbace59dc 100644
--- a/vidgear/gears/screengear.py
+++ b/vidgear/gears/screengear.py
@@ -104,7 +104,7 @@ def __init__(self, monitor = 1, colorspace = None, logging = False, **options):
self.queue = deque(maxlen=96) #max len 96 to check overflow
#log it
if logging:
- print('Enabling Threaded Queue Mode by default for ScreenGear!')
+ print('[LOG]: Enabling Threaded Queue Mode by default for ScreenGear!')
#intiate screen dimension handler
screen_dims = {}
@@ -202,13 +202,13 @@ def update(self):
else:
self.color_space = None
if self.logging:
- print('Colorspace value {} is not a valid Colorspace!'.format(self.color_space))
+ print('[LOG]: Colorspace value {} is not a valid Colorspace!'.format(self.color_space))
except Exception as e:
# Catch if any error occurred
self.color_space = None
if self.logging:
print(e)
- print('Input Colorspace is not a valid Colorspace!')
+ print('[LOG]: Input Colorspace is not a valid Colorspace!')
if not(color_frame is None):
self.frame = color_frame
else:
diff --git a/vidgear/gears/stabilizer.py b/vidgear/gears/stabilizer.py
index ab17d39b4..f5a133f14 100644
--- a/vidgear/gears/stabilizer.py
+++ b/vidgear/gears/stabilizer.py
@@ -80,7 +80,7 @@ def __init__(self, smoothing_radius = 25, border_type = 'black', border_size = 0
else:
#otherwise log if not
if logging:
- print('Invalid input border type!')
+ print('[LOG]: Invalid input border type!')
self.border_mode = border_modes['black'] #reset to default mode
# define normalized box filter
diff --git a/vidgear/gears/videogear.py b/vidgear/gears/videogear.py
index 78bac357a..ddfb6469c 100644
--- a/vidgear/gears/videogear.py
+++ b/vidgear/gears/videogear.py
@@ -105,7 +105,7 @@ def __init__(self, enablePiCamera = False, stabilize = False, source=0, y_tube =
self.stabilizer_obj = Stabilizer(smoothing_radius = s_radius, border_type = border_type, border_size = border_size, logging = logging)
#log info
if logging:
- print('Enabling Stablization Mode for the current video source!')
+ print('[LOG]: Enabling Stablization Mode for the current video source!')
if enablePiCamera:
# only import the pigear module only if required
diff --git a/vidgear/gears/writegear.py b/vidgear/gears/writegear.py
index ff1bbfb37..4de8c3d05 100755
--- a/vidgear/gears/writegear.py
+++ b/vidgear/gears/writegear.py
@@ -148,7 +148,7 @@ def __init__(self, output_filename = '', compression_mode = True , custom_ffmpeg
if self.compression:
if self.logging:
- print('Compression Mode is enabled therefore checking for valid FFmpeg executables!')
+ print('[LOG]: Compression Mode is enabled therefore checking for valid FFmpeg executables!')
print(self.output_parameters)
# handles where to save the downloaded FFmpeg Static Binaries on Windows(if specified)
@@ -169,12 +169,12 @@ def __init__(self, output_filename = '', compression_mode = True , custom_ffmpeg
if actual_command:
self.ffmpeg += actual_command #assign it to class variable
if self.logging:
- print('Found valid FFmpeg executables: `{}`'.format(self.ffmpeg))
+ print('[LOG]: Found valid FFmpeg executables: `{}`'.format(self.ffmpeg))
else:
#otherwise disable Compression Mode
if self.logging and not self.os_windows:
- print('Kindly install working FFmpeg or provide a valid custom FFmpeg Path')
- print('Caution: Disabling Video Compression Mode since no valid FFmpeg executables found on this machine!')
+ print('[LOG]: Kindly install working FFmpeg or provide a valid custom FFmpeg Path')
+ print('[LOG]: Caution: Disabling Video Compression Mode since no valid FFmpeg executables found on this machine!')
self.compression = False # compression mode disabled
#validate this class has the access rights to specified directory or not
@@ -184,10 +184,10 @@ def __init__(self, output_filename = '', compression_mode = True , custom_ffmpeg
if self.compression and self.ffmpeg:
self.DEVNULL = open(os.devnull, 'wb')
if self.logging:
- print('Compression Mode is configured properly!')
+ print('[LOG]: Compression Mode is configured properly!')
else:
if self.logging:
- print('Compression Mode is disabled, Activating OpenCV In-built Writer!')
+ print('[LOG]: Compression Mode is disabled, Activating OpenCV In-built Writer!')
@@ -214,7 +214,7 @@ def write(self, frame, rgb_mode = False):
self.inputwidth = width
self.inputchannels = channels
if self.logging:
- print('InputFrame => Height:{} Width:{} Channels:{}'.format(self.inputheight, self.inputwidth, self.inputchannels))
+ print('[LOG]: InputFrame => Height:{} Width:{} Channels:{}'.format(self.inputheight, self.inputwidth, self.inputchannels))
#validate size of frame
if height != self.inputheight or width != self.inputwidth:
@@ -250,7 +250,7 @@ def write(self, frame, rgb_mode = False):
assert self.process is not None
if self.logging:
# log OpenCV warning
- print('Warning: RGBA and 16-bit grayscale video frames are not supported by OpenCV yet, switch to `compression_mode` to use them!')
+ print('[WARNING]: RGBA and 16-bit grayscale video frames are not supported by OpenCV yet, switch to `compression_mode` to use them!')
#write the frame
self.process.write(frame)
@@ -388,7 +388,7 @@ def startCV_Process(self):
if self.logging:
#log values for debugging
- print('FILE_PATH: {}, FOURCC = {}, FPS = {}, WIDTH = {}, HEIGHT = {}, BACKEND = {}'.format(self.out_file,FOURCC, FPS, WIDTH, HEIGHT, BACKEND))
+ print('[LOG]: FILE_PATH: {}, FOURCC = {}, FPS = {}, WIDTH = {}, HEIGHT = {}, BACKEND = {}'.format(self.out_file,FOURCC, FPS, WIDTH, HEIGHT, BACKEND))
#start different process for with/without Backend.
if BACKEND:
diff --git a/vidgear/tests/test_helper.py b/vidgear/tests/test_helper.py
index c5071f5a9..decf1404b 100644
--- a/vidgear/tests/test_helper.py
+++ b/vidgear/tests/test_helper.py
@@ -53,10 +53,10 @@ def test_ffmpeg_static_installation():
for root, dirs, files in os.walk(startpath):
level = root.replace(startpath, '').count(os.sep)
indent = ' ' * 4 * (level)
- print('{}{}/'.format(indent, os.path.basename(root)))
+ print('[LOG]: {}{}/'.format(indent, os.path.basename(root)))
subindent = ' ' * 4 * (level + 1)
for f in files:
- print('{}{}'.format(subindent, f))
+ print('[LOG]: {}{}'.format(subindent, f))
diff --git a/vidgear/tests/videocapture_tests/test_camgear.py b/vidgear/tests/videocapture_tests/test_camgear.py
index 30de1a98a..3750ad0c0 100644
--- a/vidgear/tests/videocapture_tests/test_camgear.py
+++ b/vidgear/tests/videocapture_tests/test_camgear.py
@@ -118,8 +118,8 @@ def test_youtube_playback():
if height == 0 or width == 0:
fps = stream.framerate
height,width = frame.shape[:2]
- print('WIDTH: {} HEIGHT: {} FPS: {}'.format(true_video_param[0],true_video_param[1],true_video_param[2]))
- print('WIDTH: {} HEIGHT: {} FPS: {}'.format(width,height,fps))
+ print('[LOG]: WIDTH: {} HEIGHT: {} FPS: {}'.format(true_video_param[0],true_video_param[1],true_video_param[2]))
+ print('[LOG]: WIDTH: {} HEIGHT: {} FPS: {}'.format(width,height,fps))
except Exception as error:
print(error)
errored = True
@@ -127,10 +127,10 @@ def test_youtube_playback():
if not errored:
assert true_video_param[0] == width and true_video_param[1] == height and true_video_param[2] == fps
else:
- print('YouTube playback Test is skipped due to above error!')
+ print('[LOG]: YouTube playback Test is skipped due to above error!')
else:
- print('YouTube playback Test is skipped due to bug with opencv-python library builds on windows and macOS!')
+ print('[LOG]: YouTube playback Test is skipped due to bug with opencv-python library builds on windows and macOS!')
@@ -151,7 +151,7 @@ def test_network_playback():
Output_data.append(frame)
i+=1
output_stream.stop()
- print('Output data shape:', np.array(Output_data).shape)
+ print('[LOG]: Output data shape:', np.array(Output_data).shape)
except Exception as e:
pytest.fail(str(e))
diff --git a/vidgear/tests/writer_tests/test_compression_mode.py b/vidgear/tests/writer_tests/test_compression_mode.py
index 0a6047b73..b3cb22afb 100644
--- a/vidgear/tests/writer_tests/test_compression_mode.py
+++ b/vidgear/tests/writer_tests/test_compression_mode.py
@@ -120,7 +120,7 @@ def test_write(conversion):
if result:
if not isinstance(result, string_types):
result = result.decode()
- print('Result: {}'.format(result))
+ print('[LOG]: Result: {}'.format(result))
for i in ["Error", "Invalid", "error", "invalid"]:
assert not(i in result)
os.remove(os.path.abspath('Output_tw.mp4'))
diff --git a/vidgear/tests/writer_tests/test_non_compression_mode.py b/vidgear/tests/writer_tests/test_non_compression_mode.py
index 2853032bb..f777b0b83 100644
--- a/vidgear/tests/writer_tests/test_non_compression_mode.py
+++ b/vidgear/tests/writer_tests/test_non_compression_mode.py
@@ -86,7 +86,7 @@ def test_write(conversion):
if result:
if not isinstance(result, string_types):
result = result.decode()
- print('Result: {}'.format(result))
+ print('[LOG]: Result: {}'.format(result))
for i in ["Error", "Invalid", "error", "invalid"]:
assert not(i in result)
os.remove(os.path.abspath('Output_twc.avi'))
From 8f7153cbd822da9a669cd292dc46ae4cdb8e457d Mon Sep 17 00:00:00 2001
From: Abhishek
Date: Tue, 20 Aug 2019 16:06:29 +0530
Subject: [PATCH 05/39] Updates and Bug fixes for NetGear API - Bug Fix for
issue #45 - Added Feature to send data in mult-server mode #44
---
vidgear/gears/netgear.py | 24 +++++++++++++++---------
1 file changed, 15 insertions(+), 9 deletions(-)
diff --git a/vidgear/gears/netgear.py b/vidgear/gears/netgear.py
index 19e9212db..82e679095 100644
--- a/vidgear/gears/netgear.py
+++ b/vidgear/gears/netgear.py
@@ -171,8 +171,8 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
self.multiserver_mode = value
elif key == 'filter' and isinstance(value, str):
recv_filter = value
- elif key == 'flag' and isinstance(value, str):
- self.msg_flag = getattr(zmq, value)
+ elif key == 'flag' and isinstance(value, int):
+ self.msg_flag = value
elif key == 'copy' and isinstance(value, bool):
self.msg_copy = value
elif key == 'track' and isinstance(value, bool):
@@ -374,18 +374,24 @@ def update(self):
frame_buffer = np.frombuffer(msg_data, dtype=msg_json['dtype'])
# reshape frame
frame = frame_buffer.reshape(msg_json['shape'])
- #extract if any message from server and display it
- if msg_json['message']:
- print(msg_json['message'])
- if self.multiserver_mode:
+ if self.multiserver_mode:
+ # check if multiserver_mode
+
+ #save the unique port addresses
if not msg_json['port'] in self.port_buffer:
self.port_buffer.append(msg_json['port'])
- # append recovered unique port and frame to queue
- self.queue.append((msg_json['port'],frame))
+
+ #extract if any message from server and display it
+ if msg_json['message']:
+ self.queue.append((msg_json['port'], msg_json['message'], frame))
+ else:
+ # append recovered unique port and frame to queue
+ self.queue.append((msg_json['port'],frame))
else:
# append recovered frame to queue
self.queue.append(frame)
+
# finally properly close the socket
self.msg_socket.close()
@@ -453,7 +459,7 @@ def send(self, frame, message = None):
# otherwise prepare normal json dict and assign values
msg_dict = dict(terminate_flag = exit_flag,
message = message if not(message is None) else '',
- pattern = self.pattern,
+ pattern = str(self.pattern),
dtype = str(frame.dtype),
shape = frame.shape)
From 816a92bdd012a43bea800189dec36491c1fa0496 Mon Sep 17 00:00:00 2001
From: Abhishek
Date: Sun, 8 Sep 2019 22:29:29 +0530
Subject: [PATCH 06/39] [Enhancements] Introducing Smart & Secure ZMQ
Authentication for NetGear API: - added exclusive `secure_mode` for enabling
this feature - added support for powerful `Stonehouse` & `Ironhouse` ZMQ
security mechanisms. - added smart auth certificates/key generation and
validation - added additional dict params for enabling various tweaks. -
fixed bugs related to this implementation
---
vidgear/gears/helper.py | 105 ++++++++++++++++++++++++++++++++++++++-
vidgear/gears/netgear.py | 62 +++++++++++++++++++++--
2 files changed, 163 insertions(+), 4 deletions(-)
diff --git a/vidgear/gears/helper.py b/vidgear/gears/helper.py
index 21604d534..7070aba17 100644
--- a/vidgear/gears/helper.py
+++ b/vidgear/gears/helper.py
@@ -250,4 +250,107 @@ def check_output(*args, **kwargs):
error = sp.CalledProcessError(retcode, cmd)
error.output = output
raise error
- return output
\ No newline at end of file
+ return output
+
+
+def generate_auth_certificates(path, overwrite = False):
+ """ Generate client and server CURVE ZMQ certificate files """
+
+ import shutil, errno
+ import zmq.auth
+
+ if (os.path.basename(path) != ".vidgear"):
+ path = os.path.join(path,".vidgear")
+
+ keys_dir = os.path.join(path, 'keys')
+ try:
+ os.makedirs(keys_dir)
+ except OSError as e:
+ if e.errno != errno.EEXIST:
+ raise
+
+ public_keys_dir = os.path.join(keys_dir, 'public_keys')
+ secret_keys_dir = os.path.join(keys_dir, 'private_keys')
+
+ if overwrite:
+
+ for d in [public_keys_dir, secret_keys_dir]:
+ if os.path.exists(d):
+ shutil.rmtree(d)
+ os.mkdir(d)
+
+ # create new keys in certificates dir
+ server_public_file, server_secret_file = zmq.auth.create_certificates(keys_dir, "server")
+ client_public_file, client_secret_file = zmq.auth.create_certificates(keys_dir, "client")
+
+ # move public keys to appropriate directory
+ for key_file in os.listdir(keys_dir):
+ if key_file.endswith(".key"):
+ shutil.move(os.path.join(keys_dir, key_file), public_keys_dir)
+ elif key_file.endswith(".key_secret"):
+ shutil.move(os.path.join(keys_dir, key_file), public_keys_dir)
+ else:
+ os.remove(key_file)
+
+ else:
+
+ status_public_keys = validate_auth_keys(public_keys_dir, '.key')
+ status_private_keys = validate_auth_keys(secret_keys_dir, '.key_secret')
+
+ if status_private_keys and status_public_keys:
+ return keys_dir
+
+ if not(status_public_keys):
+ try:
+ os.makedirs(public_keys_dir)
+ except OSError as e:
+ if e.errno != errno.EEXIST:
+ raise
+
+ if not(status_private_keys):
+ try:
+ os.makedirs(secret_keys_dir)
+ except OSError as e:
+ if e.errno != errno.EEXIST:
+ raise
+
+ # create new keys in certificates dir
+ server_public_file, server_secret_file = zmq.auth.create_certificates(keys_dir, "server")
+ client_public_file, client_secret_file = zmq.auth.create_certificates(keys_dir, "client")
+
+
+ # move public keys to appropriate directory
+ for key_file in os.listdir(keys_dir):
+ if key_file.endswith(".key") and not(status_public_keys):
+ shutil.move(os.path.join(keys_dir, key_file), os.path.join(public_keys_dir, '.'))
+ elif key_file.endswith(".key_secret") and not(status_private_keys):
+ shutil.move(os.path.join(keys_dir, key_file), os.path.join(secret_keys_dir, '.'))
+ else:
+ redundant_key = os.path.join(keys_dir,key_file)
+ if os.path.isfile(redundant_key):
+ os.remove(redundant_key)
+
+ status_public_keys = validate_auth_keys(public_keys_dir, '.key')
+ status_private_keys = validate_auth_keys(secret_keys_dir, '.key_secret')
+
+ if not(status_private_keys) or not(status_public_keys):
+ raise RuntimeError('[Error]: Unable to create ZMQ all authentication certificates at `{}`!'.format(keys_dir))
+
+ return keys_dir
+
+
+def validate_auth_keys(path, extension):
+
+ if not(os.path.exists(path)): return False
+ if not(os.listdir(path)): return False
+
+ keys_buffer = []
+
+ for key_file in os.listdir(path):
+ key = os.path.splitext(key_file)
+ if key and (key[0] in ['server','client']) and (key[1] == extension):
+ keys_buffer.append(key_file)
+
+ if(len(keys_buffer) == 1): os.remove(os.path.join(path,keys_buffer[0]))
+
+ return True if(len(keys_buffer) == 2) else False
\ No newline at end of file
diff --git a/vidgear/gears/netgear.py b/vidgear/gears/netgear.py
index 82e679095..3887b18a8 100644
--- a/vidgear/gears/netgear.py
+++ b/vidgear/gears/netgear.py
@@ -26,8 +26,11 @@
# import the necessary packages
from threading import Thread
from pkg_resources import parse_version
+from .helper import check_python_version
+from .helper import generate_auth_certificates
import numpy as np
import time
+import os
import random
@@ -118,7 +121,7 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
#log and enable threaded queue mode
if logging:
- print('[LOG]:Threaded Mode is enabled by default for NetGear.')
+ print('[LOG]: Threaded Mode is enabled by default for NetGear.')
#import deque
from collections import deque
#define deque and assign it to global var
@@ -150,7 +153,7 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
protocol = 'tcp'
if logging:
#log it
- print('[LOG]:protocol is not valid or provided. Defaulting to `tcp` protocol!')
+ print('[LOG]: protocol is not valid or provided. Defaulting to `tcp` protocol!')
#generate random device id
self.id = ''.join(random.choice('0123456789ABCDEF') for i in range(5))
@@ -161,16 +164,41 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
self.multiserver_mode = False #handles multiserver_mode state
recv_filter = '' #user-defined filter to allow specific port/servers only in multiserver_mode
+
+ valid_security_mech = {0:'Grasslands', 1:'Stonehouse', 2:'Ironhouse'}
+ self.secure_mode = 0
+ self.auth_cert_location = ''
+ overwrite_cert = False
+ custom_cert_location = ''
try:
#reformat dict
options = {k.lower().strip(): v for k,v in options.items()}
# assign values to global variables if specified and valid
for key, value in options.items():
+
if key == 'multiserver_mode' and isinstance(value, bool) and self.pattern == 2:
self.multiserver_mode = value
elif key == 'filter' and isinstance(value, str):
recv_filter = value
+
+ elif key == 'secure_mode' and isinstance(value,int) and (value in valid_security_mech):
+ try:
+ assert check_python_version() >= 3,"[ERROR]: ZMQ Security feature is not available with python version < 3.0."
+ assert zmq.zmq_version_info() >= (4,0), "[ERROR]: ZMQ Security feature is not supported in libzmq version < 4.0."
+ self.secure_mode = value
+ except AssertionError as e:
+ print(e)
+ elif key == 'custom_cert_location' and isinstance(value,str):
+ try:
+ assert os.access(value, os.W_OK), "[ERROR]: Permission Denied!, cannot write ZMQ authentication certificates to '{}' directory!".format(value)
+ assert not(os.path.isfile(value)), "[ERROR]: `custom_cert_location` value must be the path to a directory and not to a file!"
+ custom_cert_location = os.path.abspath(value)
+ except AssertionError as e:
+ print(e)
+ elif key == 'overwrite_cert' and isinstance(value,bool):
+ overwrite_cert = value
+
elif key == 'flag' and isinstance(value, int):
self.msg_flag = value
elif key == 'copy' and isinstance(value, bool):
@@ -182,7 +210,35 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
except Exception as e:
# Catch if any error occurred
if logging:
- print('[Exception]: '+ e)
+ print(e)
+
+
+ if self.secure_mode:
+
+ if logging and overwrite_cert: print('[WARNING]: Overwriting ZMQ Authentication certificates over previous ones!')
+
+ try:
+ if custom_cert_location:
+ if os.path.isdir(custom_cert_location):
+ self.auth_cert_location = generate_auth_certificates(custom_cert_location, overwrite = overwrite_cert)
+ else:
+ raise ValueError("[ERROR]: Invalid `custom_cert_location` value!")
+ else:
+ from os.path import expanduser
+ self.auth_cert_location = generate_auth_certificates(os.path.join(expanduser("~"),".vidgear"), overwrite = overwrite_cert)
+
+ if logging:
+ print('[LOG]: Successfully enabled ZMQ Security Mechanism: `{}` for this connection.'.format(valid_security_mech[self.secure_mode]))
+ print('[LOG]: `{}` is the default location for storing ZMQ authentication certificates/keys.'.format(self.auth_cert_location))
+
+ except Exception as e:
+ # Catch if any error occurred
+ print(e)
+ self.secure_mode = 0
+ print('[WARNING]: ZMQ Security Mechanism is disabled for this connection!')
+ else:
+ if logging: print('[LOG]: ZMQ Security Mechanism is disabled for this connection!')
+
# enable logging if specified
self.logging = logging
From 3d65344a303d9e2a52cdf691f8a43e7aec80dd76 Mon Sep 17 00:00:00 2001
From: Abhishek
Date: Mon, 9 Sep 2019 09:54:10 +0530
Subject: [PATCH 07/39] Hot fix: added support for screen casting from all
monitors
---
vidgear/gears/screengear.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/vidgear/gears/screengear.py b/vidgear/gears/screengear.py
index bbace59dc..7d4cbd9e0 100644
--- a/vidgear/gears/screengear.py
+++ b/vidgear/gears/screengear.py
@@ -90,7 +90,7 @@ def __init__(self, monitor = 1, colorspace = None, logging = False, **options):
self.mss_object = mss()
# create monitor instance for the user-defined monitor
monitor_instance = None
- if (monitor > 0):
+ if (monitor >= 0):
monitor_instance = self.mss_object.monitors[monitor]
else:
raise ValueError("`monitor` value cannot be negative, Read Docs!")
From c8451a123e0ad6147723f6f66934c02b21018274 Mon Sep 17 00:00:00 2001
From: Abhishek
Date: Mon, 9 Sep 2019 18:59:16 +0530
Subject: [PATCH 08/39] Bug Fixes and Enhancements: - implemented `IronHouse`
ZMQ security mechanism - intially tested & validated this security mechanism
for both Server & Client end. - added various related methods for different
modes and patterns. - fixed important bugs and typos
---
vidgear/gears/helper.py | 11 +++---
vidgear/gears/netgear.py | 84 ++++++++++++++++++++++++++++++++++------
2 files changed, 79 insertions(+), 16 deletions(-)
diff --git a/vidgear/gears/helper.py b/vidgear/gears/helper.py
index 7070aba17..0886d0e48 100644
--- a/vidgear/gears/helper.py
+++ b/vidgear/gears/helper.py
@@ -288,17 +288,18 @@ def generate_auth_certificates(path, overwrite = False):
if key_file.endswith(".key"):
shutil.move(os.path.join(keys_dir, key_file), public_keys_dir)
elif key_file.endswith(".key_secret"):
- shutil.move(os.path.join(keys_dir, key_file), public_keys_dir)
+ shutil.move(os.path.join(keys_dir, key_file), secret_keys_dir)
else:
- os.remove(key_file)
-
+ redundant_key = os.path.join(keys_dir,key_file)
+ if os.path.isfile(redundant_key):
+ os.remove(redundant_key)
else:
status_public_keys = validate_auth_keys(public_keys_dir, '.key')
status_private_keys = validate_auth_keys(secret_keys_dir, '.key_secret')
if status_private_keys and status_public_keys:
- return keys_dir
+ return (keys_dir, secret_keys_dir, public_keys_dir)
if not(status_public_keys):
try:
@@ -336,7 +337,7 @@ def generate_auth_certificates(path, overwrite = False):
if not(status_private_keys) or not(status_public_keys):
raise RuntimeError('[Error]: Unable to create ZMQ all authentication certificates at `{}`!'.format(keys_dir))
- return keys_dir
+ return (keys_dir, secret_keys_dir, public_keys_dir)
def validate_auth_keys(path, extension):
diff --git a/vidgear/gears/netgear.py b/vidgear/gears/netgear.py
index 3887b18a8..0ae76e3a0 100644
--- a/vidgear/gears/netgear.py
+++ b/vidgear/gears/netgear.py
@@ -121,7 +121,7 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
#log and enable threaded queue mode
if logging:
- print('[LOG]: Threaded Mode is enabled by default for NetGear.')
+ print('[LOG]: Threaded Queue Mode is enabled by default for NetGear.')
#import deque
from collections import deque
#define deque and assign it to global var
@@ -165,9 +165,11 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
self.multiserver_mode = False #handles multiserver_mode state
recv_filter = '' #user-defined filter to allow specific port/servers only in multiserver_mode
- valid_security_mech = {0:'Grasslands', 1:'Stonehouse', 2:'Ironhouse'}
+ valid_security_mech = {0:'Grasslands', 1:'StoneHouse', 2:'IronHouse'}
self.secure_mode = 0
- self.auth_cert_location = ''
+ auth_cert_dir = ''
+ self.auth_publickeys_dir = ''
+ self.auth_secretkeys_dir = ''
overwrite_cert = False
custom_cert_location = ''
@@ -215,21 +217,24 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
if self.secure_mode:
+ #import libraries
+ import zmq.auth
+ from zmq.auth.thread import ThreadAuthenticator
+
if logging and overwrite_cert: print('[WARNING]: Overwriting ZMQ Authentication certificates over previous ones!')
try:
if custom_cert_location:
if os.path.isdir(custom_cert_location):
- self.auth_cert_location = generate_auth_certificates(custom_cert_location, overwrite = overwrite_cert)
+ (auth_cert_dir, self.auth_secretkeys_dir, self.auth_publickeys_dir) = generate_auth_certificates(custom_cert_location, overwrite = overwrite_cert)
else:
raise ValueError("[ERROR]: Invalid `custom_cert_location` value!")
else:
from os.path import expanduser
- self.auth_cert_location = generate_auth_certificates(os.path.join(expanduser("~"),".vidgear"), overwrite = overwrite_cert)
+ (auth_cert_dir, self.auth_secretkeys_dir, self.auth_publickeys_dir) = generate_auth_certificates(os.path.join(expanduser("~"),".vidgear"), overwrite = overwrite_cert)
if logging:
- print('[LOG]: Successfully enabled ZMQ Security Mechanism: `{}` for this connection.'.format(valid_security_mech[self.secure_mode]))
- print('[LOG]: `{}` is the default location for storing ZMQ authentication certificates/keys.'.format(self.auth_cert_location))
+ print('[LOG]: `{}` is the default location for storing ZMQ authentication certificates/keys.'.format(auth_cert_dir))
except Exception as e:
# Catch if any error occurred
@@ -252,8 +257,9 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
# initialize and assign receive mode to global variable
self.receive_mode = receive_mode
- # define messaging context
- self.msg_context = zmq.Context()
+ # define messaging context instance
+ self.msg_context = zmq.Context.instance() # TODO changed
+
#check whether `receive_mode` is enabled by user
if receive_mode:
@@ -278,12 +284,30 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
port = '5555'
try:
+ if self.secure_mode == 2 and not(self.multiserver_mode):
+ # Start an authenticator for this context.
+ auth = ThreadAuthenticator(self.msg_context)
+ auth.start()
+ auth.allow(str(address))
+ # Tell authenticator to use the certificate in a directory
+ auth.configure_curve(domain='*', location=self.auth_publickeys_dir)
+
# initialize and define thread-safe messaging socket
self.msg_socket = self.msg_context.socket(msg_pattern[1])
if self.multiserver_mode:
#if multiserver_mode is enabled assign port addresses to zmq socket
for pt in port:
+ if self.secure_mode == 2: #IronHouse security mechanism
+ client_secret_file = os.path.join(self.auth_secretkeys_dir, "client.key_secret")
+ client_public, client_secret = zmq.auth.load_certificate(client_secret_file)
+ self.msg_socket.curve_secretkey = client_secret
+ self.msg_socket.curve_publickey = client_public
+
+ server_public_file = os.path.join(self.auth_publickeys_dir, "server.key")
+ server_public, _ = zmq.auth.load_certificate(server_public_file)
+ # The client must know the server's public key to make a CURVE connection.
+ self.msg_socket.curve_serverkey = server_public
# connect socket to given server protocol, address and ports
self.msg_socket.connect(protocol+'://' + str(address) + ':' + str(pt))
self.msg_socket.setsockopt(zmq.LINGER, 0)
@@ -291,6 +315,14 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
self.msg_socket.setsockopt_string(zmq.SUBSCRIBE, recv_filter)
else:
# otherwise bind socket to given protocol, address and port normally
+
+ if self.secure_mode == 2: #IronHouse security mechanism
+ server_secret_file = os.path.join(self.auth_secretkeys_dir, "server.key_secret")
+ server_public, server_secret = zmq.auth.load_certificate(server_secret_file)
+ self.msg_socket.curve_secretkey = server_secret
+ self.msg_socket.curve_publickey = server_public
+ self.msg_socket.curve_server = True # must come before bind
+
self.msg_socket.bind(protocol+'://' + str(address) + ':' + str(port))
# define exclusive socket options for patterns
if self.pattern == 2:
@@ -300,7 +332,10 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
except Exception as e:
# otherwise raise value error if errored
- raise ValueError('Failed to bind address: {} and pattern: {}! Kindly recheck all parameters.'.format((protocol+'://' + str(address) + ':' + str(port)), pattern))
+ if self.multiserver_mode:
+ raise ValueError('[ERROR]: Multi-Server Mode, failed to connect to ports: {} with pattern: {}! Kindly recheck all parameters.'.format( str(port), pattern))
+ else:
+ raise ValueError('[ERROR]: Failed to bind address: {} and pattern: {}! Kindly recheck all parameters.'.format((protocol+'://' + str(address) + ':' + str(port)), pattern))
# initialize and start threading instance
self.thread = Thread(target=self.update, args=())
@@ -313,6 +348,7 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
print('[LOG]: Multi-threaded Receive Mode is enabled Successfully!')
print('[LOG]: This device Unique ID is {}.'.format(self.id))
print('[LOG]: Receive Mode is activated successfully!')
+ if self.secure_mode: print('[LOG]: Successfully enabled ZMQ Security Mechanism: `{}` for this connection.'.format(valid_security_mech[self.secure_mode]))
else:
#otherwise default to `Send Mode`
@@ -335,10 +371,24 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
port = 5555 #define port normally
try:
+ if self.secure_mode == 2 and self.multiserver_mode:
+ # Start an authenticator for this context.
+ auth = ThreadAuthenticator(self.msg_context)
+ auth.start()
+ auth.allow(str(address))
+ # Tell authenticator to use the certificate in a directory
+ auth.configure_curve(domain='*', location=self.auth_publickeys_dir)
+
# initialize and define thread-safe messaging socket
self.msg_socket = self.msg_context.socket(msg_pattern[0])
if self.multiserver_mode:
+ if self.secure_mode == 2: #IronHouse security mechanism
+ server_secret_file = os.path.join(self.auth_secretkeys_dir, "server.key_secret")
+ server_public, server_secret = zmq.auth.load_certificate(server_secret_file)
+ self.msg_socket.curve_secretkey = server_secret
+ self.msg_socket.curve_publickey = server_public
+ self.msg_socket.curve_server = True # must come before bind
# connect socket to protocol, address and a unique port if multiserver_mode is activated
self.msg_socket.bind(protocol+'://' + str(address) + ':' + str(port))
else:
@@ -347,6 +397,18 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
self.msg_socket.REQ_RELAXED = True
self.msg_socket.REQ_CORRELATE = True
+ if self.secure_mode == 2: #IronHouse security mechanism
+ client_secret_file = os.path.join(self.auth_secretkeys_dir, "client.key_secret")
+ client_public, client_secret = zmq.auth.load_certificate(client_secret_file)
+ self.msg_socket.curve_secretkey = client_secret
+ self.msg_socket.curve_publickey = client_public
+
+ server_public_file = os.path.join(self.auth_publickeys_dir, "server.key")
+ server_public, _ = zmq.auth.load_certificate(server_public_file)
+
+ # The client must know the server's public key to make a CURVE connection.
+ self.msg_socket.curve_serverkey = server_public
+
# connect socket to given protocol, address and port
self.msg_socket.connect(protocol+'://' + str(address) + ':' + str(port))
@@ -362,7 +424,7 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
print('[LOG]: Successfully connected to address: {}.'.format(protocol+'://' + str(address) + ':' + str(port)))
print('[LOG]: This device Unique ID is {}.'.format(self.id))
print('[LOG]: Send Mode is successfully activated and ready to send data!')
-
+ if self.secure_mode: print('[LOG]: Successfully enabled ZMQ Security Mechanism: `{}` for this connection.'.format(valid_security_mech[self.secure_mode]))
def update(self):
"""
From 2551989a902076df6af4b39a367ae2f9ac4fea95 Mon Sep 17 00:00:00 2001
From: Abhishek
Date: Tue, 10 Sep 2019 23:08:50 +0530
Subject: [PATCH 09/39] Enhancements pt-2: - implemented new `StoneHouse` ZMQ
security mechanism - intially tested & validated this security mechanism for
both Server & Client end. - added various methods for different modes and
patterns.
---
vidgear/gears/netgear.py | 24 ++++++++++++++++--------
1 file changed, 16 insertions(+), 8 deletions(-)
diff --git a/vidgear/gears/netgear.py b/vidgear/gears/netgear.py
index 0ae76e3a0..0d732d3c2 100644
--- a/vidgear/gears/netgear.py
+++ b/vidgear/gears/netgear.py
@@ -284,13 +284,17 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
port = '5555'
try:
- if self.secure_mode == 2 and not(self.multiserver_mode):
+ if self.secure_mode > 0 and not(self.multiserver_mode):
# Start an authenticator for this context.
auth = ThreadAuthenticator(self.msg_context)
auth.start()
auth.allow(str(address))
# Tell authenticator to use the certificate in a directory
- auth.configure_curve(domain='*', location=self.auth_publickeys_dir)
+ if self.secure_mode == 2:
+ auth.configure_curve(domain='*', location=self.auth_publickeys_dir)
+ else:
+ # Tell the authenticator how to handle CURVE requests
+ auth.configure_curve(domain='*', location=zmq.auth.CURVE_ALLOW_ANY)
# initialize and define thread-safe messaging socket
self.msg_socket = self.msg_context.socket(msg_pattern[1])
@@ -298,7 +302,7 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
if self.multiserver_mode:
#if multiserver_mode is enabled assign port addresses to zmq socket
for pt in port:
- if self.secure_mode == 2: #IronHouse security mechanism
+ if self.secure_mode > 0:
client_secret_file = os.path.join(self.auth_secretkeys_dir, "client.key_secret")
client_public, client_secret = zmq.auth.load_certificate(client_secret_file)
self.msg_socket.curve_secretkey = client_secret
@@ -316,7 +320,7 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
else:
# otherwise bind socket to given protocol, address and port normally
- if self.secure_mode == 2: #IronHouse security mechanism
+ if self.secure_mode > 0:
server_secret_file = os.path.join(self.auth_secretkeys_dir, "server.key_secret")
server_public, server_secret = zmq.auth.load_certificate(server_secret_file)
self.msg_socket.curve_secretkey = server_secret
@@ -371,19 +375,23 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
port = 5555 #define port normally
try:
- if self.secure_mode == 2 and self.multiserver_mode:
+ if self.secure_mode > 0 and self.multiserver_mode:
# Start an authenticator for this context.
auth = ThreadAuthenticator(self.msg_context)
auth.start()
auth.allow(str(address))
# Tell authenticator to use the certificate in a directory
- auth.configure_curve(domain='*', location=self.auth_publickeys_dir)
+ if self.secure_mode == 2:
+ auth.configure_curve(domain='*', location=self.auth_publickeys_dir)
+ else:
+ # Tell the authenticator how to handle CURVE requests
+ auth.configure_curve(domain='*', location=zmq.auth.CURVE_ALLOW_ANY)
# initialize and define thread-safe messaging socket
self.msg_socket = self.msg_context.socket(msg_pattern[0])
if self.multiserver_mode:
- if self.secure_mode == 2: #IronHouse security mechanism
+ if self.secure_mode > 0:
server_secret_file = os.path.join(self.auth_secretkeys_dir, "server.key_secret")
server_public, server_secret = zmq.auth.load_certificate(server_secret_file)
self.msg_socket.curve_secretkey = server_secret
@@ -397,7 +405,7 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
self.msg_socket.REQ_RELAXED = True
self.msg_socket.REQ_CORRELATE = True
- if self.secure_mode == 2: #IronHouse security mechanism
+ if self.secure_mode > 0:
client_secret_file = os.path.join(self.auth_secretkeys_dir, "client.key_secret")
client_public, client_secret = zmq.auth.load_certificate(client_secret_file)
self.msg_socket.curve_secretkey = client_secret
From b0b51e694da687821408b03ab539d70e5e9d8f92 Mon Sep 17 00:00:00 2001
From: Abhishek
Date: Thu, 12 Sep 2019 12:19:51 +0530
Subject: [PATCH 10/39] NetGear API enhancements pt-3: - Reformatted and
beautify code with proper indentation - Fixed Bugs and typos - Added proper
Code Documentation
---
vidgear/gears/helper.py | 55 +++++++++---
vidgear/gears/netgear.py | 178 +++++++++++++++++++++++++--------------
2 files changed, 155 insertions(+), 78 deletions(-)
diff --git a/vidgear/gears/helper.py b/vidgear/gears/helper.py
index 0886d0e48..c7ff58280 100644
--- a/vidgear/gears/helper.py
+++ b/vidgear/gears/helper.py
@@ -25,7 +25,7 @@
# Contains all the support functions/modules required by Vidgear
-# import the neccesary packages
+# import the necessary packages
import os, sys
import cv2
import numpy as np
@@ -101,7 +101,7 @@ def get_valid_ffmpeg_path(custom_ffmpeg = '', is_windows = False, ffmpeg_downloa
final_path += _path
except Exception as e:
- #log if any error occured
+ #log if any error occurred
if logging:
print(e)
print('[LOG]: Error downloading FFmpeg binaries, Check your network and Try again!')
@@ -242,7 +242,7 @@ def check_output(*args, **kwargs):
#close the process
if closeNULL:
DEVNULL.close()
- #if error occured raise error
+ #if error occurred raise error
if retcode:
cmd = kwargs.get("args")
if cmd is None:
@@ -253,15 +253,22 @@ def check_output(*args, **kwargs):
return output
+
def generate_auth_certificates(path, overwrite = False):
- """ Generate client and server CURVE ZMQ certificate files """
+ """
+ Auto-Generates and handles valid client and server CURVE ZMQ keys/certificates
+ """
+
+ #import necessary libs
import shutil, errno
import zmq.auth
+ #check if path corresponds to vidgear only
if (os.path.basename(path) != ".vidgear"):
path = os.path.join(path,".vidgear")
+ #generate keys dir
keys_dir = os.path.join(path, 'keys')
try:
os.makedirs(keys_dir)
@@ -269,38 +276,43 @@ def generate_auth_certificates(path, overwrite = False):
if e.errno != errno.EEXIST:
raise
+ #generate separate public and private key dirs
public_keys_dir = os.path.join(keys_dir, 'public_keys')
secret_keys_dir = os.path.join(keys_dir, 'private_keys')
+ #check if overwriting is allowed
if overwrite:
-
+ #delete previous certificates
for d in [public_keys_dir, secret_keys_dir]:
if os.path.exists(d):
shutil.rmtree(d)
os.mkdir(d)
- # create new keys in certificates dir
+ # generate new keys
server_public_file, server_secret_file = zmq.auth.create_certificates(keys_dir, "server")
client_public_file, client_secret_file = zmq.auth.create_certificates(keys_dir, "client")
- # move public keys to appropriate directory
+ # move keys to their appropriate directory respectively
for key_file in os.listdir(keys_dir):
if key_file.endswith(".key"):
shutil.move(os.path.join(keys_dir, key_file), public_keys_dir)
elif key_file.endswith(".key_secret"):
shutil.move(os.path.join(keys_dir, key_file), secret_keys_dir)
else:
+ # clean redundant keys if present
redundant_key = os.path.join(keys_dir,key_file)
if os.path.isfile(redundant_key):
os.remove(redundant_key)
else:
-
+ # otherwise validate available keys
status_public_keys = validate_auth_keys(public_keys_dir, '.key')
status_private_keys = validate_auth_keys(secret_keys_dir, '.key_secret')
+ # check if all valid keys are found
if status_private_keys and status_public_keys:
return (keys_dir, secret_keys_dir, public_keys_dir)
+ # check if valid public keys are found
if not(status_public_keys):
try:
os.makedirs(public_keys_dir)
@@ -308,6 +320,7 @@ def generate_auth_certificates(path, overwrite = False):
if e.errno != errno.EEXIST:
raise
+ # check if valid private keys are found
if not(status_private_keys):
try:
os.makedirs(secret_keys_dir)
@@ -315,43 +328,57 @@ def generate_auth_certificates(path, overwrite = False):
if e.errno != errno.EEXIST:
raise
- # create new keys in certificates dir
+ # generate new keys
server_public_file, server_secret_file = zmq.auth.create_certificates(keys_dir, "server")
client_public_file, client_secret_file = zmq.auth.create_certificates(keys_dir, "client")
-
- # move public keys to appropriate directory
+ # move keys to their appropriate directory respectively
for key_file in os.listdir(keys_dir):
if key_file.endswith(".key") and not(status_public_keys):
shutil.move(os.path.join(keys_dir, key_file), os.path.join(public_keys_dir, '.'))
elif key_file.endswith(".key_secret") and not(status_private_keys):
shutil.move(os.path.join(keys_dir, key_file), os.path.join(secret_keys_dir, '.'))
else:
+ # clean redundant keys if present
redundant_key = os.path.join(keys_dir,key_file)
if os.path.isfile(redundant_key):
os.remove(redundant_key)
+ # validate newly generated keys
status_public_keys = validate_auth_keys(public_keys_dir, '.key')
status_private_keys = validate_auth_keys(secret_keys_dir, '.key_secret')
+ # raise error is validation test fails
if not(status_private_keys) or not(status_public_keys):
- raise RuntimeError('[Error]: Unable to create ZMQ all authentication certificates at `{}`!'.format(keys_dir))
+ raise RuntimeError('[Error]: Unable to generate valid ZMQ authentication certificates at `{}`!'.format(keys_dir))
+ # finally return valid key paths
return (keys_dir, secret_keys_dir, public_keys_dir)
+
def validate_auth_keys(path, extension):
+ """
+ validates and maintains ZMQ Auth Keys/Certificates
+ """
+ #check for valid path
if not(os.path.exists(path)): return False
+
+ #check if directory empty
if not(os.listdir(path)): return False
- keys_buffer = []
+ keys_buffer = [] #stores auth-keys
+ # loop over auth-keys
for key_file in os.listdir(path):
key = os.path.splitext(key_file)
+ #check if valid key is generated
if key and (key[0] in ['server','client']) and (key[1] == extension):
- keys_buffer.append(key_file)
+ keys_buffer.append(key_file) #store it
+ #remove invalid keys if found
if(len(keys_buffer) == 1): os.remove(os.path.join(path,keys_buffer[0]))
+ #return results
return True if(len(keys_buffer) == 2) else False
\ No newline at end of file
diff --git a/vidgear/gears/netgear.py b/vidgear/gears/netgear.py
index 0d732d3c2..a9817ac48 100644
--- a/vidgear/gears/netgear.py
+++ b/vidgear/gears/netgear.py
@@ -57,11 +57,27 @@ class NetGear:
Furthermore, NetGear currently supports three ZeroMQ messaging patterns: i.e zmq.PAIR, zmq.REQ and zmq.REP,and zmq.PUB,zmq.SUB whereas
supported protocol are: 'tcp', 'upd', 'pgm', 'inproc', 'ipc'.
- Multi-Server Mode: This mode in NetGear API can robustly handle multiple servers at once through exclusive Publish/Subscribe (zmq.PUB/zmq.SUB)
+ Multi-Server Mode: This mode enables NetGear API to robustly handle multiple servers at once through exclusive Publish/Subscribe (zmq.PUB/zmq.SUB)
messaging pattern for seamless Live Streaming across various device at the same time. Each device new server on network is
- identied using its unique port address. Also, when all the connected servers on the network get disconnected, the client
+ identified using its unique port address. Also, when all the connected servers on the network get disconnected, the client
itself automatically exits too. This mode can be activated through`multiserver_mode` option boolean attribute during
- netgear initialization easily.
+ NetGear API initialization easily.
+
+ Secure Mode: This mode provides secure ZeroMQ's Security Layers for NetGear API that enables strong encryption on data, and (as far as we know) unbreakable
+ authentication between the Server and the Client with the help of custom auth certificates/keys. It's default value is `Grassland`:0 => which means no
+ security at all.
+
+ This mode supports the two most powerful ZMQ security mechanisms:
+
+ * `StoneHouse`: 1 => which switches to the "CURVE" security mechanism, giving us strong encryption on data, and unbreakable authentication.
+ Stonehouse is the minimum you would use over public networks and assures clients that they are speaking to an authentic server while allowing any client
+ to connect. It is less secure but at the same time faster than IronHouse security mechanism.
+
+ * `IronHouse`: 2 => which extends Stonehouse with client public key authentication. This is the strongest security model ZMQ have today,
+ protecting against every attack we know about, except end-point attacks (where an attacker plants spyware on a machine
+ to capture data before it's encrypted, or after it's decrypted). IronHouse enhanced security comes at a price of additional latency.
+
+
:param address(string): sets the valid network address of the server/client. Network addresses unique identifiers across the
network. Its default value of this parameter is based on mode it is working, 'localhost' for Send Mode
@@ -97,9 +113,9 @@ class NetGear:
when this flag is disabled(i.e.`Send Mode`). Checkout VidGear docs for usage details.
Its default value is False(i.e. Send Mode is activated by default).
- :param **options(dict): can be used to pass parameters to NetGear Class.
- /This attribute provides the flexibility to manipulate ZeroMQ input parameters
- /directly. Checkout vidgear docs for usage details.
+ :param **options(dict): can be used to pass flexible parameters to NetGear API.
+ This attribute provides the flexibility to manipulate ZeroMQ internal parameters
+ directly. Checkout vidgear docs for usage details.
:param (boolean) logging: set this flag to enable/disable error logging essential for debugging. Its default value is False.
@@ -112,7 +128,7 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
import zmq
#import ZMQError
from zmq.error import ZMQError
- #assign values to global variable
+ #assign values to global variable for further use
self.zmq = zmq
self.ZMQError = ZMQError
except ImportError as error:
@@ -127,7 +143,7 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
#define deque and assign it to global var
self.queue = deque(maxlen=96) #max len 96 to check overflow
- #define valid messaging pattern `0`: zmq.PAIR, `1`:(zmq.REQ,zmq.REP), and `1`:(zmq.SUB,zmq.PUB)
+ #define valid messaging patterns => `0`: zmq.PAIR, `1`:(zmq.REQ,zmq.REP), and `1`:(zmq.SUB,zmq.PUB)
valid_messaging_patterns = {0:(zmq.PAIR,zmq.PAIR), 1:(zmq.REQ,zmq.REP), 2:(zmq.PUB,zmq.SUB)}
# initialize messaging pattern
@@ -165,33 +181,38 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
self.multiserver_mode = False #handles multiserver_mode state
recv_filter = '' #user-defined filter to allow specific port/servers only in multiserver_mode
+ #define valid ZMQ security mechanisms => `0`: Grasslands, `1`:StoneHouse, and `1`:IronHouse
valid_security_mech = {0:'Grasslands', 1:'StoneHouse', 2:'IronHouse'}
- self.secure_mode = 0
- auth_cert_dir = ''
- self.auth_publickeys_dir = ''
- self.auth_secretkeys_dir = ''
- overwrite_cert = False
- custom_cert_location = ''
+ self.secure_mode = 0 #handles ZMQ security layer status
+ auth_cert_dir = '' #handles valid ZMQ certificates dir
+ self.auth_publickeys_dir = '' #handles valid ZMQ public certificates dir
+ self.auth_secretkeys_dir = '' #handles valid ZMQ private certificates dir
+ overwrite_cert = False #checks if certificates overwriting allowed
+ custom_cert_location = '' #handles custom ZMQ certificates path
try:
#reformat dict
options = {k.lower().strip(): v for k,v in options.items()}
+
# assign values to global variables if specified and valid
for key, value in options.items():
-
if key == 'multiserver_mode' and isinstance(value, bool) and self.pattern == 2:
+ # multi-server mode
self.multiserver_mode = value
elif key == 'filter' and isinstance(value, str):
+ #custom filter in multi-server mode
recv_filter = value
elif key == 'secure_mode' and isinstance(value,int) and (value in valid_security_mech):
+ #secure mode
try:
- assert check_python_version() >= 3,"[ERROR]: ZMQ Security feature is not available with python version < 3.0."
+ assert check_python_version() >= 3, "[ERROR]: ZMQ Security feature is not available with python version < 3.0."
assert zmq.zmq_version_info() >= (4,0), "[ERROR]: ZMQ Security feature is not supported in libzmq version < 4.0."
self.secure_mode = value
except AssertionError as e:
print(e)
elif key == 'custom_cert_location' and isinstance(value,str):
+ # custom auth certificates path
try:
assert os.access(value, os.W_OK), "[ERROR]: Permission Denied!, cannot write ZMQ authentication certificates to '{}' directory!".format(value)
assert not(os.path.isfile(value)), "[ERROR]: `custom_cert_location` value must be the path to a directory and not to a file!"
@@ -199,8 +220,10 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
except AssertionError as e:
print(e)
elif key == 'overwrite_cert' and isinstance(value,bool):
+ # enable/disable auth certificate overwriting
overwrite_cert = value
+ # various ZMQ flags
elif key == 'flag' and isinstance(value, int):
self.msg_flag = value
elif key == 'copy' and isinstance(value, bool):
@@ -214,34 +237,38 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
if logging:
print(e)
-
+ #handle secure mode
if self.secure_mode:
-
- #import libraries
+ #import important libs
import zmq.auth
from zmq.auth.thread import ThreadAuthenticator
+ # log if overwriting is enabled
if logging and overwrite_cert: print('[WARNING]: Overwriting ZMQ Authentication certificates over previous ones!')
+ #generate and validate certificates path
try:
+ #check if custom certificates path is specified
if custom_cert_location:
- if os.path.isdir(custom_cert_location):
+ if os.path.isdir(custom_cert_location): #custom certificate location must be a directory
(auth_cert_dir, self.auth_secretkeys_dir, self.auth_publickeys_dir) = generate_auth_certificates(custom_cert_location, overwrite = overwrite_cert)
else:
raise ValueError("[ERROR]: Invalid `custom_cert_location` value!")
else:
+ # otherwise auto-generate suitable path
from os.path import expanduser
(auth_cert_dir, self.auth_secretkeys_dir, self.auth_publickeys_dir) = generate_auth_certificates(os.path.join(expanduser("~"),".vidgear"), overwrite = overwrite_cert)
-
- if logging:
- print('[LOG]: `{}` is the default location for storing ZMQ authentication certificates/keys.'.format(auth_cert_dir))
-
+
+ #log it
+ if logging: print('[LOG]: `{}` is the default location for storing ZMQ authentication certificates/keys.'.format(auth_cert_dir))
except Exception as e:
- # Catch if any error occurred
+ # catch if any error occurred
print(e)
+ # also disable secure mode
self.secure_mode = 0
print('[WARNING]: ZMQ Security Mechanism is disabled for this connection!')
else:
+ #log if disabled
if logging: print('[LOG]: ZMQ Security Mechanism is disabled for this connection!')
@@ -258,8 +285,7 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
self.receive_mode = receive_mode
# define messaging context instance
- self.msg_context = zmq.Context.instance() # TODO changed
-
+ self.msg_context = zmq.Context.instance()
#check whether `receive_mode` is enabled by user
if receive_mode:
@@ -284,49 +310,60 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
port = '5555'
try:
+ # initiate and handle secure mode
if self.secure_mode > 0 and not(self.multiserver_mode):
- # Start an authenticator for this context.
+ # start an authenticator for this context
auth = ThreadAuthenticator(self.msg_context)
auth.start()
- auth.allow(str(address))
- # Tell authenticator to use the certificate in a directory
+ auth.allow(str(address)) #allow current address
+
+ #check if `IronHouse` is activated
if self.secure_mode == 2:
+ # tell authenticator to use the certificate from given valid dir
auth.configure_curve(domain='*', location=self.auth_publickeys_dir)
else:
- # Tell the authenticator how to handle CURVE requests
+ #otherwise tell the authenticator how to handle the CURVE requests, if `StoneHouse` is activated
auth.configure_curve(domain='*', location=zmq.auth.CURVE_ALLOW_ANY)
# initialize and define thread-safe messaging socket
self.msg_socket = self.msg_context.socket(msg_pattern[1])
if self.multiserver_mode:
- #if multiserver_mode is enabled assign port addresses to zmq socket
+ # if multiserver_mode is enabled, then assign port addresses to zmq socket
for pt in port:
- if self.secure_mode > 0:
+ # enable specified secure mode for the zmq socket
+ if self.secure_mode > 0:
+ # load client key
client_secret_file = os.path.join(self.auth_secretkeys_dir, "client.key_secret")
- client_public, client_secret = zmq.auth.load_certificate(client_secret_file)
+ client_public, client_secret = zmq.auth.load_certificate(client_secret_file)
+ # load all CURVE keys
self.msg_socket.curve_secretkey = client_secret
self.msg_socket.curve_publickey = client_public
-
+ # load server key
server_public_file = os.path.join(self.auth_publickeys_dir, "server.key")
server_public, _ = zmq.auth.load_certificate(server_public_file)
- # The client must know the server's public key to make a CURVE connection.
+ # inject public key to make a CURVE connection.
self.msg_socket.curve_serverkey = server_public
+
# connect socket to given server protocol, address and ports
self.msg_socket.connect(protocol+'://' + str(address) + ':' + str(pt))
self.msg_socket.setsockopt(zmq.LINGER, 0)
# define socket options
self.msg_socket.setsockopt_string(zmq.SUBSCRIBE, recv_filter)
- else:
- # otherwise bind socket to given protocol, address and port normally
+ else:
+ # enable specified secure mode for the zmq socket
if self.secure_mode > 0:
+ # load server key
server_secret_file = os.path.join(self.auth_secretkeys_dir, "server.key_secret")
server_public, server_secret = zmq.auth.load_certificate(server_secret_file)
+ # load all CURVE keys
self.msg_socket.curve_secretkey = server_secret
self.msg_socket.curve_publickey = server_public
- self.msg_socket.curve_server = True # must come before bind
+ # enable CURVE connection for this socket
+ self.msg_socket.curve_server = True
+ # bind socket to given protocol, address and port normally
self.msg_socket.bind(protocol+'://' + str(address) + ':' + str(port))
# define exclusive socket options for patterns
if self.pattern == 2:
@@ -336,6 +373,7 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
except Exception as e:
# otherwise raise value error if errored
+ if self.secure_mode: print('Failed to activate ZMQ Security Mechanism: `{}` for this address!'.format(valid_security_mech[self.secure_mode]))
if self.multiserver_mode:
raise ValueError('[ERROR]: Multi-Server Mode, failed to connect to ports: {} with pattern: {}! Kindly recheck all parameters.'.format( str(port), pattern))
else:
@@ -347,14 +385,13 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
self.thread.start()
if logging:
- #log progress
+ #finally log progress
print('[LOG]: Successfully Binded to address: {}.'.format(protocol+'://' + str(address) + ':' + str(port)))
+ if self.secure_mode: print('[LOG]: Enabled ZMQ Security Mechanism: `{}` for this address, Successfully!'.format(valid_security_mech[self.secure_mode]))
print('[LOG]: Multi-threaded Receive Mode is enabled Successfully!')
- print('[LOG]: This device Unique ID is {}.'.format(self.id))
+ print('[LOG]: Device Unique ID is {}.'.format(self.id))
print('[LOG]: Receive Mode is activated successfully!')
- if self.secure_mode: print('[LOG]: Successfully enabled ZMQ Security Mechanism: `{}` for this connection.'.format(valid_security_mech[self.secure_mode]))
else:
-
#otherwise default to `Send Mode`
if address is None: #define address
@@ -375,46 +412,56 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
port = 5555 #define port normally
try:
+ # initiate and handle secure mode
if self.secure_mode > 0 and self.multiserver_mode:
- # Start an authenticator for this context.
+ # start an authenticator for this context
auth = ThreadAuthenticator(self.msg_context)
auth.start()
- auth.allow(str(address))
- # Tell authenticator to use the certificate in a directory
+ auth.allow(str(address)) #allow current address
+
+ #check if `IronHouse` is activated
if self.secure_mode == 2:
+ # tell authenticator to use the certificate/key from given valid dir
auth.configure_curve(domain='*', location=self.auth_publickeys_dir)
else:
- # Tell the authenticator how to handle CURVE requests
+ #otherwise tell the authenticator how to handle the CURVE requests, if `StoneHouse` is activated
auth.configure_curve(domain='*', location=zmq.auth.CURVE_ALLOW_ANY)
# initialize and define thread-safe messaging socket
self.msg_socket = self.msg_context.socket(msg_pattern[0])
if self.multiserver_mode:
- if self.secure_mode > 0:
+ # enable specified secure mode for the zmq socket
+ if self.secure_mode > 0:
+ # load server key
server_secret_file = os.path.join(self.auth_secretkeys_dir, "server.key_secret")
server_public, server_secret = zmq.auth.load_certificate(server_secret_file)
+ # load all CURVE keys
self.msg_socket.curve_secretkey = server_secret
self.msg_socket.curve_publickey = server_public
- self.msg_socket.curve_server = True # must come before bind
+ # enable CURVE connection for this socket
+ self.msg_socket.curve_server = True
# connect socket to protocol, address and a unique port if multiserver_mode is activated
self.msg_socket.bind(protocol+'://' + str(address) + ':' + str(port))
else:
+
if self.pattern == 1:
# if pattern is 1, define additional flags
self.msg_socket.REQ_RELAXED = True
self.msg_socket.REQ_CORRELATE = True
- if self.secure_mode > 0:
+ # enable specified secure mode for the zmq socket
+ if self.secure_mode > 0:
+ # load client key
client_secret_file = os.path.join(self.auth_secretkeys_dir, "client.key_secret")
- client_public, client_secret = zmq.auth.load_certificate(client_secret_file)
+ client_public, client_secret = zmq.auth.load_certificate(client_secret_file)
+ # load all CURVE keys
self.msg_socket.curve_secretkey = client_secret
self.msg_socket.curve_publickey = client_public
-
+ # load server key
server_public_file = os.path.join(self.auth_publickeys_dir, "server.key")
server_public, _ = zmq.auth.load_certificate(server_public_file)
-
- # The client must know the server's public key to make a CURVE connection.
+ # inject public key to make a CURVE connection.
self.msg_socket.curve_serverkey = server_public
# connect socket to given protocol, address and port
@@ -422,17 +469,20 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
# define socket options
self.msg_socket.setsockopt(zmq.LINGER, 0)
-
except Exception as e:
- # otherwise raise value error
+ #log if errored
+ if self.secure_mode: print('Failed to activate ZMQ Security Mechanism: `{}` for this address!'.format(valid_security_mech[self.secure_mode]))
+ # raise value error
raise ValueError('Failed to connect address: {} and pattern: {}! Kindly recheck all parameters.'.format((protocol+'://' + str(address) + ':' + str(port)), pattern))
if logging:
- #log progress
+ #finally log progress
print('[LOG]: Successfully connected to address: {}.'.format(protocol+'://' + str(address) + ':' + str(port)))
+ if self.secure_mode: print('[LOG]: Enabled ZMQ Security Mechanism: `{}` for this address, Successfully!'.format(valid_security_mech[self.secure_mode]))
print('[LOG]: This device Unique ID is {}.'.format(self.id))
print('[LOG]: Send Mode is successfully activated and ready to send data!')
- if self.secure_mode: print('[LOG]: Successfully enabled ZMQ Security Mechanism: `{}` for this connection.'.format(valid_security_mech[self.secure_mode]))
+
+
def update(self):
"""
@@ -469,13 +519,13 @@ def update(self):
self.port_buffer.remove(msg_json['port'])
#if termination signal received from all servers then exit client.
if not self.port_buffer:
- print('Termination signal received from all Servers!!!')
+ print('[WARNING]: Termination signal received from all Servers!!!')
self.terminate = True #termination
continue
else:
if self.pattern == 1:
# if pattern is 1, then send back server the info about termination
- self.msg_socket.send_string('Termination signal received from server!')
+ self.msg_socket.send_string('[INFO]: Termination signal received from server!')
#termination
self.terminate = msg_json['terminate_flag']
continue
@@ -485,7 +535,7 @@ def update(self):
assert int(msg_json['pattern']) == self.pattern
except AssertionError as e:
#otherwise raise error and exit
- raise ValueError("Messaging pattern on the Server-end & Client-end must a valid pairs! Kindly refer VidGear docs.")
+ raise ValueError("[ERROR]: Messaging patterns on both Server-end & Client-end must a valid pairs! Kindly refer VidGear docs.")
self.terminate = True
continue
@@ -494,7 +544,7 @@ def update(self):
if self.pattern != 2:
# send confirmation message to server for debugging
- self.msg_socket.send_string('Data received on device: {} !'.format(self.id))
+ self.msg_socket.send_string('[LOG]: Data received on device: {} !'.format(self.id))
# recover frame from array buffer
frame_buffer = np.frombuffer(msg_data, dtype=msg_json['dtype'])
@@ -532,7 +582,7 @@ def recv(self):
pass
else:
#otherwise raise value error and exit
- raise ValueError('recv() function cannot be used while receive_mode is disabled. Kindly refer vidgear docs!')
+ raise ValueError('[ERROR]: `recv()` function cannot be used while receive_mode is disabled. Kindly refer vidgear docs!')
self.terminate = True
# check whether or not termination flag is enabled
while not self.terminate:
@@ -558,7 +608,7 @@ def send(self, frame, message = None):
pass
else:
#otherwise raise value error and exit
- raise ValueError('send() function cannot be used while receive_mode is enabled. Kindly refer vidgear docs!')
+ raise ValueError('[ERROR]: `send()` function cannot be used while receive_mode is enabled. Kindly refer vidgear docs!')
self.terminate = True
# define exit_flag and assign value
From 073bca131ba280d0fdf3b68ef4d2c6b916c6e7b1 Mon Sep 17 00:00:00 2001
From: Abhishek Thakur
Date: Sat, 5 Oct 2019 20:36:36 +0530
Subject: [PATCH 11/39] Hotfix: assigned Port address ignored bug
---
vidgear/gears/netgear.py | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/vidgear/gears/netgear.py b/vidgear/gears/netgear.py
index a9817ac48..dba4da99f 100644
--- a/vidgear/gears/netgear.py
+++ b/vidgear/gears/netgear.py
@@ -306,8 +306,8 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
#create port address buffer for keeping track of incoming server's port
self.port_buffer = []
else:
- # otherwise assign local port address
- port = '5555'
+ # otherwise assign local port address if None
+ if port is None: port = '5555'
try:
# initiate and handle secure mode
@@ -409,7 +409,8 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
#assign value to global variable
self.port = port
else:
- port = 5555 #define port normally
+ # otherwise assign local port address if None
+ if port is None: port = '5555'
try:
# initiate and handle secure mode
From b0cb7cd7ea4dda3ff894f93dea8ad6f4f85e3ba7 Mon Sep 17 00:00:00 2001
From: Abhishek
Date: Wed, 9 Oct 2019 10:55:59 +0530
Subject: [PATCH 12/39] Code Maintaince & Enhancements: - Updated bash script
path to $TMPDIR from $HOME - Updated Code Readability - Fixed code
definitions & Typos
---
changelog.md | 25 ++++++++++++++--
scripts/bash/install_opencv.sh | 6 +++-
scripts/bash/prepare_dataset.sh | 10 ++++---
vidgear/gears/camgear.py | 6 ++--
vidgear/gears/helper.py | 2 +-
vidgear/gears/netgear.py | 10 +++----
vidgear/gears/pigear.py | 4 +--
vidgear/gears/screengear.py | 10 +++----
vidgear/gears/writegear.py | 29 +++++++++----------
.../test_benchmark_Videocapture.py | 3 +-
.../test_benchmark_Videowriter.py | 9 +++---
.../test_benchmark_playback.py | 3 +-
vidgear/tests/test_helper.py | 8 ++---
.../tests/videocapture_tests/test_camgear.py | 3 +-
.../writer_tests/test_compression_mode.py | 8 ++---
.../writer_tests/test_non_compression_mode.py | 8 ++---
16 files changed, 86 insertions(+), 58 deletions(-)
diff --git a/changelog.md b/changelog.md
index 64eb14df8..a6d3d2326 100644
--- a/changelog.md
+++ b/changelog.md
@@ -3,23 +3,42 @@
## VidGear 0.1.6-dev
### New Features:
+ * Added powerful ZMQ Authentication & Data Encryption features for NetGear API:
+ * Added exclusive `secure_mode` param for enabling it.
+ * Added support for two most powerful `Stonehouse` & `Ironhouse` ZMQ security mechanisms.
+ * Added smart auth-certificates/key generation and validation features.
+ * Implemented Robust Multi-Server support for NetGear API.
+ * Enables Multiple Servers messaging support with a single client.
+ * Added exclusive `multiserver_mode` param for enabling it.
+ * Added ability to send additional data of any datatype along with the frame in realtime in this mode.
+ * Implemented new *Publish/Subscribe(`zmq.PUB/zmq.SUB`)* pattern for seamless Live Streaming.
* Added VidGear's official native support for MacOS environment.
### Updates/Improvements:
+ * Updated support for screen casting from all monitors in ScreenGear API.
+ * Updated ScreenGear API to use *Threaded Queue Mode* by default, thereby removed redundant `THREADED_QUEUE_MODE` param.
+ * Updated Tests bash scripts to use system-specific **Temp** directory instead of **Home** for downloading content.
+ * Updated Wiki-Documentation with latest examples and Information.
* Updated Travis CLI Tests with support for macOS environment
- * Reformatted & implemented necessary changes and dependencies in `travis.yml`.
+ * Reformatted & implemented necessary MacOS related changes and dependencies in `travis.yml`.
### Breaking Updates / Improvements / Changes
* `Python 2.7` legacy support removed from CLI tests.
+ * Newly implemented `secure_mode` will only support Python 3 and above legacies.
### Fixes
- * Fixed unreliable dataset video file URL(rehosted file on github.com)
- * Remove duplicate code to import MSS(@BoboTiG) from NetGear
+ * Fixed assigned Port address ignored bug (commit 073bca1)
+ * Fixed several wrong definition bugs from NetGear API(commit 8f7153c).
+ * Fixed unreliable dataset video URL(rehosted file on `github.com`)
+ * Removed duplicate code to import MSS(@BoboTiG) from ScreenGear API.
+ * Fixed Several bugs related to new `secure_mode` & `multiserver_mode` Modes.
* Fixed various macOS environment bugs
### Pull requests(PR) involved:
* PR #39
* PR #42
+ * PR #44
+ * PR #52
:warning: PyPi Release does NOT contain Tests and Scripts!
diff --git a/scripts/bash/install_opencv.sh b/scripts/bash/install_opencv.sh
index f3323c2e1..5db9428e8 100644
--- a/scripts/bash/install_opencv.sh
+++ b/scripts/bash/install_opencv.sh
@@ -20,6 +20,10 @@
OPENCV_VERSION='4.1.0'
+#determining system specific temp directory
+TMPDIR=$(dirname "$(mktemp tmp.XXXXXXXXXX -ut)")
+
+#determining system Python suffix and version
PYTHONSUFFIX=$(python -c 'import platform; a = platform.python_version(); print(".".join(a.split(".")[:2]))')
PYTHONVERSION=$(python -c 'import platform; print(platform.python_version())')
@@ -41,7 +45,7 @@ sudo apt-get install -y libgstreamer-plugins-base1.0-dev libgstreamer1.0-dev gst
echo "Installing OpenCV Library"
-cd $HOME
+cd $TMPDIR
wget https://github.com/abhiTronix/OpenCV-Travis-Builds/releases/download/latest/OpenCV-$OPENCV_VERSION-$PYTHONVERSION.deb
diff --git a/scripts/bash/prepare_dataset.sh b/scripts/bash/prepare_dataset.sh
index fd31ae0d9..d0b5b223c 100644
--- a/scripts/bash/prepare_dataset.sh
+++ b/scripts/bash/prepare_dataset.sh
@@ -12,10 +12,12 @@
#The above copyright notice and this permission notice shall be included in all
#copies or substantial portions of the Software.
+#determining system specific temp directory
+TMPDIR=$(dirname "$(mktemp tmp.XXXXXXXXXX -ut)")
# Creating necessary directories
-mkdir -p $HOME/Downloads
-mkdir -p $HOME/Downloads/{FFmpeg_static,Test_videos}
+mkdir -p $TMPDIR/Downloads
+mkdir -p $TMPDIR/Downloads/{FFmpeg_static,Test_videos}
# Acknowledging machine architecture
MACHINE_BIT=$(uname -m)
@@ -38,7 +40,7 @@ esac
#Download and Configure FFmpeg Static
-cd $HOME/Downloads/FFmpeg_static
+cd $TMPDIR/Downloads/FFmpeg_static
if [ $OS_NAME = "linux" ]; then
@@ -81,7 +83,7 @@ else
fi
# Downloading Test Data
-cd $HOME/Downloads/Test_videos
+cd $TMPDIR/Downloads/Test_videos
echo "Downloading Test-Data..."
curl http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4 -o BigBuckBunny.mp4
diff --git a/vidgear/gears/camgear.py b/vidgear/gears/camgear.py
index 6473fabc9..940f40882 100644
--- a/vidgear/gears/camgear.py
+++ b/vidgear/gears/camgear.py
@@ -53,10 +53,10 @@
if parse_version(cv2.__version__) >= parse_version('3'):
pass
else:
- raise ImportError('OpenCV library version >= 3.0 is only supported by this library')
+ raise ImportError('[ERROR]: OpenCV library version >= 3.0 is only supported by this library')
except ImportError as error:
- raise ImportError('Failed to detect OpenCV executables, install it with `pip install opencv-contrib-python` command.')
+ raise ImportError('[ERROR]: Failed to detect OpenCV executables, install it with `pip install opencv-python` command.')
@@ -140,7 +140,7 @@ def __init__(self, source = 0, y_tube = False, backend = 0, colorspace = None, l
except Exception as e:
if logging:
print(e)
- raise ValueError('YouTube Mode is enabled and the input YouTube Url is invalid!')
+ raise ValueError('[ERROR]: YouTube Mode is enabled and the input YouTube Url is invalid!')
# youtube mode variable initialization
self.youtube_mode = y_tube
diff --git a/vidgear/gears/helper.py b/vidgear/gears/helper.py
index c7ff58280..6eb33aa91 100644
--- a/vidgear/gears/helper.py
+++ b/vidgear/gears/helper.py
@@ -54,7 +54,7 @@ def check_CV_version():
def capPropId(property):
"""
- Retrieves the Property's Integer(Actual) value.
+ Retrieves the OpenCV property Integer(Actual) value.
"""
return getattr(cv2, property)
diff --git a/vidgear/gears/netgear.py b/vidgear/gears/netgear.py
index dba4da99f..b01989eab 100644
--- a/vidgear/gears/netgear.py
+++ b/vidgear/gears/netgear.py
@@ -41,9 +41,9 @@
if parse_version(cv2.__version__) >= parse_version('3'):
pass
else:
- raise ImportError('OpenCV library version >= 3.0 is only supported by this library')
+ raise ImportError('[ERROR]: OpenCV library version >= 3.0 is only supported by this library')
except ImportError as error:
- raise ImportError('Failed to detect OpenCV executables, install it with `pip install opencv-contrib-python` command.')
+ raise ImportError('[ERROR]: Failed to detect OpenCV executables, install it with `pip install opencv-python` command.')
@@ -133,7 +133,7 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
self.ZMQError = ZMQError
except ImportError as error:
#raise error
- raise ImportError('pyzmq python library not installed. Kindly install it with `pip install pyzmq` command.')
+ raise ImportError('[ERROR]: pyzmq python library not installed. Kindly install it with `pip install pyzmq` command.')
#log and enable threaded queue mode
if logging:
@@ -299,7 +299,7 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
# check if unique server port address list/tuple is assigned or not in multiserver_mode
if port is None or not isinstance(port, (tuple, list)):
# raise error if not
- raise ValueError('Incorrect port value! Kindly provide a list/tuple of ports at Receiver-end while Multi-Server mode is enabled. For more information refer VidGear docs.')
+ raise ValueError('[ERROR]: Incorrect port value! Kindly provide a list/tuple of ports at Receiver-end while Multi-Server mode is enabled. For more information refer VidGear docs.')
else:
#otherwise log it
print('[LOG]: Enabling Multi-Server Mode at PORTS: {}!'.format(port))
@@ -474,7 +474,7 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
#log if errored
if self.secure_mode: print('Failed to activate ZMQ Security Mechanism: `{}` for this address!'.format(valid_security_mech[self.secure_mode]))
# raise value error
- raise ValueError('Failed to connect address: {} and pattern: {}! Kindly recheck all parameters.'.format((protocol+'://' + str(address) + ':' + str(port)), pattern))
+ raise ValueError('[ERROR]: Failed to connect address: {} and pattern: {}! Kindly recheck all parameters.'.format((protocol+'://' + str(address) + ':' + str(port)), pattern))
if logging:
#finally log progress
diff --git a/vidgear/gears/pigear.py b/vidgear/gears/pigear.py
index 694c5a268..e921323ec 100644
--- a/vidgear/gears/pigear.py
+++ b/vidgear/gears/pigear.py
@@ -52,7 +52,7 @@ class PiGear:
PiGear is similar to CamGear but exclusively made to support various Raspberry Pi Camera Modules
(such as OmniVision OV5647 Camera Module and Sony IMX219 Camera Module). To interface with these
modules correctly, PiGear provides a flexible multi-threaded wrapper around complete picamera
- python library and provides us the ability to exploit its various features like brightness, saturation, sensor_mode, etc. effortlessly.
+ python library and provides us the ability to exploit its various features like `brightness, saturation, sensor_mode`, etc. effortlessly.
:param (tuple) resolution: sets the resolution (width,height). Its default value is (640,480).
@@ -81,7 +81,7 @@ def __init__(self, resolution=(640, 480), framerate=25, colorspace = None, loggi
#print(cv2.__version__)
except ImportError as error:
# Output expected ImportErrors.
- raise ImportError('Failed to detect Picamera executables, install it with "pip install picamera" command.')
+ raise ImportError('[ERROR]: Failed to detect Picamera executables, install it with "pip install picamera" command.')
# initialize the picamera stream
self.camera = PiCamera()
diff --git a/vidgear/gears/screengear.py b/vidgear/gears/screengear.py
index 7d4cbd9e0..78bea2c79 100644
--- a/vidgear/gears/screengear.py
+++ b/vidgear/gears/screengear.py
@@ -39,10 +39,10 @@
if parse_version(cv2.__version__) >= parse_version('3'):
pass
else:
- raise ImportError('OpenCV library version >= 3.0 is only supported by this library')
+ raise ImportError('[ERROR]: OpenCV library version >= 3.0 is only supported by this library')
except ImportError as error:
- raise ImportError('Failed to detect OpenCV executables, install it with `pip install opencv-contrib-python` command.')
+ raise ImportError('[ERROR]: Failed to detect OpenCV executables, install it with `pip install opencv-python` command.')
@@ -85,7 +85,7 @@ def __init__(self, monitor = 1, colorspace = None, logging = False, **options):
from mss.exception import ScreenShotError
except ImportError as error:
# otherwise raise import error
- raise ImportError('python-mss library not found, install it with `pip install mss` command.')
+ raise ImportError('[ERROR]: python-mss library not found, install it with `pip install mss` command.')
# create mss object
self.mss_object = mss()
# create monitor instance for the user-defined monitor
@@ -93,7 +93,7 @@ def __init__(self, monitor = 1, colorspace = None, logging = False, **options):
if (monitor >= 0):
monitor_instance = self.mss_object.monitors[monitor]
else:
- raise ValueError("`monitor` value cannot be negative, Read Docs!")
+ raise ValueError("[ERROR]: `monitor` value cannot be negative, Read Docs!")
# Initialize Queue
self.queue = None
@@ -136,7 +136,7 @@ def __init__(self, monitor = 1, colorspace = None, logging = False, **options):
self.queue.append(self.frame)
except ScreenShotError:
#otherwise catch and log errors
- raise ValueError("ScreenShotError caught: Wrong dimensions passed to python-mss, Kindly Refer Docs!")
+ raise ValueError("[ERROR]: ScreenShotError caught: Wrong dimensions passed to python-mss, Kindly Refer Docs!")
if logging:
print(self.mss_object.get_error_details())
diff --git a/vidgear/gears/writegear.py b/vidgear/gears/writegear.py
index 4de8c3d05..b0d0c1fc4 100755
--- a/vidgear/gears/writegear.py
+++ b/vidgear/gears/writegear.py
@@ -41,19 +41,18 @@
if parse_version(cv2.__version__) >= parse_version('3'):
pass
else:
- raise ImportError('OpenCV library version >= 3.0 is only supported by this library')
+ raise ImportError('[ERROR]: OpenCV library version >= 3.0 is only supported by this library')
except ImportError as error:
- raise ImportError('Failed to detect OpenCV executables, install it with `pip install opencv-contrib-python` command.')
+ raise ImportError('[ERROR]: Failed to detect OpenCV executables, install it with `pip install opencv-contrib-python` command.')
class WriteGear:
"""
- WriteGear solely handles various powerful FFmpeg tools that allow us to do almost anything you can imagine with multimedia files.
- With WriteGear API, you can process real-time video frames into a lossless format and specification suitable for our playback in
- just a few lines of codes. These specifications include setting bitrate, codec, framerate, resolution, subtitles, compression, etc.
- Furthermore, we can multiplex extracted audio at the output with compression and all that in real-time(see this example).
+ solely handles various powerful FFmpeg tools that allow us to do almost anything you can imagine with multimedia files.
+ With WriteGear API, you can process real-time video frames into a lossless format and specification suitable for our playback
+ in just a few lines of codes. These specifications include setting bitrate, codec, framerate, resolution, subtitles, compression, etc.
In addition to this, WriteGear also provides flexible access to OpenCV's VideoWriter API which provide some basic tools
for video frames encoding but without compression.
@@ -61,11 +60,11 @@ class WriteGear:
Compression Mode: In this mode, WriteGear utilizes FFmpeg's inbuilt encoders to encode lossless multimedia files.
It provides us the ability to exploit almost any available parameters available within FFmpeg, with so much ease
- and flexibility and while doing that it robustly handles all errors/warnings quietly. You can find more about this mode here.
+ and flexibility and while doing that it robustly handles all errors/warnings quietly.
Non-Compression Mode: In this mode, WriteGear utilizes basic OpenCV's inbuilt VideoWriter API. Similar to compression mode, WriteGear also supports
all parameters manipulation available within OpenCV's VideoWriter API. But this mode lacks the ability to manipulate encoding parameters
- and other important features like video compression, audio encoding, etc. You can learn about this mode here.
+ and other important features like video compression, audio encoding, etc.
```Warning: In case, This class fails to detect valid FFmpeg executables on your system, It can automatically fallbacks to Non-Compression Mode.```
@@ -119,7 +118,7 @@ def __init__(self, output_filename = '', compression_mode = True , custom_ffmpeg
# handles output file name (if not given)
if not output_filename:
- raise ValueError('Kindly provide a valid `output_filename` value, Refer VidGear Docs for more information!')
+ raise ValueError('[ERROR]: Kindly provide a valid `output_filename` value, Refer VidGear Docs for more information!')
elif output_filename and os.path.isdir(output_filename): # check if directory path is given instead
output_filename = os.path.join(output_filename, 'VidGear-{}.mp4'.format(time.strftime("%Y%m%d-%H%M%S"))) # auto-assign valid name and adds it to path
else:
@@ -142,7 +141,7 @@ def __init__(self, output_filename = '', compression_mode = True , custom_ffmpeg
except Exception as e:
if self.logging:
print(e)
- raise ValueError('Wrong output_params parameters passed to WriteGear class!')
+ raise ValueError('[ERROR]: Wrong output_params parameters passed to WriteGear class!')
#handles FFmpeg binaries validity tests
if self.compression:
@@ -218,10 +217,10 @@ def write(self, frame, rgb_mode = False):
#validate size of frame
if height != self.inputheight or width != self.inputwidth:
- raise ValueError('All frames in a video should have same size')
+ raise ValueError('[ERROR]: All frames in a video should have same size')
#validate number of channels
if channels != self.inputchannels:
- raise ValueError('All frames in a video should have same number of channels')
+ raise ValueError('[ERROR]: All frames in a video should have same number of channels')
if self.compression:
# checks if compression mode is enabled
@@ -238,7 +237,7 @@ def write(self, frame, rgb_mode = False):
self.process.stdin.write(frame.tostring())
except (OSError, IOError):
# log something is wrong!
- print ('BrokenPipeError caught: Wrong Values passed to FFmpeg Pipe, Kindly Refer Docs!')
+ print ('[ERROR]: BrokenPipeError caught: Wrong Values passed to FFmpeg Pipe, Kindly Refer Docs!')
self.DEVNULL.close()
raise ValueError #for testing purpose only
else:
@@ -373,7 +372,7 @@ def startCV_Process(self):
FOURCC = cv2.VideoWriter_fourcc(*(value.upper()))
elif key == '-fps':
FPS = float(value)
- elif key =='-backend' and value.upper() in ['CAP_FFMPEG','CAP_GSTREAMER']:
+ elif key =='-backend':
BACKEND = capPropId(value.upper())
elif key == '-color':
COLOR = bool(int(value))
@@ -384,7 +383,7 @@ def startCV_Process(self):
# log if something is wrong
if self.logging:
print(e)
- raise ValueError('Wrong Values passed to OpenCV Writer, Kindly Refer Docs!')
+ raise ValueError('[ERROR]: Wrong Values passed to OpenCV Writer, Kindly Refer Docs!')
if self.logging:
#log values for debugging
diff --git a/vidgear/tests/benchmark_tests/test_benchmark_Videocapture.py b/vidgear/tests/benchmark_tests/test_benchmark_Videocapture.py
index 7ef0b29f0..5b89dd3e9 100644
--- a/vidgear/tests/benchmark_tests/test_benchmark_Videocapture.py
+++ b/vidgear/tests/benchmark_tests/test_benchmark_Videocapture.py
@@ -26,6 +26,7 @@
import os
import cv2
import pytest
+import tempfile
from vidgear.gears import CamGear
from .fps import FPS
@@ -35,7 +36,7 @@ def return_testvideo_path():
"""
return Test Video Data path
"""
- path = '{}/Downloads/Test_videos/BigBuckBunny.mp4'.format(os.environ['USERPROFILE'] if os.name == 'nt' else os.environ['HOME'])
+ path = '{}/Downloads/Test_videos/BigBuckBunny.mp4'.format(tempfile.gettempdir())
return os.path.abspath(path)
diff --git a/vidgear/tests/benchmark_tests/test_benchmark_Videowriter.py b/vidgear/tests/benchmark_tests/test_benchmark_Videowriter.py
index 6d2b4af43..4d45b928d 100644
--- a/vidgear/tests/benchmark_tests/test_benchmark_Videowriter.py
+++ b/vidgear/tests/benchmark_tests/test_benchmark_Videowriter.py
@@ -25,6 +25,7 @@
import os, platform
import pytest
+import tempfile
from vidgear.gears import WriteGear
from vidgear.gears import VideoGear
from .fps import FPS
@@ -35,7 +36,7 @@ def return_testvideo_path():
"""
return Test Video Data path
"""
- path = '{}/Downloads/Test_videos/BigBuckBunny.mp4'.format(os.environ['USERPROFILE'] if os.name == 'nt' else os.environ['HOME'])
+ path = '{}/Downloads/Test_videos/BigBuckBunny.mp4'.format(tempfile.gettempdir())
return os.path.abspath(path)
@@ -46,11 +47,11 @@ def return_static_ffmpeg():
"""
path = ''
if platform.system() == 'Windows':
- path += os.path.join(os.environ['USERPROFILE'],'Downloads/FFmpeg_static/ffmpeg/bin/ffmpeg.exe')
+ path += os.path.join(tempfile.gettempdir(),'Downloads/FFmpeg_static/ffmpeg/bin/ffmpeg.exe')
elif platform.system() == 'Darwin':
- path += os.path.join(os.environ['HOME'],'Downloads/FFmpeg_static/ffmpeg/bin/ffmpeg')
+ path += os.path.join(tempfile.gettempdir(),'Downloads/FFmpeg_static/ffmpeg/bin/ffmpeg')
else:
- path += os.path.join(os.environ['HOME'],'Downloads/FFmpeg_static/ffmpeg/ffmpeg')
+ path += os.path.join(tempfile.gettempdir(),'Downloads/FFmpeg_static/ffmpeg/ffmpeg')
return os.path.abspath(path)
diff --git a/vidgear/tests/benchmark_tests/test_benchmark_playback.py b/vidgear/tests/benchmark_tests/test_benchmark_playback.py
index 482f296b6..ffc58eb99 100644
--- a/vidgear/tests/benchmark_tests/test_benchmark_playback.py
+++ b/vidgear/tests/benchmark_tests/test_benchmark_playback.py
@@ -25,6 +25,7 @@
import os, platform
import pytest
+import tempfile
from vidgear.gears import CamGear
from .fps import FPS
@@ -35,7 +36,7 @@ def return_testvideo(level=0):
return Test Video Data path with different Video quality/resolution/bitrate for different levels(Level-0(Lowest ~HD 2Mbps) and Level-5(Highest ~4k UHD 120mpbs))
"""
Levels = ['BigBuckBunny.mp4','20_mbps_hd_hevc_10bit.mkv','50_mbps_hd_h264.mkv','90_mbps_hd_hevc_10bit.mkv','120_mbps_4k_uhd_h264.mkv']
- path = '{}/Downloads/Test_videos/{}'.format(os.environ['USERPROFILE'] if os.name == 'nt' else os.environ['HOME'], Levels[level])
+ path = '{}/Downloads/Test_videos/{}'.format(tempfile.gettempdir(), Levels[level])
return os.path.abspath(path)
diff --git a/vidgear/tests/test_helper.py b/vidgear/tests/test_helper.py
index decf1404b..09a6ea9a6 100644
--- a/vidgear/tests/test_helper.py
+++ b/vidgear/tests/test_helper.py
@@ -37,11 +37,11 @@ def return_static_ffmpeg():
"""
path = ''
if platform.system() == 'Windows':
- path += os.path.join(os.environ['USERPROFILE'],'Downloads/FFmpeg_static/ffmpeg/bin/ffmpeg.exe')
+ path += os.path.join(tempfile.gettempdir(),'Downloads/FFmpeg_static/ffmpeg/bin/ffmpeg.exe')
elif platform.system() == 'Darwin':
- path += os.path.join(os.environ['HOME'],'Downloads/FFmpeg_static/ffmpeg/bin/ffmpeg')
+ path += os.path.join(tempfile.gettempdir(),'Downloads/FFmpeg_static/ffmpeg/bin/ffmpeg')
else:
- path += os.path.join(os.environ['HOME'],'Downloads/FFmpeg_static/ffmpeg/ffmpeg')
+ path += os.path.join(tempfile.gettempdir(),'Downloads/FFmpeg_static/ffmpeg/ffmpeg')
return os.path.abspath(path)
@@ -49,7 +49,7 @@ def test_ffmpeg_static_installation():
"""
Auxilary Tests to ensure successful Static FFmpeg Installation on Windows through script
"""
- startpath = os.path.abspath(os.path.join( os.environ['USERPROFILE'] if os.name == 'nt' else os.environ['HOME'],'Downloads/FFmpeg_static'))
+ startpath = os.path.abspath(tempfile.gettempdir(),'Downloads/FFmpeg_static')
for root, dirs, files in os.walk(startpath):
level = root.replace(startpath, '').count(os.sep)
indent = ' ' * 4 * (level)
diff --git a/vidgear/tests/videocapture_tests/test_camgear.py b/vidgear/tests/videocapture_tests/test_camgear.py
index 3750ad0c0..d63e7e1fc 100644
--- a/vidgear/tests/videocapture_tests/test_camgear.py
+++ b/vidgear/tests/videocapture_tests/test_camgear.py
@@ -28,6 +28,7 @@
import platform
import os, time
import pytest
+import tempfile
import numpy as np
from vidgear.gears import CamGear
@@ -49,7 +50,7 @@ def return_testvideo_path():
"""
return Test Video Data path
"""
- path = '{}/Downloads/Test_videos/BigBuckBunny_4sec.mp4'.format(os.environ['USERPROFILE'] if os.name == 'nt' else os.environ['HOME'])
+ path = '{}/Downloads/Test_videos/BigBuckBunny_4sec.mp4'.format(tempfile.gettempdir())
return os.path.abspath(path)
diff --git a/vidgear/tests/writer_tests/test_compression_mode.py b/vidgear/tests/writer_tests/test_compression_mode.py
index b3cb22afb..504222698 100644
--- a/vidgear/tests/writer_tests/test_compression_mode.py
+++ b/vidgear/tests/writer_tests/test_compression_mode.py
@@ -42,11 +42,11 @@ def return_static_ffmpeg():
"""
path = ''
if platform.system() == 'Windows':
- path += os.path.join(os.environ['USERPROFILE'],'Downloads/FFmpeg_static/ffmpeg/bin/ffmpeg.exe')
+ path += os.path.join(tempfile.gettempdir(),'Downloads/FFmpeg_static/ffmpeg/bin/ffmpeg.exe')
elif platform.system() == 'Darwin':
- path += os.path.join(os.environ['HOME'],'Downloads/FFmpeg_static/ffmpeg/bin/ffmpeg')
+ path += os.path.join(tempfile.gettempdir(),'Downloads/FFmpeg_static/ffmpeg/bin/ffmpeg')
else:
- path += os.path.join(os.environ['HOME'],'Downloads/FFmpeg_static/ffmpeg/ffmpeg')
+ path += os.path.join(tempfile.gettempdir(),'Downloads/FFmpeg_static/ffmpeg/ffmpeg')
return os.path.abspath(path)
@@ -55,7 +55,7 @@ def return_testvideo_path():
"""
return Test Video Data path
"""
- path = '{}/Downloads/Test_videos/BigBuckBunny_4sec.mp4'.format(os.environ['USERPROFILE'] if os.name == 'nt' else os.environ['HOME'])
+ path = '{}/Downloads/Test_videos/BigBuckBunny_4sec.mp4'.format(tempfile.gettempdir())
return os.path.abspath(path)
diff --git a/vidgear/tests/writer_tests/test_non_compression_mode.py b/vidgear/tests/writer_tests/test_non_compression_mode.py
index f777b0b83..f0ddc4279 100644
--- a/vidgear/tests/writer_tests/test_non_compression_mode.py
+++ b/vidgear/tests/writer_tests/test_non_compression_mode.py
@@ -41,11 +41,11 @@ def return_static_ffmpeg():
"""
path = ''
if platform.system() == 'Windows':
- path += os.path.join(os.environ['USERPROFILE'],'Downloads/FFmpeg_static/ffmpeg/bin/ffmpeg.exe')
+ path += os.path.join(tempfile.gettempdir(),'Downloads/FFmpeg_static/ffmpeg/bin/ffmpeg.exe')
elif platform.system() == 'Darwin':
- path += os.path.join(os.environ['HOME'],'Downloads/FFmpeg_static/ffmpeg/bin/ffmpeg')
+ path += os.path.join(tempfile.gettempdir(),'Downloads/FFmpeg_static/ffmpeg/bin/ffmpeg')
else:
- path += os.path.join(os.environ['HOME'],'Downloads/FFmpeg_static/ffmpeg/ffmpeg')
+ path += os.path.join(tempfile.gettempdir(),'Downloads/FFmpeg_static/ffmpeg/ffmpeg')
return os.path.abspath(path)
@@ -54,7 +54,7 @@ def return_testvideo_path():
"""
return Test Video Data path
"""
- path = '{}/Downloads/Test_videos/BigBuckBunny_4sec.mp4'.format(os.environ['USERPROFILE'] if os.name == 'nt' else os.environ['HOME'])
+ path = '{}/Downloads/Test_videos/BigBuckBunny_4sec.mp4'.format(tempfile.gettempdir())
return os.path.abspath(path)
From a5f84d1bd2daaa1c69e59547bdc639af96baeb74 Mon Sep 17 00:00:00 2001
From: Abhishek
Date: Wed, 9 Oct 2019 12:32:32 +0530
Subject: [PATCH 13/39] Fixed Typo
---
vidgear/tests/test_helper.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/vidgear/tests/test_helper.py b/vidgear/tests/test_helper.py
index 09a6ea9a6..0a87b1e26 100644
--- a/vidgear/tests/test_helper.py
+++ b/vidgear/tests/test_helper.py
@@ -23,7 +23,7 @@
===============================================
"""
-import os, pytest, tempfile, shutil, platform
+import os, pytest, tempfile, shutil, platform, tempfile
from vidgear.gears.helper import download_ffmpeg_binaries
from vidgear.gears.helper import validate_ffmpeg
@@ -49,7 +49,7 @@ def test_ffmpeg_static_installation():
"""
Auxilary Tests to ensure successful Static FFmpeg Installation on Windows through script
"""
- startpath = os.path.abspath(tempfile.gettempdir(),'Downloads/FFmpeg_static')
+ startpath = os.path.abspath(os.path.join(tempfile.gettempdir(),'Downloads/FFmpeg_static'))
for root, dirs, files in os.walk(startpath):
level = root.replace(startpath, '').count(os.sep)
indent = ' ' * 4 * (level)
From 349432bc7a3deeea2c72d9104767dad800255e59 Mon Sep 17 00:00:00 2001
From: Abhishek
Date: Wed, 9 Oct 2019 13:34:39 +0530
Subject: [PATCH 14/39] Fix for MacOS environments
---
scripts/bash/prepare_dataset.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/scripts/bash/prepare_dataset.sh b/scripts/bash/prepare_dataset.sh
index d0b5b223c..7c8f0dd30 100644
--- a/scripts/bash/prepare_dataset.sh
+++ b/scripts/bash/prepare_dataset.sh
@@ -13,7 +13,7 @@
#copies or substantial portions of the Software.
#determining system specific temp directory
-TMPDIR=$(dirname "$(mktemp tmp.XXXXXXXXXX -ut)")
+TMPDIR=$(dirname "$(mktemp tmp.XXXXXXXXXX -dt)")
# Creating necessary directories
mkdir -p $TMPDIR/Downloads
From b0c01235cbd9778e6664069e93efd01de1618083 Mon Sep 17 00:00:00 2001
From: Abhishek
Date: Wed, 9 Oct 2019 14:08:00 +0530
Subject: [PATCH 15/39] Fixes for MacOS environment-2
---
scripts/bash/install_opencv.sh | 4 ++--
scripts/bash/prepare_dataset.sh | 10 +++++-----
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/scripts/bash/install_opencv.sh b/scripts/bash/install_opencv.sh
index 5db9428e8..fd6f400d1 100644
--- a/scripts/bash/install_opencv.sh
+++ b/scripts/bash/install_opencv.sh
@@ -21,7 +21,7 @@
OPENCV_VERSION='4.1.0'
#determining system specific temp directory
-TMPDIR=$(dirname "$(mktemp tmp.XXXXXXXXXX -ut)")
+TMPFOLDER=$(python -c 'import tempfile; print(tempfile.gettempdir())')
#determining system Python suffix and version
PYTHONSUFFIX=$(python -c 'import platform; a = platform.python_version(); print(".".join(a.split(".")[:2]))')
@@ -45,7 +45,7 @@ sudo apt-get install -y libgstreamer-plugins-base1.0-dev libgstreamer1.0-dev gst
echo "Installing OpenCV Library"
-cd $TMPDIR
+cd $TMPFOLDER
wget https://github.com/abhiTronix/OpenCV-Travis-Builds/releases/download/latest/OpenCV-$OPENCV_VERSION-$PYTHONVERSION.deb
diff --git a/scripts/bash/prepare_dataset.sh b/scripts/bash/prepare_dataset.sh
index 7c8f0dd30..cf806a73c 100644
--- a/scripts/bash/prepare_dataset.sh
+++ b/scripts/bash/prepare_dataset.sh
@@ -13,11 +13,11 @@
#copies or substantial portions of the Software.
#determining system specific temp directory
-TMPDIR=$(dirname "$(mktemp tmp.XXXXXXXXXX -dt)")
+TMPFOLDER=$(python -c 'import tempfile; print(tempfile.gettempdir())')
# Creating necessary directories
-mkdir -p $TMPDIR/Downloads
-mkdir -p $TMPDIR/Downloads/{FFmpeg_static,Test_videos}
+mkdir -p $TMPFOLDER/Downloads
+mkdir -p $TMPFOLDER/Downloads/{FFmpeg_static,Test_videos}
# Acknowledging machine architecture
MACHINE_BIT=$(uname -m)
@@ -40,7 +40,7 @@ esac
#Download and Configure FFmpeg Static
-cd $TMPDIR/Downloads/FFmpeg_static
+cd $TMPFOLDER/Downloads/FFmpeg_static
if [ $OS_NAME = "linux" ]; then
@@ -83,7 +83,7 @@ else
fi
# Downloading Test Data
-cd $TMPDIR/Downloads/Test_videos
+cd $TMPFOLDER/Downloads/Test_videos
echo "Downloading Test-Data..."
curl http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4 -o BigBuckBunny.mp4
From 54c3881f5bd591b098d8af749215c1da3082d5cc Mon Sep 17 00:00:00 2001
From: Abhishek
Date: Wed, 9 Oct 2019 18:19:42 +0530
Subject: [PATCH 16/39] Updates for ReadME.md and Minor Fixes
---
README.md | 69 ++++++++++++++++++++----------------
changelog.md | 5 +--
vidgear/gears/pigear.py | 2 +-
vidgear/gears/writegear.py | 2 +-
vidgear/tests/test_helper.py | 2 +-
5 files changed, 45 insertions(+), 35 deletions(-)
diff --git a/README.md b/README.md
index 89fd996c0..07046b7d7 100644
--- a/README.md
+++ b/README.md
@@ -42,7 +42,7 @@ THE SOFTWARE.
-VidGear is a powerful python Video Processing library built with multi-threaded [**Gears**](#gear)(_a.k.a APIs_) each with a unique set of trailblazing features. These APIs provides a easy-to-use, highly extensible, and multi-threaded wrapper around many underlying state-of-the-art python libraries such as *[OpenCV ➶][opencv], [FFmpeg ➶][ffmpeg], [picamera ➶][picamera], [pafy ➶][pafy], [pyzmq ➶][pyzmq] and [python-mss ➶][mss]*
+VidGear is a powerful python Video Processing library built with multi-threaded [**Gears**](#gear)(_a.k.a APIs_) each with a unique set of trailblazing features. These APIs provides a easy-to-use, highly extensible, and multi-threaded wrapper around many underlying state-of-the-art libraries such as *[OpenCV ➶][opencv], [FFmpeg ➶][ffmpeg], [picamera ➶][picamera], [pafy ➶][pafy], [pyzmq ➶][pyzmq] and [python-mss ➶][mss]*
@@ -58,7 +58,7 @@ The following **functional block diagram** clearly depicts the functioning of Vi
[**TL;DR**](#tldr)
-[**New Release : VidGear 0.1.5**](#new-release-sneekpeak--vidgear-015)
+[**New Release SneekPeak**](#new-release-sneekpeak--vidgear-016)
[**Installation Options**](#installation)
* [**Prerequisites**](#prerequisites)
@@ -90,26 +90,28 @@ The following **functional block diagram** clearly depicts the functioning of Vi
## TL;DR
- *VidGear is an [ultrafast➶][ultrafast-wiki], compact, flexible and easy-to-adapt complete Video Processing Python Library.*
+ *"VidGear is an [ultrafast➶][ultrafast-wiki], compact, flexible and easy-to-adapt complete Video Processing Python Library."*
- Built with simplicity in mind, VidGear lets programmers and software developers to easily integrate and perform complex Video Processing tasks in their existing or new applications, without going through various underlying python library's documentation and using just a few lines of code. Beneficial for both, if you're new to Programming with Python language or a pro at it.
+ Built with simplicity in mind, VidGear lets programmers and software developers to easily integrate and perform complex Video Processing tasks in their existing or new applications, without going through various underlying library's documentation and using just a few lines of code. Beneficial for both, if you're new to programming with Python language or already a pro at it.
- For more advanced information see the [Wiki Documentation ➶][wiki].
+ **For more detailed information see the [*Wiki Documentation ➶*][wiki].**
-## New Release SneekPeak : VidGear 0.1.5
- * Released new ScreenGear API, supports Live ScreenCasting.
- * Released new NetGear API, aids real-time frame transfer through messaging(ZmQ) over the network.
- * Released new Stabilizer Class, for minimum latency Video Stabilization with OpenCV.
- * Updated VideoGear API to be used as an internal wrapper around Stabilizer Class.
- * Implemented exclusive Threaded Queue Mode for blazingly fast, synchronized and error-free multi-threading APIs.
- * Added Option to use VidGear API's standalone.
- * Several Performance enhancements and Bugs exterminated.
- * Revamped Docs and [many more...](changelog.md)
+## New Release SneekPeak : VidGear 0.1.6
+ * Added powerful ZMQ Authentication & Data Encryption features for NetGear API:
+ * Added exclusive `secure_mode` param for enabling it.
+ * Added support for two most powerful `Stonehouse` & `Ironhouse` ZMQ security mechanisms.
+ * Added auto auth-certificates/key generation and validation feature.
+ * Implemented Robust Multi-Server support for NetGear API:
+ * Enables Multiple Servers messaging support with a single client.
+ * Added exclusive `multiserver_mode` param for enabling it.
+ * Added ability to send additional data of any datatype along with the frame in realtime in this mode.
+ * Implemented new *Publish/Subscribe(`zmq.PUB/zmq.SUB`)* pattern for seamless Live Streaming in NetGear API.
+ * Added VidGear's official native support for MacOS environment and [many more...](changelog.md)
@@ -117,38 +119,38 @@ The following **functional block diagram** clearly depicts the functioning of Vi
### Prerequisites
-To use VidGear in your python application, you must check the following dependencies before you install VidGear :
+Before installing VidGear, you must verify that the following dependencies are met:
-* Must support [these Python legacies](#supported-python-legacies) and [pip][pip] already installed.
+* Must be using [supported Python legacies](#supported-python-legacies) only and thereby [pip][pip] already installed properly.
* **`OpenCV:`** VidGear must require OpenCV(3.0+) python enabled binaries to be installed on your machine for its core functions. For its installation, you can follow these online tutorials for [linux][OpenCV-linux] and [raspberry pi][OpenCV-pi], otherwise, install it via pip:
```sh
- pip install opencv-python
+ pip install -U opencv-python #or install opencv-contrib-python similarly
```
* **`FFmpeg:`** VidGear must require FFmpeg for its powerful video compression and encoding capabilities. :star2: Follow this [**FFmpeg wiki page**][ffmpeg-wiki] for its installation. :star2:
-* **`picamera:`** Required for using Raspberry Pi Camera Modules(_such as OmniVision OV5647 Camera Module_) on your Raspberry Pi machine. You can easily install it via pip:
+* **`picamera:`** Required if using Raspberry Pi Camera Modules(_such as OmniVision OV5647 Camera Module_) with your Raspberry Pi machine. You can easily install it via pip:
```sh
pip install picamera
```
Also, make sure to enable Raspberry Pi hardware-specific settings prior to using this library.
-* **`mss:`** Required for Screen Casting. Install it via pip:
+* **`mss:`** Required for using Screen Casting. Install it via pip:
```sh
pip install mss
```
-* **`pyzmq:`** Required for transferring video frames through _ZeroMQ messaging system_ over the network. Install it via pip:
+* **`pyzmq:`** Required for transferring live video frames through _ZeroMQ messaging system_ over the network. Install it via pip:
```sh
pip install pyzmq
```
-* **`pafy:`** For direct YouTube Video streaming, Vidgear needs [`pafy`][pafy] and latest [`youtube-dl`][yt-dl](as pafy's backend) python libraries installed. Install it via pip:
+* **`pafy:`** Required for direct YouTube Video streaming capabilities. Both [`pafy`][pafy] and latest only [`youtube-dl`][yt-dl](_as pafy's backend_) libraries must be installed via pip as follows:
```sh
pip install pafy
@@ -177,7 +179,7 @@ VidGear releases are available for download as packages in the [latest release][
### Option 3: Clone the Repository
-> Best option for **automatically installing required dependencies**(_except FFmpeg_), or for **latest patches**(_maybe experimental_), or **contributing** to development.
+> Best option for **latest patches**(_maybe experimental_), or **contributing** to development.
You can clone this repository's `testing` branch for development and thereby can install as follows:
```sh
@@ -191,7 +193,7 @@ You can clone this repository's `testing` branch for development and thereby can
## Gears:
-VidGear is built with multi-threaded **Gears** each with some unique function/mechanism. Each **Gear** is designed exclusively to handle/control different device-specific video streams, network streams, and media encoders. These APIs provides an easy-to-use, highly extensible, and a multi-threaded wrapper around many underlying various python libraries to exploit their features and functions directly while providing robust error-handling.
+VidGear is built with multi-threaded **Gears** each with some unique function/mechanism. Each **Gear** is designed exclusively to handle/control different device-specific video streams, network streams, and media encoders. These APIs provides an easy-to-use, highly extensible, and a multi-threaded wrapper around various underlying libraries to exploit their features and functions directly while providing robust error-handling.
**These Gears can be classified as follows:**
@@ -214,7 +216,7 @@ VidGear is built with multi-threaded **Gears** each with some unique function/me
### CamGear
-CamGear supports a diverse range of video streams which can handle/control video stream almost any IP/USB Cameras, multimedia video file format ([_upto 4k tested_][test-4k]), network stream URL such as `http(s), rtp, rstp, mms, etc.` In addition to this, it also supports live Gstreamer's RAW pipelines and YouTube video/livestreams URLs. CamGear provides a flexible, high-level multi-threaded wrapper around `OpenCV's` [VideoCapture class][opencv-vc] with access almost all of its available parameters and also employs `pafy's` APIs for live [YouTube streaming][youtube-wiki]. Furthermore, CamGear relies exclusively on [**Threaded Queue mode**][TQM-wiki] for ultra-fast, error-free and synchronized frame handling.
+CamGear supports a diverse range of video streams which can handle/control video stream almost any IP/USB Cameras, multimedia video file format ([_upto 4k tested_][test-4k]), network stream URL such as `http(s), rtp, rstp, mms, etc.` In addition to this, it also supports live Gstreamer's RAW pipelines and YouTube video/livestreams URLs. CamGear provides a flexible, high-level multi-threaded wrapper around `OpenCV's` [VideoCapture class][opencv-vc] with access almost all of its available parameters and also employs `pafy` python APIs for live [YouTube streaming][youtube-wiki]. Furthermore, CamGear relies exclusively on [**Threaded Queue mode**][TQM-wiki] for ultra-fast, error-free and synchronized frame handling.
**Following simplified functional block diagram depicts CamGear API's generalized working:**
@@ -368,7 +370,7 @@ stream.stop()
### WriteGear
-WriteGear is undoubtedly the most powerful Video Processing Gear of them all. It solely handles various powerful FFmpeg tools that allow us to do almost anything you can imagine with multimedia files. With WriteGear API, you can process real-time video frames into a lossless format and specification suitable for our playback in just a few lines of codes. These specifications include setting bitrate, codec, framerate, resolution, subtitles, compression, etc. Furthermore, we can multiplex extracted audio at the output with compression and all that in real-time(see this example). In addition to this, WriteGear also provides flexible access to OpenCV's VideoWriter API which provides some basic tools for video frames encoding but without compression.
+WriteGear is undoubtedly the most powerful Video Processing Gear of them all. It solely handles various powerful FFmpeg tools that provide us the freedom to do almost anything imagine with multimedia files. For example with WriteGear API, we can process real-time video frames into a lossless compressed format with any suitable specification in just few easy [lines of codes][compression-mode-ex]. These specifications include setting any video/audio property such as `bitrate, codec, framerate, resolution, subtitles, etc.` Furthermore, we can multiplex extracted audio at the output with compression and all that in real-time(see this [example wiki][live-audio-wiki]). In addition to this, WriteGear also provides flexible access to OpenCV's VideoWriter API which provides some basic tools for video frames encoding but without compression.
**WriteGear primarily operates in the following two modes:**
@@ -390,14 +392,19 @@ WriteGear is undoubtedly the most powerful Video Processing Gear of them all. It
### NetGear
-NetGear is exclusively designed to transfer video frames synchronously between interconnecting systems over the network in real-time. This is achieved by implementing a high-level wrapper around [PyZmQ][pyzmq] python library that contains python bindings for [ZeroMQ](http://zeromq.org/) - a high-performance asynchronous distributed messaging library that aim to be used in distributed or concurrent applications. It provides a message queue, but unlike message-oriented middleware, a ZeroMQ system can run without a dedicated message broker. Furthermore, NetGear currently supports two ZeroMQ messaging patterns: i.e `zmq.PAIR` and `zmq.REQ and zmq.REP`.
+NetGear is exclusively designed to transfer video frames synchronously between interconnecting systems over the network in real-time. This is achieved by implementing a high-level wrapper around [PyZmQ][pyzmq] python library that contains python bindings for [ZeroMQ](http://zeromq.org/) - a high-performance asynchronous distributed messaging library that aim to be used in distributed or concurrent applications. It provides a message queue, but unlike message-oriented middleware, a ZeroMQ system can run without a dedicated message broker. Furthermore, It also provides easy access to powerful, smart & secure ZeroMQ's Security Layers in NetGear API that enables strong encryption on data, and unbreakable authentication between the Server and the Client with the help of custom certificates/keys and brings cheap, standardized privacy and authentication for distributed systems over the network. On top of that, this API can robustly handle Multiple Servers at once, thereby providing access to seamless Live Streams of the various device in a network at the same time.
-**NetGear API has two modes of operations:**
- * **Send Mode:**(a.k.a Server configuration) which employs `send()` function to send frames to the client(s). You can use this function to send messages to client(s) too.
- * **Receive Mode:**(a.k.a Client configuration) which employs `recv()` function to receive frames sent by server. Client send back confirmation when frame is received successfully.
+**NetGear as of now seamlessly supports three ZeroMQ messaging patterns:**
-**Following functional block diagram depicts NetGear API:**
+ * ZMQ Pair Pattern
+ * ZMQ Client/Server Pattern
+ * ZMQ Publish/Subscribe Pattern
+
+whereas the supported protocol are: `tcp, upd, pgm, inproc, ipc`.
+
+
+**Following functional block diagram depicts generalized functioning of NetGear API:**
@@ -511,6 +518,8 @@ Internal URLs
[wiki]:https://github.com/abhiTronix/vidgear/wiki
[wiki-vidgear-purpose]:https://github.com/abhiTronix/vidgear/wiki/Project-Motivation#why-is-vidgear-a-thing
[ultrafast-wiki]:https://github.com/abhiTronix/vidgear/wiki/FAQ-&-Troubleshooting#2-vidgear-is-ultrafast-but-how
+[compression-mode-ex]:https://github.com/abhiTronix/vidgear/wiki/Compression-Mode:-FFmpeg#1-writegear-bare-minimum-examplecompression-mode
+[live-audio-wiki]:https://github.com/abhiTronix/vidgear/wiki/Working-with-Audio#a-live-audio-input-to-writegear-class
[ffmpeg-wiki]:https://github.com/abhiTronix/vidgear/wiki/FFmpeg-Installation
[youtube-wiki]:https://github.com/abhiTronix/vidgear/wiki/CamGear#2-camgear-api-with-live-youtube-piplineing-using-video-url
[TQM-wiki]:https://github.com/abhiTronix/vidgear/wiki/Threaded-Queue-Mode
diff --git a/changelog.md b/changelog.md
index a6d3d2326..6187fe92b 100644
--- a/changelog.md
+++ b/changelog.md
@@ -7,11 +7,11 @@
* Added exclusive `secure_mode` param for enabling it.
* Added support for two most powerful `Stonehouse` & `Ironhouse` ZMQ security mechanisms.
* Added smart auth-certificates/key generation and validation features.
- * Implemented Robust Multi-Server support for NetGear API.
+ * Implemented Robust Multi-Server support for NetGear API:
* Enables Multiple Servers messaging support with a single client.
* Added exclusive `multiserver_mode` param for enabling it.
* Added ability to send additional data of any datatype along with the frame in realtime in this mode.
- * Implemented new *Publish/Subscribe(`zmq.PUB/zmq.SUB`)* pattern for seamless Live Streaming.
+ * Implemented new *Publish/Subscribe(`zmq.PUB/zmq.SUB`)* pattern for seamless Live Streaming in NetGear API.
* Added VidGear's official native support for MacOS environment.
### Updates/Improvements:
@@ -39,6 +39,7 @@
* PR #42
* PR #44
* PR #52
+ * PR #55
:warning: PyPi Release does NOT contain Tests and Scripts!
diff --git a/vidgear/gears/pigear.py b/vidgear/gears/pigear.py
index e921323ec..7f6088815 100644
--- a/vidgear/gears/pigear.py
+++ b/vidgear/gears/pigear.py
@@ -42,7 +42,7 @@
raise ImportError('OpenCV library version >= 3.0 is only supported by this library')
except ImportError as error:
- raise ImportError('Failed to detect OpenCV executables, install it with `pip install opencv-contrib-python` command.')
+ raise ImportError('Failed to detect OpenCV executables, install it with `pip install opencv-python` command.')
diff --git a/vidgear/gears/writegear.py b/vidgear/gears/writegear.py
index b0d0c1fc4..bc46c688e 100755
--- a/vidgear/gears/writegear.py
+++ b/vidgear/gears/writegear.py
@@ -43,7 +43,7 @@
else:
raise ImportError('[ERROR]: OpenCV library version >= 3.0 is only supported by this library')
except ImportError as error:
- raise ImportError('[ERROR]: Failed to detect OpenCV executables, install it with `pip install opencv-contrib-python` command.')
+ raise ImportError('[ERROR]: Failed to detect OpenCV executables, install it with `pip install opencv-python` command.')
diff --git a/vidgear/tests/test_helper.py b/vidgear/tests/test_helper.py
index 0a87b1e26..a96d204e5 100644
--- a/vidgear/tests/test_helper.py
+++ b/vidgear/tests/test_helper.py
@@ -23,7 +23,7 @@
===============================================
"""
-import os, pytest, tempfile, shutil, platform, tempfile
+import os, pytest, tempfile, shutil, platform
from vidgear.gears.helper import download_ffmpeg_binaries
from vidgear.gears.helper import validate_ffmpeg
From d9a4c133f6ab73468cd4fc23ebc8e5f3a054004e Mon Sep 17 00:00:00 2001
From: Abhishek
Date: Wed, 30 Oct 2019 14:06:06 +0530
Subject: [PATCH 17/39] PiGear: Bugfixes for #61 - Fixed ValueError on picamera
API failure. - Improved Safe Handling of any stream related api failure. -
PiGear will now raise RuntimeError Exception on any API failure.
---
vidgear/gears/pigear.py | 88 ++++++++++++++++++++++-------------------
1 file changed, 47 insertions(+), 41 deletions(-)
diff --git a/vidgear/gears/pigear.py b/vidgear/gears/pigear.py
index 7f6088815..8d9a3a631 100644
--- a/vidgear/gears/pigear.py
+++ b/vidgear/gears/pigear.py
@@ -72,7 +72,7 @@ class PiGear:
"""
- def __init__(self, resolution=(640, 480), framerate=25, colorspace = None, logging = False, time_delay = 0, **options):
+ def __init__(self, resolution=(640, 480), framerate=30, colorspace = None, logging = False, time_delay = 0, **options):
try:
import picamera
@@ -115,12 +115,15 @@ def __init__(self, resolution=(640, 480), framerate=25, colorspace = None, loggi
self.rawCapture = PiRGBArray(self.camera, size=resolution)
self.stream = self.camera.capture_continuous(self.rawCapture,format="bgr", use_video_port=True)
- #frame variable initialization
- for stream in self.stream:
+ #frame variable initialization
+ try:
+ stream = next(self.stream)
self.frame = stream.array
self.rawCapture.seek(0)
self.rawCapture.truncate()
- break
+ except Exception as e:
+ print(e)
+ raise RuntimeError('[ERROR]: Camera Module failed to initialize!')
# applying time delay to warm-up picamera only if specified
if time_delay:
@@ -154,50 +157,53 @@ def update(self):
Update frames from stream
"""
# keep looping infinitely until the thread is terminated
- try:
- for stream in self.stream:
+ while True:
+
+ #check for termination flag
+ if self.terminate: break
+
+ try:
+ #Try to iterate next frame from generator
+ stream = next(self.stream)
+ except Exception as e:
+ #Handle if Something is wrong
+ if isinstance(e, (StopIteration, ValueError)):
+ #raise picamera API's failures
+ raise RuntimeError('[ERROR]: Camera Module API failure occured!')
+ if self.logging: print(traceback.format_exc()) #log traceback for debugging errors
+ break
+
# grab the frame from the stream and clear the stream in
# preparation for the next frame
- if stream is None:
- if self.logging:
- print('[LOG]: The Camera Module is not working Properly!')
- self.terminate = True
- if self.terminate:
- break
- frame = stream.array
- self.rawCapture.seek(0)
- self.rawCapture.truncate()
-
- if not(self.color_space is None):
- # apply colorspace to frames
- color_frame = None
- try:
- if isinstance(self.color_space, int):
- color_frame = cv2.cvtColor(frame, self.color_space)
- else:
- self.color_space = None
- if self.logging:
- print('[LOG]: Colorspace value {} is not a valid Colorspace!'.format(self.color_space))
-
- except Exception as e:
- # Catch if any error occurred
+ frame = stream.array
+ self.rawCapture.seek(0)
+ self.rawCapture.truncate()
+
+ #apply colorspace if specified
+ if not(self.color_space is None):
+ # apply colorspace to frames
+ color_frame = None
+ try:
+ if isinstance(self.color_space, int):
+ color_frame = cv2.cvtColor(frame, self.color_space)
+ else:
self.color_space = None
if self.logging:
- print(e)
- print('[LOG]: Input Colorspace is not a valid Colorspace!')
+ print('[LOG]: Colorspace value {} is not a valid Colorspace!'.format(self.color_space))
+
+ except Exception as e:
+ # Catch if any error occurred
+ self.color_space = None
+ if self.logging:
+ print(e)
+ print('[LOG]: Input Colorspace is not a valid Colorspace!')
- if not(color_frame is None):
- self.frame = color_frame
- else:
- self.frame = frame
+ if not(color_frame is None):
+ self.frame = color_frame
else:
self.frame = frame
-
- except Exception as e:
- if self.logging:
- print(traceback.format_exc())
- self.terminate =True
- pass
+ else:
+ self.frame = frame
# release picamera resources
self.stream.close()
From 46a472f52d660f0cc3fac62cc9c772882061ffc8 Mon Sep 17 00:00:00 2001
From: Abhishek
Date: Wed, 6 Nov 2019 10:09:38 +0530
Subject: [PATCH 18/39] Major Updates and BugFixes for PiGear - added new
`camera_num` to support multiple Picameras(#64) - moved thread exceptions to
main thread and then re-raised(#61) - added new threaded timeout function to
handle any hardware failures/frozen threads(#61) - PiGear will not exit
safely if Picamera ribbon cable is pulled out to save resources - Minor
tweaks for more robust overall error handling.
---
vidgear/gears/pigear.py | 148 +++++++++++++++++++++++++++++++---------
1 file changed, 116 insertions(+), 32 deletions(-)
diff --git a/vidgear/gears/pigear.py b/vidgear/gears/pigear.py
index 8d9a3a631..ad783349e 100644
--- a/vidgear/gears/pigear.py
+++ b/vidgear/gears/pigear.py
@@ -26,7 +26,7 @@
# import the packages
from threading import Thread
from pkg_resources import parse_version
-import traceback
+import sys, time, platform
from .helper import capPropId
@@ -36,9 +36,7 @@
import cv2
# check whether OpenCV Binaries are 3.x+
- if parse_version(cv2.__version__) >= parse_version('3'):
- pass
- else:
+ if parse_version(cv2.__version__) < parse_version('3'):
raise ImportError('OpenCV library version >= 3.0 is only supported by this library')
except ImportError as error:
@@ -72,21 +70,29 @@ class PiGear:
"""
- def __init__(self, resolution=(640, 480), framerate=30, colorspace = None, logging = False, time_delay = 0, **options):
+ def __init__(self, camera_num = 0, resolution=(640, 480), framerate=30, colorspace = None, logging = False, time_delay = 0, **options):
try:
import picamera
from picamera.array import PiRGBArray
from picamera import PiCamera
- #print(cv2.__version__)
- except ImportError as error:
- # Output expected ImportErrors.
- raise ImportError('[ERROR]: Failed to detect Picamera executables, install it with "pip install picamera" command.')
-
- # initialize the picamera stream
- self.camera = PiCamera()
- self.camera.resolution = resolution
- self.camera.framerate = framerate
+ except Exception as error:
+ if isinstance(error, ImportError):
+ # Output expected ImportErrors.
+ raise ImportError('[ERROR]: Failed to detect Picamera executables, install it with "pip install picamera" command.')
+ else:
+ #Handle any API errors
+ raise RuntimeError('[ERROR]: Picamera API failure: {}'.format(error))
+
+ # initialize the picamera stream at given index
+ self.camera = None
+ if isinstance(camera_num, int) and camera_num >= 0:
+ self.camera = PiCamera(camera_num = camera_num)
+ self.camera.resolution = resolution
+ self.camera.framerate = framerate
+ if logging: print("[LOG]: Activating Pi camera at index: {}".format(camera_num))
+ else:
+ raise ValueError("[ERROR]: `camera_num` value is invalid, Kindly read docs!")
#initialize framerate variable
self.framerate = framerate
@@ -97,7 +103,18 @@ def __init__(self, resolution=(640, 480), framerate=30, colorspace = None, loggi
#reformat dict
options = {k.strip(): v for k,v in options.items()}
- try:
+ #intialize timeout variable(handles hardware failures)
+ self.failure_timeout = 2.0
+
+ #User-Defined parameter
+ if options and "HWFAILURE_TIMEOUT" in options:
+ #for altering timeout variable manually
+ if isinstance(options["HWFAILURE_TIMEOUT"],(int, float)):
+ if not(10.0 > options["HWFAILURE_TIMEOUT"] > 1.0): raise ValueError('[ERROR]: `HWFAILURE_TIMEOUT` value can only be between 1.0 ~ 10.0')
+ self.failure_timeout = options["HWFAILURE_TIMEOUT"] #assign special parameter
+ del options["HWFAILURE_TIMEOUT"] #clean
+
+ try:
# apply attributes to source if specified
for key, value in options.items():
setattr(self.camera, key, value)
@@ -108,11 +125,10 @@ def __init__(self, resolution=(640, 480), framerate=30, colorspace = None, loggi
except Exception as e:
# Catch if any error occurred
- if logging:
- print(e)
+ if logging: print(e)
# enable rgb capture array thread and capture stream
- self.rawCapture = PiRGBArray(self.camera, size=resolution)
+ self.rawCapture = PiRGBArray(self.camera, size = resolution)
self.stream = self.camera.capture_continuous(self.rawCapture,format="bgr", use_video_port=True)
#frame variable initialization
@@ -127,15 +143,21 @@ def __init__(self, resolution=(640, 480), framerate=30, colorspace = None, loggi
# applying time delay to warm-up picamera only if specified
if time_delay:
- import time
time.sleep(time_delay)
#thread initialization
self.thread = None
+ #timer thread initialization(Keeps check on frozen thread)
+ self._timer = None
+ self.t_elasped = 0.0 #records time taken by thread
+
# enable logging if specified
self.logging = logging
+ # catching thread exceptions
+ self.exceptions = None
+
# initialize termination flag
self.terminate = False
@@ -145,18 +167,45 @@ def start(self):
"""
start the thread to read frames from the video stream
"""
+ #Start frame producer thread
self.thread = Thread(target=self.update, args=())
self.thread.daemon = True
self.thread.start()
+
+ #Start internal timer thread
+ self._timer = Thread(target=self._timeit, args=())
+ self._timer.daemon = True
+ self._timer.start()
+
return self
+ def _timeit(self):
+ """
+ Keep checks on Thread excecution timing
+ """
+ #assign current time
+ self.t_elasped = time.time()
+
+ #loop until termainated
+ while not(self.terminate):
+ #check for frozen thread
+ if time.time() - self.t_elasped > self.failure_timeout:
+ #log failure
+ if self.logging: print("[WARNING]: Camera Module Disconnected!")
+
+ #prepare for clean exit
+ self.exceptions = True
+ self.terminate = True #self-terminate
+
+
+
def update(self):
"""
Update frames from stream
"""
- # keep looping infinitely until the thread is terminated
+ #keep looping infinitely until the thread is terminated
while True:
#check for termination flag
@@ -165,13 +214,13 @@ def update(self):
try:
#Try to iterate next frame from generator
stream = next(self.stream)
- except Exception as e:
- #Handle if Something is wrong
- if isinstance(e, (StopIteration, ValueError)):
- #raise picamera API's failures
- raise RuntimeError('[ERROR]: Camera Module API failure occured!')
- if self.logging: print(traceback.format_exc()) #log traceback for debugging errors
- break
+ except Exception:
+ #catch and save any exceptions
+ self.exceptions = sys.exc_info()
+ break #exit
+
+ #update timer
+ self.t_elasped = time.time()
# grab the frame from the stream and clear the stream in
# preparation for the next frame
@@ -205,6 +254,9 @@ def update(self):
else:
self.frame = frame
+ # terminate processes
+ if not(self.terminate): self.terminate = True
+
# release picamera resources
self.stream.close()
self.rawCapture.close()
@@ -216,6 +268,21 @@ def read(self):
"""
return the frame
"""
+ #check if there are any thread exceptions
+ if not(self.exceptions is None):
+ if isinstance(self.exceptions, bool):
+ #clear frame
+ self.frame = None
+ #notify user about hardware failure
+ raise SystemError('[ERROR]: Hardware failure occurred, Kindly reconnect Camera Module and restart your Pi!')
+ else:
+ #clear frame
+ self.frame = None
+ # re-raise error for debugging
+ error_msg = "[ERROR]: Camera Module API failure occured: {}".format(self.exceptions[1])
+ raise RuntimeError(error_msg).with_traceback(self.exceptions[2])
+
+ # return the frame
return self.frame
@@ -224,10 +291,27 @@ def stop(self):
"""
Terminates the Read process
"""
- # indicate that the thread should be terminated
+ if self.logging: print("[LOG]: Terminating PiGear Process.")
+
+ # make sure that the threads should be terminated
self.terminate = True
- # wait until stream resources are released (producer thread might be still grabbing frame)
- if self.thread is not None:
- self.thread.join()
- #properly handle thread exit
+
+ #stop timer thread
+ self._timer.join()
+
+ if self.thread is not None:
+ #check if hardware failure occured
+ if not(self.exceptions is None) and isinstance(self.exceptions, bool):
+ # force release picamera resources
+ self.stream.close()
+ self.rawCapture.close()
+ self.camera.close()
+
+ #properly handle thread exit
+ self.thread.terminate()
+ self.thread.wait() #wait if still process is still processing some information
+ self.thread = None
+ else:
+ #properly handle thread exit
+ self.thread.join()
From 214b729230b70bfe46b731f8b8034f1bb6be3023 Mon Sep 17 00:00:00 2001
From: Abhishek
Date: Wed, 13 Nov 2019 14:48:49 +0530
Subject: [PATCH 19/39] Enhancement: Introducing real-time Encoding/Decoding
for NetGear API - added compression support with on-the-fly flexible frame
encoding on server side(#65) - added initial support for `jpg,png,bmp`
encoding formats - added exclusive options attribute `compression_format` &
`compression_param` to tweak this feature - client side will now decode frame
automatically based on the encoding - Updated docs - Minor fixes
---
vidgear/gears/netgear.py | 54 ++++++++++++++++++++++++++++++++++------
vidgear/gears/pigear.py | 2 +-
2 files changed, 47 insertions(+), 9 deletions(-)
diff --git a/vidgear/gears/netgear.py b/vidgear/gears/netgear.py
index b01989eab..af65d5883 100644
--- a/vidgear/gears/netgear.py
+++ b/vidgear/gears/netgear.py
@@ -189,6 +189,10 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
self.auth_secretkeys_dir = '' #handles valid ZMQ private certificates dir
overwrite_cert = False #checks if certificates overwriting allowed
custom_cert_location = '' #handles custom ZMQ certificates path
+
+ #define stream compression handlers
+ self.compression = '' #disabled by default
+ self.compression_params = None
try:
#reformat dict
@@ -223,6 +227,22 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
# enable/disable auth certificate overwriting
overwrite_cert = value
+ # handle encoding and decoding if specified
+ elif key == 'compression_format' and isinstance(value,str) and value.lower().strip() in ['.jpg', '.jpeg', '.bmp', '.png']: #few are supported
+ # enable encoding
+ if not(receive_mode): self.compression = value.lower().strip()
+ elif key == 'compression_param':
+ # specifiy encoding/decoding params
+ if receive_mode and isinstance(value, int):
+ self.compression_params = value
+ if logging: print("[LOG]: Decoding flag: {}.".format(value))
+ elif not(receive_mode) and isinstance(value, (list,tuple)):
+ if logging: print("[LOG]: Encoding parameters: {}.".format(value))
+ self.compression_params = list(value)
+ else:
+ if logging: print("[WARNING]: Invalid compression parameters: {} skipped!".format(value))
+ self.compression_params = cv2.IMREAD_COLOR if receive_mode else [] # skip to defaults
+
# various ZMQ flags
elif key == 'flag' and isinstance(value, int):
self.msg_flag = value
@@ -552,6 +572,16 @@ def update(self):
# reshape frame
frame = frame_buffer.reshape(msg_json['shape'])
+ #check if encoding was enabled
+ if msg_json['compression']:
+ frame = cv2.imdecode(frame, self.compression_params)
+ #check if valid frame returned
+ if frame is None:
+ #otherwise raise error and exit
+ raise ValueError("[ERROR]: `{}` Frame Decoding failed with Parameter: {}".format(msg_json['compression'], self.compression_params))
+ self.terminate = True
+ continue
+
if self.multiserver_mode:
# check if multiserver_mode
@@ -579,10 +609,8 @@ def recv(self):
return the recovered frame
"""
# check whether `receive mode` is activated
- if self.receive_mode:
- pass
- else:
- #otherwise raise value error and exit
+ if not(self.receive_mode):
+ #raise value error and exit
raise ValueError('[ERROR]: `recv()` function cannot be used while receive_mode is disabled. Kindly refer vidgear docs!')
self.terminate = True
# check whether or not termination flag is enabled
@@ -605,10 +633,8 @@ def send(self, frame, message = None):
:param message(string/int): additional message for the client(s)
"""
# check whether `receive_mode` is disabled
- if not self.receive_mode:
- pass
- else:
- #otherwise raise value error and exit
+ if self.receive_mode:
+ #raise value error and exit
raise ValueError('[ERROR]: `send()` function cannot be used while receive_mode is enabled. Kindly refer vidgear docs!')
self.terminate = True
@@ -623,10 +649,21 @@ def send(self, frame, message = None):
#otherwise make it contiguous
frame = np.ascontiguousarray(frame, dtype=frame.dtype)
+ #handle encoding
+ if self.compression:
+ retval, frame = cv2.imencode(self.compression, frame, self.compression_params)
+ #check if it works
+ if not(retval):
+ #otherwise raise error and exit
+ raise ValueError("[ERROR]: Frame Encoding failed with format: {} and Parameters: {}".format(self.compression, self.compression_params))
+ self.terminate = True
+
+
#check if multiserver_mode is activated
if self.multiserver_mode:
# prepare the exclusive json dict and assign values with unique port
msg_dict = dict(terminate_flag = exit_flag,
+ compression=str(self.compression),
port = self.port,
pattern = str(self.pattern),
message = message if not(message is None) else '',
@@ -635,6 +672,7 @@ def send(self, frame, message = None):
else:
# otherwise prepare normal json dict and assign values
msg_dict = dict(terminate_flag = exit_flag,
+ compression=str(self.compression),
message = message if not(message is None) else '',
pattern = str(self.pattern),
dtype = str(frame.dtype),
diff --git a/vidgear/gears/pigear.py b/vidgear/gears/pigear.py
index ad783349e..e18a72cc0 100644
--- a/vidgear/gears/pigear.py
+++ b/vidgear/gears/pigear.py
@@ -26,7 +26,7 @@
# import the packages
from threading import Thread
from pkg_resources import parse_version
-import sys, time, platform
+import sys, time
from .helper import capPropId
From 1c5254ab992c50d55ba27e364f70fb66e8b70dd8 Mon Sep 17 00:00:00 2001
From: Abhishek
Date: Sun, 1 Dec 2019 16:19:54 +0530
Subject: [PATCH 20/39] Major Updates and Bugfixes for NetGear API
- Updates:
- Introducing Bi-Directional mode for bi-directional data transmission in NetGear(#75)
- Added new `return_data` parameter to `recv()` function.
- Added new `bidirectional_mode` for enabling this mode.
- Added support for `PAIR` & `REQ/REP` patterns for this mode
- Added support for all python datatypes.
- Added support for `REQ/REP` pattern for Multiserver Mode
- BugFixes:
- Reimplemented `Pub/Sub` pattern for smoother performance(#70)
- Fixed `multiserver_mode` not working properly over some networks(#57)
- Nemorous minor bugfixes and optimizations for robust output
- Repaired broken error handling and frozen threads
---
vidgear/gears/netgear.py | 340 +++++++++++++++++++++------------------
1 file changed, 182 insertions(+), 158 deletions(-)
diff --git a/vidgear/gears/netgear.py b/vidgear/gears/netgear.py
index af65d5883..a9bc30acf 100644
--- a/vidgear/gears/netgear.py
+++ b/vidgear/gears/netgear.py
@@ -28,6 +28,7 @@
from pkg_resources import parse_version
from .helper import check_python_version
from .helper import generate_auth_certificates
+from collections import deque
import numpy as np
import time
import os
@@ -135,14 +136,6 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
#raise error
raise ImportError('[ERROR]: pyzmq python library not installed. Kindly install it with `pip install pyzmq` command.')
- #log and enable threaded queue mode
- if logging:
- print('[LOG]: Threaded Queue Mode is enabled by default for NetGear.')
- #import deque
- from collections import deque
- #define deque and assign it to global var
- self.queue = deque(maxlen=96) #max len 96 to check overflow
-
#define valid messaging patterns => `0`: zmq.PAIR, `1`:(zmq.REQ,zmq.REP), and `1`:(zmq.SUB,zmq.PUB)
valid_messaging_patterns = {0:(zmq.PAIR,zmq.PAIR), 1:(zmq.REQ,zmq.REP), 2:(zmq.PUB,zmq.SUB)}
@@ -157,9 +150,8 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
#otherwise default to 0:`zmq.PAIR`
self.pattern = 0
msg_pattern = valid_messaging_patterns[self.pattern]
- if logging:
- #log it
- print('[LOG]: Wrong pattern value, Defaulting to `zmq.PAIR`! Kindly refer Docs for more Information.')
+ #log it
+ if logging: print('[LOG]: Wrong pattern value, Defaulting to `zmq.PAIR`! Kindly refer Docs for more Information.')
#check whether user-defined messaging protocol is valid
if protocol in ['tcp', 'upd', 'pgm', 'inproc', 'ipc']:
@@ -167,20 +159,23 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
else:
# else default to `tcp` protocol
protocol = 'tcp'
- if logging:
- #log it
- print('[LOG]: protocol is not valid or provided. Defaulting to `tcp` protocol!')
+ #log it
+ if logging: print('[LOG]: protocol is not valid or provided. Defaulting to `tcp` protocol!')
#generate random device id
self.id = ''.join(random.choice('0123456789ABCDEF') for i in range(5))
self.msg_flag = 0 #handles connection flags
- self.msg_copy = False #handles whether to copy data
+ self.msg_copy = True #handles whether to copy data
self.msg_track = False #handles whether to track packets
self.multiserver_mode = False #handles multiserver_mode state
recv_filter = '' #user-defined filter to allow specific port/servers only in multiserver_mode
+ #define bi-directional data transmission mode
+ self.bi_mode = False #handles bi_mode state
+ self.bi_data = None #handles return data
+
#define valid ZMQ security mechanisms => `0`: Grasslands, `1`:StoneHouse, and `1`:IronHouse
valid_security_mech = {0:'Grasslands', 1:'StoneHouse', 2:'IronHouse'}
self.secure_mode = 0 #handles ZMQ security layer status
@@ -194,68 +189,78 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
self.compression = '' #disabled by default
self.compression_params = None
- try:
- #reformat dict
- options = {k.lower().strip(): v for k,v in options.items()}
+ #reformat dict
+ options = {k.lower().strip(): v for k,v in options.items()}
- # assign values to global variables if specified and valid
- for key, value in options.items():
- if key == 'multiserver_mode' and isinstance(value, bool) and self.pattern == 2:
+ # assign values to global variables if specified and valid
+ for key, value in options.items():
+ if key == 'multiserver_mode' and isinstance(value, bool):
+ if pattern > 0:
# multi-server mode
self.multiserver_mode = value
- elif key == 'filter' and isinstance(value, str):
- #custom filter in multi-server mode
- recv_filter = value
-
- elif key == 'secure_mode' and isinstance(value,int) and (value in valid_security_mech):
- #secure mode
- try:
- assert check_python_version() >= 3, "[ERROR]: ZMQ Security feature is not available with python version < 3.0."
- assert zmq.zmq_version_info() >= (4,0), "[ERROR]: ZMQ Security feature is not supported in libzmq version < 4.0."
- self.secure_mode = value
- except AssertionError as e:
- print(e)
- elif key == 'custom_cert_location' and isinstance(value,str):
- # custom auth certificates path
- try:
- assert os.access(value, os.W_OK), "[ERROR]: Permission Denied!, cannot write ZMQ authentication certificates to '{}' directory!".format(value)
- assert not(os.path.isfile(value)), "[ERROR]: `custom_cert_location` value must be the path to a directory and not to a file!"
- custom_cert_location = os.path.abspath(value)
- except AssertionError as e:
- print(e)
- elif key == 'overwrite_cert' and isinstance(value,bool):
- # enable/disable auth certificate overwriting
- overwrite_cert = value
-
- # handle encoding and decoding if specified
- elif key == 'compression_format' and isinstance(value,str) and value.lower().strip() in ['.jpg', '.jpeg', '.bmp', '.png']: #few are supported
- # enable encoding
- if not(receive_mode): self.compression = value.lower().strip()
- elif key == 'compression_param':
- # specifiy encoding/decoding params
- if receive_mode and isinstance(value, int):
- self.compression_params = value
- if logging: print("[LOG]: Decoding flag: {}.".format(value))
- elif not(receive_mode) and isinstance(value, (list,tuple)):
- if logging: print("[LOG]: Encoding parameters: {}.".format(value))
- self.compression_params = list(value)
- else:
- if logging: print("[WARNING]: Invalid compression parameters: {} skipped!".format(value))
- self.compression_params = cv2.IMREAD_COLOR if receive_mode else [] # skip to defaults
-
- # various ZMQ flags
- elif key == 'flag' and isinstance(value, int):
- self.msg_flag = value
- elif key == 'copy' and isinstance(value, bool):
- self.msg_copy = value
- elif key == 'track' and isinstance(value, bool):
- self.msg_track = value
else:
- pass
- except Exception as e:
- # Catch if any error occurred
- if logging:
- print(e)
+ self.multiserver_mode = False
+ print('[ALERT]: Multi-Server is disabled!')
+ raise ValueError('[ERROR]: `{}` pattern is not valid when Multi-Server Mode is enabled. Kindly refer Docs for more Information.'.format(pattern))
+ elif key == 'filter' and isinstance(value, str):
+ #custom filter in multi-server mode
+ recv_filter = value
+
+ elif key == 'secure_mode' and isinstance(value,int) and (value in valid_security_mech):
+ #secure mode
+ try:
+ assert check_python_version() >= 3, "[ERROR]: ZMQ Security feature is not available with python version < 3.0."
+ assert zmq.zmq_version_info() >= (4,0), "[ERROR]: ZMQ Security feature is not supported in libzmq version < 4.0."
+ self.secure_mode = value
+ except Exception as e:
+ print(e)
+ elif key == 'custom_cert_location' and isinstance(value,str):
+ # custom auth certificates path
+ try:
+ assert os.access(value, os.W_OK), "[ERROR]: Permission Denied!, cannot write ZMQ authentication certificates to '{}' directory!".format(value)
+ assert not(os.path.isfile(value)), "[ERROR]: `custom_cert_location` value must be the path to a directory and not to a file!"
+ custom_cert_location = os.path.abspath(value)
+ except Exception as e:
+ print(e)
+ elif key == 'overwrite_cert' and isinstance(value,bool):
+ # enable/disable auth certificate overwriting
+ overwrite_cert = value
+
+ # handle encoding and decoding if specified
+ elif key == 'compression_format' and isinstance(value,str) and value.lower().strip() in ['.jpg', '.jpeg', '.bmp', '.png']: #few are supported
+ # enable encoding
+ if not(receive_mode): self.compression = value.lower().strip()
+ elif key == 'compression_param':
+ # specify encoding/decoding params
+ if receive_mode and isinstance(value, int):
+ self.compression_params = value
+ if logging: print("[LOG]: Decoding flag: {}.".format(value))
+ elif not(receive_mode) and isinstance(value, (list,tuple)):
+ if logging: print("[LOG]: Encoding parameters: {}.".format(value))
+ self.compression_params = list(value)
+ else:
+ if logging: print("[WARNING]: Invalid compression parameters: {} skipped!".format(value))
+ self.compression_params = cv2.IMREAD_COLOR if receive_mode else [] # skip to defaults
+
+ # enable bi-directional data transmission if specified
+ elif key == 'bidirectional_mode' and isinstance(value, bool):
+ # check if pattern is valid
+ if pattern < 2:
+ self.bi_mode = True
+ else:
+ self.bi_mode = False
+ print('[ALERT]: Bi-Directional data transmission is disabled!')
+ raise ValueError('[ERROR]: `{}` pattern is not valid when Bi-Directional Mode is enabled. Kindly refer Docs for more Information.'.format(pattern))
+
+ # various ZMQ flags
+ elif key == 'flag' and isinstance(value, int):
+ self.msg_flag = value
+ elif key == 'copy' and isinstance(value, bool):
+ self.msg_copy = value
+ elif key == 'track' and isinstance(value, bool):
+ self.msg_track = value
+ else:
+ pass
#handle secure mode
if self.secure_mode:
@@ -291,7 +296,14 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
#log if disabled
if logging: print('[LOG]: ZMQ Security Mechanism is disabled for this connection!')
-
+ #disable bi_mode if multi-server is enabled
+ if self.bi_mode:
+ if self.multiserver_mode:
+ self.bi_mode = False
+ print('[ALERT]: Bi-Directional Data Transmission is disabled when Multi-Server Mode is Enabled due to incompatibility!')
+ else:
+ if logging: print('[LOG]: Bi-Directional Data Transmission is enabled for this connection!')
+
# enable logging if specified
self.logging = logging
@@ -319,7 +331,7 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
# check if unique server port address list/tuple is assigned or not in multiserver_mode
if port is None or not isinstance(port, (tuple, list)):
# raise error if not
- raise ValueError('[ERROR]: Incorrect port value! Kindly provide a list/tuple of ports at Receiver-end while Multi-Server mode is enabled. For more information refer VidGear docs.')
+ raise ValueError('[ERROR]: Incorrect port value! Kindly provide a list/tuple of ports while Multi-Server mode is enabled. For more information refer VidGear docs.')
else:
#otherwise log it
print('[LOG]: Enabling Multi-Server Mode at PORTS: {}!'.format(port))
@@ -347,29 +359,30 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
# initialize and define thread-safe messaging socket
self.msg_socket = self.msg_context.socket(msg_pattern[1])
+ if self.pattern == 2: self.msg_socket.set_hwm(1)
if self.multiserver_mode:
# if multiserver_mode is enabled, then assign port addresses to zmq socket
for pt in port:
# enable specified secure mode for the zmq socket
if self.secure_mode > 0:
- # load client key
- client_secret_file = os.path.join(self.auth_secretkeys_dir, "client.key_secret")
- client_public, client_secret = zmq.auth.load_certificate(client_secret_file)
- # load all CURVE keys
- self.msg_socket.curve_secretkey = client_secret
- self.msg_socket.curve_publickey = client_public
# load server key
- server_public_file = os.path.join(self.auth_publickeys_dir, "server.key")
- server_public, _ = zmq.auth.load_certificate(server_public_file)
- # inject public key to make a CURVE connection.
- self.msg_socket.curve_serverkey = server_public
+ server_secret_file = os.path.join(self.auth_secretkeys_dir, "server.key_secret")
+ server_public, server_secret = zmq.auth.load_certificate(server_secret_file)
+ # load all CURVE keys
+ self.msg_socket.curve_secretkey = server_secret
+ self.msg_socket.curve_publickey = server_public
+ # enable CURVE connection for this socket
+ self.msg_socket.curve_server = True
+
+ # define socket options
+ if self.pattern == 2: self.msg_socket.setsockopt_string(zmq.SUBSCRIBE, recv_filter)
+
+ # bind socket to given server protocol, address and ports
+ self.msg_socket.bind(protocol+'://' + str(address) + ':' + str(pt))
- # connect socket to given server protocol, address and ports
- self.msg_socket.connect(protocol+'://' + str(address) + ':' + str(pt))
- self.msg_socket.setsockopt(zmq.LINGER, 0)
- # define socket options
- self.msg_socket.setsockopt_string(zmq.SUBSCRIBE, recv_filter)
+ # define socket optimizer
+ self.msg_socket.setsockopt(zmq.LINGER, 0)
else:
# enable specified secure mode for the zmq socket
@@ -383,14 +396,15 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
# enable CURVE connection for this socket
self.msg_socket.curve_server = True
+ # define exclusive socket options for patterns
+ if self.pattern == 2: self.msg_socket.setsockopt_string(zmq.SUBSCRIBE,'')
+
# bind socket to given protocol, address and port normally
self.msg_socket.bind(protocol+'://' + str(address) + ':' + str(port))
- # define exclusive socket options for patterns
- if self.pattern == 2:
- self.msg_socket.setsockopt_string(zmq.SUBSCRIBE,'')
- else:
- self.msg_socket.setsockopt(zmq.LINGER, 0)
+ # define socket optimizer
+ self.msg_socket.setsockopt(zmq.LINGER, 0)
+
except Exception as e:
# otherwise raise value error if errored
if self.secure_mode: print('Failed to activate ZMQ Security Mechanism: `{}` for this address!'.format(valid_security_mech[self.secure_mode]))
@@ -399,6 +413,11 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
else:
raise ValueError('[ERROR]: Failed to bind address: {} and pattern: {}! Kindly recheck all parameters.'.format((protocol+'://' + str(address) + ':' + str(port)), pattern))
+ #log and enable threaded queue mode
+ if logging: print('[LOG]: Threaded Queue Mode is enabled by default for NetGear.')
+ #define deque and assign it to global var
+ self.queue = deque(maxlen=96) #max len 96 to check overflow
+
# initialize and start threading instance
self.thread = Thread(target=self.update, args=())
self.thread.daemon = True
@@ -413,7 +432,6 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
print('[LOG]: Receive Mode is activated successfully!')
else:
#otherwise default to `Send Mode`
-
if address is None: #define address
address = '*' if self.multiserver_mode else 'localhost'
@@ -422,7 +440,7 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
# check if unique server port address is assigned or not in multiserver_mode
if port is None:
#raise error is not
- raise ValueError('Incorrect port value! Kindly provide a unique & valid port value at Server-end while Multi-Server mode is enabled. For more information refer VidGear docs.')
+ raise ValueError('[ERROR]: Kindly provide a unique & valid port value at Server-end. For more information refer VidGear docs.')
else:
#otherwise log it
print('[LOG]: Enabling Multi-Server Mode at PORT: {} on this device!'.format(port))
@@ -451,45 +469,32 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
# initialize and define thread-safe messaging socket
self.msg_socket = self.msg_context.socket(msg_pattern[0])
- if self.multiserver_mode:
- # enable specified secure mode for the zmq socket
- if self.secure_mode > 0:
- # load server key
- server_secret_file = os.path.join(self.auth_secretkeys_dir, "server.key_secret")
- server_public, server_secret = zmq.auth.load_certificate(server_secret_file)
- # load all CURVE keys
- self.msg_socket.curve_secretkey = server_secret
- self.msg_socket.curve_publickey = server_public
- # enable CURVE connection for this socket
- self.msg_socket.curve_server = True
- # connect socket to protocol, address and a unique port if multiserver_mode is activated
- self.msg_socket.bind(protocol+'://' + str(address) + ':' + str(port))
- else:
-
- if self.pattern == 1:
- # if pattern is 1, define additional flags
- self.msg_socket.REQ_RELAXED = True
- self.msg_socket.REQ_CORRELATE = True
-
- # enable specified secure mode for the zmq socket
- if self.secure_mode > 0:
- # load client key
- client_secret_file = os.path.join(self.auth_secretkeys_dir, "client.key_secret")
- client_public, client_secret = zmq.auth.load_certificate(client_secret_file)
- # load all CURVE keys
- self.msg_socket.curve_secretkey = client_secret
- self.msg_socket.curve_publickey = client_public
- # load server key
- server_public_file = os.path.join(self.auth_publickeys_dir, "server.key")
- server_public, _ = zmq.auth.load_certificate(server_public_file)
- # inject public key to make a CURVE connection.
- self.msg_socket.curve_serverkey = server_public
-
- # connect socket to given protocol, address and port
- self.msg_socket.connect(protocol+'://' + str(address) + ':' + str(port))
+ if self.pattern == 1:
+ # if pattern is 1, define additional flags
+ self.msg_socket.REQ_RELAXED = True
+ self.msg_socket.REQ_CORRELATE = True
+ if self.pattern == 2: self.msg_socket.set_hwm(1) # if pattern is 2, define additional optimizer
+
+ # enable specified secure mode for the zmq socket
+ if self.secure_mode > 0:
+ # load client key
+ client_secret_file = os.path.join(self.auth_secretkeys_dir, "client.key_secret")
+ client_public, client_secret = zmq.auth.load_certificate(client_secret_file)
+ # load all CURVE keys
+ self.msg_socket.curve_secretkey = client_secret
+ self.msg_socket.curve_publickey = client_public
+ # load server key
+ server_public_file = os.path.join(self.auth_publickeys_dir, "server.key")
+ server_public, _ = zmq.auth.load_certificate(server_public_file)
+ # inject public key to make a CURVE connection.
+ self.msg_socket.curve_serverkey = server_public
+
+ # connect socket to given protocol, address and port
+ self.msg_socket.connect(protocol+'://' + str(address) + ':' + str(port))
+
+ # define socket options
+ self.msg_socket.setsockopt(zmq.LINGER, 0)
- # define socket options
- self.msg_socket.setsockopt(zmq.LINGER, 0)
except Exception as e:
#log if errored
if self.secure_mode: print('Failed to activate ZMQ Security Mechanism: `{}` for this address!'.format(valid_security_mech[self.secure_mode]))
@@ -511,6 +516,7 @@ def update(self):
"""
# initialize frame variable
frame = None
+
# keep looping infinitely until the thread is terminated
while not self.exit_loop:
@@ -529,6 +535,7 @@ def update(self):
#stop iterating if overflowing occurs
time.sleep(0.000001)
continue
+
# extract json data out of socket
msg_json = self.msg_socket.recv_json(flags=self.msg_flag)
@@ -542,6 +549,7 @@ def update(self):
if not self.port_buffer:
print('[WARNING]: Termination signal received from all Servers!!!')
self.terminate = True #termination
+ if self.logging: print('[ALERT]: Termination signal received from server at port: {}!'.format(msg_json['port']))
continue
else:
if self.pattern == 1:
@@ -549,13 +557,11 @@ def update(self):
self.msg_socket.send_string('[INFO]: Termination signal received from server!')
#termination
self.terminate = msg_json['terminate_flag']
+ if self.logging: print('[ALERT]: Termination signal received from server!')
continue
- try:
- #check if pattern is same at both server's and client's end.
- assert int(msg_json['pattern']) == self.pattern
- except AssertionError as e:
- #otherwise raise error and exit
+ #check if pattern is same at both server's and client's end.
+ if int(msg_json['pattern']) != self.pattern:
raise ValueError("[ERROR]: Messaging patterns on both Server-end & Client-end must a valid pairs! Kindly refer VidGear docs.")
self.terminate = True
continue
@@ -564,8 +570,14 @@ def update(self):
msg_data = self.msg_socket.recv(flags=self.msg_flag, copy=self.msg_copy, track=self.msg_track)
if self.pattern != 2:
- # send confirmation message to server for debugging
- self.msg_socket.send_string('[LOG]: Data received on device: {} !'.format(self.id))
+ # check if bi-directional mode is enabled
+ if self.bi_mode:
+ # handle return data
+ bi_dict = dict(data = self.bi_data)
+ self.msg_socket.send_json(bi_dict, self.msg_flag)
+ else:
+ # send confirmation message to server
+ self.msg_socket.send_string('[LOG]: Data received on device: {} !'.format(self.id))
# recover frame from array buffer
frame_buffer = np.frombuffer(msg_data, dtype=msg_json['dtype'])
@@ -604,15 +616,21 @@ def update(self):
- def recv(self):
+ def recv(self, return_data = None):
"""
return the recovered frame
+
+ :param return_data: handles return data for bi-directional mode
"""
# check whether `receive mode` is activated
if not(self.receive_mode):
#raise value error and exit
raise ValueError('[ERROR]: `recv()` function cannot be used while receive_mode is disabled. Kindly refer vidgear docs!')
self.terminate = True
+
+ #handle bi-directional return data
+ if (self.bi_mode and not(return_data is None)): self.bi_data = return_data
+
# check whether or not termination flag is enabled
while not self.terminate:
# check if queue is empty
@@ -630,7 +648,7 @@ def send(self, frame, message = None):
send the frames over the messaging network
:param frame(ndarray): frame array to send
- :param message(string/int): additional message for the client(s)
+ :param message(string/int): additional message for the client(s)
"""
# check whether `receive_mode` is disabled
if self.receive_mode:
@@ -640,14 +658,11 @@ def send(self, frame, message = None):
# define exit_flag and assign value
exit_flag = True if (frame is None or self.terminate) else False
+
#check whether exit_flag is False
- if not exit_flag:
+ if not(exit_flag) and not(frame.flags['C_CONTIGUOUS']):
#check whether the incoming frame is contiguous
- if frame.flags['C_CONTIGUOUS']:
- pass
- else:
- #otherwise make it contiguous
- frame = np.ascontiguousarray(frame, dtype=frame.dtype)
+ frame = np.ascontiguousarray(frame, dtype=frame.dtype)
#handle encoding
if self.compression:
@@ -685,12 +700,17 @@ def send(self, frame, message = None):
# wait for confirmation
if self.pattern != 2:
- if self.logging:
- # log confirmation
- print(self.msg_socket.recv())
+ #check if bi-directional data transmission is enabled
+ if self.bi_mode:
+ #handle return data
+ return_dict = self.msg_socket.recv_json(flags=self.msg_flag)
+ return return_dict['data'] if return_dict else None
else:
- # otherwise be quiet
- self.msg_socket.recv()
+ #otherwise log normally
+ recv_confirmation = self.msg_socket.recv()
+ # log confirmation
+ if self.logging : print(recv_confirmation)
+
@@ -716,15 +736,19 @@ def close(self):
self.thread = None
#properly handle thread exit
else:
- # otherwise in `send_mode`, inform client(s) that the termination is reached
+ # indicate that process should be terminated
self.terminate = True
- #check if multiserver_mode
if self.multiserver_mode:
+ #check if multiserver_mode
# send termination flag to client with its unique port
term_dict = dict(terminate_flag = True, port = self.port)
else:
# otherwise send termination flag to client
term_dict = dict(terminate_flag = True)
- self.msg_socket.send_json(term_dict)
+ # otherwise inform client(s) that the termination has been reached
+ if self.pattern == 2 or self.bi_mode:
+ for _ in range(500): self.msg_socket.send_json(term_dict)
+ else:
+ self.msg_socket.send_json(term_dict)
# properly close the socket
self.msg_socket.close()
\ No newline at end of file
From c15e1c001f57f270f073ca2cc10f7478574e1546 Mon Sep 17 00:00:00 2001
From: Abhishek
Date: Sun, 1 Dec 2019 23:35:48 +0530
Subject: [PATCH 21/39] NetGear API: Added support for `message` parameter for
non-exclusive modes
---
vidgear/gears/netgear.py | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/vidgear/gears/netgear.py b/vidgear/gears/netgear.py
index a9bc30acf..7566783d2 100644
--- a/vidgear/gears/netgear.py
+++ b/vidgear/gears/netgear.py
@@ -608,8 +608,12 @@ def update(self):
# append recovered unique port and frame to queue
self.queue.append((msg_json['port'],frame))
else:
- # append recovered frame to queue
- self.queue.append(frame)
+ #extract if any message from server and display it
+ if msg_json['message']:
+ self.queue.append((msg_json['message'], frame))
+ else:
+ # append recovered frame to queue
+ self.queue.append(frame)
# finally properly close the socket
self.msg_socket.close()
From 68cde2f1af53c612a3bc6193d689fd0cd146c6a3 Mon Sep 17 00:00:00 2001
From: Abhishek
Date: Mon, 2 Dec 2019 11:49:38 +0530
Subject: [PATCH 22/39] Bash Script Fix: - Moved FFmpeg static binaries to more
reliable source - Mirrored files on github.com
---
scripts/bash/prepare_dataset.sh | 25 ++++++++++++++-----------
1 file changed, 14 insertions(+), 11 deletions(-)
diff --git a/scripts/bash/prepare_dataset.sh b/scripts/bash/prepare_dataset.sh
index cf806a73c..2b3f89807 100644
--- a/scripts/bash/prepare_dataset.sh
+++ b/scripts/bash/prepare_dataset.sh
@@ -16,12 +16,15 @@
TMPFOLDER=$(python -c 'import tempfile; print(tempfile.gettempdir())')
# Creating necessary directories
-mkdir -p $TMPFOLDER/Downloads
-mkdir -p $TMPFOLDER/Downloads/{FFmpeg_static,Test_videos}
+mkdir -p "$TMPFOLDER"/Downloads
+mkdir -p "$TMPFOLDER"/Downloads/{FFmpeg_static,Test_videos}
# Acknowledging machine architecture
MACHINE_BIT=$(uname -m)
+#Defining alternate ffmpeg static binaries date/version
+ALTBINARIES_DATE=02-12-19
+
# Acknowledging machine OS type
case $(uname | tr '[:upper:]' '[:lower:]') in
linux*)
@@ -40,18 +43,18 @@ esac
#Download and Configure FFmpeg Static
-cd $TMPFOLDER/Downloads/FFmpeg_static
+cd "$TMPFOLDER"/Downloads/FFmpeg_static
if [ $OS_NAME = "linux" ]; then
echo "Downloading Linux Static FFmpeg Binaries..."
- if [ $MACHINE_BIT = "x86_64" ]; then
- curl https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz -o ffmpeg-release-amd64-static.tar.xz
+ if [ "$MACHINE_BIT" = "x86_64" ]; then
+ curl https://github.com/abhiTronix/ffmpeg-static-builds/raw/master/$ALTBINARIES_DATE/ffmpeg-release-amd64-static.tar.xz -o ffmpeg-release-amd64-static.tar.xz
tar -xJf ffmpeg-release-amd64-static.tar.xz
rm *.tar.*
mv ffmpeg* ffmpeg
else
- curl https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-i686-static.tar.xz -o ffmpeg-release-i686-static.tar.xz
+ curl https://github.com/abhiTronix/ffmpeg-static-builds/raw/master/$ALTBINARIES_DATE/ffmpeg-release-i686-static.tar.xz -o ffmpeg-release-i686-static.tar.xz
tar -xJf ffmpeg-release-i686-static.tar.xz
rm *.tar.*
mv ffmpeg* ffmpeg
@@ -60,13 +63,13 @@ if [ $OS_NAME = "linux" ]; then
elif [ $OS_NAME = "windows" ]; then
echo "Downloading Windows Static FFmpeg Binaries..."
- if [ $MACHINE_BIT = "x86_64" ]; then
- curl https://ffmpeg.zeranoe.com/builds/win64/static/ffmpeg-latest-win64-static.zip -o ffmpeg-latest-win64-static.zip
+ if [ "$MACHINE_BIT" = "x86_64" ]; then
+ curl https://github.com/abhiTronix/ffmpeg-static-builds/raw/master/$ALTBINARIES_DATE/ffmpeg-latest-win64-static.zip -o ffmpeg-latest-win64-static.zip
unzip -qq ffmpeg-latest-win64-static.zip
rm ffmpeg-latest-win64-static.zip
mv ffmpeg-latest-win64-static ffmpeg
else
- curl https://ffmpeg.zeranoe.com/builds/win32/static/ffmpeg-latest-win32-static.zip -o ffmpeg-latest-win32-static.zip
+ curl https://github.com/abhiTronix/ffmpeg-static-builds/raw/master/$ALTBINARIES_DATE/ffmpeg-latest-win32-static.zip -o ffmpeg-latest-win32-static.zip
unzip -qq ffmpeg-latest-win32-static.zip
rm ffmpeg-latest-win32-static.zip
mv ffmpeg-latest-win32-static ffmpeg
@@ -75,7 +78,7 @@ elif [ $OS_NAME = "windows" ]; then
else
echo "Downloading MacOS64 Static FFmpeg Binary..."
- curl -LO https://ffmpeg.zeranoe.com/builds/macos64/static/ffmpeg-latest-macos64-static.zip
+ curl -LO https://github.com/abhiTronix/ffmpeg-static-builds/raw/master/$ALTBINARIES_DATE/ffmpeg-latest-macos64-static.zip
unzip -qq ffmpeg-latest-macos64-static.zip
rm ffmpeg-latest-macos64-static.zip
mv ffmpeg-latest-macos64-static ffmpeg
@@ -83,7 +86,7 @@ else
fi
# Downloading Test Data
-cd $TMPFOLDER/Downloads/Test_videos
+cd "$TMPFOLDER"/Downloads/Test_videos
echo "Downloading Test-Data..."
curl http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4 -o BigBuckBunny.mp4
From 526a209c840193b0b4ee39c7317dd7622e8fbcf3 Mon Sep 17 00:00:00 2001
From: Abhishek
Date: Mon, 2 Dec 2019 16:17:20 +0530
Subject: [PATCH 23/39] Bash Script Fixes: Fixed curl flags
---
scripts/bash/prepare_dataset.sh | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/scripts/bash/prepare_dataset.sh b/scripts/bash/prepare_dataset.sh
index 2b3f89807..d530dcf36 100644
--- a/scripts/bash/prepare_dataset.sh
+++ b/scripts/bash/prepare_dataset.sh
@@ -49,12 +49,12 @@ if [ $OS_NAME = "linux" ]; then
echo "Downloading Linux Static FFmpeg Binaries..."
if [ "$MACHINE_BIT" = "x86_64" ]; then
- curl https://github.com/abhiTronix/ffmpeg-static-builds/raw/master/$ALTBINARIES_DATE/ffmpeg-release-amd64-static.tar.xz -o ffmpeg-release-amd64-static.tar.xz
+ curl -L https://github.com/abhiTronix/ffmpeg-static-builds/raw/master/$ALTBINARIES_DATE/ffmpeg-release-amd64-static.tar.xz -o ffmpeg-release-amd64-static.tar.xz
tar -xJf ffmpeg-release-amd64-static.tar.xz
rm *.tar.*
mv ffmpeg* ffmpeg
else
- curl https://github.com/abhiTronix/ffmpeg-static-builds/raw/master/$ALTBINARIES_DATE/ffmpeg-release-i686-static.tar.xz -o ffmpeg-release-i686-static.tar.xz
+ curl -L https://github.com/abhiTronix/ffmpeg-static-builds/raw/master/$ALTBINARIES_DATE/ffmpeg-release-i686-static.tar.xz -o ffmpeg-release-i686-static.tar.xz
tar -xJf ffmpeg-release-i686-static.tar.xz
rm *.tar.*
mv ffmpeg* ffmpeg
@@ -64,12 +64,12 @@ elif [ $OS_NAME = "windows" ]; then
echo "Downloading Windows Static FFmpeg Binaries..."
if [ "$MACHINE_BIT" = "x86_64" ]; then
- curl https://github.com/abhiTronix/ffmpeg-static-builds/raw/master/$ALTBINARIES_DATE/ffmpeg-latest-win64-static.zip -o ffmpeg-latest-win64-static.zip
+ curl -L https://github.com/abhiTronix/ffmpeg-static-builds/raw/master/$ALTBINARIES_DATE/ffmpeg-latest-win64-static.zip -o ffmpeg-latest-win64-static.zip
unzip -qq ffmpeg-latest-win64-static.zip
rm ffmpeg-latest-win64-static.zip
mv ffmpeg-latest-win64-static ffmpeg
else
- curl https://github.com/abhiTronix/ffmpeg-static-builds/raw/master/$ALTBINARIES_DATE/ffmpeg-latest-win32-static.zip -o ffmpeg-latest-win32-static.zip
+ curl -L https://github.com/abhiTronix/ffmpeg-static-builds/raw/master/$ALTBINARIES_DATE/ffmpeg-latest-win32-static.zip -o ffmpeg-latest-win32-static.zip
unzip -qq ffmpeg-latest-win32-static.zip
rm ffmpeg-latest-win32-static.zip
mv ffmpeg-latest-win32-static ffmpeg
From 4718d06ebed1331466bcff70fd6e8ce38b24ee3c Mon Sep 17 00:00:00 2001
From: Abhishek
Date: Mon, 2 Dec 2019 16:55:30 +0530
Subject: [PATCH 24/39] Fix Bash Script: Fix unsecure apt sources
---
scripts/bash/install_opencv.sh | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/scripts/bash/install_opencv.sh b/scripts/bash/install_opencv.sh
index fd6f400d1..138f2a895 100644
--- a/scripts/bash/install_opencv.sh
+++ b/scripts/bash/install_opencv.sh
@@ -31,17 +31,17 @@ echo "Installing OpenCV..."
echo "Installing OpenCV Dependencies..."
-sudo apt-get install -y build-essential cmake pkg-config gfortran
+sudo apt-get install -y --allow-unauthenticated build-essential cmake pkg-config gfortran
-sudo apt-get install -y yasm libv4l-dev libgtk-3-dev libtbb-dev
+sudo apt-get install -y --allow-unauthenticated yasm libv4l-dev libgtk-3-dev libtbb-dev
-sudo apt-get install -y libavcodec-dev libavformat-dev libswscale-dev libjasper-dev libopenexr-dev
+sudo apt-get install -y --allow-unauthenticated libavcodec-dev libavformat-dev libswscale-dev libjasper-dev libopenexr-dev
-sudo apt-get install -y libxvidcore-dev libx264-dev libatlas-base-dev libtiff5-dev python3-dev
+sudo apt-get install -y --allow-unauthenticated libxvidcore-dev libx264-dev libatlas-base-dev libtiff5-dev python3-dev
-sudo apt-get install -y zlib1g-dev libjpeg-dev checkinstall libwebp-dev libpng-dev libopenblas-base
+sudo apt-get install -y --allow-unauthenticated zlib1g-dev libjpeg-dev checkinstall libwebp-dev libpng-dev libopenblas-base
-sudo apt-get install -y libgstreamer-plugins-base1.0-dev libgstreamer1.0-dev gstreamer1.0-plugins-ugly gstreamer1.0-libav gstreamer1.0-doc gstreamer1.0-tools
+sudo apt-get install -y --allow-unauthenticated libgstreamer-plugins-base1.0-dev libgstreamer1.0-dev gstreamer1.0-plugins-ugly gstreamer1.0-libav gstreamer1.0-doc gstreamer1.0-tools
echo "Installing OpenCV Library"
From 2e21e729a5b7e1f4064cad8c270c38a30ad1b5de Mon Sep 17 00:00:00 2001
From: Abhishek
Date: Mon, 2 Dec 2019 18:29:40 +0530
Subject: [PATCH 25/39] NetGear API: moved `message` parameter support for
non-exclusive primary modes under Bi-Directional Mode.
---
vidgear/gears/netgear.py | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/vidgear/gears/netgear.py b/vidgear/gears/netgear.py
index 7566783d2..c7ce0c535 100644
--- a/vidgear/gears/netgear.py
+++ b/vidgear/gears/netgear.py
@@ -608,11 +608,12 @@ def update(self):
# append recovered unique port and frame to queue
self.queue.append((msg_json['port'],frame))
else:
- #extract if any message from server and display it
- if msg_json['message']:
+ #extract if any message from server if Bi-Directional Mode is enabled
+ if self.bi_mode and msg_json['message']:
+ # append grouped frame and data to queue
self.queue.append((msg_json['message'], frame))
else:
- # append recovered frame to queue
+ # otherwise append recovered frame to queue
self.queue.append(frame)
# finally properly close the socket
From fad7dee222ff940824344485abb357191319313e Mon Sep 17 00:00:00 2001
From: Abhishek
Date: Tue, 3 Dec 2019 08:44:45 +0530
Subject: [PATCH 26/39] NetGear API: Fixed empty data bug at server
---
vidgear/gears/netgear.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/vidgear/gears/netgear.py b/vidgear/gears/netgear.py
index c7ce0c535..2666d7821 100644
--- a/vidgear/gears/netgear.py
+++ b/vidgear/gears/netgear.py
@@ -686,14 +686,14 @@ def send(self, frame, message = None):
compression=str(self.compression),
port = self.port,
pattern = str(self.pattern),
- message = message if not(message is None) else '',
+ message = message,
dtype = str(frame.dtype),
shape = frame.shape)
else:
# otherwise prepare normal json dict and assign values
msg_dict = dict(terminate_flag = exit_flag,
compression=str(self.compression),
- message = message if not(message is None) else '',
+ message = message,
pattern = str(self.pattern),
dtype = str(frame.dtype),
shape = frame.shape)
From 88482eb66403b97aa58f592cb1385ddf659f4ebe Mon Sep 17 00:00:00 2001
From: Abhishek
Date: Tue, 3 Dec 2019 14:13:22 +0530
Subject: [PATCH 27/39] NetGear API: - Added `force_terminate` attribute flag
for force socket termination at server if there's latency in network - fixes
and updates for REQ/REP pattern in Multiserver-mode - Minor other bugfixes
and doc updates
---
vidgear/gears/netgear.py | 48 +++++++++++++++++++++++++++-------------
1 file changed, 33 insertions(+), 15 deletions(-)
diff --git a/vidgear/gears/netgear.py b/vidgear/gears/netgear.py
index 2666d7821..802724fd5 100644
--- a/vidgear/gears/netgear.py
+++ b/vidgear/gears/netgear.py
@@ -185,6 +185,9 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
overwrite_cert = False #checks if certificates overwriting allowed
custom_cert_location = '' #handles custom ZMQ certificates path
+ #handle force socket termination if there's latency in network
+ self.force_close = False
+
#define stream compression handlers
self.compression = '' #disabled by default
self.compression_params = None
@@ -252,6 +255,16 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
print('[ALERT]: Bi-Directional data transmission is disabled!')
raise ValueError('[ERROR]: `{}` pattern is not valid when Bi-Directional Mode is enabled. Kindly refer Docs for more Information.'.format(pattern))
+ # enable force socket closing if specified
+ elif key == 'force_terminate' and isinstance(value, bool):
+ # check if pattern is valid
+ if address is None and not(receive_mode):
+ self.force_close = False
+ print('[ALERT]: Force termination is disabled for local servers!')
+ else:
+ self.force_close = True
+ if logging: print("[LOG]: Force termination is enabled for this connection!")
+
# various ZMQ flags
elif key == 'flag' and isinstance(value, int):
self.msg_flag = value
@@ -296,12 +309,16 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
#log if disabled
if logging: print('[LOG]: ZMQ Security Mechanism is disabled for this connection!')
- #disable bi_mode if multi-server is enabled
+ #handle bi_mode
if self.bi_mode:
+ #disable bi_mode if multi-server is enabled
if self.multiserver_mode:
self.bi_mode = False
print('[ALERT]: Bi-Directional Data Transmission is disabled when Multi-Server Mode is Enabled due to incompatibility!')
else:
+ #enable force termination by default
+ self.force_close = True
+ if logging: print("[LOG]: Force termination is enabled for this connection by default!")
if logging: print('[LOG]: Bi-Directional Data Transmission is enabled for this connection!')
# enable logging if specified
@@ -323,8 +340,7 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
if receive_mode:
# if does than define connection address
- if address is None: #define address
- address = 'localhost' if self.multiserver_mode else '*'
+ if address is None: address = '*' #define address
#check if multiserver_mode is enabled
if self.multiserver_mode:
@@ -425,15 +441,14 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
if logging:
#finally log progress
- print('[LOG]: Successfully Binded to address: {}.'.format(protocol+'://' + str(address) + ':' + str(port)))
+ print('[LOG]: Successfully Binded to address: {} with pattern: {}.'.format((protocol+'://' + str(address) + ':' + str(port)), pattern))
if self.secure_mode: print('[LOG]: Enabled ZMQ Security Mechanism: `{}` for this address, Successfully!'.format(valid_security_mech[self.secure_mode]))
print('[LOG]: Multi-threaded Receive Mode is enabled Successfully!')
print('[LOG]: Device Unique ID is {}.'.format(self.id))
print('[LOG]: Receive Mode is activated successfully!')
else:
#otherwise default to `Send Mode`
- if address is None: #define address
- address = '*' if self.multiserver_mode else 'localhost'
+ if address is None: address = 'localhost' #define address
#check if multiserver_mode is enabled
if self.multiserver_mode:
@@ -503,7 +518,7 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
if logging:
#finally log progress
- print('[LOG]: Successfully connected to address: {}.'.format(protocol+'://' + str(address) + ':' + str(port)))
+ print('[LOG]: Successfully connected to address: {} with pattern: {}.'.format((protocol+'://' + str(address) + ':' + str(port)), pattern))
if self.secure_mode: print('[LOG]: Enabled ZMQ Security Mechanism: `{}` for this address, Successfully!'.format(valid_security_mech[self.secure_mode]))
print('[LOG]: This device Unique ID is {}.'.format(self.id))
print('[LOG]: Send Mode is successfully activated and ready to send data!')
@@ -543,20 +558,23 @@ def update(self):
if msg_json['terminate_flag']:
#if multiserver_mode is enabled
if self.multiserver_mode:
- # check from which ports signal is received
- self.port_buffer.remove(msg_json['port'])
+ # check and remove from which ports signal is received
+ if msg_json['port'] in self.port_buffer:
+ # if pattern is 1, then send back server the info about termination
+ if self.pattern == 1: self.msg_socket.send_string('[INFO]: Termination signal received at client!')
+ self.port_buffer.remove(msg_json['port'])
+ if self.logging: print('[ALERT]: Termination signal received from server at port: {}!'.format(msg_json['port']))
#if termination signal received from all servers then exit client.
if not self.port_buffer:
print('[WARNING]: Termination signal received from all Servers!!!')
self.terminate = True #termination
- if self.logging: print('[ALERT]: Termination signal received from server at port: {}!'.format(msg_json['port']))
continue
else:
- if self.pattern == 1:
- # if pattern is 1, then send back server the info about termination
- self.msg_socket.send_string('[INFO]: Termination signal received from server!')
+ # if pattern is 1, then send back server the info about termination
+ if self.pattern == 1: self.msg_socket.send_string('[INFO]: Termination signal received at client!')
#termination
- self.terminate = msg_json['terminate_flag']
+ self.terminate = True
+ #notify client
if self.logging: print('[ALERT]: Termination signal received from server!')
continue
@@ -751,7 +769,7 @@ def close(self):
# otherwise send termination flag to client
term_dict = dict(terminate_flag = True)
# otherwise inform client(s) that the termination has been reached
- if self.pattern == 2 or self.bi_mode:
+ if self.force_close:
for _ in range(500): self.msg_socket.send_json(term_dict)
else:
self.msg_socket.send_json(term_dict)
From c8a6339867877c9b9cd35c0de1307bb2209a2c03 Mon Sep 17 00:00:00 2001
From: Abhishek Thakur
Date: Mon, 16 Dec 2019 03:10:47 +0000
Subject: [PATCH 28/39] Important Enhancements and BugFixes for next release
(#77)
- :warning: Dropped python 2.7 legacy support for vidgear.
- Enhancements/Updates:
- VideoGear API:
* Added `framerate` global variable and removed redundant function.
* Added `CROP_N_ZOOM` attribute in Videogear API for supporting Crop and Zoom feature.
- WriteGear API: Added new `execute_ffmpeg_cmd` function to pass a custom command to its FFmpeg pipeline.
- CLI & Tests updates:
* Replaced python 3.5 matrices with latest python 3.8 matrices in Linux environment
* Added full support for CODECOV in all CLI environments
* Updated OpenCV to v4.2.0-pre(master branch).
* Added various Netgear API tests
* Added initial Screengear API test
* More test RTSP feeds added with better error handling in CamGear network test
* Added tests for ZMQ authentication certificate generation
* Added badge and Minor doc updates
- Stabilizer class Update:
* Added new Crop and Zoom feature
* Added `crop_n_zoom` param for enabling this feature
* Updated docs
* Overall APIs Code and Docs optimizations and minor tweaks
- Bugfixes
- NetGear API:
* Fixed random freezing in `Secure Mode` and several related performance updates
* Eliminated redundant code blocks
* Disabled `overwrite_cert` for client-end
- CamGear API:
* Fixed Assertion error in CamGear API during colorspace manipulation (#78)
* Implemented better error handling of colorspace in videocapture APIs
* CamGear will now throw `RuntimeError` if source provided is invalid
* Updates for threaded Queue mode in CamGear API for robust performance
* Added additional logging messages for CamGear API
* Fixed Code indentation in `setup.py`
* Numerous Bug fixes
---
.travis.yml | 33 +-
README.md | 71 +++--
appveyor.yml | 9 +-
changelog.md | 84 ++++-
codecov.yml | 11 +
scripts/bash/install_opencv.sh | 18 +-
setup.cfg | 2 +-
setup.py | 96 +++---
vidgear/gears/camgear.py | 78 +++--
vidgear/gears/helper.py | 50 ++-
vidgear/gears/netgear.py | 46 +--
vidgear/gears/pigear.py | 49 +--
vidgear/gears/screengear.py | 35 +--
vidgear/gears/stabilizer.py | 55 +++-
vidgear/gears/videogear.py | 31 +-
vidgear/gears/writegear.py | 67 ++--
vidgear/tests/benchmark_tests/fps.py | 1 -
.../test_benchmark_Videocapture.py | 2 +-
.../test_benchmark_Videowriter.py | 14 +-
.../test_benchmark_playback.py | 6 +-
vidgear/tests/network_tests/__init__.py | 2 +
vidgear/tests/network_tests/test_netgear.py | 296 ++++++++++++++++++
vidgear/tests/test_helper.py | 35 ++-
.../tests/videocapture_tests/test_camgear.py | 53 ++--
.../videocapture_tests/test_screengear.py | 51 +++
.../videocapture_tests/test_videogear.py | 32 +-
.../writer_tests/test_compression_mode.py | 55 +++-
.../writer_tests/test_non_compression_mode.py | 4 +-
28 files changed, 933 insertions(+), 353 deletions(-)
create mode 100644 codecov.yml
create mode 100644 vidgear/tests/network_tests/__init__.py
create mode 100644 vidgear/tests/network_tests/test_netgear.py
create mode 100644 vidgear/tests/videocapture_tests/test_screengear.py
diff --git a/.travis.yml b/.travis.yml
index f01ef753f..f342eb9ef 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -18,18 +18,20 @@ matrix:
osx_image: xcode11
env: PYTHON=37
- os: linux
- python: "3.5"
+ dist: bionic
+ python: "3.6"
language: python
cache: pip
- os: linux
- python: "3.6"
+ dist: bionic
+ python: "3.7"
language: python
cache: pip
- os: linux
- dist: xenial
- python: "3.7"
+ dist: bionic
+ python: "3.8"
language: python
- cache: pip
+ cache: pip
@@ -37,6 +39,7 @@ before_install:
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
brew install swig;
brew install ffmpeg;
+ brew link ffmpeg;
brew install unzip;
curl -LO https://raw.githubusercontent.com/GiovanniBussi/macports-ci/master/macports-ci;
source ./macports-ci install;
@@ -50,9 +53,8 @@ before_install:
chmod +x scripts/bash/prepare_dataset.sh;
fi
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
- sudo add-apt-repository ppa:jonathonf/ffmpeg-4 -y;
sudo apt-get update -q;
- sudo apt-get install ffmpeg unzip wget -y;
+ sudo apt-get install unzip wget -y;
sudo apt-get install dos2unix -y;
dos2unix scripts/bash/prepare_dataset.sh;
chmod +x scripts/bash/prepare_dataset.sh;
@@ -71,7 +73,9 @@ install:
pip install .;
pip uninstall opencv-contrib-python -y;
pip install six;
+ pip install codecov;
pip install --upgrade pytest;
+ pip install --upgrade pytest-cov;
pip install --upgrade youtube-dl;
fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
@@ -79,7 +83,9 @@ install:
pip install --upgrade --user numpy;
pip install --user .;
pip install --user six;
- pip install --upgrade --user pytest;
+ pip install --user codecov;
+ pip install --upgrade --user pytest;
+ pip install --upgrade --user pytest-cov;
pip install --upgrade --user youtube-dl;
fi
@@ -93,9 +99,14 @@ before_script:
fi
script:
+ - function keep_alive() { while true; do echo -en "\a"; sleep 60; done }
+ - keep_alive &
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
- travis_wait pytest -sv;
+ pytest --verbose --capture=no --cov-report term-missing --cov=vidgear vidgear/tests/;
fi
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
- python -m pytest -sv;
- fi
\ No newline at end of file
+ python -m pytest --verbose --capture=no --cov-report term-missing --cov=vidgear vidgear/tests/;
+ fi
+
+after_success:
+ - codecov
\ No newline at end of file
diff --git a/README.md b/README.md
index 07046b7d7..b217d0147 100644
--- a/README.md
+++ b/README.md
@@ -34,7 +34,9 @@ THE SOFTWARE.
[Releases][release] | [Gears](#gears) | [Wiki Documentation][wiki] | [Installation](#installation) | [License](#license)
-[![PyPi version][pypi-badge]][pypi] [![Build Status][travis-cli]][travis] [![Build Status][appveyor]][app] [![Say Thank you][Thank-you]][thanks] [![Twitter][twitter-badge]][twitter-intent]
+[![Build Status][travis-cli]][travis] [![Codecov branch][codecov]][code] [![Build Status][appveyor]][app]
+
+[![PyPi version][pypi-badge]][pypi] [![Say Thank you][Thank-you]][thanks] [![Twitter][twitter-badge]][twitter-intent]
[![Buy Me A Coffee][Coffee-badge]][coffee]
@@ -102,16 +104,25 @@ The following **functional block diagram** clearly depicts the functioning of Vi
## New Release SneekPeak : VidGear 0.1.6
- * Added powerful ZMQ Authentication & Data Encryption features for NetGear API:
- * Added exclusive `secure_mode` param for enabling it.
- * Added support for two most powerful `Stonehouse` & `Ironhouse` ZMQ security mechanisms.
- * Added auto auth-certificates/key generation and validation feature.
- * Implemented Robust Multi-Server support for NetGear API:
- * Enables Multiple Servers messaging support with a single client.
- * Added exclusive `multiserver_mode` param for enabling it.
- * Added ability to send additional data of any datatype along with the frame in realtime in this mode.
+
+***:warning: Python 2.7 legacy support [dropped in v0.1.6][drop27]!***
+
+**NetGear API:**
+ * Added powerful ZMQ Authentication & Data Encryption features for NetGear API
+ * Added robust Multi-Server support for NetGear API.
+ * Added exclusive Bi-Directional Mode for bidirectional data transmission.
+ * Added frame-compression support with on-the-fly flexible encoding/decoding.
* Implemented new *Publish/Subscribe(`zmq.PUB/zmq.SUB`)* pattern for seamless Live Streaming in NetGear API.
- * Added VidGear's official native support for MacOS environment and [many more...](changelog.md)
+
+**PiGear API:**
+ * Added new threaded internal timing function for PiGear to handle any hardware failures/frozen threads
+ * PiGear will not exit safely with `SystemError` if Picamera ribbon cable is pulled out to save resources.
+
+**WriteGear API:** Added new `execute_ffmpeg_cmd` function to pass a custom command to its internal FFmpeg pipeline.
+
+**Stabilizer class:** Added new _Crop and Zoom_ feature.
+
+***Added VidGear's official native support for MacOS environment and [many more...](changelog.md)***
@@ -127,7 +138,7 @@ Before installing VidGear, you must verify that the following dependencies are m
* **`OpenCV:`** VidGear must require OpenCV(3.0+) python enabled binaries to be installed on your machine for its core functions. For its installation, you can follow these online tutorials for [linux][OpenCV-linux] and [raspberry pi][OpenCV-pi], otherwise, install it via pip:
```sh
- pip install -U opencv-python #or install opencv-contrib-python similarly
+ pip3 install -U opencv-python #or install opencv-contrib-python similarly
```
* **`FFmpeg:`** VidGear must require FFmpeg for its powerful video compression and encoding capabilities. :star2: Follow this [**FFmpeg wiki page**][ffmpeg-wiki] for its installation. :star2:
@@ -135,26 +146,26 @@ Before installing VidGear, you must verify that the following dependencies are m
* **`picamera:`** Required if using Raspberry Pi Camera Modules(_such as OmniVision OV5647 Camera Module_) with your Raspberry Pi machine. You can easily install it via pip:
```sh
- pip install picamera
+ pip3 install picamera
```
Also, make sure to enable Raspberry Pi hardware-specific settings prior to using this library.
* **`mss:`** Required for using Screen Casting. Install it via pip:
```sh
- pip install mss
+ pip3 install mss
```
* **`pyzmq:`** Required for transferring live video frames through _ZeroMQ messaging system_ over the network. Install it via pip:
```sh
- pip install pyzmq
+ pip3 install pyzmq
```
* **`pafy:`** Required for direct YouTube Video streaming capabilities. Both [`pafy`][pafy] and latest only [`youtube-dl`][yt-dl](_as pafy's backend_) libraries must be installed via pip as follows:
```sh
- pip install pafy
- pip install -U youtube-dl
+ pip3 install pafy
+ pip3 install -U youtube-dl
```
@@ -165,7 +176,7 @@ Before installing VidGear, you must verify that the following dependencies are m
> Best option for **quickly** getting VidGear installed.
```sh
- pip install vidgear
+ pip3 install vidgear
```
@@ -179,14 +190,14 @@ VidGear releases are available for download as packages in the [latest release][
### Option 3: Clone the Repository
-> Best option for **latest patches**(_maybe experimental_), or **contributing** to development.
+> Best option for **latest patches & updates**(_but experimental_), or **contributing** to development.
You can clone this repository's `testing` branch for development and thereby can install as follows:
```sh
git clone https://github.com/abhiTronix/vidgear.git
cd vidgear
git checkout testing
- pip install .
+ sudo pip3 install .
```
@@ -216,7 +227,7 @@ VidGear is built with multi-threaded **Gears** each with some unique function/me
### CamGear
-CamGear supports a diverse range of video streams which can handle/control video stream almost any IP/USB Cameras, multimedia video file format ([_upto 4k tested_][test-4k]), network stream URL such as `http(s), rtp, rstp, mms, etc.` In addition to this, it also supports live Gstreamer's RAW pipelines and YouTube video/livestreams URLs. CamGear provides a flexible, high-level multi-threaded wrapper around `OpenCV's` [VideoCapture class][opencv-vc] with access almost all of its available parameters and also employs `pafy` python APIs for live [YouTube streaming][youtube-wiki]. Furthermore, CamGear relies exclusively on [**Threaded Queue mode**][TQM-wiki] for ultra-fast, error-free and synchronized frame handling.
+CamGear supports a diverse range of video streams which can handle/control video stream almost any IP/USB Cameras, multimedia video file format ([_upto 4k tested_][test-4k]), network stream URL such as `http(s), rtp, rstp, rtmp, mms, etc.` In addition to this, it also supports live Gstreamer's RAW pipelines and YouTube video/livestreams URLs. CamGear provides a flexible, high-level multi-threaded wrapper around `OpenCV's` [VideoCapture class][opencv-vc] with access almost all of its available parameters and also employs `pafy` python APIs for live [YouTube streaming][youtube-wiki]. Furthermore, CamGear relies exclusively on [**Threaded Queue mode**][TQM-wiki] for ultra-fast, error-free and synchronized frame handling.
**Following simplified functional block diagram depicts CamGear API's generalized working:**
@@ -233,7 +244,7 @@ CamGear supports a diverse range of video streams which can handle/control video
### VideoGear
-VideoGear API provides a special internal wrapper around VidGear's exclusive [**Video Stabilizer**][stablizer-wiki] class. Furthermore, VideoGear API can provide internal access to both [CamGear](#camgear) and [PiGear](#pigear) APIs separated by a special flag. Thereby, _this API holds the exclusive power for any incoming VideoStream from any source, whether it is live or not, to stabilize it directly with minimum latency and memory requirements._
+VideoGear API provides a special internal wrapper around VidGear's exclusive [**Video Stabilizer**][stablizer-wiki] class. Furthermore, VideoGear API can provide internal access to both [CamGear](#camgear) and [PiGear](#pigear) APIs separated by a special flag. Thereby, _this API holds the exclusive power for any incoming VideoStream from any source, whether it is live or not, to access and stabilize it directly with minimum latency and memory requirements._
**Below is a snapshot of a VideoGear Stabilizer in action:**
@@ -300,7 +311,7 @@ stream_stab.stop()
### PiGear
-PiGear is similar to CamGear but made to support various Raspberry Pi Camera Modules (such as [OmniVision OV5647 Camera Module][OV5647-picam] and [Sony IMX219 Camera Module][IMX219-picam]). To interface with these modules correctly, PiGear provides a flexible multi-threaded wrapper around complete [picamera][picamera] python library, and provides us the ability to exploit its various features like `brightness, saturation, sensor_mode, etc.` effortlessly.
+PiGear is similar to CamGear but made to support various Raspberry Pi Camera Modules (such as [OmniVision OV5647 Camera Module][OV5647-picam] and [Sony IMX219 Camera Module][IMX219-picam]). To interface with these modules correctly, PiGear provides a flexible multi-threaded wrapper around complete [picamera][picamera] python library, and provides us the ability to exploit its various features like `brightness, saturation, sensor_mode, etc.` effortlessly. In addition to this, PiGear API provides excellent Error-Handling with features like a threaded internal timer that keeps active track of any frozen threads and handles hardware failures/frozen threads robustly thereby will exit safely if any failure occurs. So if you accidently pulled your camera cable out when running PiGear API in your script, instead of going into possible kernel panic due to IO error it will exit safely to save resources.
**Following simplified functional block diagram depicts PiGear API:**
@@ -392,7 +403,9 @@ WriteGear is undoubtedly the most powerful Video Processing Gear of them all. It
### NetGear
-NetGear is exclusively designed to transfer video frames synchronously between interconnecting systems over the network in real-time. This is achieved by implementing a high-level wrapper around [PyZmQ][pyzmq] python library that contains python bindings for [ZeroMQ](http://zeromq.org/) - a high-performance asynchronous distributed messaging library that aim to be used in distributed or concurrent applications. It provides a message queue, but unlike message-oriented middleware, a ZeroMQ system can run without a dedicated message broker. Furthermore, It also provides easy access to powerful, smart & secure ZeroMQ's Security Layers in NetGear API that enables strong encryption on data, and unbreakable authentication between the Server and the Client with the help of custom certificates/keys and brings cheap, standardized privacy and authentication for distributed systems over the network. On top of that, this API can robustly handle Multiple Servers at once, thereby providing access to seamless Live Streams of the various device in a network at the same time.
+NetGear is exclusively designed to transfer video frames synchronously and asynchronously between interconnecting systems over the network in real-time. This is achieved by implementing a high-level wrapper around [PyZmQ][pyzmq] python library that contains python bindings for [ZeroMQ](http://zeromq.org/) - a high-performance asynchronous distributed messaging library that aim to be used in distributed or concurrent applications. It provides a message queue, but unlike message-oriented middleware, a ZeroMQ system can run without a dedicated message broker. It provides seamless support for bidirectional data transmission between receiver(client) and sender(server) through bi-directional synchronous messaging patterns such as zmq.PAIR (ZMQ Pair Pattern) & zmq.REQ/zmq.REP (ZMQ Request/Reply Pattern). Plus also introduces real-time frame Encoding/Decoding compression capabilities for optimizing performance while sending the frames of large size directly over the network by encoding the frame before sending it and decoding it on the client's end automatically all in real-time.
+
+For security, NetGear also supports easy access to ZeroMQ's powerful, smart & secure Security Layers in that enables strong encryption on data, and unbreakable authentication between the Server and the Client with the help of custom certificates/keys and brings cheap, standardized privacy and authentication for distributed systems over the network. On top of that, this API can robustly handle Multiple Servers at once, thereby providing access to seamless Live Streams of the various device in a network at the same time.
**NetGear as of now seamlessly supports three ZeroMQ messaging patterns:**
@@ -433,8 +446,8 @@ The full documentation for all VidGear classes and functions can be found in the
* **Download few additional python libraries:**
```sh
- pip install six
- pip install pytest
+ pip3 install six
+ pip3 install pytest
```
* **Download Test Dataset:** To perform tests, additional *test dataset* is required, which can be downloaded by running [*bash script*][bs_script_dataset] as follows:
@@ -466,9 +479,8 @@ See [Wiki: Project Motivation][wiki-vidgear-purpose]
## Supported Python legacies
- * **Python 2.7 legacies:** *VidGear v0.1.5 is officially the last Python 2.7 legacies supporting version.* Kindly migrate your source code to Python 3 as soon as possible.
-
- * **Python 3.x legacies:** follows the [numpy][numpy] releases.
+ * **Python 3+ are only supported legacies for installing Vidgear v0.1.6 and above.**
+ * **:warning: Python 2.7 legacy support [dropped in v0.1.6][drop27].**
@@ -492,6 +504,7 @@ Badges
-->
[appveyor]:https://img.shields.io/appveyor/ci/abhitronix/vidgear.svg?style=for-the-badge&logo=appveyor
+[codecov]:https://img.shields.io/codecov/c/github/abhiTronix/vidgear/testing?style=for-the-badge&logo=codecov
[travis-cli]:https://img.shields.io/travis/abhiTronix/vidgear.svg?style=for-the-badge&logo=travis
[prs-badge]:https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=for-the-badge&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABC0lEQVRYhdWVPQoCMRCFX6HY2ghaiZUXsLW0EDyBrbWtN/EUHsHTWFnYyCL4gxibVZZlZzKTnWz0QZpk5r0vIdkF/kBPAMOKeddE+CQPKoc5Yt5cTjBMdQSwDQToWgBJAn3jmhqgltapAV6E6b5U17MGGAUaUj07TficMfIBZDV6vxowBm1BP9WbSQE4o5h9IjPJmy73TEPDDxVmoZdQrQ5jRhly9Q8tgMUXkIIWn0oG4GYQfAXQzz1PGoCiQndM7b4RgJay/h7zBLT3hASgoKjamQJMreKf0gfuAGyYtXEIAKcL/Dss15iq6ohXghozLYiAMxPuACwtIT4yeQUxAaLrZwAoqGRKGk7qDSYTfYQ8LuYnAAAAAElFTkSuQmCC
[twitter-badge]:https://img.shields.io/twitter/url/http/shields.io.svg?style=for-the-badge&logo=twitter
@@ -511,6 +524,7 @@ Internal URLs
[license]:https://github.com/abhiTronix/vidgear/blob/master/LICENSE
[travis]:https://travis-ci.org/abhiTronix/vidgear
[app]:https://ci.appveyor.com/project/abhiTronix/vidgear
+[code]:https://codecov.io/gh/abhiTronix/vidgear
[test-4k]:https://github.com/abhiTronix/vidgear/blob/e0843720202b0921d1c26e2ce5b11fadefbec892/vidgear/tests/benchmark_tests/test_benchmark_playback.py#L65
[bs_script_dataset]:https://github.com/abhiTronix/vidgear/blob/testing/scripts/bash/prepare_dataset.sh
@@ -533,6 +547,7 @@ Internal URLs
[screengear-wiki]:https://github.com/abhiTronix/vidgear/wiki/ScreenGear#screengear-api
[writegear-wiki]:https://github.com/abhiTronix/vidgear/wiki/WriteGear#writegear-api
[netgear-wiki]:https://github.com/abhiTronix/vidgear/wiki/NetGear#netgear-api
+[drop27]:https://github.com/abhiTronix/vidgear/issues/29
@@ -44,7 +39,7 @@ THE SOFTWARE.
-VidGear is a powerful python Video Processing library built with multi-threaded [**Gears**](#gear)(_a.k.a APIs_) each with a unique set of trailblazing features. These APIs provides a easy-to-use, highly extensible, and multi-threaded wrapper around many underlying state-of-the-art libraries such as *[OpenCV ➶][opencv], [FFmpeg ➶][ffmpeg], [picamera ➶][picamera], [pafy ➶][pafy], [pyzmq ➶][pyzmq] and [python-mss ➶][mss]*
+VidGear is a powerful python Video Processing library built with multi-threaded [**Gears**](#gears) each with a unique set of trailblazing features. These APIs provides a easy-to-use, highly extensible, and multi-threaded wrapper around many underlying state-of-the-art libraries such as *[OpenCV ➶][opencv], [FFmpeg ➶][ffmpeg], [picamera ➶][picamera], [pafy ➶][pafy], [pyzmq ➶][pyzmq] and [python-mss ➶][mss]*
@@ -60,14 +55,6 @@ The following **functional block diagram** clearly depicts the functioning of Vi
[**TL;DR**](#tldr)
-[**New Release SneekPeak**](#new-release-sneekpeak--vidgear-016)
-
-[**Installation Options**](#installation)
- * [**Prerequisites**](#prerequisites)
- * [**1 - PyPI Install**](#option-1-pypi-install)
- * [**2 - Release Archive Download**](#option-2-release-archive-download)
- * [**3 - Clone Repo**](#option-3-clone-the-repo)
-
[**Gears: What are these?**](#gears)
* [**CamGear**](#camgear)
* [**PiGear**](#pigear)
@@ -76,135 +63,46 @@ The following **functional block diagram** clearly depicts the functioning of Vi
* [**WriteGear**](#writegear)
* [**NetGear**](#netgear)
-**For Developers/Contributors**
- * [**Testing**](#testing)
- * [**Contributing**](#contributing)
+[**Installation Options**](#installation)
+ * [**Prerequisites**](#prerequisites)
+ * [**1 - PyPI Install**](#option-1-pypi-install)
+ * [**2 - Release Archive Download**](#option-2-release-archive-download)
+ * [**3 - Clone Repo**](#option-3-clone-the-repo)
+
+[**New-Release SneekPeak: v0.1.6**](#new-release-sneekpeak--vidgear-016)
[**Documentation**](#documentation)
-[**Project Motivation**](#project-motivation)
+**For Developers/Contributors**
+ * [**Testing**](#testing)
+ * [**Contributing**](#contributing)
**Additional Info**
* [**Supported Python legacies**](#supported-python-legacies)
* [**Changelog**](#changelog)
* [**License**](#license)
-
-
-## TL;DR
- *"VidGear is an [ultrafast➶][ultrafast-wiki], compact, flexible and easy-to-adapt complete Video Processing Python Library."*
-
- Built with simplicity in mind, VidGear lets programmers and software developers to easily integrate and perform complex Video Processing tasks in their existing or new applications, without going through various underlying library's documentation and using just a few lines of code. Beneficial for both, if you're new to programming with Python language or already a pro at it.
-
- **For more detailed information see the [*Wiki Documentation ➶*][wiki].**
-
-
-
-## New Release SneekPeak : VidGear 0.1.6
-
-***:warning: Python 2.7 legacy support [dropped in v0.1.6][drop27]!***
-
-**NetGear API:**
- * Added powerful ZMQ Authentication & Data Encryption features for NetGear API
- * Added robust Multi-Server support for NetGear API.
- * Added exclusive Bi-Directional Mode for bidirectional data transmission.
- * Added frame-compression support with on-the-fly flexible encoding/decoding.
- * Implemented new *Publish/Subscribe(`zmq.PUB/zmq.SUB`)* pattern for seamless Live Streaming in NetGear API.
-
-**PiGear API:**
- * Added new threaded internal timing function for PiGear to handle any hardware failures/frozen threads
- * PiGear will not exit safely with `SystemError` if Picamera ribbon cable is pulled out to save resources.
-
-**WriteGear API:** Added new `execute_ffmpeg_cmd` function to pass a custom command to its internal FFmpeg pipeline.
-**Stabilizer class:** Added new _Crop and Zoom_ feature.
-
-***Added VidGear's official native support for MacOS environment and [many more...](changelog.md)***
-
-
-
-## Installation
-
-### Prerequisites
-
-Before installing VidGear, you must verify that the following dependencies are met:
-
-* Must be using [supported Python legacies](#supported-python-legacies) only and thereby [pip][pip] already installed properly.
-
-
-* **`OpenCV:`** VidGear must require OpenCV(3.0+) python enabled binaries to be installed on your machine for its core functions. For its installation, you can follow these online tutorials for [linux][OpenCV-linux] and [raspberry pi][OpenCV-pi], otherwise, install it via pip:
-
- ```sh
- pip3 install -U opencv-python #or install opencv-contrib-python similarly
- ```
-
-* **`FFmpeg:`** VidGear must require FFmpeg for its powerful video compression and encoding capabilities. :star2: Follow this [**FFmpeg wiki page**][ffmpeg-wiki] for its installation. :star2:
-
-* **`picamera:`** Required if using Raspberry Pi Camera Modules(_such as OmniVision OV5647 Camera Module_) with your Raspberry Pi machine. You can easily install it via pip:
-
- ```sh
- pip3 install picamera
- ```
- Also, make sure to enable Raspberry Pi hardware-specific settings prior to using this library.
-
-* **`mss:`** Required for using Screen Casting. Install it via pip:
-
- ```sh
- pip3 install mss
- ```
-* **`pyzmq:`** Required for transferring live video frames through _ZeroMQ messaging system_ over the network. Install it via pip:
-
- ```sh
- pip3 install pyzmq
- ```
-
-* **`pafy:`** Required for direct YouTube Video streaming capabilities. Both [`pafy`][pafy] and latest only [`youtube-dl`][yt-dl](_as pafy's backend_) libraries must be installed via pip as follows:
-
- ```sh
- pip3 install pafy
- pip3 install -U youtube-dl
- ```
-
-
-
-
-### Option 1: PyPI Install
-
-> Best option for **quickly** getting VidGear installed.
-
-```sh
- pip3 install vidgear
-```
-
+## TL;DR
+
+ > ***"VidGear is an [ultrafast➶][ultrafast-wiki], compact, flexible and easy-to-adapt complete Video Processing Python Library."***
-### Option 2: Release Archive Download
+ *Built with simplicity in mind, VidGear lets programmers and software developers to easily integrate and perform complex Video Processing tasks in their existing or new applications, without going through various underlying library's documentation and using just a few lines of code. Beneficial for both, if you're new to programming with Python language or already a pro at it.*
-> Best option if you want a **compressed archive**.
+ **For more advanced information, see the [*Wiki Documentation ➶*][wiki].**
-VidGear releases are available for download as packages in the [latest release][release]
-### Option 3: Clone the Repository
-
-> Best option for **latest patches & updates**(_but experimental_), or **contributing** to development.
-
-You can clone this repository's `testing` branch for development and thereby can install as follows:
-```sh
- git clone https://github.com/abhiTronix/vidgear.git
- cd vidgear
- git checkout testing
- sudo pip3 install .
-```
-
-
## Gears:
-VidGear is built with multi-threaded **Gears** each with some unique function/mechanism. Each **Gear** is designed exclusively to handle/control different device-specific video streams, network streams, and media encoders. These APIs provides an easy-to-use, highly extensible, and a multi-threaded wrapper around various underlying libraries to exploit their features and functions directly while providing robust error-handling.
+> **VidGear is built with **multi-threaded APIs** *(a.k.a Gears)* each with some unique function/mechanism.**
+
+Each of these API is designed exclusively to handle/control different device-specific video streams, network streams, and media encoders. These APIs provides an easy-to-use, highly extensible, and a multi-threaded wrapper around various underlying libraries to exploit their features and functions directly while providing robust error-handling.
**These Gears can be classified as follows:**
@@ -221,13 +119,15 @@ VidGear is built with multi-threaded **Gears** each with some unique function/me
**C. Network Gear:**
- * [**NetGear:**](#netgear) _Targets synchronous video frames transferring between interconnecting systems over the network._
+ * [**NetGear:**](#netgear) _Targets synchronous/asynchronous video frames transferring between interconnecting systems over the network._
### CamGear
-CamGear supports a diverse range of video streams which can handle/control video stream almost any IP/USB Cameras, multimedia video file format ([_upto 4k tested_][test-4k]), network stream URL such as `http(s), rtp, rstp, rtmp, mms, etc.` In addition to this, it also supports live Gstreamer's RAW pipelines and YouTube video/livestreams URLs. CamGear provides a flexible, high-level multi-threaded wrapper around `OpenCV's` [VideoCapture class][opencv-vc] with access almost all of its available parameters and also employs `pafy` python APIs for live [YouTube streaming][youtube-wiki]. Furthermore, CamGear relies exclusively on [**Threaded Queue mode**][TQM-wiki] for ultra-fast, error-free and synchronized frame handling.
+> **CamGear can grab ultrafast frames from diverse range of VideoStreams, which includes almost any IP/USB Cameras, multimedia video file format ([_upto 4k tested_][test-4k]), various network stream protocols such as `http(s), rtp, rstp, rtmp, mms, etc.`, plus support for live Gstreamer's stream pipeline and YouTube video/livestreams URLs.**
+
+CamGear provides a flexible, high-level multi-threaded wrapper around `OpenCV's` [VideoCapture class][opencv-vc] with access almost all of its available parameters and also employs [`pafy`][pafy] python APIs for live [YouTube streaming][youtube-wiki]. Furthermore, CamGear implements exclusively on [**Threaded Queue mode**][TQM-wiki] for ultra-fast, error-free and synchronized frame handling.
**Following simplified functional block diagram depicts CamGear API's generalized working:**
@@ -236,22 +136,24 @@ CamGear supports a diverse range of video streams which can handle/control video
-**CamGear API Guide:**
+#### CamGear API Guide:
-[>>> Usage Guide][camgear-wiki]
+[**>>> Usage Guide**][camgear-wiki]
### VideoGear
-VideoGear API provides a special internal wrapper around VidGear's exclusive [**Video Stabilizer**][stablizer-wiki] class. Furthermore, VideoGear API can provide internal access to both [CamGear](#camgear) and [PiGear](#pigear) APIs separated by a special flag. Thereby, _this API holds the exclusive power for any incoming VideoStream from any source, whether it is live or not, to access and stabilize it directly with minimum latency and memory requirements._
+> **VideoGear API provides a special internal wrapper around VidGear's exclusive [**Video Stabilizer**][stablizer-wiki] class.**
+
+Furthermore, VideoGear API can provide internal access to both [CamGear](#camgear) and [PiGear](#pigear) APIs separated by a special flag. Thereby, _this API holds the exclusive power for any incoming VideoStream from any source, whether it is live or not, to access and stabilize it directly with minimum latency and memory requirements._
**Below is a snapshot of a VideoGear Stabilizer in action:**
- Original Video Courtesy@SIGGRAPH2013
+ Original Video Courtesy @SIGGRAPH2013
Code to generate above VideoGear API Stabilized Video(_See more detailed usage examples [here][stablizer-wiki-ex]_):
@@ -303,15 +205,19 @@ stream_stab.stop()
```
-**VideoGear API Guide:**
+#### VideoGear API Guide:
-[>>> Usage Guide][videogear-wiki]
+[**>>> Usage Guide**][videogear-wiki]
### PiGear
-PiGear is similar to CamGear but made to support various Raspberry Pi Camera Modules (such as [OmniVision OV5647 Camera Module][OV5647-picam] and [Sony IMX219 Camera Module][IMX219-picam]). To interface with these modules correctly, PiGear provides a flexible multi-threaded wrapper around complete [picamera][picamera] python library, and provides us the ability to exploit its various features like `brightness, saturation, sensor_mode, etc.` effortlessly. In addition to this, PiGear API provides excellent Error-Handling with features like a threaded internal timer that keeps active track of any frozen threads and handles hardware failures/frozen threads robustly thereby will exit safely if any failure occurs. So if you accidently pulled your camera cable out when running PiGear API in your script, instead of going into possible kernel panic due to IO error it will exit safely to save resources.
+> **PiGear is similar to CamGear but made to support various Raspberry Pi Camera Modules *(such as [OmniVision OV5647 Camera Module][OV5647-picam] and [Sony IMX219 Camera Module][IMX219-picam])*.**
+
+PiGear provides a flexible multi-threaded wrapper around complete [**picamera**][picamera] python library to interface with these modules correctly, and also grants the ability to exploit its various features like `brightness, saturation, sensor_mode, etc.` effortlessly.
+
+Best of all, PiGear API provides excellent Error-Handling with features like a threaded internal timer that keeps active track of any frozen threads and handles hardware failures/frozen threads robustly thereby will exit safely if any failure occurs. So if you accidently pulled your camera cable out when running PiGear API in your script, instead of going into possible kernel panic due to IO error, it will exit safely to save resources.
**Following simplified functional block diagram depicts PiGear API:**
@@ -319,15 +225,17 @@ PiGear is similar to CamGear but made to support various Raspberry Pi Camera Mod
-**PiGear API Guide:**
+#### PiGear API Guide:
-[>>> Usage Guide][pigear-wiki]
+[**>>> Usage Guide**][pigear-wiki]
### ScreenGear
-With ScreenGear, we can easily define an area on the computer screen or an open window to record the live screen frames in real-time at the expense of insignificant latency. To achieve this, ScreenGear provides a high-level multi-threaded wrapper around [**`mss`**][mss] python library API and also supports the flexible direct parameter manipulation.
+> **ScreenGear act as Screen Recorder, that can grab frames from your monitor in real-time either by define an area on the computer screen or fullscreen at the expense of insignificant latency. It also provide seemless support for capturing frames from multiple monitors.**
+
+ScreenGear provides a high-level multi-threaded wrapper around [**python-mss**][mss] python library API and also supports a easy and flexible direct internal parameter manipulation.
**Below is a snapshot of a ScreenGear API in action:**
@@ -371,9 +279,9 @@ stream.stop()
# safely close video stream.
```
-**ScreenGear API Guide:**
+#### ScreenGear API Guide:
-[>>> Usage Guide][screengear-wiki]
+[**>>> Usage Guide**][screengear-wiki]
@@ -381,7 +289,11 @@ stream.stop()
### WriteGear
-WriteGear is undoubtedly the most powerful Video Processing Gear of them all. It solely handles various powerful FFmpeg tools that provide us the freedom to do almost anything imagine with multimedia files. For example with WriteGear API, we can process real-time video frames into a lossless compressed format with any suitable specification in just few easy [lines of codes][compression-mode-ex]. These specifications include setting any video/audio property such as `bitrate, codec, framerate, resolution, subtitles, etc.` Furthermore, we can multiplex extracted audio at the output with compression and all that in real-time(see this [example wiki][live-audio-wiki]). In addition to this, WriteGear also provides flexible access to OpenCV's VideoWriter API which provides some basic tools for video frames encoding but without compression.
+> **WriteGear handles various powerful Writer Tools that provide us the freedom to do almost anything imagine with multimedia files.**
+
+WriteGear API provide a complete, flexible & robust wrapper around [**FFmpeg**][ffmpeg], a leading multimedia framework. With WriteGear, we can process real-time video frames into a lossless compressed format with any suitable specification in just few easy [lines of codes][compression-mode-ex]. These specifications include setting any video/audio property such as `bitrate, codec, framerate, resolution, subtitles, etc.` easily as well complex tasks such as multiplexing video with audio in real-time(see this [example wiki][live-audio-wiki]). Best of all, WriteGear grants the freedom to play with any FFmpeg parameter with its exclusive custom Command function(see this [example wiki][custom-command-wiki]), while handling all errors robustly.
+
+In addition to this, WriteGear also provides flexible access to [**OpenCV's VideoWriter API**][opencv-writer] which provides some basic tools for video frames encoding but without compression.
**WriteGear primarily operates in the following two modes:**
@@ -395,26 +307,32 @@ WriteGear is undoubtedly the most powerful Video Processing Gear of them all. It
-**WriteGear API Guide:**
+#### WriteGear API Guide:
-[>>> Usage Guide][writegear-wiki]
+[**>>> Usage Guide**][writegear-wiki]
### NetGear
-NetGear is exclusively designed to transfer video frames synchronously and asynchronously between interconnecting systems over the network in real-time. This is achieved by implementing a high-level wrapper around [PyZmQ][pyzmq] python library that contains python bindings for [ZeroMQ](http://zeromq.org/) - a high-performance asynchronous distributed messaging library that aim to be used in distributed or concurrent applications. It provides a message queue, but unlike message-oriented middleware, a ZeroMQ system can run without a dedicated message broker. It provides seamless support for bidirectional data transmission between receiver(client) and sender(server) through bi-directional synchronous messaging patterns such as zmq.PAIR (ZMQ Pair Pattern) & zmq.REQ/zmq.REP (ZMQ Request/Reply Pattern). Plus also introduces real-time frame Encoding/Decoding compression capabilities for optimizing performance while sending the frames of large size directly over the network by encoding the frame before sending it and decoding it on the client's end automatically all in real-time.
+> **NetGear is exclusively designed to transfer video frames synchronously and asynchronously between interconnecting systems over the network in real-time.**
-For security, NetGear also supports easy access to ZeroMQ's powerful, smart & secure Security Layers in that enables strong encryption on data, and unbreakable authentication between the Server and the Client with the help of custom certificates/keys and brings cheap, standardized privacy and authentication for distributed systems over the network. On top of that, this API can robustly handle Multiple Servers at once, thereby providing access to seamless Live Streams of the various device in a network at the same time.
+NetGear implements a high-level wrapper around [**PyZmQ**][pyzmq] python library that contains python bindings for [ZeroMQ](http://zeromq.org/) - a high-performance asynchronous distributed messaging library that aim to be used in distributed or concurrent applications. It provides a message queue, but unlike message-oriented middleware, a ZeroMQ system can run without a dedicated message broker.
+NetGear provides seamless support for bidirectional data transmission between receiver(client) and sender(server) through bi-directional synchronous messaging patterns such as zmq.PAIR _(ZMQ Pair Pattern)_ & zmq.REQ/zmq.REP _(ZMQ Request/Reply Pattern)_.
-**NetGear as of now seamlessly supports three ZeroMQ messaging patterns:**
+NetGear also introduces real-time frame Encoding/Decoding compression capabilities for optimizing performance while sending the frames of large size directly over the network by encoding the frame before sending it and decoding it on the client's end automatically all in real-time.
- * ZMQ Pair Pattern
- * ZMQ Client/Server Pattern
- * ZMQ Publish/Subscribe Pattern
+For security, NetGear also supports easy access to ZeroMQ's powerful, smart & secure Security Layers, that enables strong encryption on data, and unbreakable authentication between the Server and the Client with the help of custom certificates/keys and brings easy, standardized privacy and authentication for distributed systems over the network.
-whereas the supported protocol are: `tcp, upd, pgm, inproc, ipc`.
+Best of all, NetGear can robustly handle Multiple Servers at once, thereby providing access to seamless Live Streams of the various device in a network at the same time.
+
+
+**NetGear as of now seamlessly supports three ZeroMQ messaging patterns:**
+
+* [**`zmq.PAIR`**][zmq-pair] _(ZMQ Pair Pattern)_
+* [**`zmq.REQ/zmq.REP`**][zmq-req-rep] _(ZMQ Request/Reply Pattern)_
+* [**`zmq.PUB/zmq.SUB`**][zmq-pub-sub] _(ZMQ Publish/Subscribe Pattern)_
**Following functional block diagram depicts generalized functioning of NetGear API:**
@@ -423,13 +341,121 @@ whereas the supported protocol are: `tcp, upd, pgm, inproc, ipc`.
-**NetGear API Guide:**
+#### NetGear API Guide:
+
+[**>>> Usage Guide**][netgear-wiki]
+
+
+
+
+
+## New Release SneekPeak : VidGear 0.1.6
+
+***:warning: Python 2.7 legacy support [dropped in v0.1.6][drop27] !***
+
+**NetGear API:**
+ * Added powerful ZMQ Authentication & Data Encryption features for NetGear API
+ * Added robust Multi-Server support for NetGear API.
+ * Added exclusive Bi-Directional Mode for bidirectional data transmission.
+ * Added frame-compression support with on-the-fly flexible encoding/decoding.
+ * Implemented new *Publish/Subscribe(`zmq.PUB/zmq.SUB`)* pattern for seamless Live Streaming in NetGear API.
+
+**PiGear API:**
+ * Added new threaded internal timing function for PiGear to handle any hardware failures/frozen threads
+ * PiGear will not exit safely with `SystemError` if Picamera ribbon cable is pulled out to save resources.
+
+**WriteGear API:** Added new `execute_ffmpeg_cmd` function to pass a custom command to its internal FFmpeg pipeline.
+
+**Stabilizer class:** Added new _Crop and Zoom_ feature.
+
+***Added VidGear's official native support for MacOS environment and [many more...](changelog.md)***
+
-[>>> Usage Guide][netgear-wiki]
+
+
+## Installation
+
+### Prerequisites
+
+Before installing VidGear, you must verify that the following dependencies are met:
+
+* :warning: Must be using only [**supported Python legacies**](#supported-python-legacies) and also [**pip**][pip] already installed and configured.
+
+
+* **`OpenCV:`** VidGear must require OpenCV(3.0+) python enabled binaries to be installed on your machine for its core functions. For its installation, you can follow these online tutorials for [linux][OpenCV-linux] and [raspberry pi][OpenCV-pi], otherwise, install it via pip:
+
+ ```sh
+ pip3 install -U opencv-python #or install opencv-contrib-python similarly
+ ```
+
+* **`FFmpeg:`** VidGear must require FFmpeg for its powerful video compression and encoding capabilities. :star2: Follow this [**FFmpeg wiki page**][ffmpeg-wiki] for its installation. :star2:
+
+* **`picamera:`** Required if using Raspberry Pi Camera Modules(_such as OmniVision OV5647 Camera Module_) with your Raspberry Pi machine. You can easily install it via pip:
+
+ ```sh
+ pip3 install picamera
+ ```
+ Also, make sure to enable Raspberry Pi hardware-specific settings prior to using this library.
+
+* **`mss:`** Required for using Screen Casting. Install it via pip:
+
+ ```sh
+ pip3 install mss
+ ```
+* **`pyzmq:`** Required for transferring live video frames through _ZeroMQ messaging system_ over the network. Install it via pip:
+
+ ```sh
+ pip3 install pyzmq
+ ```
+
+* **`pafy:`** Required for direct YouTube Video streaming capabilities. Both [`pafy`][pafy] and latest only [`youtube-dl`][yt-dl](_as pafy's backend_) libraries must be installed via pip as follows:
+
+ ```sh
+ pip3 install pafy
+ pip3 install -U youtube-dl
+ ```
+
+
+
+
+### Option 1: PyPI Install
+
+> Best option for **quickly** getting VidGear installed.
+
+```sh
+ pip3 install vidgear
+```
+
+
+### Option 2: Release Archive Download
+
+> Best option if you want a **compressed archive**.
+
+VidGear releases are available for download as packages in the [latest release][release]
+
+
+
+### Option 3: Clone the Repository
+
+> Best option for trying **latest patches, Pull requests & updgrades**(_maybe experimental_), or **contributing** to development.
+
+You can clone this repository's `testing` branch for development and thereby can install as follows:
+```sh
+ git clone https://github.com/abhiTronix/vidgear.git
+ cd vidgear
+ git checkout testing
+ sudo pip3 install .
+```
+
+
+
+
+
+
## Documentation
The full documentation for all VidGear classes and functions can be found in the link below:
@@ -450,11 +476,11 @@ The full documentation for all VidGear classes and functions can be found in the
pip3 install pytest
```
- * **Download Test Dataset:** To perform tests, additional *test dataset* is required, which can be downloaded by running [*bash script*][bs_script_dataset] as follows:
+ * **Download Test Dataset:** To perform tests, additional *test dataset* is required, which can be downloaded *(to temp dir)* by running [*bash script*][bs_script_dataset] as follows:
```sh
- chmod +x scripts/prepare_dataset.sh
- ./scripts/prepare_dataset.sh #for windows, use `sh scripts/pre_install.sh`
+ chmod +x scripts/bash/prepare_dataset.sh
+ .scripts/bash/prepare_dataset.sh #for windows, use `sh scripts/pre_install.sh`
```
* **Run Tests:** Then various VidGear tests can be run with `pytest`(*in VidGear's root folder*) as below:
@@ -467,13 +493,7 @@ The full documentation for all VidGear classes and functions can be found in the
## Contributing
-See [contributing.md](contributing.md)
-
-
-
-## Project Motivation
-
-See [Wiki: Project Motivation][wiki-vidgear-purpose]
+See [**contributing.md**](contributing.md)
@@ -486,7 +506,7 @@ See [Wiki: Project Motivation][wiki-vidgear-purpose]
## Changelog
-See [changelog.md](changelog.md)
+See [**changelog.md**](changelog.md)
@@ -494,7 +514,7 @@ See [changelog.md](changelog.md)
Copyright © abhiTronix 2019
-This project is licensed under the [MIT][license] license.
+This library is licensed under the **[Apache 2.0 License][license]**.
@@ -548,17 +568,17 @@ Internal URLs
[writegear-wiki]:https://github.com/abhiTronix/vidgear/wiki/WriteGear#writegear-api
[netgear-wiki]:https://github.com/abhiTronix/vidgear/wiki/NetGear#netgear-api
[drop27]:https://github.com/abhiTronix/vidgear/issues/29
-
+[custom-command-wiki]:https://github.com/abhiTronix/vidgear/wiki/Custom-FFmpeg-Commands-in-WriteGear-API#custom-ffmpeg-commands-in-writegear-api
-
+[ffmpeg]:https://www.ffmpeg.org/
+[opencv-writer]:https://docs.opencv.org/master/dd/d9e/classcv_1_1VideoWriter.html#ad59c61d8881ba2b2da22cff5487465b5
[OpenCV-linux]:https://www.pyimagesearch.com/2018/05/28/ubuntu-18-04-how-to-install-opencv/
[OpenCV-pi]:https://www.pyimagesearch.com/2018/09/26/install-opencv-4-on-your-raspberry-pi/
-[prs]:http://makeapullrequest.com "Make a Pull Request (external link) ➶"
+[prs]:http://makeapullrequest.com
[opencv]:https://github.com/opencv/opencv
-[ffmpeg]:https://ffmpeg.org/
[picamera]:https://github.com/waveform80/picamera
[pafy]:https://github.com/mps-youtube/pafy
[pyzmq]:https://github.com/zeromq/pyzmq
@@ -569,4 +589,7 @@ External URLs
[IMX219-picam]:https://github.com/techyian/MMALSharp/wiki/Sony-IMX219-Camera-Module
[opencv-vw]:https://docs.opencv.org/3.4/d8/dfe/classcv_1_1VideoCapture.html
[yt-dl]:https://github.com/ytdl-org/youtube-dl/
-[numpy]:https://github.com/numpy/numpy
\ No newline at end of file
+[numpy]:https://github.com/numpy/numpy
+[zmq-pair]:https://learning-0mq-with-pyzmq.readthedocs.io/en/latest/pyzmq/patterns/pair.html
+[zmq-req-rep]:https://learning-0mq-with-pyzmq.readthedocs.io/en/latest/pyzmq/patterns/client_server.html
+[zmq-pub-sub]:https://learning-0mq-with-pyzmq.readthedocs.io/en/latest/pyzmq/patterns/pubsub.html
\ No newline at end of file
diff --git a/appveyor.yml b/appveyor.yml
index 82bed9beb..0374cecff 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,3 +1,17 @@
+# Copyright (c) 2019 Abhishek Thakur(@abhiTronix)
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
environment:
matrix:
diff --git a/changelog.md b/changelog.md
index 39a64d242..b8b6e32bd 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,6 +1,6 @@
# CHANGELOG
-## VidGear 0.1.6-dev
+## VidGear 0.1.6
### New Features:
* **NetGear API:**
@@ -55,7 +55,8 @@
### Updates/Improvements:
- * Updated support for screen casting from all monitors in ScreenGear API.
+ * Replace `print` logging commands with python's logging module completely.
+ * Updated support for screen casting from multiple/all monitors in ScreenGear API.
* Updated ScreenGear API to use *Threaded Queue Mode* by default, thereby removed redundant `THREADED_QUEUE_MODE` param.
* Updated bash script path to download test dataset in `$TMPDIR` rather than `$HOME` directory for downloading testdata.
* Added support for `REQ/REP` pattern in Multi-Server Mode
@@ -70,7 +71,9 @@
### Breaking Updates / Improvements / Changes
* :warning: Python 2.7 legacy support dropped completely.
- * Python 3+ are only supported legacies for installing Vidgear v0.1.6 and above.
+ * :warning: Source-code Relicensed to Apache 2.0 License.
+ * Python 3+ are only supported legacies for installing Vidgear v0.1.6 and above.
+ * Python 2.7 and 3.4 legacies support dropped from VidGear CLI tests.
### Fixes
* Reimplemented `Pub/Sub` pattern for smoother performance(#70)
@@ -94,6 +97,7 @@
* PR #72
* PR #77
* PR #78
+ * PR #82
:warning: PyPi Release does NOT contain Tests and Scripts!
diff --git a/codecov.yml b/codecov.yml
index 66a94bdc3..36888fe5f 100644
--- a/codecov.yml
+++ b/codecov.yml
@@ -1,3 +1,17 @@
+# Copyright (c) 2019 Abhishek Thakur(@abhiTronix)
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
codecov:
require_ci_to_pass: yes
ci:
@@ -6,6 +20,10 @@ codecov:
branch: testing
strict_yaml_branch: testing
+coverage:
+ status:
+ patch: off
+
ignore:
- "vidgear/tests"
- "setup.py"
\ No newline at end of file
diff --git a/scripts/bash/install_opencv.sh b/scripts/bash/install_opencv.sh
index 9e375b8a0..ddd69fe2c 100644
--- a/scripts/bash/install_opencv.sh
+++ b/scripts/bash/install_opencv.sh
@@ -1,23 +1,26 @@
#!/bin/sh
-#Copyright (c) 2019 Abhishek Thakur
+# Copyright (c) 2019 Abhishek Thakur(@abhiTronix)
-#Permission is hereby granted, free of charge, to any person obtaining a copy
-#of this software and associated documentation files (the "Software"), to deal
-#in the Software without restriction, including without limitation the rights
-#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-#copies of the Software, and to permit persons to whom the Software is
-#furnished to do so, subject to the following conditions:
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
-#The above copyright notice and this permission notice shall be included in all
-#copies or substantial portions of the Software.
+# http://www.apache.org/licenses/LICENSE-2.0
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
-######################################
-# Installing OpenCV Binaries #
-######################################
+########################################
+# Installs OpenCV Offical Binaries for #
+# CLI Linux Environments #
+########################################
+#opencv version to install
OPENCV_VERSION='4.2.0-pre'
#determining system specific temp directory
@@ -26,9 +29,9 @@ TMPFOLDER=$(python -c 'import tempfile; print(tempfile.gettempdir())')
#determining system Python suffix and version
PYTHONSUFFIX=$(python -c 'import platform; a = platform.python_version(); print(".".join(a.split(".")[:2]))')
PYTHONVERSION=$(python -c 'import platform; print(platform.python_version())')
+PYTHONVERSIONMIN=$(python3 -c 'import platform; print(platform.python_version()[:3])')
echo "Installing OpenCV..."
-
echo "Installing OpenCV Dependencies..."
sudo apt-get install -y --allow-unauthenticated build-essential cmake pkg-config gfortran libavutil-dev ffmpeg
@@ -49,9 +52,13 @@ cd $TMPFOLDER
export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH
-wget https://github.com/abhiTronix/OpenCV-Travis-Builds/releases/download/opencv-$OPENCV_VERSION/OpenCV-$OPENCV_VERSION-$PYTHONVERSION.deb
+curl -s https://api.github.com/repos/abhiTronix/OpenCV-Travis-Builds/releases/latest \
+| grep "OpenCV-$OPENCV_VERSION-$PYTHONVERSIONMIN.*.deb" \
+| cut -d : -f 2,3 \
+| tr -d \" \
+| wget -qi -
-sudo dpkg -i OpenCV-$OPENCV_VERSION-$(python -c 'import platform; print(platform.python_version())').deb
+sudo dpkg -i OpenCV-$OPENCV_VERSION-$PYTHONVERSIONMIN.*.deb
sudo ln -s /usr/local/lib/python$PYTHONSUFFIX/site-packages/*.so $HOME/virtualenv/python$PYTHONVERSION/lib/python$PYTHONSUFFIX/site-packages
diff --git a/scripts/bash/prepare_dataset.sh b/scripts/bash/prepare_dataset.sh
index d530dcf36..b1cc3e4a1 100644
--- a/scripts/bash/prepare_dataset.sh
+++ b/scripts/bash/prepare_dataset.sh
@@ -1,16 +1,18 @@
#!/bin/sh
-#Copyright (c) 2019 Abhishek Thakur
+# Copyright (c) 2019 Abhishek Thakur(@abhiTronix)
-#Permission is hereby granted, free of charge, to any person obtaining a copy
-#of this software and associated documentation files (the "Software"), to deal
-#in the Software without restriction, including without limitation the rights
-#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-#copies of the Software, and to permit persons to whom the Software is
-#furnished to do so, subject to the following conditions:
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
-#The above copyright notice and this permission notice shall be included in all
-#copies or substantial portions of the Software.
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
#determining system specific temp directory
TMPFOLDER=$(python -c 'import tempfile; print(tempfile.gettempdir())')
diff --git a/setup.py b/setup.py
index b3724c6ea..7e3a03971 100644
--- a/setup.py
+++ b/setup.py
@@ -1,25 +1,20 @@
"""
-============================================
-vidgear library code is placed under the MIT license
-Copyright (c) 2019 Abhishek Thakur
+===============================================
+vidgear library source-code is deployed under the Apache 2.0 License:
+
+Copyright (c) 2019 Abhishek Thakur(@abhiTronix)
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
+ http://www.apache.org/licenses/LICENSE-2.0
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
===============================================
"""
@@ -28,6 +23,8 @@
from pkg_resources import parse_version
from setuptools import setup
+
+
def test_opencv():
"""
This function is workaround to
@@ -37,7 +34,6 @@ def test_opencv():
try:
# import OpenCV Binaries
import cv2
-
# check whether OpenCV Binaries are 3.x+
if parse_version(cv2.__version__) < parse_version('3'):
raise ImportError('Incompatible (< 3.0) OpenCV version-{} Installation found on this machine!'.format(parse_version(cv2.__version__)))
@@ -45,15 +41,17 @@ def test_opencv():
return True
return False
+
+
with open("README.md", "r") as fh:
long_description = fh.read()
setup(
name='vidgear',
packages=['vidgear','vidgear.gears'],
- version='0.1.6-dev',
+ version='0.1.6',
description='Most Powerful multi-threaded Video Processing Python framework powerpacked with unique trailblazing features.',
- license='MIT License',
+ license='Apache License 2.0',
author='abhiTronix',
install_requires = ["pafy", "mss", "youtube-dl", "requests","pyzmq"]
+ (["opencv-contrib-python"] if test_opencv() else [])
@@ -66,8 +64,13 @@ def test_opencv():
keywords=['opencv', 'multithreading', 'FFmpeg', 'picamera', 'mss', 'pyzmq', 'pafy', 'Video Processing', 'Video Stablization', 'Computer Vision'],
classifiers=[
'Development Status :: 5 - Production/Stable',
+ 'Operating System :: POSIX',
+ 'Operating System :: MacOS :: MacOS X',
+ 'Operating System :: Microsoft :: Windows',
+ 'Topic :: Multimedia :: Video',
+ 'Topic :: Scientific/Engineering',
'Intended Audience :: Developers',
- 'License :: OSI Approved :: MIT License',
+ 'License :: OSI Approved :: Apache Software License',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
@@ -76,8 +79,8 @@ def test_opencv():
'Programming Language :: Python :: 3.8'],
python_requires='>=3',
scripts=[],
- project_urls={ # Optional
+ project_urls={
'Bug Reports': 'https://github.com/abhiTronix/vidgear/issues',
'Funding': 'https://www.buymeacoffee.com/2twOXFvlA',
'Source': 'https://github.com/abhiTronix/vidgear',},
-)
+)
\ No newline at end of file
diff --git a/vidgear/__init__.py b/vidgear/__init__.py
index 65e6a4896..f5f5f95b4 100644
--- a/vidgear/__init__.py
+++ b/vidgear/__init__.py
@@ -1,3 +1,3 @@
from .version import __version__
-__author__ = "Abhishek Thakur "
+__author__ = "Abhishek Thakur (@abhiTronix) "
\ No newline at end of file
diff --git a/vidgear/gears/__init__.py b/vidgear/gears/__init__.py
index 2a7753686..615aa3b1e 100644
--- a/vidgear/gears/__init__.py
+++ b/vidgear/gears/__init__.py
@@ -8,4 +8,4 @@
__all__ = ["PiGear", "CamGear", "VideoGear", "ScreenGear", "WriteGear", "NetGear"]
-__author__ = "Abhishek Thakur "
\ No newline at end of file
+__author__ = "Abhishek Thakur (@abhiTronix) "
\ No newline at end of file
diff --git a/vidgear/gears/camgear.py b/vidgear/gears/camgear.py
index 56d78f17b..131b62a9f 100644
--- a/vidgear/gears/camgear.py
+++ b/vidgear/gears/camgear.py
@@ -1,25 +1,20 @@
"""
-============================================
-vidgear library code is placed under the MIT license
-Copyright (c) 2019 Abhishek Thakur
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
+===============================================
+vidgear library source-code is deployed under the Apache 2.0 License:
+
+Copyright (c) 2019 Abhishek Thakur(@abhiTronix)
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
===============================================
"""
@@ -29,6 +24,7 @@
from .helper import capPropId
from .helper import check_CV_version
import re, time
+import logging as log
@@ -51,10 +47,10 @@
import cv2
# check whether OpenCV Binaries are 3.x+
if parse_version(cv2.__version__) < parse_version('3'):
- raise ImportError('[ERROR]: OpenCV library version >= 3.0 is only supported by this library')
+ raise ImportError('[CamGear:ERROR] :: OpenCV library version >= 3.0 is only supported by this library')
except ImportError as error:
- raise ImportError('[ERROR]: Failed to detect OpenCV executables, install it with `pip install opencv-python` command.')
+ raise ImportError('[CamGear:ERROR] :: Failed to detect OpenCV executables, install it with `pip install opencv-python` command.')
@@ -118,6 +114,11 @@ def __init__(self, source = 0, y_tube = False, backend = 0, colorspace = None, l
#intialize threaded queue mode
self.threaded_queue_mode = True
+ # enable logging if specified
+ self.logging = False
+ self.logger = log.getLogger('CamGear')
+ if logging: self.logging = logging
+
# check if Youtube Mode is ON (True)
if y_tube:
try:
@@ -129,11 +130,12 @@ def __init__(self, source = 0, y_tube = False, backend = 0, colorspace = None, l
source_object = pafy.new(url)
_source = source_object.getbestvideo("any", ftypestrict=False)
if _source is None: _source = source_object.getbest("any", ftypestrict=False)
- if logging: print('[LOG]: YouTube source ID: `{}`, Title: `{}` & Video_Extension: `{}`'.format(url, source_object.title, _source.extension))
+ if self.logging: self.logger.debug('YouTube source ID: `{}`, Title: `{}` & Video_Extension: `{}`'.format(url, source_object.title, _source.extension))
source = _source.url
+ else: raise RuntimeError('URL cannot be processed!')
except Exception as e:
- if logging: print(e)
- raise ValueError('[ERROR]: YouTube Mode is enabled and the input YouTube Url is invalid!')
+ if self.logging: self.logger.exception(str(e))
+ raise ValueError('[CamGear:ERROR] :: YouTube Mode is enabled and the input YouTube URL is invalid!')
# youtube mode variable initialization
self.youtube_mode = y_tube
@@ -154,12 +156,12 @@ def __init__(self, source = 0, y_tube = False, backend = 0, colorspace = None, l
#define deque and assign it to global var
self.queue = deque(maxlen=96) #max len 96 to check overflow
#log it
- if logging: print('[LOG]: Enabling Threaded Queue Mode for the current video source!')
+ if self.logging: self.logger.debug('Enabling Threaded Queue Mode for the current video source!')
else:
#otherwise disable it
self.threaded_queue_mode = False
#log it
- if logging: print('[LOG]: Threaded Queue Mode is disabled for the current video source!')
+ if self.logging: self.logger.debug('Threaded Queue Mode is disabled for the current video source!')
# stream variable initialization
self.stream = None
@@ -190,12 +192,11 @@ def __init__(self, source = 0, y_tube = False, backend = 0, colorspace = None, l
# separately handle colorspace value to int conversion
if not(colorspace is None):
self.color_space = capPropId(colorspace.strip())
- if logging: print('[LOG]: Enabling `{}` colorspace for this video stream!'.format(colorspace.strip()))
+ if self.logging: self.logger.debug('Enabling `{}` colorspace for this video stream!'.format(colorspace.strip()))
except Exception as e:
# Catch if any error occurred
- if logging:
- print(e)
+ if self.logging: self.logger.exception(str(e))
#initialize and assign framerate variable
self.framerate = 0.0
@@ -203,7 +204,7 @@ def __init__(self, source = 0, y_tube = False, backend = 0, colorspace = None, l
_fps = self.stream.get(cv2.CAP_PROP_FPS)
if _fps>1: self.framerate = _fps
except Exception as e:
- if logging: print(e)
+ if self.logging: self.logger.exception(str(e))
self.framerate = 0.0
# applying time delay to warm-up webcam only if specified
@@ -221,10 +222,7 @@ def __init__(self, source = 0, y_tube = False, backend = 0, colorspace = None, l
#intitialize and append to queue
self.queue.append(self.frame)
else:
- raise RuntimeError('[ERROR]: Source is invalid, CamGear failed to intitialize stream on this source!')
-
- # enable logging if specified
- self.logging = logging
+ raise RuntimeError('[CamGear:ERROR] :: Source is invalid, CamGear failed to intitialize stream on this source!')
# thread initialization
self.thread=None
@@ -238,7 +236,7 @@ def start(self):
"""
start the thread to read frames from the video stream
"""
- self.thread = Thread(target=self.update, args=())
+ self.thread = Thread(target=self.update, name='CamGear', args=())
self.thread.daemon = True
self.thread.start()
return self
@@ -285,13 +283,13 @@ def update(self):
color_frame = cv2.cvtColor(frame, self.color_space)
else:
self.color_space = None
- if self.logging: print('[LOG]: Colorspace value {} is not a valid Colorspace!'.format(self.color_space))
+ if self.logging: self.logger.debug('Colorspace value {} is not a valid Colorspace!'.format(self.color_space))
except Exception as e:
# Catch if any error occurred
self.color_space = None
if self.logging:
- print(e)
- print('[LOG]: Input Colorspace is not a valid Colorspace!')
+ self.logger.exception(str(e))
+ self.logger.debug('Input Colorspace is not a valid Colorspace!')
if not(color_frame is None):
self.frame = color_frame
@@ -325,7 +323,7 @@ def stop(self):
"""
Terminates the Read process
"""
- if self.logging: print('[LOG]: Terminating processes')
+ if self.logging: self.logger.debug('Terminating processes.')
#terminate Threaded queue mode seperately
if self.threaded_queue_mode and not(self.queue is None):
if len(self.queue)>0: self.queue.clear()
diff --git a/vidgear/gears/helper.py b/vidgear/gears/helper.py
index e3b8202f2..4c4178b17 100644
--- a/vidgear/gears/helper.py
+++ b/vidgear/gears/helper.py
@@ -1,25 +1,20 @@
"""
-============================================
-vidgear library code is placed under the MIT license
-Copyright (c) 2019 Abhishek Thakur
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
+===============================================
+vidgear library source-code is deployed under the Apache 2.0 License:
+
+Copyright (c) 2019 Abhishek Thakur(@abhiTronix)
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
===============================================
"""
@@ -30,15 +25,11 @@
import cv2
import numpy as np
from pkg_resources import parse_version
+import logging as log
-
-def check_python_version():
- """
- returns current python version's - first bit
- """
- return sys.version_info[0]
-
+log.basicConfig(format='%(name)s :: %(levelname)s :: %(message)s', level=log.DEBUG)
+logger = log.getLogger('Helper')
def check_CV_version():
@@ -60,7 +51,7 @@ def capPropId(property):
try:
integer_value = getattr(cv2, property)
except Exception:
- print('[ALERT]: {} is not a valid OpenCV property!'.format(property))
+ logger.critical('{} is not a valid OpenCV property!'.format(property))
return None
return integer_value
@@ -97,7 +88,7 @@ def get_valid_ffmpeg_path(custom_ffmpeg = '', is_windows = False, ffmpeg_downloa
ffmpeg_download_path = tempfile.gettempdir()
if logging:
- print('[LOG]: FFmpeg Windows Download Path: {}'.format(ffmpeg_download_path))
+ logger.debug('FFmpeg Windows Download Path: {}'.format(ffmpeg_download_path))
#download Binaries
_path = download_ffmpeg_binaries(path = ffmpeg_download_path, os_windows = is_windows)
@@ -107,8 +98,8 @@ def get_valid_ffmpeg_path(custom_ffmpeg = '', is_windows = False, ffmpeg_downloa
except Exception as e:
#log if any error occurred
if logging:
- print(e)
- print('[LOG]: Error downloading FFmpeg binaries, Check your network and Try again!')
+ self.logger.exception(str(e))
+ logger.debug('Error in downloading FFmpeg binaries, Check your network and Try again!')
return False
if os.path.isfile(final_path):
@@ -119,7 +110,7 @@ def get_valid_ffmpeg_path(custom_ffmpeg = '', is_windows = False, ffmpeg_downloa
final_path = os.path.join(final_path, 'ffmpeg.exe')
else:
#else return False
- if logging: print('[LOG]: No valid FFmpeg executables found at Custom FFmpeg path!')
+ if logging: logger.debug('No valid FFmpeg executables found at Custom FFmpeg path!')
return False
else:
#otherwise perform test for Unix
@@ -134,14 +125,14 @@ def get_valid_ffmpeg_path(custom_ffmpeg = '', is_windows = False, ffmpeg_downloa
else:
#else return False
if logging:
- print('[LOG]: No valid FFmpeg executables found at Custom FFmpeg path!')
+ logger.debug('No valid FFmpeg executables found at Custom FFmpeg path!')
return False
else:
#otherwise assign ffmpeg binaries from system
final_path += "ffmpeg"
if logging:
- print('[LOG]: Final FFmpeg Path: {}'.format(final_path))
+ logger.debug('Final FFmpeg Path: {}'.format(final_path))
# Final Auto-Validation for FFmeg Binaries. returns final path if test is passed
if validate_ffmpeg(final_path, logging = logging):
@@ -177,7 +168,7 @@ def download_ffmpeg_binaries(path, os_windows = False):
os.remove(file_name)
#download and write file to the given path
with open(file_name, "wb") as f:
- print("No Custom FFmpeg path provided, Auto-Downloading binaries for Windows. Please wait...")
+ logger.debug("No Custom FFmpeg path provided, Auto-Downloading binaries for Windows. Please wait...")
response = requests.get(file_url, stream=True)
total_length = response.headers.get('content-length')
if total_length is None: # no content length header
@@ -191,12 +182,12 @@ def download_ffmpeg_binaries(path, os_windows = False):
done = int(50 * dl / total_length)
sys.stdout.write("\r[{}{}]{}{}".format('=' * done, ' ' * (50-done), done * 2, '%') )
sys.stdout.flush()
- print("\nExtracting executables, Please Wait...")
+ logger.debug("\nExtracting executables, Please Wait...")
with zipfile.ZipFile(file_name, "r") as zip_ref:
zip_ref.extractall(base_path)
#perform cleaning
os.remove(file_name)
- print("FFmpeg binaries for Windows Configured Successfully!")
+ logger.debug("FFmpeg binaries for Windows Configured Successfully!")
final_path += file_path
#return final path
return final_path
@@ -214,13 +205,13 @@ def validate_ffmpeg(path, logging = False):
version = firstline.split(b' ')[2].strip()
if logging:
#log if test are passed
- print('[LOG]: FFmpeg validity Test Passed!')
- print('[LOG]: Found valid FFmpeg Version: `{}` installed on this system'.format(version))
+ logger.debug('FFmpeg validity Test Passed!')
+ logger.debug('Found valid FFmpeg Version: `{}` installed on this system'.format(version))
except Exception as e:
#log if test are failed
if logging:
- print(e)
- print('[LOG]: FFmpeg validity Test Failed!')
+ self.logger.exception(str(e))
+ logger.debug('FFmpeg validity Test Failed!')
return False
return True
@@ -345,7 +336,7 @@ def generate_auth_certificates(path, overwrite = False):
status_private_keys = validate_auth_keys(secret_keys_dir, '.key_secret')
# raise error is validation test fails
- if not(status_private_keys) or not(status_public_keys): raise RuntimeError('[Error]: Unable to generate valid ZMQ authentication certificates at `{}`!'.format(keys_dir))
+ if not(status_private_keys) or not(status_public_keys): raise RuntimeError('[Helper:ERROR] :: Unable to generate valid ZMQ authentication certificates at `{}`!'.format(keys_dir))
# finally return valid key paths
return (keys_dir, secret_keys_dir, public_keys_dir)
diff --git a/vidgear/gears/netgear.py b/vidgear/gears/netgear.py
index 45ffbe742..b72f415da 100644
--- a/vidgear/gears/netgear.py
+++ b/vidgear/gears/netgear.py
@@ -1,25 +1,20 @@
"""
-============================================
-vidgear library code is placed under the MIT license
-Copyright (c) 2019 Abhishek Thakur
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
+===============================================
+vidgear library source-code is deployed under the Apache 2.0 License:
+
+Copyright (c) 2019 Abhishek Thakur(@abhiTronix)
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
===============================================
"""
@@ -32,6 +27,7 @@
import time
import os
import random
+import logging as log
try:
@@ -39,9 +35,9 @@
import cv2
# check whether OpenCV Binaries are 3.x+
if parse_version(cv2.__version__) < parse_version('3'):
- raise ImportError('[ERROR]: OpenCV library version >= 3.0 is only supported by this library')
+ raise ImportError('[NetGear:ERROR] :: OpenCV library version >= 3.0 is only supported by this library')
except ImportError as error:
- raise ImportError('[ERROR]: Failed to detect OpenCV executables, install it with `pip3 install opencv-python` command.')
+ raise ImportError('[NetGear:ERROR] :: Failed to detect OpenCV executables, install it with `pip3 install opencv-python` command.')
@@ -115,7 +111,7 @@ class NetGear:
This attribute provides the flexibility to manipulate ZeroMQ internal parameters
directly. Checkout vidgear docs for usage details.
- :param (boolean) logging: set this flag to enable/disable error logging essential for debugging. Its default value is False.
+ :param (boolean) self.logging: set this flag to enable/disable error logging essential for debugging. Its default value is False.
"""
@@ -131,7 +127,12 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
self.ZMQError = ZMQError
except ImportError as error:
#raise error
- raise ImportError('[ERROR]: pyzmq python library not installed. Kindly install it with `pip install pyzmq` command.')
+ raise ImportError('[NetGear:ERROR] :: pyzmq python library not installed. Kindly install it with `pip install pyzmq` command.')
+
+ # enable logging if specified
+ self.logging = False
+ self.logger = log.getLogger('NetGear')
+ if logging: self.logging = logging
#define valid messaging patterns => `0`: zmq.PAIR, `1`:(zmq.REQ,zmq.REP), and `1`:(zmq.SUB,zmq.PUB)
valid_messaging_patterns = {0:(zmq.PAIR,zmq.PAIR), 1:(zmq.REQ,zmq.REP), 2:(zmq.PUB,zmq.SUB)}
@@ -148,14 +149,14 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
self.pattern = 0
msg_pattern = valid_messaging_patterns[self.pattern]
#log it
- if logging: print('[LOG]: Wrong pattern value, Defaulting to `zmq.PAIR`! Kindly refer Docs for more Information.')
+ if self.logging: self.logger.debug('Wrong pattern value, Defaulting to `zmq.PAIR`! Kindly refer Docs for more Information.')
#check whether user-defined messaging protocol is valid
if not(protocol in ['tcp', 'udp', 'pgm', 'epgm', 'inproc', 'ipc']):
# else default to `tcp` protocol
protocol = 'tcp'
#log it
- if logging: print('[LOG]: protocol is not valid or provided. Defaulting to `tcp` protocol!')
+ if self.logging: self.logger.debug('protocol is not valid or provided. Defaulting to `tcp` protocol!')
#generate random device id
self.id = ''.join(random.choice('0123456789ABCDEF') for i in range(5))
@@ -198,8 +199,8 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
self.multiserver_mode = value
else:
self.multiserver_mode = False
- print('[ALERT]: Multi-Server is disabled!')
- raise ValueError('[ERROR]: `{}` pattern is not valid when Multi-Server Mode is enabled. Kindly refer Docs for more Information.'.format(pattern))
+ self.logger.critical('Multi-Server is disabled!')
+ raise ValueError('[NetGear:ERROR] :: `{}` pattern is not valid when Multi-Server Mode is enabled. Kindly refer Docs for more Information.'.format(pattern))
elif key == 'filter' and isinstance(value, str):
#custom filter in multi-server mode
recv_filter = value
@@ -207,18 +208,18 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
elif key == 'secure_mode' and isinstance(value,int) and (value in valid_security_mech):
#secure mode
try:
- assert zmq.zmq_version_info() >= (4,0), "[ERROR]: ZMQ Security feature is not supported in libzmq version < 4.0."
+ assert zmq.zmq_version_info() >= (4,0), "[NetGear:ERROR] :: ZMQ Security feature is not supported in libzmq version < 4.0."
self.secure_mode = value
except Exception as e:
- print(e)
+ self.logger.exception(str(e))
elif key == 'custom_cert_location' and isinstance(value,str):
# custom auth certificates path
try:
- assert os.access(value, os.W_OK), "[ERROR]: Permission Denied!, cannot write ZMQ authentication certificates to '{}' directory!".format(value)
- assert not(os.path.isfile(value)), "[ERROR]: `custom_cert_location` value must be the path to a directory and not to a file!"
+ assert os.access(value, os.W_OK), "[NetGear:ERROR] :: Permission Denied!, cannot write ZMQ authentication certificates to '{}' directory!".format(value)
+ assert not(os.path.isfile(value)), "[NetGear:ERROR] :: `custom_cert_location` value must be the path to a directory and not to a file!"
custom_cert_location = os.path.abspath(value)
except Exception as e:
- print(e)
+ self.logger.exception(str(e))
elif key == 'overwrite_cert' and isinstance(value,bool):
# enable/disable auth certificate overwriting
overwrite_cert = value
@@ -231,12 +232,12 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
# specify encoding/decoding params
if receive_mode and isinstance(value, int):
self.compression_params = value
- if logging: print("[LOG]: Decoding flag: {}.".format(value))
+ if self.logging: self.logger.debug("Decoding flag: {}.".format(value))
elif not(receive_mode) and isinstance(value, (list,tuple)):
- if logging: print("[LOG]: Encoding parameters: {}.".format(value))
+ if self.logging: self.logger.debug("Encoding parameters: {}.".format(value))
self.compression_params = list(value)
else:
- if logging: print("[WARNING]: Invalid compression parameters: {} skipped!".format(value))
+ if self.logging: self.logger.warning("Invalid compression parameters: {} skipped!".format(value))
self.compression_params = cv2.IMREAD_COLOR if receive_mode else [] # skip to defaults
# enable bi-directional data transmission if specified
@@ -246,18 +247,18 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
self.bi_mode = True
else:
self.bi_mode = False
- print('[ALERT]: Bi-Directional data transmission is disabled!')
- raise ValueError('[ERROR]: `{}` pattern is not valid when Bi-Directional Mode is enabled. Kindly refer Docs for more Information.'.format(pattern))
+ self.logger.critical('Bi-Directional data transmission is disabled!')
+ raise ValueError('[NetGear:ERROR] :: `{}` pattern is not valid when Bi-Directional Mode is enabled. Kindly refer Docs for more Information.'.format(pattern))
# enable force socket closing if specified
elif key == 'force_terminate' and isinstance(value, bool):
# check if pattern is valid
if address is None and not(receive_mode):
self.force_close = False
- print('[ALERT]: Force termination is disabled for local servers!')
+ self.logger.critical('Force termination is disabled for local servers!')
else:
self.force_close = True
- if logging: print("[LOG]: Force termination is enabled for this connection!")
+ if self.logging: self.logger.debug("Force termination is enabled for this connection!")
# various ZMQ flags
elif key == 'flag' and isinstance(value, int):
@@ -278,10 +279,10 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
# log if overwriting is enabled
if overwrite_cert:
if not receive_mode:
- if logging: print('[WARNING]: Overwriting ZMQ Authentication certificates over previous ones!')
+ if self.logging: self.logger.warning('Overwriting ZMQ Authentication certificates over previous ones!')
else:
overwrite_cert = False
- if logging: print('[ALERT]: Overwriting ZMQ Authentication certificates is disabled for Client-end!')
+ if self.logging: self.logger.critical('Overwriting ZMQ Authentication certificates is disabled for Client-end!')
#generate and validate certificates path
try:
@@ -290,38 +291,36 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
if os.path.isdir(custom_cert_location): #custom certificate location must be a directory
(auth_cert_dir, self.auth_secretkeys_dir, self.auth_publickeys_dir) = generate_auth_certificates(custom_cert_location, overwrite = overwrite_cert)
else:
- raise ValueError("[ERROR]: Invalid `custom_cert_location` value!")
+ raise ValueError("[NetGear:ERROR] :: Invalid `custom_cert_location` value!")
else:
# otherwise auto-generate suitable path
from os.path import expanduser
(auth_cert_dir, self.auth_secretkeys_dir, self.auth_publickeys_dir) = generate_auth_certificates(os.path.join(expanduser("~"),".vidgear"), overwrite = overwrite_cert)
#log it
- if logging: print('[LOG]: `{}` is the default location for storing ZMQ authentication certificates/keys.'.format(auth_cert_dir))
+ if self.logging: self.logger.debug('`{}` is the default location for storing ZMQ authentication certificates/keys.'.format(auth_cert_dir))
except Exception as e:
# catch if any error occurred
- print(e)
+ self.logger.exception(str(e))
# also disable secure mode
self.secure_mode = 0
- print('[WARNING]: ZMQ Security Mechanism is disabled for this connection!')
+ self.logger.warning('ZMQ Security Mechanism is disabled for this connection!')
else:
#log if disabled
- if logging: print('[LOG]: ZMQ Security Mechanism is disabled for this connection!')
+ if self.logging: self.logger.debug('ZMQ Security Mechanism is disabled for this connection!')
#handle bi_mode
if self.bi_mode:
#disable bi_mode if multi-server is enabled
if self.multiserver_mode:
self.bi_mode = False
- print('[ALERT]: Bi-Directional Data Transmission is disabled when Multi-Server Mode is Enabled due to incompatibility!')
+ self.logger.critical('Bi-Directional Data Transmission is disabled when Multi-Server Mode is Enabled due to incompatibility!')
else:
#enable force termination by default
self.force_close = True
- if logging: print("[LOG]: Force termination is enabled for this connection by default!")
- if logging: print('[LOG]: Bi-Directional Data Transmission is enabled for this connection!')
-
- # enable logging if specified
- self.logging = logging
+ if self.logging:
+ self.logger.debug("Force termination is enabled for this connection by default!")
+ self.logger.debug('Bi-Directional Data Transmission is enabled for this connection!')
# initialize termination flag
self.terminate = False
@@ -346,10 +345,10 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
# check if unique server port address list/tuple is assigned or not in multiserver_mode
if port is None or not isinstance(port, (tuple, list)):
# raise error if not
- raise ValueError('[ERROR]: Incorrect port value! Kindly provide a list/tuple of ports while Multi-Server mode is enabled. For more information refer VidGear docs.')
+ raise ValueError('[NetGear:ERROR] :: Incorrect port value! Kindly provide a list/tuple of ports while Multi-Server mode is enabled. For more information refer VidGear docs.')
else:
#otherwise log it
- print('[LOG]: Enabling Multi-Server Mode at PORTS: {}!'.format(port))
+ self.logger.debug('Enabling Multi-Server Mode at PORTS: {}!'.format(port))
#create port address buffer for keeping track of incoming server's port
self.port_buffer = []
else:
@@ -421,30 +420,31 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
self.msg_socket.setsockopt(zmq.LINGER, 0)
except Exception as e:
+ self.logger.exception(str(e))
# otherwise raise value error if errored
- if self.secure_mode: print('Failed to activate ZMQ Security Mechanism: `{}` for this address!'.format(valid_security_mech[self.secure_mode]))
+ if self.secure_mode: self.logger.warning('Failed to activate ZMQ Security Mechanism: `{}` for this address!'.format(valid_security_mech[self.secure_mode]))
if self.multiserver_mode:
- raise ValueError('[ERROR]: Multi-Server Mode, failed to connect to ports: {} with pattern: {}! Kindly recheck all parameters.'.format( str(port), pattern))
+ raise ValueError('[NetGear:ERROR] :: Multi-Server Mode, failed to connect to ports: {} with pattern: {}! Kindly recheck all parameters.'.format( str(port), pattern))
else:
- raise ValueError('[ERROR]: Failed to bind address: {} and pattern: {}! Kindly recheck all parameters.'.format((protocol+'://' + str(address) + ':' + str(port)), pattern))
+ raise ValueError('[NetGear:ERROR] :: Failed to bind address: {} and pattern: {}! Kindly recheck all parameters.'.format((protocol+'://' + str(address) + ':' + str(port)), pattern))
#log and enable threaded queue mode
- if logging: print('[LOG]: Threaded Queue Mode is enabled by default for NetGear.')
+ if self.logging: self.logger.debug('Threaded Queue Mode is enabled by default for NetGear.')
#define deque and assign it to global var
self.queue = deque(maxlen=96) #max len 96 to check overflow
# initialize and start threading instance
- self.thread = Thread(target=self.update, args=())
+ self.thread = Thread(target=self.update, name='NetGear', args=())
self.thread.daemon = True
self.thread.start()
- if logging:
+ if self.logging:
#finally log progress
- print('[LOG]: Successfully Binded to address: {} with pattern: {}.'.format((protocol+'://' + str(address) + ':' + str(port)), pattern))
- if self.secure_mode: print('[LOG]: Enabled ZMQ Security Mechanism: `{}` for this address, Successfully!'.format(valid_security_mech[self.secure_mode]))
- print('[LOG]: Multi-threaded Receive Mode is enabled Successfully!')
- print('[LOG]: Device Unique ID is {}.'.format(self.id))
- print('[LOG]: Receive Mode is activated successfully!')
+ self.logger.debug('Successfully Binded to address: {} with pattern: {}.'.format((protocol+'://' + str(address) + ':' + str(port)), pattern))
+ if self.secure_mode: self.logger.debug('Enabled ZMQ Security Mechanism: `{}` for this address, Successfully!'.format(valid_security_mech[self.secure_mode]))
+ self.logger.debug('Multi-threaded Receive Mode is enabled Successfully!')
+ self.logger.debug('Device Unique ID is {}.'.format(self.id))
+ self.logger.debug('Receive Mode is activated successfully!')
else:
#otherwise default to `Send Mode`
if address is None: address = 'localhost'#define address
@@ -454,10 +454,10 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
# check if unique server port address is assigned or not in multiserver_mode
if port is None:
#raise error is not
- raise ValueError('[ERROR]: Kindly provide a unique & valid port value at Server-end. For more information refer VidGear docs.')
+ raise ValueError('[NetGear:ERROR] :: Kindly provide a unique & valid port value at Server-end. For more information refer VidGear docs.')
else:
#otherwise log it
- print('[LOG]: Enabling Multi-Server Mode at PORT: {} on this device!'.format(port))
+ self.logger.debug('Enabling Multi-Server Mode at PORT: {} on this device!'.format(port))
#assign value to global variable
self.port = port
else:
@@ -510,17 +510,18 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
self.msg_socket.setsockopt(zmq.LINGER, 0)
except Exception as e:
+ self.logger.exception(str(e))
#log if errored
- if self.secure_mode: print('Failed to activate ZMQ Security Mechanism: `{}` for this address!'.format(valid_security_mech[self.secure_mode]))
+ if self.secure_mode: self.logger.warning('Failed to activate ZMQ Security Mechanism: `{}` for this address!'.format(valid_security_mech[self.secure_mode]))
# raise value error
- raise ValueError('[ERROR]: Failed to connect address: {} and pattern: {}! Kindly recheck all parameters.'.format((protocol+'://' + str(address) + ':' + str(port)), pattern))
+ raise ValueError('[NetGear:ERROR] :: Failed to connect address: {} and pattern: {}! Kindly recheck all parameters.'.format((protocol+'://' + str(address) + ':' + str(port)), pattern))
- if logging:
+ if self.logging:
#finally log progress
- print('[LOG]: Successfully connected to address: {} with pattern: {}.'.format((protocol+'://' + str(address) + ':' + str(port)), pattern))
- if self.secure_mode: print('[LOG]: Enabled ZMQ Security Mechanism: `{}` for this address, Successfully!'.format(valid_security_mech[self.secure_mode]))
- print('[LOG]: This device Unique ID is {}.'.format(self.id))
- print('[LOG]: Send Mode is successfully activated and ready to send data!')
+ self.logger.debug('Successfully connected to address: {} with pattern: {}.'.format((protocol+'://' + str(address) + ':' + str(port)), pattern))
+ if self.secure_mode: self.logger.debug('Enabled ZMQ Security Mechanism: `{}` for this address, Successfully!'.format(valid_security_mech[self.secure_mode]))
+ self.logger.debug('This device Unique ID is {}.'.format(self.id))
+ self.logger.debug('Send Mode is successfully activated and ready to send data!')
@@ -558,26 +559,26 @@ def update(self):
# check and remove from which ports signal is received
if msg_json['port'] in self.port_buffer:
# if pattern is 1, then send back server the info about termination
- if self.pattern == 1: self.msg_socket.send_string('[INFO]: Termination signal received at client!')
+ if self.pattern == 1: self.msg_socket.send_string('Termination signal received at client!')
self.port_buffer.remove(msg_json['port'])
- if self.logging: print('[ALERT]: Termination signal received from server at port: {}!'.format(msg_json['port']))
+ if self.logging: self.logger.warning('Termination signal received from server at port: {}!'.format(msg_json['port']))
#if termination signal received from all servers then exit client.
if not self.port_buffer:
- print('[WARNING]: Termination signal received from all Servers!!!')
+ self.logger.warning('Termination signal received from all Servers!!!')
self.terminate = True #termination
continue
else:
# if pattern is 1, then send back server the info about termination
- if self.pattern == 1: self.msg_socket.send_string('[INFO]: Termination signal received at client!')
+ if self.pattern == 1: self.msg_socket.send_string('Termination signal received at client!')
#termination
self.terminate = True
#notify client
- if self.logging: print('[ALERT]: Termination signal received from server!')
+ if self.logging: self.logger.warning('Termination signal received from server!')
continue
#check if pattern is same at both server's and client's end.
if int(msg_json['pattern']) != self.pattern:
- raise ValueError("[ERROR]: Messaging patterns on both Server-end & Client-end must a valid pairs! Kindly refer VidGear docs.")
+ raise ValueError("[NetGear:ERROR] :: Messaging patterns on both Server-end & Client-end must a valid pairs! Kindly refer VidGear docs.")
self.terminate = True
continue
@@ -592,7 +593,7 @@ def update(self):
self.msg_socket.send_json(bi_dict, self.msg_flag)
else:
# send confirmation message to server
- self.msg_socket.send_string('[LOG]: Data received on device: {} !'.format(self.id))
+ self.msg_socket.send_string('Data received on device: {} !'.format(self.id))
# recover frame from array buffer
frame_buffer = np.frombuffer(msg_data, dtype=msg_json['dtype'])
@@ -605,7 +606,7 @@ def update(self):
#check if valid frame returned
if frame is None:
#otherwise raise error and exit
- raise ValueError("[ERROR]: `{}` Frame Decoding failed with Parameter: {}".format(msg_json['compression'], self.compression_params))
+ raise ValueError("[NetGear:ERROR] :: `{}` Frame Decoding failed with Parameter: {}".format(msg_json['compression'], self.compression_params))
self.terminate = True
continue
@@ -645,7 +646,7 @@ def recv(self, return_data = None):
# check whether `receive mode` is activated
if not(self.receive_mode):
#raise value error and exit
- raise ValueError('[ERROR]: `recv()` function cannot be used while receive_mode is disabled. Kindly refer vidgear docs!')
+ raise ValueError('[NetGear:ERROR] :: `recv()` function cannot be used while receive_mode is disabled. Kindly refer vidgear docs!')
self.terminate = True
#handle bi-directional return data
@@ -673,7 +674,7 @@ def send(self, frame, message = None):
# check whether `receive_mode` is disabled
if self.receive_mode:
#raise value error and exit
- raise ValueError('[ERROR]: `send()` function cannot be used while receive_mode is enabled. Kindly refer vidgear docs!')
+ raise ValueError('[NetGear:ERROR] :: `send()` function cannot be used while receive_mode is enabled. Kindly refer vidgear docs!')
self.terminate = True
# define exit_flag and assign value
@@ -690,7 +691,7 @@ def send(self, frame, message = None):
#check if it works
if not(retval):
#otherwise raise error and exit
- raise ValueError("[ERROR]: Frame Encoding failed with format: {} and Parameters: {}".format(self.compression, self.compression_params))
+ raise ValueError("[NetGear:ERROR] :: Frame Encoding failed with format: {} and Parameters: {}".format(self.compression, self.compression_params))
self.terminate = True
@@ -729,7 +730,7 @@ def send(self, frame, message = None):
#otherwise log normally
recv_confirmation = self.msg_socket.recv()
# log confirmation
- if self.logging : print(recv_confirmation)
+ if self.logging : self.logger.debug(recv_confirmation)
@@ -740,7 +741,7 @@ def close(self):
"""
if self.logging:
#log it
- print(' \n[LOG]: Terminating various {} Processes \n'.format('Receive Mode' if self.receive_mode else 'Send Mode'))
+ self.logger.debug('Terminating various {} Processes.'.format('Receive Mode' if self.receive_mode else 'Send Mode'))
# whether `receive_mode` is enabled or not
if self.receive_mode:
# indicate that process should be terminated
diff --git a/vidgear/gears/pigear.py b/vidgear/gears/pigear.py
index 86dd07efd..d5651cb23 100644
--- a/vidgear/gears/pigear.py
+++ b/vidgear/gears/pigear.py
@@ -1,25 +1,20 @@
"""
-============================================
-vidgear library code is placed under the MIT license
-Copyright (c) 2019 Abhishek Thakur
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
+===============================================
+vidgear library source-code is deployed under the Apache 2.0 License:
+
+Copyright (c) 2019 Abhishek Thakur(@abhiTronix)
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
===============================================
"""
@@ -28,7 +23,7 @@
from pkg_resources import parse_version
import sys, time
from .helper import capPropId
-
+import logging as log
try:
@@ -37,10 +32,10 @@
# check whether OpenCV Binaries are 3.x+
if parse_version(cv2.__version__) < parse_version('3'):
- raise ImportError('[ERROR]: OpenCV library version >= 3.0 is only supported by this library')
+ raise ImportError('[PiGear:ERROR] :: OpenCV library version >= 3.0 is only supported by this library')
except ImportError as error:
- raise ImportError('[ERROR]: Failed to detect OpenCV executables, install it with `pip3 install opencv-python` command.')
+ raise ImportError('[PiGear:ERROR] :: Failed to detect OpenCV executables, install it with `pip3 install opencv-python` command.')
@@ -62,7 +57,7 @@ class PiGear:
/ These attribute provides the flexibility to manipulate input raspicam video stream directly.
/ Parameters can be passed using this **option, allows you to pass key worded variable length of arguments to PiGear Class.
- :param (boolean) logging: set this flag to enable/disable error logging essential for debugging. Its default value is False.
+ :param (boolean) self.logging: set this flag to enable/disable error logging essential for debugging. Its default value is False.
:param (integer) time_delay: sets time delay(in seconds) before start reading the frames.
/ This delay is essentially required for camera to warm-up.
@@ -79,20 +74,27 @@ def __init__(self, camera_num = 0, resolution = (640, 480), framerate = 30, colo
except Exception as error:
if isinstance(error, ImportError):
# Output expected ImportErrors.
- raise ImportError('[ERROR]: Failed to detect Picamera executables, install it with "pip3 install picamera" command.')
+ raise ImportError('[PiGear:ERROR] :: Failed to detect Picamera executables, install it with "pip3 install picamera" command.')
else:
#Handle any API errors
- raise RuntimeError('[ERROR]: Picamera API failure: {}'.format(error))
+ raise RuntimeError('[PiGear:ERROR] :: Picamera API failure: {}'.format(error))
- assert (isinstance(framerate, (int, float)) and framerate > 5.0), "[ERROR]: Input framerate value `{}` is a Invalid! Kindly read docs.".format(framerate)
- assert (isinstance(resolution, (tuple, list)) and len(resolution) == 2), "[ERROR]: Input resolution value `{}` is a Invalid! Kindly read docs.".format(resolution)
- if not(isinstance(camera_num, int) and camera_num >= 0): print("[ERROR]: `camera_num` value is invalid, Kindly read docs!")
+ # enable logging if specified
+ self.logging = False
+ self.logger = log.getLogger('PiGear')
+ if logging: self.logging = logging
+
+ assert (isinstance(framerate, (int, float)) and framerate > 5.0), "[PiGear:ERROR] :: Input framerate value `{}` is a Invalid! Kindly read docs.".format(framerate)
+ assert (isinstance(resolution, (tuple, list)) and len(resolution) == 2), "[PiGear:ERROR] :: Input resolution value `{}` is a Invalid! Kindly read docs.".format(resolution)
+ if not(isinstance(camera_num, int) and camera_num >= 0):
+ camera_num = 0
+ self.logger.warning("Input camera_num value `{}` is invalid, Defaulting to index 0!")
# initialize the picamera stream at given index
self.camera = PiCamera(camera_num = camera_num)
self.camera.resolution = tuple(resolution)
self.camera.framerate = framerate
- if logging: print("[LOG]: Activating Pi camera at index: {} with resolution: {} & framerate: {}".format(camera_num, resolution, framerate))
+ if self.logging: self.logger.debug("Activating Pi camera at index: {} with resolution: {} & framerate: {}".format(camera_num, resolution, framerate))
#initialize framerate variable
self.framerate = framerate
@@ -110,9 +112,9 @@ def __init__(self, camera_num = 0, resolution = (640, 480), framerate = 30, colo
if options and "HWFAILURE_TIMEOUT" in options:
#for altering timeout variable manually
if isinstance(options["HWFAILURE_TIMEOUT"],(int, float)):
- if not(10.0 > options["HWFAILURE_TIMEOUT"] > 1.0): raise ValueError('[ERROR]: `HWFAILURE_TIMEOUT` value can only be between 1.0 ~ 10.0')
+ if not(10.0 > options["HWFAILURE_TIMEOUT"] > 1.0): raise ValueError('[PiGear:ERROR] :: `HWFAILURE_TIMEOUT` value can only be between 1.0 ~ 10.0')
self.failure_timeout = options["HWFAILURE_TIMEOUT"] #assign special parameter
- if logging: print("[LOG]: Setting HW Failure Timeout: {} seconds".format(self.failure_timeout))
+ if self.logging: self.logger.debug("Setting HW Failure Timeout: {} seconds".format(self.failure_timeout))
del options["HWFAILURE_TIMEOUT"] #clean
try:
@@ -123,11 +125,11 @@ def __init__(self, camera_num = 0, resolution = (640, 480), framerate = 30, colo
# separately handle colorspace value to int conversion
if not(colorspace is None):
self.color_space = capPropId(colorspace.strip())
- if logging: print('[LOG]: Enabling `{}` colorspace for this video stream!'.format(colorspace.strip()))
+ if self.logging: self.logger.debug('Enabling `{}` colorspace for this video stream!'.format(colorspace.strip()))
except Exception as e:
# Catch if any error occurred
- if logging: print(e)
+ if self.logging: self.logger.exception(str(e))
# enable rgb capture array thread and capture stream
self.rawCapture = PiRGBArray(self.camera, size = resolution)
@@ -143,8 +145,8 @@ def __init__(self, camera_num = 0, resolution = (640, 480), framerate = 30, colo
#render colorspace if defined
if not(self.frame is None and self.color_space is None): self.frame = cv2.cvtColor(self.frame, self.color_space)
except Exception as e:
- print(e)
- raise RuntimeError('[ERROR]: Camera Module failed to initialize!')
+ self.logger.exception(str(e))
+ raise RuntimeError('[PiGear:ERROR] :: Camera Module failed to initialize!')
# applying time delay to warm-up picamera only if specified
if time_delay: time.sleep(time_delay)
@@ -156,9 +158,6 @@ def __init__(self, camera_num = 0, resolution = (640, 480), framerate = 30, colo
self._timer = None
self.t_elasped = 0.0 #records time taken by thread
- # enable logging if specified
- self.logging = logging
-
# catching thread exceptions
self.exceptions = None
@@ -172,12 +171,12 @@ def start(self):
start the thread to read frames from the video stream and initiate internal timer
"""
#Start frame producer thread
- self.thread = Thread(target=self.update, args=())
+ self.thread = Thread(target=self.update, name='PiGear', args=())
self.thread.daemon = True
self.thread.start()
#Start internal timer thread
- self._timer = Thread(target=self._timeit, args=())
+ self._timer = Thread(target=self._timeit, name='PiTimer', args=())
self._timer.daemon = True
self._timer.start()
@@ -197,7 +196,7 @@ def _timeit(self):
#check for frozen thread
if time.time() - self.t_elasped > self.failure_timeout:
#log failure
- if self.logging: print("[WARNING]: Camera Module Disconnected!")
+ if self.logging: self.logger.critical("Camera Module Disconnected!")
#prepare for clean exit
self.exceptions = True
self.terminate = True #self-terminate
@@ -240,14 +239,14 @@ def update(self):
color_frame = cv2.cvtColor(frame, self.color_space)
else:
self.color_space = None
- if self.logging: print('[LOG]: Colorspace value `{}` is not a valid colorspace!'.format(self.color_space))
+ if self.logging: self.logger.debug('Colorspace value `{}` is not a valid colorspace!'.format(self.color_space))
except Exception as e:
# Catch if any error occurred
self.color_space = None
if self.logging:
- print(e)
- print('[WARNING]: Input colorspace is not a valid Colorspace!')
+ self.logger.exception(str(e))
+ self.logger.warning('Input colorspace is not a valid Colorspace!')
if not(color_frame is None):
self.frame = color_frame
@@ -276,12 +275,12 @@ def read(self):
#clear frame
self.frame = None
#notify user about hardware failure
- raise SystemError('[ERROR]: Hardware failure occurred, Kindly reconnect Camera Module and restart your Pi!')
+ raise SystemError('[PiGear:ERROR] :: Hardware failure occurred, Kindly reconnect Camera Module and restart your Pi!')
else:
#clear frame
self.frame = None
# re-raise error for debugging
- error_msg = "[ERROR]: Camera Module API failure occured: {}".format(self.exceptions[1])
+ error_msg = "[PiGear:ERROR] :: Camera Module API failure occured: {}".format(self.exceptions[1])
raise RuntimeError(error_msg).with_traceback(self.exceptions[2])
# return the frame
@@ -293,7 +292,7 @@ def stop(self):
"""
Terminates the Read process
"""
- if self.logging: print("[LOG]: Terminating PiGear Process.")
+ if self.logging: self.logger.debug("Terminating PiGear Processes.")
# make sure that the threads should be terminated
self.terminate = True
@@ -316,5 +315,4 @@ def stop(self):
self.thread = None
else:
#properly handle thread exit
- self.thread.join()
-
+ self.thread.join()
\ No newline at end of file
diff --git a/vidgear/gears/screengear.py b/vidgear/gears/screengear.py
index 6c6f97fb2..d7902185f 100644
--- a/vidgear/gears/screengear.py
+++ b/vidgear/gears/screengear.py
@@ -1,25 +1,20 @@
"""
-============================================
-vidgear library code is placed under the MIT license
-Copyright (c) 2019 Abhishek Thakur
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
+===============================================
+vidgear library source-code is deployed under the Apache 2.0 License:
+
+Copyright (c) 2019 Abhishek Thakur(@abhiTronix)
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
===============================================
"""
@@ -29,7 +24,7 @@
from .helper import capPropId
import numpy as np
import time
-
+import logging as log
try:
@@ -37,10 +32,10 @@
import cv2
# check whether OpenCV Binaries are 3.x+
if parse_version(cv2.__version__) < parse_version('3'):
- raise ImportError('[ERROR]: OpenCV library version >= 3.0 is only supported by this library')
+ raise ImportError('[ScreenGear:ERROR] :: OpenCV library version >= 3.0 is only supported by this library')
except ImportError as error:
- raise ImportError('[ERROR]: Failed to detect OpenCV executables, install it with `pip install opencv-python` command.')
+ raise ImportError('[ScreenGear:ERROR] :: Failed to detect OpenCV executables, install it with `pip install opencv-python` command.')
@@ -83,7 +78,12 @@ def __init__(self, monitor = 1, colorspace = None, logging = False, **options):
from mss.exception import ScreenShotError
except ImportError as error:
# otherwise raise import error
- raise ImportError('[ERROR]: python-mss library not found, install it with `pip install mss` command.')
+ raise ImportError('[ScreenGear:ERROR] :: python-mss library not found, install it with `pip install mss` command.')
+
+ # enable logging if specified
+ self.logging = False
+ self.logger = log.getLogger('ScreenGear')
+ if logging: self.logging = logging
# create mss object
self.mss_object = mss()
@@ -92,7 +92,7 @@ def __init__(self, monitor = 1, colorspace = None, logging = False, **options):
if (monitor >= 0):
monitor_instance = self.mss_object.monitors[monitor]
else:
- raise ValueError("[ERROR]: `monitor` value cannot be negative, Read Docs!")
+ raise ValueError("[ScreenGear:ERROR] :: `monitor` value cannot be negative, Read Docs!")
# Initialize Queue
self.queue = None
@@ -102,7 +102,7 @@ def __init__(self, monitor = 1, colorspace = None, logging = False, **options):
#define deque and assign it to global var
self.queue = deque(maxlen=96) #max len 96 to check overflow
#log it
- if logging: print('[LOG]: Enabling Threaded Queue Mode by default for ScreenGear!')
+ if logging: self.logger.debug('Enabling Threaded Queue Mode by default for ScreenGear!')
#intiate screen dimension handler
screen_dims = {}
@@ -114,17 +114,17 @@ def __init__(self, monitor = 1, colorspace = None, logging = False, **options):
# separately handle colorspace value to int conversion
if not(colorspace is None):
self.color_space = capPropId(colorspace.strip())
- if logging: print('[LOG]: Enabling `{}` colorspace for this video stream!'.format(colorspace.strip()))
+ if logging: self.logger.debug('Enabling `{}` colorspace for this video stream!'.format(colorspace.strip()))
except Exception as e:
# Catch if any error occurred
- if logging: print(e)
+ if logging: self.logger.exception(str(e))
# intialize mss capture instance
self.mss_capture_instance = None
try:
# check whether user-defined dimensions are provided
if screen_dims and len(screen_dims) == 4:
- if logging: print('[LOG]: Setting capture dimensions: {}!'.format(screen_dims))
+ if logging: self.logger.debug('Setting capture dimensions: {}!'.format(screen_dims))
self.mss_capture_instance = screen_dims #create instance from dimensions
else:
self.mss_capture_instance = monitor_instance #otherwise create instance from monitor
@@ -135,11 +135,9 @@ def __init__(self, monitor = 1, colorspace = None, logging = False, **options):
self.queue.append(self.frame)
except ScreenShotError:
#otherwise catch and log errors
- raise ValueError("[ERROR]: ScreenShotError caught: Wrong dimensions passed to python-mss, Kindly Refer Docs!")
- if logging: print(self.mss_object.get_error_details())
+ if logging: self.logger.error(self.mss_object.get_error_details())
+ raise ValueError("[ScreenGear:ERROR] :: ScreenShotError caught, Wrong dimensions passed to python-mss, Kindly Refer Docs!")
- # enable logging if specified
- self.logging = logging
# thread initialization
self.thread=None
# initialize termination flag
@@ -150,7 +148,7 @@ def start(self):
"""
start the thread to read frames from the video stream
"""
- self.thread = Thread(target=self.update, args=())
+ self.thread = Thread(target=self.update, name='ScreenGear', args=())
self.thread.daemon = True
self.thread.start()
return self
@@ -196,13 +194,13 @@ def update(self):
color_frame = cv2.cvtColor(frame, self.color_space)
else:
self.color_space = None
- if self.logging: print('[LOG]: Colorspace value {} is not a valid Colorspace!'.format(self.color_space))
+ if self.logging: self.logger.debug('Colorspace value {} is not a valid Colorspace!'.format(self.color_space))
except Exception as e:
# Catch if any error occurred
self.color_space = None
if self.logging:
- print(e)
- print('[LOG]: Input Colorspace is not a valid Colorspace!')
+ self.logger.exception(str(e))
+ self.logger.debug('Input Colorspace is not a valid Colorspace!')
if not(color_frame is None):
self.frame = color_frame
else:
@@ -231,6 +229,7 @@ def stop(self):
"""
Terminates the Read process
"""
+ if self.logging: self.logger.debug("Terminating ScreenGear Processes.")
#terminate Threaded queue mode seperately
if self.threaded_queue_mode and not(self.queue is None):
self.queue.clear()
@@ -241,10 +240,4 @@ def stop(self):
# wait until stream resources are released (producer thread might be still grabbing frame)
if self.thread is not None:
self.thread.join()
- #properly handle thread exit
-
-
-
-
-
-
+ #properly handle thread exit
\ No newline at end of file
diff --git a/vidgear/gears/stabilizer.py b/vidgear/gears/stabilizer.py
index fea117c99..afc8aa61c 100644
--- a/vidgear/gears/stabilizer.py
+++ b/vidgear/gears/stabilizer.py
@@ -2,27 +2,22 @@
# published on February 20, 2014 by nghiaho12 (http://nghiaho.com/?p=2093)
"""
-============================================
-vidgear library code is placed under the MIT license
-Copyright (c) 2019 Abhishek Thakur
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
+===============================================
+vidgear library source-code is deployed under the Apache 2.0 License:
+
+Copyright (c) 2019 Abhishek Thakur(@abhiTronix)
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
===============================================
"""
@@ -31,7 +26,7 @@
from .helper import check_CV_version
import numpy as np
import cv2
-
+import logging as log
class Stabilizer:
@@ -59,6 +54,11 @@ def __init__(self, smoothing_radius = 25, border_type = 'black', border_size = 0
self.frame_queue = deque(maxlen=smoothing_radius)
self.frame_queue_indexes = deque(maxlen=smoothing_radius)
+ # enable logging if specified
+ self.logging = False
+ self.logger = log.getLogger('Stabilizer')
+ if logging: self.logging = logging
+
# define and create Adaptive histogram equalization (AHE) object for optimizations
self.clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
@@ -78,11 +78,11 @@ def __init__(self, smoothing_radius = 25, border_type = 'black', border_size = 0
self.crop_n_zoom = border_size #crops and zoom frame to original size
self.border_size = 0 #zero out border size
self.frame_size = None #handles frame size for zooming
- if logging: print('[LOG]: Setting Cropping margin {} pixels'.format(border_size))
+ if logging: self.logger.debug('Setting Cropping margin {} pixels'.format(border_size))
else:
# Add output borders to frame
self.border_size = border_size
- if logging and border_size: print('[LOG]: Setting Border size {} pixels'.format(border_size))
+ if self.logging and border_size: self.logger.debug('Setting Border size {} pixels'.format(border_size))
# define valid border modes
border_modes = {'black': cv2.BORDER_CONSTANT,'reflect': cv2.BORDER_REFLECT, 'reflect_101': cv2.BORDER_REFLECT_101, 'replicate': cv2.BORDER_REPLICATE, 'wrap': cv2.BORDER_WRAP}
@@ -91,22 +91,19 @@ def __init__(self, smoothing_radius = 25, border_type = 'black', border_size = 0
if not crop_n_zoom:
#initialize global border mode variable
self.border_mode = border_modes[border_type]
- if logging and border_type != 'black': print('[LOG]: Setting Border type: {}'.format(border_type))
+ if self.logging and border_type != 'black': self.logger.debug('Setting Border type: {}'.format(border_type))
else:
#log and reset to default
- if logging and border_type != 'black': print('[LOG]: Setting border type is disabled if cropping is enabled!')
+ if self.logging and border_type != 'black': self.logger.debug('Setting border type is disabled if cropping is enabled!')
self.border_mode = border_modes['black']
else:
#otherwise log if not
- if logging: print('[LOG]: Invalid input border type!')
+ if logging: self.logger.debug('Invalid input border type!')
self.border_mode = border_modes['black'] #reset to default mode
# define normalized box filter
self.box_filter = np.ones(smoothing_radius)/smoothing_radius
- # decide whether to log
- self.logging = logging
-
def stabilize(self, frame):
@@ -294,10 +291,4 @@ def clean(self):
#clear frame deque
self.frame_queue.clear()
#clear frame indexes deque
- self.frame_queue_indexes.clear()
-
-
-
-
-
-
\ No newline at end of file
+ self.frame_queue_indexes.clear()
\ No newline at end of file
diff --git a/vidgear/gears/videogear.py b/vidgear/gears/videogear.py
index 189576eb0..58bca1f14 100644
--- a/vidgear/gears/videogear.py
+++ b/vidgear/gears/videogear.py
@@ -1,31 +1,26 @@
"""
-============================================
-vidgear library code is placed under the MIT license
-Copyright (c) 2019 Abhishek Thakur
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
+===============================================
+vidgear library source-code is deployed under the Apache 2.0 License:
+
+Copyright (c) 2019 Abhishek Thakur(@abhiTronix)
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
===============================================
"""
# import the necessary packages
from .camgear import CamGear
-
+import logging as log
class VideoGear:
@@ -84,8 +79,14 @@ class VideoGear:
def __init__(self, enablePiCamera = False, stabilize = False, source = 0, y_tube = False, backend = 0, colorspace = None, resolution = (640, 480), framerate = 25, logging = False, time_delay = 0, **options):
+ #initialize stabilizer
self.stablization_mode = stabilize
+ # enable logging if specified
+ self.logging = False
+ self.logger = log.getLogger('VideoGear')
+ if logging: self.logging = logging
+
if self.stablization_mode:
from .stabilizer import Stabilizer
s_radius, border_size, border_type, crop_n_zoom = (25, 0, 'black', False) #defaults
@@ -107,7 +108,7 @@ def __init__(self, enablePiCamera = False, stabilize = False, source = 0, y_tube
crop_n_zoom = options["CROP_N_ZOOM"] #assigsn special parameter
del options["CROP_N_ZOOM"] #clean
self.stabilizer_obj = Stabilizer(smoothing_radius = s_radius, border_type = border_type, border_size = border_size, crop_n_zoom = crop_n_zoom, logging = logging)
- if logging: print('[LOG]: Enabling Stablization Mode for the current video source!') #log info
+ if self.logging: self.logger.debug('Enabling Stablization Mode for the current video source!') #log info
if enablePiCamera:
# only import the pigear module only if required
@@ -143,6 +144,7 @@ def read(self):
def stop(self):
+ if self.logging: self.logger.debug("Terminating VideoGear.")
# stop the thread and release any resources
self.stream.stop()
#clean queue
diff --git a/vidgear/gears/writegear.py b/vidgear/gears/writegear.py
index 3036cef92..96e6b7563 100755
--- a/vidgear/gears/writegear.py
+++ b/vidgear/gears/writegear.py
@@ -1,25 +1,20 @@
"""
-============================================
-vidgear library code is placed under the MIT license
-Copyright (c) 2019 Abhishek Thakur
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
+===============================================
+vidgear library source-code is deployed under the Apache 2.0 License:
+
+Copyright (c) 2019 Abhishek Thakur(@abhiTronix)
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
===============================================
"""
@@ -27,6 +22,7 @@
from pkg_resources import parse_version
import os, sys, time
import subprocess as sp
+import logging as log
from .helper import get_valid_ffmpeg_path
from .helper import capPropId
@@ -39,9 +35,9 @@
import cv2
# check whether OpenCV Binaries are 3.x+
if parse_version(cv2.__version__) < parse_version('3'):
- raise ImportError('[ERROR]: OpenCV library version >= 3.0 is only supported by this library')
+ raise ImportError('[WriteGear:ERROR] :: OpenCV library version >= 3.0 is only supported by this library')
except ImportError as error:
- raise ImportError('[ERROR]: Failed to detect OpenCV executables, install it with `pip install opencv-python` command.')
+ raise ImportError('[WriteGear:ERROR] :: Failed to detect OpenCV executables, install it with `pip install opencv-python` command.')
@@ -98,7 +94,11 @@ def __init__(self, output_filename = '', compression_mode = True, custom_ffmpeg
# assign parameter values to class variables
self.compression = compression_mode
self.os_windows = True if os.name == 'nt' else False #checks if machine in-use is running windows os or not
- self.logging = logging #enable logging
+
+ # enable logging if specified
+ self.logging = False
+ self.logger = log.getLogger('WriteGear')
+ if logging: self.logging = logging
# initialize various important class variables
self.output_parameters = {}
@@ -116,7 +116,7 @@ def __init__(self, output_filename = '', compression_mode = True, custom_ffmpeg
# handles output file name (if not given)
if not output_filename:
- raise ValueError('[ERROR]: Kindly provide a valid `output_filename` value, Refer VidGear Docs for more information!')
+ raise ValueError('[WriteGear:ERROR] :: Kindly provide a valid `output_filename` value, Refer VidGear Docs for more information!')
elif output_filename and os.path.isdir(output_filename): # check if directory path is given instead
output_filename = os.path.join(output_filename, 'VidGear-{}.mp4'.format(time.strftime("%Y%m%d-%H%M%S"))) # auto-assign valid name and adds it to path
else:
@@ -137,15 +137,15 @@ def __init__(self, output_filename = '', compression_mode = True, custom_ffmpeg
try:
self.output_parameters = {str(k).strip().lower(): str(v).strip() for k,v in output_params.items()}
except Exception as e:
- if self.logging: print(e)
- raise ValueError('[ERROR]: Wrong output_params parameters passed to WriteGear class!')
+ if self.logging: self.logger.exception(str(e))
+ raise ValueError('[WriteGear:ERROR] :: Wrong output_params parameters passed to WriteGear class!')
#handles FFmpeg binaries validity tests
if self.compression:
if self.logging:
- print('[LOG]: Compression Mode is enabled therefore checking for valid FFmpeg executables!')
- print(self.output_parameters)
+ self.logger.debug('Compression Mode is enabled therefore checking for valid FFmpeg executables!')
+ self.logger.debug(self.output_parameters)
# handles where to save the downloaded FFmpeg Static Binaries on Windows(if specified)
ffmpeg_download_path_ = ''
@@ -165,12 +165,12 @@ def __init__(self, output_filename = '', compression_mode = True, custom_ffmpeg
if actual_command:
self.ffmpeg += actual_command #assign it to class variable
if self.logging:
- print('[LOG]: Found valid FFmpeg executables: `{}`'.format(self.ffmpeg))
+ self.logger.debug('Found valid FFmpeg executables: `{}`'.format(self.ffmpeg))
else:
#otherwise disable Compression Mode
if self.logging and not self.os_windows:
- print('[LOG]: Kindly install working FFmpeg or provide a valid custom FFmpeg Path')
- print('[LOG]: Caution: Disabling Video Compression Mode since no valid FFmpeg executables found on this machine!')
+ self.logger.debug('Kindly install working FFmpeg or provide a valid custom FFmpeg Path')
+ self.logger.debug('Caution: Disabling Video Compression Mode since no valid FFmpeg executables found on this machine!')
self.compression = False # compression mode disabled
#validate this class has the access rights to specified directory or not
@@ -179,9 +179,9 @@ def __init__(self, output_filename = '', compression_mode = True, custom_ffmpeg
#display confirmation if logging is enabled/disabled
if self.compression and self.ffmpeg:
self.DEVNULL = open(os.devnull, 'wb')
- if self.logging: print('[LOG]: Compression Mode is configured properly!')
+ if self.logging: self.logger.debug('Compression Mode is configured properly!')
else:
- if self.logging: print('[LOG]: Compression Mode is disabled, Activating OpenCV In-built Writer!')
+ if self.logging: self.logger.debug('Compression Mode is disabled, Activating OpenCV In-built Writer!')
@@ -207,14 +207,14 @@ def write(self, frame, rgb_mode = False):
self.inputwidth = width
self.inputchannels = channels
if self.logging:
- print('[LOG]: InputFrame => Height:{} Width:{} Channels:{}'.format(self.inputheight, self.inputwidth, self.inputchannels))
+ self.logger.debug('InputFrame => Height:{} Width:{} Channels:{}'.format(self.inputheight, self.inputwidth, self.inputchannels))
#validate size of frame
if height != self.inputheight or width != self.inputwidth:
- raise ValueError('[ERROR]: All frames in a video should have same size')
+ raise ValueError('[WriteGear:ERROR] :: All frames in a video should have same size')
#validate number of channels
if channels != self.inputchannels:
- raise ValueError('[ERROR]: All frames in a video should have same number of channels')
+ raise ValueError('[WriteGear:ERROR] :: All frames in a video should have same number of channels')
if self.compression:
# checks if compression mode is enabled
@@ -231,7 +231,7 @@ def write(self, frame, rgb_mode = False):
self.process.stdin.write(frame.tostring())
except (OSError, IOError):
# log something is wrong!
- print ('[ERROR]: BrokenPipeError caught: Wrong Values passed to FFmpeg Pipe, Kindly Refer Docs!')
+ self.logger.error('BrokenPipeError caught: Wrong Values passed to FFmpeg Pipe, Kindly Refer Docs!')
self.DEVNULL.close()
raise ValueError #for testing purpose only
else:
@@ -243,7 +243,7 @@ def write(self, frame, rgb_mode = False):
assert self.process is not None
if self.logging:
# log OpenCV warning
- print('[WARNING]: RGBA and 16-bit grayscale video frames are not supported by OpenCV yet, switch to `compression_mode` to use them!')
+ self.logger.warning('RGBA and 16-bit grayscale video frames are not supported by OpenCV yet, switch to `compression_mode` to use them!')
#write the frame
self.process.write(frame)
@@ -283,8 +283,7 @@ def Preprocess(self, channels, rgb = False):
if self.inputframerate > 5:
#set input framerate - minimum threshold is 5.0
- if self.logging:
- print("Setting Input FrameRate = {}".format(self.inputframerate))
+ if self.logging: self.logger.debug("Setting Input FrameRate = {}".format(self.inputframerate))
input_parameters["-framerate"] = str(self.inputframerate)
#initiate FFmpeg process
@@ -324,7 +323,7 @@ def startFFmpeg_Process(self, input_params, output_params):
self.cmd += " ".join(cmd)
# Launch the FFmpeg process
if self.logging:
- print('[LOG]: Executing FFmpeg command: `{}`'.format(self.cmd))
+ self.logger.debug('Executing FFmpeg command: `{}`'.format(self.cmd))
# In debugging mode
self.process = sp.Popen(cmd, stdin=sp.PIPE, stdout=sp.PIPE, stderr=None)
else:
@@ -341,21 +340,21 @@ def execute_ffmpeg_cmd(self, cmd = None):
"""
#check if valid command
if cmd is None:
- print('[Alert]: Input FFmpeg command is empty, Nothing to execute!')
+ self.logger.warning('Input FFmpeg command is empty, Nothing to execute!')
return
else:
if not(isinstance(cmd, list)):
- raise ValueError("[ERROR]: Invalid input FFmpeg command! Kindly read docs.")
+ raise ValueError("[WriteGear:ERROR] :: Invalid input FFmpeg command! Kindly read docs.")
#check if Compression Mode is enabled
- if not(self.compression): raise RuntimeError("[ERROR]: Compression Mode is disabled, Kindly enable it to access this function!")
+ if not(self.compression): raise RuntimeError("[WriteGear:ERROR] :: Compression Mode is disabled, Kindly enable it to access this function!")
#add configured FFmpeg path
cmd = [self.ffmpeg] + cmd
try:
if self.logging:
- print('[LOG]: Executing FFmpeg command: `{}`'.format(' '.join(cmd)))
+ self.logger.debug('Executing FFmpeg command: `{}`'.format(' '.join(cmd)))
# In debugging mode
sp.call(cmd, stdin=sp.PIPE, stdout=sp.PIPE, stderr=None)
else:
@@ -363,7 +362,7 @@ def execute_ffmpeg_cmd(self, cmd = None):
sp.call(cmd, stdin=sp.PIPE, stdout=self.DEVNULL, stderr=sp.STDOUT)
except (OSError, IOError):
# log something is wrong!
- print ('[ERROR]: BrokenPipeError caught: Wrong command passed to FFmpeg Pipe, Kindly Refer Docs!')
+ self.logger.error('BrokenPipeError caught, Wrong command passed to FFmpeg Pipe, Kindly Refer Docs!')
self.DEVNULL.close()
raise ValueError #for testing purpose only
@@ -409,13 +408,12 @@ def startCV_Process(self):
except Exception as e:
# log if something is wrong
- if self.logging:
- print(e)
- raise ValueError('[ERROR]: Wrong Values passed to OpenCV Writer, Kindly Refer Docs!')
+ if self.logging: self.logger.exception(str(e))
+ raise ValueError('[WriteGear:ERROR] :: Wrong Values passed to OpenCV Writer, Kindly Refer Docs!')
if self.logging:
#log values for debugging
- print('[LOG]: FILE_PATH: {}, FOURCC = {}, FPS = {}, WIDTH = {}, HEIGHT = {}, BACKEND = {}'.format(self.out_file, FOURCC, FPS, WIDTH, HEIGHT, BACKEND))
+ self.logger.debug('FILE_PATH: {}, FOURCC = {}, FPS = {}, WIDTH = {}, HEIGHT = {}, BACKEND = {}'.format(self.out_file, FOURCC, FPS, WIDTH, HEIGHT, BACKEND))
#start different process for with/without Backend.
if BACKEND:
@@ -429,6 +427,8 @@ def close(self):
"""
Terminates the Write process
"""
+ if self.logging: self.logger.debug("Terminating WriteGear Processes.")
+
if self.compression:
#if Compression Mode is enabled
if self.process is None:
@@ -450,14 +450,4 @@ def close(self):
#if Compression Mode is disabled
if self.process is None:
return #no process was initiated at first place
- self.process.release() #close it
-
-
-
-
-
-
-
-
-
-
+ self.process.release() #close it
\ No newline at end of file
diff --git a/vidgear/tests/__init__.py b/vidgear/tests/__init__.py
index a9c9dad2b..3473f6813 100644
--- a/vidgear/tests/__init__.py
+++ b/vidgear/tests/__init__.py
@@ -1 +1 @@
-__author__ = "Abhishek Thakur "
+__author__ = "Abhishek Thakur (@abhiTronix) "
\ No newline at end of file
diff --git a/vidgear/tests/benchmark_tests/__init__.py b/vidgear/tests/benchmark_tests/__init__.py
index b531d785d..446caa1ef 100644
--- a/vidgear/tests/benchmark_tests/__init__.py
+++ b/vidgear/tests/benchmark_tests/__init__.py
@@ -1,3 +1,3 @@
from .fps import FPS
-__author__ = "Abhishek Thakur "
+__author__ = "Abhishek Thakur (@abhiTronix) "
\ No newline at end of file
diff --git a/vidgear/tests/benchmark_tests/test_benchmark_Videocapture.py b/vidgear/tests/benchmark_tests/test_benchmark_Videocapture.py
index 32d3a6561..f0e639d62 100644
--- a/vidgear/tests/benchmark_tests/test_benchmark_Videocapture.py
+++ b/vidgear/tests/benchmark_tests/test_benchmark_Videocapture.py
@@ -1,25 +1,20 @@
"""
-============================================
-vidgear library code is placed under the MIT license
-Copyright (c) 2019 Abhishek Thakur
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
+===============================================
+vidgear library source-code is deployed under the Apache 2.0 License:
+
+Copyright (c) 2019 Abhishek Thakur(@abhiTronix)
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
===============================================
"""
@@ -29,7 +24,9 @@
import tempfile
from vidgear.gears import CamGear
from .fps import FPS
+import logging as log
+logger = log.getLogger('Test_benchmark_videocapture')
def return_testvideo_path():
@@ -54,9 +51,9 @@ def Videocapture_withCV(path):
fps_CV.update()
fps_CV.stop()
stream.release()
- print("OpenCV")
- print("[LOG] total elasped time: {:.2f}".format(fps_CV.total_time_elapsed()))
- print("[LOG] approx. FPS: {:.2f}".format(fps_CV.fps()))
+ logger.debug("OpenCV")
+ logger.debug("total elasped time: {:.2f}".format(fps_CV.total_time_elapsed()))
+ logger.debug("approx. FPS: {:.2f}".format(fps_CV.fps()))
@@ -74,9 +71,9 @@ def Videocapture_withVidGear(path):
fps_Vid.update()
fps_Vid.stop()
stream.stop()
- print("VidGear")
- print("[LOG] total elasped time: {:.2f}".format(fps_Vid.total_time_elapsed()))
- print("[LOG] approx. FPS: {:.2f}".format(fps_Vid.fps()))
+ logger.debug("VidGear")
+ logger.debug("total elasped time: {:.2f}".format(fps_Vid.total_time_elapsed()))
+ logger.debug("approx. FPS: {:.2f}".format(fps_Vid.fps()))
@pytest.mark.xfail(raises=RuntimeError)
@@ -88,4 +85,4 @@ def test_benchmark_videocapture():
Videocapture_withCV(return_testvideo_path())
Videocapture_withVidGear(return_testvideo_path())
except Exception as e:
- raise RuntimeError(e)
+ raise RuntimeError(e)
\ No newline at end of file
diff --git a/vidgear/tests/benchmark_tests/test_benchmark_Videowriter.py b/vidgear/tests/benchmark_tests/test_benchmark_Videowriter.py
index a784cb775..52a66c7d4 100644
--- a/vidgear/tests/benchmark_tests/test_benchmark_Videowriter.py
+++ b/vidgear/tests/benchmark_tests/test_benchmark_Videowriter.py
@@ -1,25 +1,20 @@
"""
-============================================
-vidgear library code is placed under the MIT license
-Copyright (c) 2019 Abhishek Thakur
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
+===============================================
+vidgear library source-code is deployed under the Apache 2.0 License:
+
+Copyright (c) 2019 Abhishek Thakur(@abhiTronix)
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
===============================================
"""
@@ -29,7 +24,9 @@
from vidgear.gears import WriteGear
from vidgear.gears import VideoGear
from .fps import FPS
+import logging as log
+logger = log.getLogger('Test_benchmark_videowriter')
def return_testvideo_path():
@@ -56,12 +53,12 @@ def return_static_ffmpeg():
-def WriteGear_non_compression_mode(path):
+def WriteGear_non_compression_mode():
"""
Function to Benchmark WriteGear's Non-Compression Mode(OpenCV)
"""
options = {'THREADED_QUEUE_MODE':False}
- stream = VideoGear(source=path, **options).start()
+ stream = VideoGear(source=return_testvideo_path(), **options).start()
writer = WriteGear(output_filename = 'Output_vnc.mp4', compression_mode = False )
fps_CV = FPS().start()
while True:
@@ -73,19 +70,19 @@ def WriteGear_non_compression_mode(path):
fps_CV.stop()
stream.stop()
writer.close()
- print("OpenCV Writer")
- print("[LOG] total elasped time: {:.2f}".format(fps_CV.total_time_elapsed()))
- print("[LOG] approx. FPS: {:.2f}".format(fps_CV.fps()))
+ logger.debug("OpenCV Writer")
+ logger.debug("total elasped time: {:.2f}".format(fps_CV.total_time_elapsed()))
+ logger.debug("approx. FPS: {:.2f}".format(fps_CV.fps()))
os.remove(os.path.abspath('Output_vnc.mp4'))
-def WriteGear_compression_mode(path):
+def WriteGear_compression_mode():
"""
Function to Benchmark WriteGear's Compression Mode(FFmpeg)
"""
options = {'THREADED_QUEUE_MODE':False}
- stream = VideoGear(source=path, **options).start()
+ stream = VideoGear(source=return_testvideo_path(), **options).start()
writer = WriteGear(output_filename = 'Output_vc.mp4', custom_ffmpeg = return_static_ffmpeg())
fps_Vid = FPS().start()
while True:
@@ -97,9 +94,9 @@ def WriteGear_compression_mode(path):
fps_Vid.stop()
stream.stop()
writer.close()
- print("FFmpeg Writer")
- print("[LOG] total elasped time: {:.2f}".format(fps_Vid.total_time_elapsed()))
- print("[LOG] approx. FPS: {:.2f}".format(fps_Vid.fps()))
+ logger.debug("FFmpeg Writer")
+ logger.debug("total elasped time: {:.2f}".format(fps_Vid.total_time_elapsed()))
+ logger.debug("approx. FPS: {:.2f}".format(fps_Vid.fps()))
os.remove(os.path.abspath('Output_vc.mp4'))
@@ -112,4 +109,4 @@ def test_benchmark_videowriter():
Videowriter_non_compression_mode(return_testvideo_path())
Videowriter_compression_mode(return_testvideo_path())
except Exception as e:
- raise RuntimeError(e)
+ raise RuntimeError(e)
\ No newline at end of file
diff --git a/vidgear/tests/benchmark_tests/test_benchmark_playback.py b/vidgear/tests/benchmark_tests/test_benchmark_playback.py
index 1eb1a3dc4..cb4e2955a 100644
--- a/vidgear/tests/benchmark_tests/test_benchmark_playback.py
+++ b/vidgear/tests/benchmark_tests/test_benchmark_playback.py
@@ -1,25 +1,20 @@
"""
-============================================
-vidgear library code is placed under the MIT license
-Copyright (c) 2019 Abhishek Thakur
+===============================================
+vidgear library source-code is deployed under the Apache 2.0 License:
+
+Copyright (c) 2019 Abhishek Thakur(@abhiTronix)
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
+ http://www.apache.org/licenses/LICENSE-2.0
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
===============================================
"""
@@ -28,7 +23,9 @@
import tempfile
from vidgear.gears import CamGear
from .fps import FPS
+import logging as log
+logger = log.getLogger('Test_benchmark_playback')
def return_testvideo(level=0):
@@ -55,8 +52,8 @@ def playback(level):
fps.update()
stream.stop()
fps.stop()
- print("[LOG] total elasped time: {:.2f}".format(fps.total_time_elapsed()))
- print("[LOG] approx. FPS: {:.2f}".format(fps.fps()))
+ logger.debug("total elasped time: {:.2f}".format(fps.total_time_elapsed()))
+ logger.debug("approx. FPS: {:.2f}".format(fps.fps()))
@@ -69,6 +66,6 @@ def test_benchmark(level):
try:
playback(level)
except Exception as e:
- print(e)
+ logger.exception(str(e))
else:
- print("Skipping this test for macOS!")
+ logger.debug("Skipping this test for macOS!")
\ No newline at end of file
diff --git a/vidgear/tests/network_tests/__init__.py b/vidgear/tests/network_tests/__init__.py
index 03cf63684..096aade25 100644
--- a/vidgear/tests/network_tests/__init__.py
+++ b/vidgear/tests/network_tests/__init__.py
@@ -1,2 +1,2 @@
-__author__ = "Abhishek Thakur "
+__author__ = "Abhishek Thakur (@abhiTronix) "
\ No newline at end of file
diff --git a/vidgear/tests/network_tests/test_netgear.py b/vidgear/tests/network_tests/test_netgear.py
index b22b56b29..1ef3a60d2 100644
--- a/vidgear/tests/network_tests/test_netgear.py
+++ b/vidgear/tests/network_tests/test_netgear.py
@@ -1,27 +1,23 @@
"""
-============================================
-vidgear library code is placed under the MIT license
-Copyright (c) 2019 Abhishek Thakur
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
+===============================================
+vidgear library source-code is deployed under the Apache 2.0 License:
+
+Copyright (c) 2019 Abhishek Thakur(@abhiTronix)
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
===============================================
"""
+
from vidgear.gears import NetGear
from vidgear.gears import VideoGear
@@ -33,7 +29,9 @@
import numpy as np
import traceback
from zmq.error import ZMQError
+import logging as log
+logger = log.getLogger('Test_netgear')
def return_testvideo_path():
@@ -67,7 +65,7 @@ def test_playback():
client.close()
except Exception as e:
if isinstance(e, (ZMQError, ValueError)):
- print(traceback.print_tb(e.__traceback__))
+ logger.debug(traceback.print_tb(e.__traceback__))
else:
pytest.fail(str(e))
@@ -106,7 +104,7 @@ def test_patterns(pattern):
assert np.array_equal(frame_server, frame_client)
except Exception as e:
if isinstance(e, (ZMQError, ValueError)):
- print(traceback.print_tb(e.__traceback__))
+ logger.error(traceback.print_tb(e.__traceback__))
else:
pytest.fail(str(e))
@@ -136,7 +134,7 @@ def test_compression():
client.close()
except Exception as e:
if isinstance(e, (ZMQError, ValueError)):
- print(traceback.print_tb(e.__traceback__))
+ logger.error(traceback.print_tb(e.__traceback__))
else:
pytest.fail(str(e))
@@ -180,7 +178,7 @@ def test_secure_mode(pattern, security_mech, custom_cert_location, overwrite_cer
assert np.array_equal(frame_server, frame_client)
except Exception as e:
if isinstance(e, (ZMQError, ValueError)):
- print(traceback.print_tb(e.__traceback__))
+ logger.error(traceback.print_tb(e.__traceback__))
else:
pytest.fail(str(e))
@@ -193,7 +191,7 @@ def test_bidirectional_mode(target_data):
Testing NetGear's Bidirectional Mode with different datatypes
"""
try:
- print('[LOG] Given Input Data: {}'.format(target_data))
+ logger.debug('Given Input Data: {}'.format(target_data))
#open strem
stream = VideoGear(source=return_testvideo_path()).start()
@@ -218,9 +216,9 @@ def test_bidirectional_mode(target_data):
server.close()
client.close()
- #print data recieved at client-end and server-end
- print('[LOG] Data recieved at Server-end: {}'.format(server_data))
- print('[LOG] Data recieved at Client-end: {}'.format(client_data))
+ #logger.debug data recieved at client-end and server-end
+ logger.debug('Data recieved at Server-end: {}'.format(server_data))
+ logger.debug('Data recieved at Client-end: {}'.format(client_data))
#check if recieved frame exactly matches input frame
assert np.array_equal(frame_server, frame_client)
@@ -228,7 +226,7 @@ def test_bidirectional_mode(target_data):
assert client_data == server_data
except Exception as e:
if isinstance(e, (ZMQError, ValueError)):
- print(traceback.print_tb(e.__traceback__))
+ logger.error(traceback.print_tb(e.__traceback__))
else:
pytest.fail(str(e))
@@ -291,6 +289,6 @@ def test_multiserver_mode():
except Exception as e:
if isinstance(e, (ZMQError, ValueError)):
- print(traceback.print_tb(e.__traceback__))
+ logger.error(traceback.print_tb(e.__traceback__))
else:
pytest.fail(str(e))
\ No newline at end of file
diff --git a/vidgear/tests/test_helper.py b/vidgear/tests/test_helper.py
index 3ccf47ca0..6573e1994 100644
--- a/vidgear/tests/test_helper.py
+++ b/vidgear/tests/test_helper.py
@@ -1,36 +1,33 @@
"""
-============================================
-vidgear library code is placed under the MIT license
-Copyright (c) 2019 Abhishek Thakur
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
+===============================================
+vidgear library source-code is deployed under the Apache 2.0 License:
+
+Copyright (c) 2019 Abhishek Thakur(@abhiTronix)
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
===============================================
"""
import os, pytest, tempfile, shutil, platform
from os.path import expanduser
+import logging as log
from vidgear.gears.helper import download_ffmpeg_binaries
from vidgear.gears.helper import validate_ffmpeg
from vidgear.gears.helper import get_valid_ffmpeg_path
from vidgear.gears.helper import generate_auth_certificates
+logger = log.getLogger('Test_helper')
def return_static_ffmpeg():
@@ -56,10 +53,10 @@ def test_ffmpeg_static_installation():
for root, dirs, files in os.walk(startpath):
level = root.replace(startpath, '').count(os.sep)
indent = ' ' * 4 * (level)
- print('[LOG]: {}{}/'.format(indent, os.path.basename(root)))
+ logger.debug('{}{}/'.format(indent, os.path.basename(root)))
subindent = ' ' * 4 * (level + 1)
for f in files:
- print('[LOG]: {}{}'.format(subindent, f))
+ logger.debug('{}{}'.format(subindent, f))
@@ -138,7 +135,7 @@ def test_generate_auth_certificates(paths, overwrite_cert, results):
Testing auto-Generation and auto-validation of CURVE ZMQ keys/certificates
"""
try:
- if overwrite_cert: print('[WARNING]: Overwriting ZMQ Authentication certificates over previous ones!')
+ if overwrite_cert: logger.warning('Overwriting ZMQ Authentication certificates over previous ones!')
output = generate_auth_certificates(paths, overwrite = overwrite_cert)
if paths != 'wrong_test_path':
assert bool(output) == results
diff --git a/vidgear/tests/videocapture_tests/__init__.py b/vidgear/tests/videocapture_tests/__init__.py
index a9c9dad2b..3473f6813 100644
--- a/vidgear/tests/videocapture_tests/__init__.py
+++ b/vidgear/tests/videocapture_tests/__init__.py
@@ -1 +1 @@
-__author__ = "Abhishek Thakur "
+__author__ = "Abhishek Thakur (@abhiTronix) "
\ No newline at end of file
diff --git a/vidgear/tests/videocapture_tests/test_camgear.py b/vidgear/tests/videocapture_tests/test_camgear.py
index cad6a1509..9d31ccbf7 100644
--- a/vidgear/tests/videocapture_tests/test_camgear.py
+++ b/vidgear/tests/videocapture_tests/test_camgear.py
@@ -1,25 +1,20 @@
"""
-============================================
-vidgear library code is placed under the MIT license
-Copyright (c) 2019 Abhishek Thakur
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
+===============================================
+vidgear library source-code is deployed under the Apache 2.0 License:
+
+Copyright (c) 2019 Abhishek Thakur(@abhiTronix)
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
===============================================
"""
@@ -30,10 +25,10 @@
import pytest
import tempfile
import numpy as np
-
from vidgear.gears import CamGear
+import logging as log
-
+logger = log.getLogger('Test_camgear')
def return_youtubevideo_params(url):
"""
@@ -64,7 +59,7 @@ def return_total_frame_count():
while True:
(grabbed, frame) = stream.read()
if not grabbed:
- print(num_cv)
+ logger.debug(num_cv)
break
num_cv += 1
stream.release()
@@ -84,7 +79,7 @@ def test_threaded_queue_mode():
while True:
frame = stream_camgear.read()
if frame is None:
- print(camgear_frames_num)
+ logger.debug(camgear_frames_num)
break
time.sleep(0.2) #dummy computational task
@@ -119,19 +114,19 @@ def test_youtube_playback():
if height == 0 or width == 0:
fps = stream.framerate
height,width = frame.shape[:2]
- print('[LOG]: WIDTH: {} HEIGHT: {} FPS: {}'.format(true_video_param[0],true_video_param[1],true_video_param[2]))
- print('[LOG]: WIDTH: {} HEIGHT: {} FPS: {}'.format(width,height,fps))
+ logger.debug('WIDTH: {} HEIGHT: {} FPS: {}'.format(true_video_param[0],true_video_param[1],true_video_param[2]))
+ logger.debug('WIDTH: {} HEIGHT: {} FPS: {}'.format(width,height,fps))
except Exception as error:
- print(error)
+ logger.exception(error)
errored = True
if not errored:
assert true_video_param[0] == width and true_video_param[1] == height and true_video_param[2] == fps
else:
- print('[LOG]: YouTube playback Test is skipped due to above error!')
+ logger.debug('YouTube playback Test is skipped due to above error!')
else:
- print('[LOG]: YouTube playback Test is skipped due to bug with opencv-python library builds on windows and macOS!')
+ logger.debug('YouTube playback Test is skipped due to bug with opencv-python library builds on windows and macOS!')
@@ -161,25 +156,14 @@ def test_network_playback():
Output_data.append(frame)
i+=1
output_stream.stop()
- print('[LOG]: Output data shape:', np.array(Output_data).shape)
+ logger.debug('Output data shape:', np.array(Output_data).shape)
if Output_data[-1].shape[:2] > (50,50): break
except Exception as e:
if isinstance(e, RuntimeError):
- print("[LOG] `{}` URL is not working".format(Publictest_rstp_urls[index]))
+ logger.debug("`{}` URL is not working".format(Publictest_rstp_urls[index]))
index+=1
continue
else:
pytest.fail(str(e))
- if (index == len(Publictest_rstp_urls)): pytest.fail(str(e))
-
-
-
-
-
-
-
-
-
-
-
+ if (index == len(Publictest_rstp_urls)): pytest.fail(str(e))
\ No newline at end of file
diff --git a/vidgear/tests/videocapture_tests/test_screengear.py b/vidgear/tests/videocapture_tests/test_screengear.py
index 272e0f91b..c5d3c3028 100644
--- a/vidgear/tests/videocapture_tests/test_screengear.py
+++ b/vidgear/tests/videocapture_tests/test_screengear.py
@@ -1,31 +1,29 @@
"""
-============================================
-vidgear library code is placed under the MIT license
-Copyright (c) 2019 Abhishek Thakur
+===============================================
+vidgear library source-code is deployed under the Apache 2.0 License:
+
+Copyright (c) 2019 Abhishek Thakur(@abhiTronix)
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
+ http://www.apache.org/licenses/LICENSE-2.0
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
===============================================
"""
from vidgear.gears import ScreenGear
from mss.exception import ScreenShotError
import pytest, platform
+import logging as log
+
+logger = log.getLogger('Test_screengear')
def test_screengear():
"""
@@ -46,6 +44,6 @@ def test_screengear():
stream.stop()
except Exception as e:
if platform.system() == 'Linux' or platform.system() == 'Windows':
- print(e)
+ logger.exception(e)
else:
pytest.fail(str(e))
\ No newline at end of file
diff --git a/vidgear/tests/videocapture_tests/test_videogear.py b/vidgear/tests/videocapture_tests/test_videogear.py
index 84d48d4c5..f78fd5552 100644
--- a/vidgear/tests/videocapture_tests/test_videogear.py
+++ b/vidgear/tests/videocapture_tests/test_videogear.py
@@ -1,33 +1,28 @@
"""
-============================================
-vidgear library code is placed under the MIT license
-Copyright (c) 2019 Abhishek Thakur
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
===============================================
-"""
+vidgear library source-code is deployed under the Apache 2.0 License:
+
+Copyright (c) 2019 Abhishek Thakur(@abhiTronix)
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
-#Video credit: http://www.liushuaicheng.org/CVPR2014/index.html
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+===============================================
+"""
import pytest, os, tempfile
from vidgear.gears import VideoGear
+import logging as log
+logger = log.getLogger('Test_videogear')
def return_testvideo_path():
@@ -59,7 +54,7 @@ def test_CamGear_import():
output_stream = VideoGear(source = return_testvideo_path(), logging=True, **options).start()
framerate = output_stream.framerate
output_stream.stop()
- print('[LOG] Input Framerate: {}'.format(framerate))
+ logger.debug('Input Framerate: {}'.format(framerate))
assert framerate>0
except Exception as e:
pytest.fail(str(e))
@@ -71,6 +66,7 @@ def test_video_stablization():
Testing VideoGear's Video Stablization playback capabilities
"""
try:
+ #Video credit: http://www.liushuaicheng.org/CVPR2014/index.html
Url = 'https://raw.githubusercontent.com/abhiTronix/Imbakup/master/Images/example4_train_input.mp4'
#define params
options = {'SMOOTHING_RADIUS': 5, 'BORDER_SIZE': 10, 'BORDER_TYPE': 'replicate', 'CROP_N_ZOOM': True}
diff --git a/vidgear/tests/writer_tests/__init__.py b/vidgear/tests/writer_tests/__init__.py
index a9c9dad2b..3473f6813 100644
--- a/vidgear/tests/writer_tests/__init__.py
+++ b/vidgear/tests/writer_tests/__init__.py
@@ -1 +1 @@
-__author__ = "Abhishek Thakur "
+__author__ = "Abhishek Thakur (@abhiTronix) "
\ No newline at end of file
diff --git a/vidgear/tests/writer_tests/test_IO.py b/vidgear/tests/writer_tests/test_IO.py
index 2f5ab21e2..eb3a24e0e 100644
--- a/vidgear/tests/writer_tests/test_IO.py
+++ b/vidgear/tests/writer_tests/test_IO.py
@@ -1,25 +1,20 @@
"""
-============================================
-vidgear library code is placed under the MIT license
-Copyright (c) 2019 Abhishek Thakur
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
+===============================================
+vidgear library source-code is deployed under the Apache 2.0 License:
+
+Copyright (c) 2019 Abhishek Thakur(@abhiTronix)
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
===============================================
"""
@@ -59,4 +54,4 @@ def test_failedextension():
with pytest.raises(ValueError):
writer = WriteGear("garbage.garbage")
writer.write(input_data)
- writer.close()
+ writer.close()
\ No newline at end of file
diff --git a/vidgear/tests/writer_tests/test_compression_mode.py b/vidgear/tests/writer_tests/test_compression_mode.py
index 8e5631e56..a8c1cf848 100644
--- a/vidgear/tests/writer_tests/test_compression_mode.py
+++ b/vidgear/tests/writer_tests/test_compression_mode.py
@@ -1,25 +1,20 @@
"""
-============================================
-vidgear library code is placed under the MIT license
-Copyright (c) 2019 Abhishek Thakur
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
+===============================================
+vidgear library source-code is deployed under the Apache 2.0 License:
+
+Copyright (c) 2019 Abhishek Thakur(@abhiTronix)
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
===============================================
"""
@@ -34,7 +29,9 @@
import tempfile
import os, platform
import subprocess, re
+import logging as log
+logger = log.getLogger('Test_commpression_mode')
def return_static_ffmpeg():
@@ -123,7 +120,7 @@ def test_write(conversion):
if result:
if not isinstance(result, string_types):
result = result.decode()
- print('[LOG]: Result: {}'.format(result))
+ logger.debug('Result: {}'.format(result))
for i in ["Error", "Invalid", "error", "invalid"]:
assert not(i in result)
os.remove(os.path.abspath('Output_tw.mp4'))
@@ -182,8 +179,7 @@ def test_WriteGear_compression(f_name, c_ffmpeg, output_params, result):
if f_name and f_name != tempfile.gettempdir():
os.remove(os.path.abspath(f_name))
except Exception as e:
- if result:
- pytest.fail(str(e))
+ if result: pytest.fail(str(e))
@@ -205,4 +201,4 @@ def test_WriteGear_customFFmpeg():
writer.execute_ffmpeg_cmd(ffmpeg_command_to_save_audio)
#assert audio file is created successfully
- assert os.path.isfile(output_audio_filename)
+ assert os.path.isfile(output_audio_filename)
\ No newline at end of file
diff --git a/vidgear/tests/writer_tests/test_non_compression_mode.py b/vidgear/tests/writer_tests/test_non_compression_mode.py
index 905d24753..ac9c9ca95 100644
--- a/vidgear/tests/writer_tests/test_non_compression_mode.py
+++ b/vidgear/tests/writer_tests/test_non_compression_mode.py
@@ -1,25 +1,20 @@
"""
-============================================
-vidgear library code is placed under the MIT license
-Copyright (c) 2019 Abhishek Thakur
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
+===============================================
+vidgear library source-code is deployed under the Apache 2.0 License:
+
+Copyright (c) 2019 Abhishek Thakur(@abhiTronix)
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
===============================================
"""
@@ -32,7 +27,9 @@
import pytest
import cv2
import tempfile
+import logging as log
+logger = log.getLogger('Test_non_commpression_mode')
def return_static_ffmpeg():
@@ -86,7 +83,7 @@ def test_write(conversion):
if result:
if not isinstance(result, string_types):
result = result.decode()
- print('[LOG]: Result: {}'.format(result))
+ logger.debug('Result: {}'.format(result))
for i in ["Error", "Invalid", "error", "invalid"]:
assert not(i in result)
os.remove(os.path.abspath('Output_twc.avi'))
@@ -117,5 +114,4 @@ def test_WriteGear_compression(f_name, output_params, result):
if f_name and f_name != tempfile.gettempdir():
os.remove(os.path.abspath(f_name))
except Exception as e:
- if result:
- pytest.fail(str(e))
\ No newline at end of file
+ if result: pytest.fail(str(e))
\ No newline at end of file
From e6aff9d123fff0fb808d8a39a768b565ae8719c6 Mon Sep 17 00:00:00 2001
From: Abhishek
Date: Sat, 21 Dec 2019 11:40:24 +0530
Subject: [PATCH 30/39] Code Encapsulation & Updates:
- implemented encapsulation for class functions and varibales on all gears
- Updated README readibility
---
README.md | 85 +++----
vidgear/gears/camgear.py | 108 +++++----
vidgear/gears/helper.py | 5 +-
vidgear/gears/netgear.py | 439 ++++++++++++++++++------------------
vidgear/gears/pigear.py | 145 ++++++------
vidgear/gears/screengear.py | 120 +++++-----
vidgear/gears/stabilizer.py | 162 ++++++-------
vidgear/gears/videogear.py | 22 +-
vidgear/gears/writegear.py | 249 ++++++++++----------
9 files changed, 670 insertions(+), 665 deletions(-)
diff --git a/README.md b/README.md
index 23e91b89c..75cfa73bf 100644
--- a/README.md
+++ b/README.md
@@ -51,7 +51,7 @@ The following **functional block diagram** clearly depicts the functioning of Vi
-## Table of Contents
+# Table of Contents
[**TL;DR**](#tldr)
@@ -86,7 +86,7 @@ The following **functional block diagram** clearly depicts the functioning of Vi
-## TL;DR
+# TL;DR
> ***"VidGear is an [ultrafast➶][ultrafast-wiki], compact, flexible and easy-to-adapt complete Video Processing Python Library."***
@@ -98,7 +98,7 @@ The following **functional block diagram** clearly depicts the functioning of Vi
-## Gears:
+# Gears:
> **VidGear is built with **multi-threaded APIs** *(a.k.a Gears)* each with some unique function/mechanism.**
@@ -123,9 +123,9 @@ Each of these API is designed exclusively to handle/control different device-spe
-### CamGear
+## CamGear
-> **CamGear can grab ultrafast frames from diverse range of VideoStreams, which includes almost any IP/USB Cameras, multimedia video file format ([_upto 4k tested_][test-4k]), various network stream protocols such as `http(s), rtp, rstp, rtmp, mms, etc.`, plus support for live Gstreamer's stream pipeline and YouTube video/livestreams URLs.**
+> *CamGear can grab ultrafast frames from diverse range of VideoStreams, which includes almost any IP/USB Cameras, multimedia video file format ([_upto 4k tested_][test-4k]), various network stream protocols such as `http(s), rtp, rstp, rtmp, mms, etc.`, plus support for live Gstreamer's stream pipeline and YouTube video/livestreams URLs.*
CamGear provides a flexible, high-level multi-threaded wrapper around `OpenCV's` [VideoCapture class][opencv-vc] with access almost all of its available parameters and also employs [`pafy`][pafy] python APIs for live [YouTube streaming][youtube-wiki]. Furthermore, CamGear implements exclusively on [**Threaded Queue mode**][TQM-wiki] for ultra-fast, error-free and synchronized frame handling.
@@ -136,15 +136,15 @@ CamGear provides a flexible, high-level multi-threaded wrapper around `OpenCV's`
-#### CamGear API Guide:
+### CamGear API Guide:
[**>>> Usage Guide**][camgear-wiki]
-### VideoGear
+## VideoGear
-> **VideoGear API provides a special internal wrapper around VidGear's exclusive [**Video Stabilizer**][stablizer-wiki] class.**
+> *VideoGear API provides a special internal wrapper around VidGear's exclusive [**Video Stabilizer**][stablizer-wiki] class.*
Furthermore, VideoGear API can provide internal access to both [CamGear](#camgear) and [PiGear](#pigear) APIs separated by a special flag. Thereby, _this API holds the exclusive power for any incoming VideoStream from any source, whether it is live or not, to access and stabilize it directly with minimum latency and memory requirements._
@@ -205,15 +205,15 @@ stream_stab.stop()
```
-#### VideoGear API Guide:
+### VideoGear API Guide:
[**>>> Usage Guide**][videogear-wiki]
-### PiGear
+## PiGear
-> **PiGear is similar to CamGear but made to support various Raspberry Pi Camera Modules *(such as [OmniVision OV5647 Camera Module][OV5647-picam] and [Sony IMX219 Camera Module][IMX219-picam])*.**
+> *PiGear is similar to CamGear but made to support various Raspberry Pi Camera Modules *(such as [OmniVision OV5647 Camera Module][OV5647-picam] and [Sony IMX219 Camera Module][IMX219-picam])*.*
PiGear provides a flexible multi-threaded wrapper around complete [**picamera**][picamera] python library to interface with these modules correctly, and also grants the ability to exploit its various features like `brightness, saturation, sensor_mode, etc.` effortlessly.
@@ -225,15 +225,15 @@ Best of all, PiGear API provides excellent Error-Handling with features like a t
-#### PiGear API Guide:
+### PiGear API Guide:
[**>>> Usage Guide**][pigear-wiki]
-### ScreenGear
+## ScreenGear
-> **ScreenGear act as Screen Recorder, that can grab frames from your monitor in real-time either by define an area on the computer screen or fullscreen at the expense of insignificant latency. It also provide seemless support for capturing frames from multiple monitors.**
+> *ScreenGear act as Screen Recorder, that can grab frames from your monitor in real-time either by define an area on the computer screen or fullscreen at the expense of insignificant latency. It also provide seemless support for capturing frames from multiple monitors.*
ScreenGear provides a high-level multi-threaded wrapper around [**python-mss**][mss] python library API and also supports a easy and flexible direct internal parameter manipulation.
@@ -279,7 +279,7 @@ stream.stop()
# safely close video stream.
```
-#### ScreenGear API Guide:
+### ScreenGear API Guide:
[**>>> Usage Guide**][screengear-wiki]
@@ -287,9 +287,9 @@ stream.stop()
-### WriteGear
+## WriteGear
-> **WriteGear handles various powerful Writer Tools that provide us the freedom to do almost anything imagine with multimedia files.**
+> *WriteGear handles various powerful Writer Tools that provide us the freedom to do almost anything imagine with multimedia files.*
WriteGear API provide a complete, flexible & robust wrapper around [**FFmpeg**][ffmpeg], a leading multimedia framework. With WriteGear, we can process real-time video frames into a lossless compressed format with any suitable specification in just few easy [lines of codes][compression-mode-ex]. These specifications include setting any video/audio property such as `bitrate, codec, framerate, resolution, subtitles, etc.` easily as well complex tasks such as multiplexing video with audio in real-time(see this [example wiki][live-audio-wiki]). Best of all, WriteGear grants the freedom to play with any FFmpeg parameter with its exclusive custom Command function(see this [example wiki][custom-command-wiki]), while handling all errors robustly.
@@ -307,15 +307,15 @@ In addition to this, WriteGear also provides flexible access to [**OpenCV's Vide
-#### WriteGear API Guide:
+### WriteGear API Guide:
[**>>> Usage Guide**][writegear-wiki]
-### NetGear
+## NetGear
-> **NetGear is exclusively designed to transfer video frames synchronously and asynchronously between interconnecting systems over the network in real-time.**
+> *NetGear is exclusively designed to transfer video frames synchronously and asynchronously between interconnecting systems over the network in real-time.*
NetGear implements a high-level wrapper around [**PyZmQ**][pyzmq] python library that contains python bindings for [ZeroMQ](http://zeromq.org/) - a high-performance asynchronous distributed messaging library that aim to be used in distributed or concurrent applications. It provides a message queue, but unlike message-oriented middleware, a ZeroMQ system can run without a dedicated message broker.
@@ -341,7 +341,7 @@ Best of all, NetGear can robustly handle Multiple Servers at once, thereby provi
-#### NetGear API Guide:
+### NetGear API Guide:
[**>>> Usage Guide**][netgear-wiki]
@@ -349,26 +349,26 @@ Best of all, NetGear can robustly handle Multiple Servers at once, thereby provi
-## New Release SneekPeak : VidGear 0.1.6
+# New Release SneekPeak : VidGear 0.1.6
-***:warning: Python 2.7 legacy support [dropped in v0.1.6][drop27] !***
+* ***:warning: Python 2.7 legacy support [dropped in v0.1.6][drop27] !***
-**NetGear API:**
+* **NetGear API:**
* Added powerful ZMQ Authentication & Data Encryption features for NetGear API
* Added robust Multi-Server support for NetGear API.
* Added exclusive Bi-Directional Mode for bidirectional data transmission.
* Added frame-compression support with on-the-fly flexible encoding/decoding.
* Implemented new *Publish/Subscribe(`zmq.PUB/zmq.SUB`)* pattern for seamless Live Streaming in NetGear API.
-**PiGear API:**
+* **PiGear API:**
* Added new threaded internal timing function for PiGear to handle any hardware failures/frozen threads
* PiGear will not exit safely with `SystemError` if Picamera ribbon cable is pulled out to save resources.
-**WriteGear API:** Added new `execute_ffmpeg_cmd` function to pass a custom command to its internal FFmpeg pipeline.
+* **WriteGear API:** Added new `execute_ffmpeg_cmd` function to pass a custom command to its internal FFmpeg pipeline.
-**Stabilizer class:** Added new _Crop and Zoom_ feature.
+* **Stabilizer class:** Added new _Crop and Zoom_ feature.
-***Added VidGear's official native support for MacOS environment and [many more...](changelog.md)***
+* ***Added VidGear's official native support for MacOS environment and [many more...](changelog.md)***
@@ -377,9 +377,9 @@ Best of all, NetGear can robustly handle Multiple Servers at once, thereby provi
-## Installation
+# Installation
-### Prerequisites
+## Prerequisites
Before installing VidGear, you must verify that the following dependencies are met:
@@ -419,9 +419,10 @@ Before installing VidGear, you must verify that the following dependencies are m
pip3 install -U youtube-dl
```
-
+## Available Installation Options
+
### Option 1: PyPI Install
> Best option for **quickly** getting VidGear installed.
@@ -429,15 +430,15 @@ Before installing VidGear, you must verify that the following dependencies are m
```sh
pip3 install vidgear
```
-
+
### Option 2: Release Archive Download
> Best option if you want a **compressed archive**.
-VidGear releases are available for download as packages in the [latest release][release]
+VidGear releases are available for download as packages in the [latest release][release].
+
-
### Option 3: Clone the Repository
@@ -456,7 +457,7 @@ You can clone this repository's `testing` branch for development and thereby can
-## Documentation
+# Documentation
The full documentation for all VidGear classes and functions can be found in the link below:
@@ -464,7 +465,7 @@ The full documentation for all VidGear classes and functions can be found in the
-## Testing
+# Testing
* **Prerequisites:** Testing VidGear require some *additional dependencies & data* which can be downloaded manually as follows:
@@ -491,28 +492,28 @@ The full documentation for all VidGear classes and functions can be found in the
-## Contributing
+# Contributing
-See [**contributing.md**](contributing.md)
+See [**contributing.md**](contributing.md).
-## Supported Python legacies
+# Supported Python legacies
* **Python 3+ are only supported legacies for installing Vidgear v0.1.6 and above.**
* **:warning: Python 2.7 legacy support [dropped in v0.1.6][drop27].**
-## Changelog
+# Changelog
See [**changelog.md**](changelog.md)
-## License
+# License
-Copyright © abhiTronix 2019
+**Copyright © abhiTronix 2019**
This library is licensed under the **[Apache 2.0 License][license]**.
diff --git a/vidgear/gears/camgear.py b/vidgear/gears/camgear.py
index 131b62a9f..28e665463 100644
--- a/vidgear/gears/camgear.py
+++ b/vidgear/gears/camgear.py
@@ -47,16 +47,15 @@
import cv2
# check whether OpenCV Binaries are 3.x+
if parse_version(cv2.__version__) < parse_version('3'):
- raise ImportError('[CamGear:ERROR] :: OpenCV library version >= 3.0 is only supported by this library')
-
+ raise ImportError('[CamGear:ERROR] :: OpenCV API version >= 3.0 is only supported by this library.')
except ImportError as error:
- raise ImportError('[CamGear:ERROR] :: Failed to detect OpenCV executables, install it with `pip install opencv-python` command.')
+ raise ImportError('[CamGear:ERROR] :: Failed to detect correct OpenCV executables, install it with `pip3 install opencv-python` command.')
-def youtube_url_validation(url):
+def self.__youtube_url_validation(url):
"""
- convert youtube video url and checks its validity
+ convert Youtube video URLs to a valid address
"""
youtube_regex = (
r'(https?://)?(www\.)?'
@@ -112,12 +111,12 @@ class CamGear:
def __init__(self, source = 0, y_tube = False, backend = 0, colorspace = None, logging = False, time_delay = 0, **options):
#intialize threaded queue mode
- self.threaded_queue_mode = True
+ self.__threaded_queue_mode = True
# enable logging if specified
- self.logging = False
- self.logger = log.getLogger('CamGear')
- if logging: self.logging = logging
+ self.__logging = False
+ self.__logger = log.getLogger('CamGear')
+ if logging: self.__logging = logging
# check if Youtube Mode is ON (True)
if y_tube:
@@ -125,43 +124,43 @@ def __init__(self, source = 0, y_tube = False, backend = 0, colorspace = None, l
#import pafy and parse youtube stream url
import pafy
# validate
- url = youtube_url_validation(source)
+ url = self.__youtube_url_validation(source)
if url:
source_object = pafy.new(url)
_source = source_object.getbestvideo("any", ftypestrict=False)
if _source is None: _source = source_object.getbest("any", ftypestrict=False)
- if self.logging: self.logger.debug('YouTube source ID: `{}`, Title: `{}` & Video_Extension: `{}`'.format(url, source_object.title, _source.extension))
+ if self.__logging: self.__logger.debug('YouTube source ID: `{}`, Title: `{}` & Video_Extension: `{}`'.format(url, source_object.title, _source.extension))
source = _source.url
else: raise RuntimeError('URL cannot be processed!')
except Exception as e:
- if self.logging: self.logger.exception(str(e))
+ if self.__logging: self.__logger.exception(str(e))
raise ValueError('[CamGear:ERROR] :: YouTube Mode is enabled and the input YouTube URL is invalid!')
# youtube mode variable initialization
- self.youtube_mode = y_tube
+ self.__youtube_mode = y_tube
#User-Defined Threaded Queue Mode
if options:
if "THREADED_QUEUE_MODE" in options:
if isinstance(options["THREADED_QUEUE_MODE"],bool):
- self.threaded_queue_mode = options["THREADED_QUEUE_MODE"] #assigsn special parameter to global variable
+ self.__threaded_queue_mode = options["THREADED_QUEUE_MODE"] #assigsn special parameter to global variable
del options["THREADED_QUEUE_MODE"] #clean
#reformat option dict
- self.queue = None
+ self.__queue = None
#intialize deque for video files only
- if self.threaded_queue_mode and isinstance(source,str):
+ if self.__threaded_queue_mode and isinstance(source,str):
#import deque
from collections import deque
#define deque and assign it to global var
- self.queue = deque(maxlen=96) #max len 96 to check overflow
+ self.__queue = deque(maxlen=96) #max len 96 to check overflow
#log it
- if self.logging: self.logger.debug('Enabling Threaded Queue Mode for the current video source!')
+ if self.__logging: self.__logger.debug('Enabling Threaded Queue Mode for the current video source!')
else:
#otherwise disable it
- self.threaded_queue_mode = False
+ self.__threaded_queue_mode = False
#log it
- if self.logging: self.logger.debug('Threaded Queue Mode is disabled for the current video source!')
+ if self.__logging: self.__logger.debug('Threaded Queue Mode is disabled for the current video source!')
# stream variable initialization
self.stream = None
@@ -192,11 +191,11 @@ def __init__(self, source = 0, y_tube = False, backend = 0, colorspace = None, l
# separately handle colorspace value to int conversion
if not(colorspace is None):
self.color_space = capPropId(colorspace.strip())
- if self.logging: self.logger.debug('Enabling `{}` colorspace for this video stream!'.format(colorspace.strip()))
+ if self.__logging: self.__logger.debug('Enabling `{}` colorspace for this video stream!'.format(colorspace.strip()))
except Exception as e:
# Catch if any error occurred
- if self.logging: self.logger.exception(str(e))
+ if self.__logging: self.__logger.exception(str(e))
#initialize and assign framerate variable
self.framerate = 0.0
@@ -204,7 +203,7 @@ def __init__(self, source = 0, y_tube = False, backend = 0, colorspace = None, l
_fps = self.stream.get(cv2.CAP_PROP_FPS)
if _fps>1: self.framerate = _fps
except Exception as e:
- if self.logging: self.logger.exception(str(e))
+ if self.__logging: self.__logger.exception(str(e))
self.framerate = 0.0
# applying time delay to warm-up webcam only if specified
@@ -218,17 +217,17 @@ def __init__(self, source = 0, y_tube = False, backend = 0, colorspace = None, l
#render colorspace if defined
if not(self.color_space is None): self.frame = cv2.cvtColor(self.frame, self.color_space)
- if self.threaded_queue_mode:
+ if self.__threaded_queue_mode:
#intitialize and append to queue
- self.queue.append(self.frame)
+ self.__queue.append(self.frame)
else:
raise RuntimeError('[CamGear:ERROR] :: Source is invalid, CamGear failed to intitialize stream on this source!')
# thread initialization
- self.thread=None
+ self.__thread=None
# initialize termination flag
- self.terminate = False
+ self.__terminate = False
@@ -236,14 +235,14 @@ def start(self):
"""
start the thread to read frames from the video stream
"""
- self.thread = Thread(target=self.update, name='CamGear', args=())
- self.thread.daemon = True
- self.thread.start()
+ self.__thread = Thread(target=self.__update, name='CamGear', args=())
+ self.__thread.daemon = True
+ self.__thread.start()
return self
- def update(self):
+ def __update(self):
"""
Update frames from stream
"""
@@ -251,12 +250,12 @@ def update(self):
# keep iterating infinitely until the thread is terminated or frames runs out
while True:
# if the thread indicator variable is set, stop the thread
- if self.terminate:
+ if self.__terminate:
break
- if self.threaded_queue_mode:
+ if self.__threaded_queue_mode:
#check queue buffer for overflow
- if len(self.queue) >= 96:
+ if len(self.__queue) >= 96:
#stop iterating if overflowing occurs
time.sleep(0.000001)
continue
@@ -267,8 +266,8 @@ def update(self):
#check for valid frames
if not grabbed:
#no frames received, then safely exit
- if self.threaded_queue_mode:
- if len(self.queue) == 0:
+ if self.__threaded_queue_mode:
+ if len(self.__queue) == 0:
break
else:
continue
@@ -283,14 +282,13 @@ def update(self):
color_frame = cv2.cvtColor(frame, self.color_space)
else:
self.color_space = None
- if self.logging: self.logger.debug('Colorspace value {} is not a valid Colorspace!'.format(self.color_space))
+ if self.__logging: self.__logger.warning('Colorspace value: {}, is not a valid colorspace!'.format(self.color_space))
except Exception as e:
# Catch if any error occurred
self.color_space = None
- if self.logging:
- self.logger.exception(str(e))
- self.logger.debug('Input Colorspace is not a valid Colorspace!')
-
+ if self.__logging:
+ self.__logger.exception(str(e))
+ self.__logger.warning('Input colorspace is not a valid colorspace!')
if not(color_frame is None):
self.frame = color_frame
else:
@@ -299,9 +297,9 @@ def update(self):
self.frame = frame
#append to queue
- if self.threaded_queue_mode: self.queue.append(self.frame)
+ if self.__threaded_queue_mode: self.__queue.append(self.frame)
- self.threaded_queue_mode = False
+ self.__threaded_queue_mode = False
self.frame = None
#release resources
self.stream.release()
@@ -312,9 +310,9 @@ def read(self):
"""
return the frame
"""
- while self.threaded_queue_mode:
- if len(self.queue) > 0:
- return self.queue.popleft()
+ while self.__threaded_queue_mode:
+ if len(self.__queue) > 0:
+ return self.__queue.popleft()
return self.frame
@@ -323,20 +321,20 @@ def stop(self):
"""
Terminates the Read process
"""
- if self.logging: self.logger.debug('Terminating processes.')
+ if self.__logging: self.__logger.debug('Terminating processes.')
#terminate Threaded queue mode seperately
- if self.threaded_queue_mode and not(self.queue is None):
- if len(self.queue)>0: self.queue.clear()
- self.threaded_queue_mode = False
+ if self.__threaded_queue_mode and not(self.__queue is None):
+ if len(self.__queue)>0: self.__queue.clear()
+ self.__threaded_queue_mode = False
self.frame = None
# indicate that the thread should be terminate
- self.terminate = True
+ self.__terminate = True
# wait until stream resources are released (producer thread might be still grabbing frame)
- if self.thread is not None:
- self.thread.join()
+ if self.__thread is not None:
+ self.__thread.join()
#properly handle thread exit
- if self.youtube_mode:
+ if self.__youtube_mode:
# kill thread-lock in youtube mode
- self.thread = None
\ No newline at end of file
+ self.__thread = None
\ No newline at end of file
diff --git a/vidgear/gears/helper.py b/vidgear/gears/helper.py
index 4c4178b17..1bf56fecb 100644
--- a/vidgear/gears/helper.py
+++ b/vidgear/gears/helper.py
@@ -124,8 +124,7 @@ def get_valid_ffmpeg_path(custom_ffmpeg = '', is_windows = False, ffmpeg_downloa
final_path = os.path.join(custom_ffmpeg, 'ffmpeg')
else:
#else return False
- if logging:
- logger.debug('No valid FFmpeg executables found at Custom FFmpeg path!')
+ if logging: logger.debug('No valid FFmpeg executables found at Custom FFmpeg path!')
return False
else:
#otherwise assign ffmpeg binaries from system
@@ -162,7 +161,7 @@ def download_ffmpeg_binaries(path, os_windows = False):
import requests
import zipfile
#check if given pth has write access
- assert os.access(path, os.W_OK), "Permission Denied: Cannot write ffmpeg binaries to directory = " + path
+ assert os.access(path, os.W_OK), "[Helper:ERROR] :: Permission Denied, Cannot write ffmpeg binaries to directory = " + path
#remove leftovers
if os.path.isfile(file_name):
os.remove(file_name)
diff --git a/vidgear/gears/netgear.py b/vidgear/gears/netgear.py
index b72f415da..6400f27ce 100644
--- a/vidgear/gears/netgear.py
+++ b/vidgear/gears/netgear.py
@@ -35,9 +35,9 @@
import cv2
# check whether OpenCV Binaries are 3.x+
if parse_version(cv2.__version__) < parse_version('3'):
- raise ImportError('[NetGear:ERROR] :: OpenCV library version >= 3.0 is only supported by this library')
+ raise ImportError('[NetGear:ERROR] :: OpenCV API version >= 3.0 is only supported by this library.')
except ImportError as error:
- raise ImportError('[NetGear:ERROR] :: Failed to detect OpenCV executables, install it with `pip3 install opencv-python` command.')
+ raise ImportError('[NetGear:ERROR] :: Failed to detect correct OpenCV executables, install it with `pip3 install opencv-python` command.')
@@ -67,12 +67,11 @@ class NetGear:
Stonehouse is the minimum you would use over public networks and assures clients that they are speaking to an authentic server while allowing any client
to connect. It is less secure but at the same time faster than IronHouse security mechanism.
- * `IronHouse`: 2 => which extends Stonehouse with client public key authentication. This is the strongest security model ZMQ have today,
+ * `IronHouse`: 2 => which extends Stonehouse with client public key authentication. This is the strongest security model ZMQ have today,
protecting against every attack we know about, except end-point attacks (where an attacker plants spyware on a machine
to capture data before it's encrypted, or after it's decrypted). IronHouse enhanced security comes at a price of additional latency.
-
:param address(string): sets the valid network address of the server/client. Network addresses unique identifiers across the
network. Its default value of this parameter is based on mode it is working, 'localhost' for Send Mode
and `*` for Receive Mode.
@@ -111,7 +110,7 @@ class NetGear:
This attribute provides the flexibility to manipulate ZeroMQ internal parameters
directly. Checkout vidgear docs for usage details.
- :param (boolean) self.logging: set this flag to enable/disable error logging essential for debugging. Its default value is False.
+ :param (boolean) logging: set this flag to enable/disable error logging essential for debugging. Its default value is False.
"""
@@ -123,16 +122,16 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
#import ZMQError
from zmq.error import ZMQError
#assign values to global variable for further use
- self.zmq = zmq
- self.ZMQError = ZMQError
+ self.__zmq = zmq
+ self.__ZMQError = ZMQError
except ImportError as error:
#raise error
raise ImportError('[NetGear:ERROR] :: pyzmq python library not installed. Kindly install it with `pip install pyzmq` command.')
# enable logging if specified
- self.logging = False
- self.logger = log.getLogger('NetGear')
- if logging: self.logging = logging
+ self.__logging = False
+ self.__logger = log.getLogger('NetGear')
+ if logging: self.__logging = logging
#define valid messaging patterns => `0`: zmq.PAIR, `1`:(zmq.REQ,zmq.REP), and `1`:(zmq.SUB,zmq.PUB)
valid_messaging_patterns = {0:(zmq.PAIR,zmq.PAIR), 1:(zmq.REQ,zmq.REP), 2:(zmq.PUB,zmq.SUB)}
@@ -143,50 +142,50 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
if isinstance(pattern, int) and pattern in valid_messaging_patterns:
#assign value
msg_pattern = valid_messaging_patterns[pattern]
- self.pattern = pattern #add it to global variable for further use
+ self.__pattern = pattern #add it to global variable for further use
else:
#otherwise default to 0:`zmq.PAIR`
- self.pattern = 0
- msg_pattern = valid_messaging_patterns[self.pattern]
+ self.__pattern = 0
+ msg_pattern = valid_messaging_patterns[self.__pattern]
#log it
- if self.logging: self.logger.debug('Wrong pattern value, Defaulting to `zmq.PAIR`! Kindly refer Docs for more Information.')
+ if self.__logging: self.__logger.warning('Wrong pattern value, Defaulting to `zmq.PAIR`! Kindly refer Docs for more Information.')
#check whether user-defined messaging protocol is valid
if not(protocol in ['tcp', 'udp', 'pgm', 'epgm', 'inproc', 'ipc']):
# else default to `tcp` protocol
protocol = 'tcp'
#log it
- if self.logging: self.logger.debug('protocol is not valid or provided. Defaulting to `tcp` protocol!')
+ if self.__logging: self.__logger.warning('protocol is not valid or provided. Defaulting to `tcp` protocol!')
#generate random device id
- self.id = ''.join(random.choice('0123456789ABCDEF') for i in range(5))
+ self.__id = ''.join(random.choice('0123456789ABCDEF') for i in range(5))
- self.msg_flag = 0 #handles connection flags
- self.msg_copy = True #handles whether to copy data
- self.msg_track = False #handles whether to track packets
+ self.__msg_flag = 0 #handles connection flags
+ self.__msg_copy = True #handles whether to copy data
+ self.__msg_track = False #handles whether to track packets
- self.multiserver_mode = False #handles multiserver_mode state
+ self.__multiserver_mode = False #handles multiserver_mode state
recv_filter = '' #user-defined filter to allow specific port/servers only in multiserver_mode
#define bi-directional data transmission mode
- self.bi_mode = False #handles bi_mode state
- self.bi_data = None #handles return data
+ self.__bi_mode = False #handles bi_mode state
+ self.__bi_data = None #handles return data
#define valid ZMQ security mechanisms => `0`: Grasslands, `1`:StoneHouse, and `1`:IronHouse
valid_security_mech = {0:'Grasslands', 1:'StoneHouse', 2:'IronHouse'}
- self.secure_mode = 0 #handles ZMQ security layer status
+ self.__secure_mode = 0 #handles ZMQ security layer status
auth_cert_dir = '' #handles valid ZMQ certificates dir
- self.auth_publickeys_dir = '' #handles valid ZMQ public certificates dir
- self.auth_secretkeys_dir = '' #handles valid ZMQ private certificates dir
+ self.__auth_publickeys_dir = '' #handles valid ZMQ public certificates dir
+ self.__auth_secretkeys_dir = '' #handles valid ZMQ private certificates dir
overwrite_cert = False #checks if certificates overwriting allowed
custom_cert_location = '' #handles custom ZMQ certificates path
#handle force socket termination if there's latency in network
- self.force_close = False
+ self.__force_close = False
#define stream compression handlers
- self.compression = '' #disabled by default
- self.compression_params = None
+ self.__compression = '' #disabled by default
+ self.__compression_params = None
#reformat dict
options = {k.lower().strip(): v for k,v in options.items()}
@@ -196,10 +195,10 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
if key == 'multiserver_mode' and isinstance(value, bool):
if pattern > 0:
# multi-server mode
- self.multiserver_mode = value
+ self.__multiserver_mode = value
else:
- self.multiserver_mode = False
- self.logger.critical('Multi-Server is disabled!')
+ self.__multiserver_mode = False
+ self.__logger.critical('Multi-Server is disabled!')
raise ValueError('[NetGear:ERROR] :: `{}` pattern is not valid when Multi-Server Mode is enabled. Kindly refer Docs for more Information.'.format(pattern))
elif key == 'filter' and isinstance(value, str):
#custom filter in multi-server mode
@@ -209,9 +208,9 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
#secure mode
try:
assert zmq.zmq_version_info() >= (4,0), "[NetGear:ERROR] :: ZMQ Security feature is not supported in libzmq version < 4.0."
- self.secure_mode = value
+ self.__secure_mode = value
except Exception as e:
- self.logger.exception(str(e))
+ self.__logger.exception(str(e))
elif key == 'custom_cert_location' and isinstance(value,str):
# custom auth certificates path
try:
@@ -219,7 +218,7 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
assert not(os.path.isfile(value)), "[NetGear:ERROR] :: `custom_cert_location` value must be the path to a directory and not to a file!"
custom_cert_location = os.path.abspath(value)
except Exception as e:
- self.logger.exception(str(e))
+ self.__logger.exception(str(e))
elif key == 'overwrite_cert' and isinstance(value,bool):
# enable/disable auth certificate overwriting
overwrite_cert = value
@@ -227,51 +226,51 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
# handle encoding and decoding if specified
elif key == 'compression_format' and isinstance(value,str) and value.lower().strip() in ['.jpg', '.jpeg', '.bmp', '.png']: #few are supported
# enable encoding
- if not(receive_mode): self.compression = value.lower().strip()
+ if not(receive_mode): self.__compression = value.lower().strip()
elif key == 'compression_param':
# specify encoding/decoding params
if receive_mode and isinstance(value, int):
- self.compression_params = value
- if self.logging: self.logger.debug("Decoding flag: {}.".format(value))
+ self.__compression_params = value
+ if self.__logging: self.__logger.debug("Decoding flag: {}.".format(value))
elif not(receive_mode) and isinstance(value, (list,tuple)):
- if self.logging: self.logger.debug("Encoding parameters: {}.".format(value))
- self.compression_params = list(value)
+ if self.__logging: self.__logger.debug("Encoding parameters: {}.".format(value))
+ self.__compression_params = list(value)
else:
- if self.logging: self.logger.warning("Invalid compression parameters: {} skipped!".format(value))
- self.compression_params = cv2.IMREAD_COLOR if receive_mode else [] # skip to defaults
+ if self.__logging: self.__logger.warning("Invalid compression parameters: {} skipped!".format(value))
+ self.__compression_params = cv2.IMREAD_COLOR if receive_mode else [] # skip to defaults
# enable bi-directional data transmission if specified
elif key == 'bidirectional_mode' and isinstance(value, bool):
# check if pattern is valid
if pattern < 2:
- self.bi_mode = True
+ self.__bi_mode = True
else:
- self.bi_mode = False
- self.logger.critical('Bi-Directional data transmission is disabled!')
+ self.__bi_mode = False
+ self.__logger.critical('Bi-Directional data transmission is disabled!')
raise ValueError('[NetGear:ERROR] :: `{}` pattern is not valid when Bi-Directional Mode is enabled. Kindly refer Docs for more Information.'.format(pattern))
# enable force socket closing if specified
elif key == 'force_terminate' and isinstance(value, bool):
# check if pattern is valid
if address is None and not(receive_mode):
- self.force_close = False
- self.logger.critical('Force termination is disabled for local servers!')
+ self.__force_close = False
+ self.__logger.critical('Force termination is disabled for local servers!')
else:
- self.force_close = True
- if self.logging: self.logger.debug("Force termination is enabled for this connection!")
+ self.__force_close = True
+ if self.__logging: self.__logger.warning("Force termination is enabled for this connection!")
# various ZMQ flags
elif key == 'flag' and isinstance(value, int):
- self.msg_flag = value
+ self.__msg_flag = value
elif key == 'copy' and isinstance(value, bool):
- self.msg_copy = value
+ self.__msg_copy = value
elif key == 'track' and isinstance(value, bool):
- self.msg_track = value
+ self.__msg_track = value
else:
pass
#handle secure mode
- if self.secure_mode:
+ if self.__secure_mode:
#import important libs
import zmq.auth
from zmq.auth.thread import ThreadAuthenticator
@@ -279,60 +278,60 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
# log if overwriting is enabled
if overwrite_cert:
if not receive_mode:
- if self.logging: self.logger.warning('Overwriting ZMQ Authentication certificates over previous ones!')
+ if self.__logging: self.__logger.warning('Overwriting ZMQ Authentication certificates over previous ones!')
else:
overwrite_cert = False
- if self.logging: self.logger.critical('Overwriting ZMQ Authentication certificates is disabled for Client-end!')
+ if self.__logging: self.__logger.critical('Overwriting ZMQ Authentication certificates is disabled for Client-end!')
#generate and validate certificates path
try:
#check if custom certificates path is specified
if custom_cert_location:
if os.path.isdir(custom_cert_location): #custom certificate location must be a directory
- (auth_cert_dir, self.auth_secretkeys_dir, self.auth_publickeys_dir) = generate_auth_certificates(custom_cert_location, overwrite = overwrite_cert)
+ (auth_cert_dir, self.__auth_secretkeys_dir, self.__auth_publickeys_dir) = generate_auth_certificates(custom_cert_location, overwrite = overwrite_cert)
else:
raise ValueError("[NetGear:ERROR] :: Invalid `custom_cert_location` value!")
else:
# otherwise auto-generate suitable path
from os.path import expanduser
- (auth_cert_dir, self.auth_secretkeys_dir, self.auth_publickeys_dir) = generate_auth_certificates(os.path.join(expanduser("~"),".vidgear"), overwrite = overwrite_cert)
+ (auth_cert_dir, self.__auth_secretkeys_dir, self.__auth_publickeys_dir) = generate_auth_certificates(os.path.join(expanduser("~"),".vidgear"), overwrite = overwrite_cert)
#log it
- if self.logging: self.logger.debug('`{}` is the default location for storing ZMQ authentication certificates/keys.'.format(auth_cert_dir))
+ if self.__logging: self.__logger.debug('`{}` is the default location for storing ZMQ authentication certificates/keys.'.format(auth_cert_dir))
except Exception as e:
# catch if any error occurred
- self.logger.exception(str(e))
+ self.__logger.exception(str(e))
# also disable secure mode
- self.secure_mode = 0
- self.logger.warning('ZMQ Security Mechanism is disabled for this connection!')
+ self.__secure_mode = 0
+ self.__logger.warning('ZMQ Security Mechanism is disabled for this connection!')
else:
#log if disabled
- if self.logging: self.logger.debug('ZMQ Security Mechanism is disabled for this connection!')
+ if self.__logging: self.__logger.warning('ZMQ Security Mechanism is disabled for this connection!')
#handle bi_mode
- if self.bi_mode:
+ if self.__bi_mode:
#disable bi_mode if multi-server is enabled
- if self.multiserver_mode:
- self.bi_mode = False
- self.logger.critical('Bi-Directional Data Transmission is disabled when Multi-Server Mode is Enabled due to incompatibility!')
+ if self.__multiserver_mode:
+ self.__bi_mode = False
+ self.__logger.critical('Bi-Directional Data Transmission is disabled when Multi-Server Mode is Enabled due to incompatibility!')
else:
#enable force termination by default
- self.force_close = True
- if self.logging:
- self.logger.debug("Force termination is enabled for this connection by default!")
- self.logger.debug('Bi-Directional Data Transmission is enabled for this connection!')
+ self.__force_close = True
+ if self.__logging:
+ self.__logger.warning("Force termination is enabled for this connection by default!")
+ self.__logger.debug('Bi-Directional Data Transmission is enabled for this connection!')
# initialize termination flag
- self.terminate = False
+ self.__terminate = False
# initialize exit_loop flag
- self.exit_loop = False
+ self.__exit_loop = False
# initialize and assign receive mode to global variable
- self.receive_mode = receive_mode
+ self.__receive_mode = receive_mode
# define messaging context instance
- self.msg_context = zmq.Context.instance()
+ self.__msg_context = zmq.Context.instance()
#check whether `receive_mode` is enabled by user
if receive_mode:
@@ -341,123 +340,123 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
if address is None: address = '*' #define address
#check if multiserver_mode is enabled
- if self.multiserver_mode:
+ if self.__multiserver_mode:
# check if unique server port address list/tuple is assigned or not in multiserver_mode
if port is None or not isinstance(port, (tuple, list)):
# raise error if not
raise ValueError('[NetGear:ERROR] :: Incorrect port value! Kindly provide a list/tuple of ports while Multi-Server mode is enabled. For more information refer VidGear docs.')
else:
#otherwise log it
- self.logger.debug('Enabling Multi-Server Mode at PORTS: {}!'.format(port))
+ self.__logger.debug('Enabling Multi-Server Mode at PORTS: {}!'.format(port))
#create port address buffer for keeping track of incoming server's port
- self.port_buffer = []
+ self.__port_buffer = []
else:
# otherwise assign local port address if None
if port is None: port = '5555'
try:
# initiate and handle secure mode
- if self.secure_mode > 0:
+ if self.__secure_mode > 0:
# start an authenticator for this context
- auth = ThreadAuthenticator(self.msg_context)
+ auth = ThreadAuthenticator(self.__msg_context)
auth.start()
auth.allow(str(address)) #allow current address
#check if `IronHouse` is activated
- if self.secure_mode == 2:
+ if self.__secure_mode == 2:
# tell authenticator to use the certificate from given valid dir
- auth.configure_curve(domain='*', location=self.auth_publickeys_dir)
+ auth.configure_curve(domain='*', location=self.__auth_publickeys_dir)
else:
#otherwise tell the authenticator how to handle the CURVE requests, if `StoneHouse` is activated
auth.configure_curve(domain='*', location=zmq.auth.CURVE_ALLOW_ANY)
# initialize and define thread-safe messaging socket
- self.msg_socket = self.msg_context.socket(msg_pattern[1])
- if self.pattern == 2 and not(self.secure_mode): self.msg_socket.set_hwm(1)
+ self.__msg_socket = self.__msg_context.socket(msg_pattern[1])
+ if self.__pattern == 2 and not(self.__secure_mode): self.__msg_socket.set_hwm(1)
- if self.multiserver_mode:
+ if self.__multiserver_mode:
# if multiserver_mode is enabled, then assign port addresses to zmq socket
for pt in port:
# enable specified secure mode for the zmq socket
- if self.secure_mode > 0:
+ if self.__secure_mode > 0:
# load server key
- server_secret_file = os.path.join(self.auth_secretkeys_dir, "server.key_secret")
+ server_secret_file = os.path.join(self.__auth_secretkeys_dir, "server.key_secret")
server_public, server_secret = zmq.auth.load_certificate(server_secret_file)
# load all CURVE keys
- self.msg_socket.curve_secretkey = server_secret
- self.msg_socket.curve_publickey = server_public
+ self.__msg_socket.curve_secretkey = server_secret
+ self.__msg_socket.curve_publickey = server_public
# enable CURVE connection for this socket
- self.msg_socket.curve_server = True
+ self.__msg_socket.curve_server = True
# define socket options
- if self.pattern == 2: self.msg_socket.setsockopt_string(zmq.SUBSCRIBE, recv_filter)
+ if self.__pattern == 2: self.__msg_socket.setsockopt_string(zmq.SUBSCRIBE, recv_filter)
# bind socket to given server protocol, address and ports
- self.msg_socket.bind(protocol+'://' + str(address) + ':' + str(pt))
+ self.__msg_socket.bind(protocol+'://' + str(address) + ':' + str(pt))
# define socket optimizer
- self.msg_socket.setsockopt(zmq.LINGER, 0)
+ self.__msg_socket.setsockopt(zmq.LINGER, 0)
else:
# enable specified secure mode for the zmq socket
- if self.secure_mode > 0:
+ if self.__secure_mode > 0:
# load server key
- server_secret_file = os.path.join(self.auth_secretkeys_dir, "server.key_secret")
+ server_secret_file = os.path.join(self.__auth_secretkeys_dir, "server.key_secret")
server_public, server_secret = zmq.auth.load_certificate(server_secret_file)
# load all CURVE keys
- self.msg_socket.curve_secretkey = server_secret
- self.msg_socket.curve_publickey = server_public
+ self.__msg_socket.curve_secretkey = server_secret
+ self.__msg_socket.curve_publickey = server_public
# enable CURVE connection for this socket
- self.msg_socket.curve_server = True
+ self.__msg_socket.curve_server = True
# define exclusive socket options for patterns
- if self.pattern == 2: self.msg_socket.setsockopt_string(zmq.SUBSCRIBE,'')
+ if self.__pattern == 2: self.__msg_socket.setsockopt_string(zmq.SUBSCRIBE,'')
# bind socket to given protocol, address and port normally
- self.msg_socket.bind(protocol+'://' + str(address) + ':' + str(port))
+ self.__msg_socket.bind(protocol+'://' + str(address) + ':' + str(port))
# define socket optimizer
- self.msg_socket.setsockopt(zmq.LINGER, 0)
+ self.__msg_socket.setsockopt(zmq.LINGER, 0)
except Exception as e:
- self.logger.exception(str(e))
+ self.__logger.exception(str(e))
# otherwise raise value error if errored
- if self.secure_mode: self.logger.warning('Failed to activate ZMQ Security Mechanism: `{}` for this address!'.format(valid_security_mech[self.secure_mode]))
- if self.multiserver_mode:
+ if self.__secure_mode: self.__logger.warning('Failed to activate ZMQ Security Mechanism: `{}` for this address!'.format(valid_security_mech[self.__secure_mode]))
+ if self.__multiserver_mode:
raise ValueError('[NetGear:ERROR] :: Multi-Server Mode, failed to connect to ports: {} with pattern: {}! Kindly recheck all parameters.'.format( str(port), pattern))
else:
raise ValueError('[NetGear:ERROR] :: Failed to bind address: {} and pattern: {}! Kindly recheck all parameters.'.format((protocol+'://' + str(address) + ':' + str(port)), pattern))
#log and enable threaded queue mode
- if self.logging: self.logger.debug('Threaded Queue Mode is enabled by default for NetGear.')
+ if self.__logging: self.__logger.debug('Threaded Queue Mode is enabled by default for NetGear.')
#define deque and assign it to global var
- self.queue = deque(maxlen=96) #max len 96 to check overflow
+ self.__queue = deque(maxlen=96) #max len 96 to check overflow
# initialize and start threading instance
- self.thread = Thread(target=self.update, name='NetGear', args=())
- self.thread.daemon = True
- self.thread.start()
+ self.__thread = Thread(target=self.__update, name='NetGear', args=())
+ self.__thread.daemon = True
+ self.__thread.start()
- if self.logging:
+ if self.__logging:
#finally log progress
- self.logger.debug('Successfully Binded to address: {} with pattern: {}.'.format((protocol+'://' + str(address) + ':' + str(port)), pattern))
- if self.secure_mode: self.logger.debug('Enabled ZMQ Security Mechanism: `{}` for this address, Successfully!'.format(valid_security_mech[self.secure_mode]))
- self.logger.debug('Multi-threaded Receive Mode is enabled Successfully!')
- self.logger.debug('Device Unique ID is {}.'.format(self.id))
- self.logger.debug('Receive Mode is activated successfully!')
+ self.__logger.debug('Successfully Binded to address: {} with pattern: {}.'.format((protocol+'://' + str(address) + ':' + str(port)), pattern))
+ if self.__secure_mode: self.__logger.debug('Enabled ZMQ Security Mechanism: `{}` for this address, Successfully!'.format(valid_security_mech[self.__secure_mode]))
+ self.__logger.debug('Multi-threaded Receive Mode is enabled Successfully!')
+ self.__logger.debug('Device Unique ID is {}.'.format(self.__id))
+ self.__logger.debug('Receive Mode is activated successfully!')
else:
#otherwise default to `Send Mode`
if address is None: address = 'localhost'#define address
#check if multiserver_mode is enabled
- if self.multiserver_mode:
+ if self.__multiserver_mode:
# check if unique server port address is assigned or not in multiserver_mode
if port is None:
#raise error is not
raise ValueError('[NetGear:ERROR] :: Kindly provide a unique & valid port value at Server-end. For more information refer VidGear docs.')
else:
#otherwise log it
- self.logger.debug('Enabling Multi-Server Mode at PORT: {} on this device!'.format(port))
+ self.__logger.debug('Enabling Multi-Server Mode at PORT: {} on this device!'.format(port))
#assign value to global variable
self.port = port
else:
@@ -466,66 +465,66 @@ def __init__(self, address = None, port = None, protocol = None, pattern = 0, r
try:
# initiate and handle secure mode
- if self.secure_mode > 0:
+ if self.__secure_mode > 0:
# start an authenticator for this context
- auth = ThreadAuthenticator(self.msg_context)
+ auth = ThreadAuthenticator(self.__msg_context)
auth.start()
auth.allow(str(address)) #allow current address
#check if `IronHouse` is activated
- if self.secure_mode == 2:
+ if self.__secure_mode == 2:
# tell authenticator to use the certificate from given valid dir
- auth.configure_curve(domain='*', location=self.auth_publickeys_dir)
+ auth.configure_curve(domain='*', location=self.__auth_publickeys_dir)
else:
#otherwise tell the authenticator how to handle the CURVE requests, if `StoneHouse` is activated
auth.configure_curve(domain='*', location=zmq.auth.CURVE_ALLOW_ANY)
# initialize and define thread-safe messaging socket
- self.msg_socket = self.msg_context.socket(msg_pattern[0])
+ self.__msg_socket = self.__msg_context.socket(msg_pattern[0])
- if self.pattern == 1:
+ if self.__pattern == 1:
# if pattern is 1, define additional flags
- self.msg_socket.REQ_RELAXED = True
- self.msg_socket.REQ_CORRELATE = True
- if self.pattern == 2 and not(self.secure_mode): self.msg_socket.set_hwm(1) # if pattern is 2, define additional optimizer
+ self.__msg_socket.REQ_RELAXED = True
+ self.__msg_socket.REQ_CORRELATE = True
+ if self.__pattern == 2 and not(self.__secure_mode): self.__msg_socket.set_hwm(1) # if pattern is 2, define additional optimizer
# enable specified secure mode for the zmq socket
- if self.secure_mode > 0:
+ if self.__secure_mode > 0:
# load client key
- client_secret_file = os.path.join(self.auth_secretkeys_dir, "client.key_secret")
+ client_secret_file = os.path.join(self.__auth_secretkeys_dir, "client.key_secret")
client_public, client_secret = zmq.auth.load_certificate(client_secret_file)
# load all CURVE keys
- self.msg_socket.curve_secretkey = client_secret
- self.msg_socket.curve_publickey = client_public
+ self.__msg_socket.curve_secretkey = client_secret
+ self.__msg_socket.curve_publickey = client_public
# load server key
- server_public_file = os.path.join(self.auth_publickeys_dir, "server.key")
+ server_public_file = os.path.join(self.__auth_publickeys_dir, "server.key")
server_public, _ = zmq.auth.load_certificate(server_public_file)
# inject public key to make a CURVE connection.
- self.msg_socket.curve_serverkey = server_public
+ self.__msg_socket.curve_serverkey = server_public
# connect socket to given protocol, address and port
- self.msg_socket.connect(protocol+'://' + str(address) + ':' + str(port))
+ self.__msg_socket.connect(protocol+'://' + str(address) + ':' + str(port))
# define socket options
- self.msg_socket.setsockopt(zmq.LINGER, 0)
+ self.__msg_socket.setsockopt(zmq.LINGER, 0)
except Exception as e:
- self.logger.exception(str(e))
+ self.__logger.exception(str(e))
#log if errored
- if self.secure_mode: self.logger.warning('Failed to activate ZMQ Security Mechanism: `{}` for this address!'.format(valid_security_mech[self.secure_mode]))
+ if self.__secure_mode: self.__logger.warning('Failed to activate ZMQ Security Mechanism: `{}` for this address!'.format(valid_security_mech[self.__secure_mode]))
# raise value error
raise ValueError('[NetGear:ERROR] :: Failed to connect address: {} and pattern: {}! Kindly recheck all parameters.'.format((protocol+'://' + str(address) + ':' + str(port)), pattern))
- if self.logging:
+ if self.__logging:
#finally log progress
- self.logger.debug('Successfully connected to address: {} with pattern: {}.'.format((protocol+'://' + str(address) + ':' + str(port)), pattern))
- if self.secure_mode: self.logger.debug('Enabled ZMQ Security Mechanism: `{}` for this address, Successfully!'.format(valid_security_mech[self.secure_mode]))
- self.logger.debug('This device Unique ID is {}.'.format(self.id))
- self.logger.debug('Send Mode is successfully activated and ready to send data!')
+ self.__logger.debug('Successfully connected to address: {} with pattern: {}.'.format((protocol+'://' + str(address) + ':' + str(port)), pattern))
+ if self.__secure_mode: self.__logger.debug('Enabled ZMQ Security Mechanism: `{}` for this address, Successfully!'.format(valid_security_mech[self.__secure_mode]))
+ self.__logger.debug('This device Unique ID is {}.'.format(self.__id))
+ self.__logger.debug('Send Mode is successfully activated and ready to send data!')
- def update(self):
+ def __update(self):
"""
Updates recovered frames from messaging network to the queue
"""
@@ -533,67 +532,67 @@ def update(self):
frame = None
# keep looping infinitely until the thread is terminated
- while not self.exit_loop:
+ while not self.__exit_loop:
# check if global termination_flag is enabled
- if self.terminate:
+ if self.__terminate:
# check whether there is still frames in queue before breaking out
- if len(self.queue)>0:
+ if len(self.__queue)>0:
continue
else:
break
#check queue buffer for overflow
- if len(self.queue) >= 96:
+ if len(self.__queue) >= 96:
#stop iterating if overflowing occurs
time.sleep(0.000001)
continue
# extract json data out of socket
- msg_json = self.msg_socket.recv_json(flags=self.msg_flag)
+ msg_json = self.__msg_socket.recv_json(flags=self.__msg_flag)
# check if terminate_flag` received
if msg_json['terminate_flag']:
#if multiserver_mode is enabled
- if self.multiserver_mode:
+ if self.__multiserver_mode:
# check and remove from which ports signal is received
- if msg_json['port'] in self.port_buffer:
+ if msg_json['port'] in self.__port_buffer:
# if pattern is 1, then send back server the info about termination
- if self.pattern == 1: self.msg_socket.send_string('Termination signal received at client!')
- self.port_buffer.remove(msg_json['port'])
- if self.logging: self.logger.warning('Termination signal received from server at port: {}!'.format(msg_json['port']))
+ if self.__pattern == 1: self.__msg_socket.send_string('Termination signal received at client!')
+ self.__port_buffer.remove(msg_json['port'])
+ if self.__logging: self.__logger.warning('Termination signal received from server at port: {}!'.format(msg_json['port']))
#if termination signal received from all servers then exit client.
- if not self.port_buffer:
- self.logger.warning('Termination signal received from all Servers!!!')
- self.terminate = True #termination
+ if not self.__port_buffer:
+ self.__logger.warning('Termination signal received from all Servers!!!')
+ self.__terminate = True #termination
continue
else:
# if pattern is 1, then send back server the info about termination
- if self.pattern == 1: self.msg_socket.send_string('Termination signal received at client!')
+ if self.__pattern == 1: self.__msg_socket.send_string('Termination signal received at client!')
#termination
- self.terminate = True
+ self.__terminate = True
#notify client
- if self.logging: self.logger.warning('Termination signal received from server!')
+ if self.__logging: self.__logger.warning('Termination signal received from server!')
continue
#check if pattern is same at both server's and client's end.
- if int(msg_json['pattern']) != self.pattern:
+ if int(msg_json['pattern']) != self.__pattern:
raise ValueError("[NetGear:ERROR] :: Messaging patterns on both Server-end & Client-end must a valid pairs! Kindly refer VidGear docs.")
- self.terminate = True
+ self.__terminate = True
continue
# extract array from socket
- msg_data = self.msg_socket.recv(flags=self.msg_flag, copy=self.msg_copy, track=self.msg_track)
+ msg_data = self.__msg_socket.recv(flags=self.__msg_flag, copy=self.__msg_copy, track=self.__msg_track)
- if self.pattern != 2:
+ if self.__pattern != 2:
# check if bi-directional mode is enabled
- if self.bi_mode:
+ if self.__bi_mode:
# handle return data
- bi_dict = dict(data = self.bi_data)
- self.msg_socket.send_json(bi_dict, self.msg_flag)
+ bi_dict = dict(data = self.__bi_data)
+ self.__msg_socket.send_json(bi_dict, self.__msg_flag)
else:
# send confirmation message to server
- self.msg_socket.send_string('Data received on device: {} !'.format(self.id))
+ self.__msg_socket.send_string('Data received on device: {} !'.format(self.__id))
# recover frame from array buffer
frame_buffer = np.frombuffer(msg_data, dtype=msg_json['dtype'])
@@ -602,38 +601,38 @@ def update(self):
#check if encoding was enabled
if msg_json['compression']:
- frame = cv2.imdecode(frame, self.compression_params)
+ frame = cv2.imdecode(frame, self.__compression_params)
#check if valid frame returned
if frame is None:
#otherwise raise error and exit
- raise ValueError("[NetGear:ERROR] :: `{}` Frame Decoding failed with Parameter: {}".format(msg_json['compression'], self.compression_params))
- self.terminate = True
+ raise ValueError("[NetGear:ERROR] :: `{}` Frame Decoding failed with Parameter: {}".format(msg_json['compression'], self.__compression_params))
+ self.__terminate = True
continue
- if self.multiserver_mode:
+ if self.__multiserver_mode:
# check if multiserver_mode
#save the unique port addresses
- if not msg_json['port'] in self.port_buffer:
- self.port_buffer.append(msg_json['port'])
+ if not msg_json['port'] in self.__port_buffer:
+ self.__port_buffer.append(msg_json['port'])
#extract if any message from server and display it
if msg_json['message']:
- self.queue.append((msg_json['port'], msg_json['message'], frame))
+ self.__queue.append((msg_json['port'], msg_json['message'], frame))
else:
# append recovered unique port and frame to queue
- self.queue.append((msg_json['port'],frame))
+ self.__queue.append((msg_json['port'],frame))
else:
#extract if any message from server if Bi-Directional Mode is enabled
- if self.bi_mode and msg_json['message']:
+ if self.__bi_mode and msg_json['message']:
# append grouped frame and data to queue
- self.queue.append((msg_json['message'], frame))
+ self.__queue.append((msg_json['message'], frame))
else:
# otherwise append recovered frame to queue
- self.queue.append(frame)
+ self.__queue.append(frame)
# finally properly close the socket
- self.msg_socket.close()
+ self.__msg_socket.close()
@@ -644,19 +643,19 @@ def recv(self, return_data = None):
:param return_data: handles return data for bi-directional mode
"""
# check whether `receive mode` is activated
- if not(self.receive_mode):
+ if not(self.__receive_mode):
#raise value error and exit
raise ValueError('[NetGear:ERROR] :: `recv()` function cannot be used while receive_mode is disabled. Kindly refer vidgear docs!')
- self.terminate = True
+ self.__terminate = True
#handle bi-directional return data
- if (self.bi_mode and not(return_data is None)): self.bi_data = return_data
+ if (self.__bi_mode and not(return_data is None)): self.__bi_data = return_data
# check whether or not termination flag is enabled
- while not self.terminate:
+ while not self.__terminate:
# check if queue is empty
- if len(self.queue)>0:
- return self.queue.popleft()
+ if len(self.__queue)>0:
+ return self.__queue.popleft()
else:
continue
# otherwise return NoneType
@@ -672,13 +671,13 @@ def send(self, frame, message = None):
:param message(string/int): additional message for the client(s)
"""
# check whether `receive_mode` is disabled
- if self.receive_mode:
+ if self.__receive_mode:
#raise value error and exit
raise ValueError('[NetGear:ERROR] :: `send()` function cannot be used while receive_mode is enabled. Kindly refer vidgear docs!')
- self.terminate = True
+ self.__terminate = True
# define exit_flag and assign value
- exit_flag = True if (frame is None or self.terminate) else False
+ exit_flag = True if (frame is None or self.__terminate) else False
#check whether exit_flag is False
if not(exit_flag) and not(frame.flags['C_CONTIGUOUS']):
@@ -686,51 +685,51 @@ def send(self, frame, message = None):
frame = np.ascontiguousarray(frame, dtype=frame.dtype)
#handle encoding
- if self.compression:
- retval, frame = cv2.imencode(self.compression, frame, self.compression_params)
+ if self.__compression:
+ retval, frame = cv2.imencode(self.__compression, frame, self.__compression_params)
#check if it works
if not(retval):
#otherwise raise error and exit
- raise ValueError("[NetGear:ERROR] :: Frame Encoding failed with format: {} and Parameters: {}".format(self.compression, self.compression_params))
- self.terminate = True
+ raise ValueError("[NetGear:ERROR] :: Frame Encoding failed with format: {} and Parameters: {}".format(self.__compression, self.__compression_params))
+ self.__terminate = True
#check if multiserver_mode is activated
- if self.multiserver_mode:
+ if self.__multiserver_mode:
# prepare the exclusive json dict and assign values with unique port
msg_dict = dict(terminate_flag = exit_flag,
- compression=str(self.compression),
+ compression=str(self.__compression),
port = self.port,
- pattern = str(self.pattern),
+ pattern = str(self.__pattern),
message = message,
dtype = str(frame.dtype),
shape = frame.shape)
else:
# otherwise prepare normal json dict and assign values
msg_dict = dict(terminate_flag = exit_flag,
- compression=str(self.compression),
+ compression=str(self.__compression),
message = message,
- pattern = str(self.pattern),
+ pattern = str(self.__pattern),
dtype = str(frame.dtype),
shape = frame.shape)
# send the json dict
- self.msg_socket.send_json(msg_dict, self.msg_flag|self.zmq.SNDMORE)
+ self.__msg_socket.send_json(msg_dict, self.__msg_flag|self.__zmq.SNDMORE)
# send the frame array with correct flags
- self.msg_socket.send(frame, flags = self.msg_flag, copy=self.msg_copy, track=self.msg_track)
+ self.__msg_socket.send(frame, flags = self.__msg_flag, copy=self.__msg_copy, track=self.__msg_track)
# wait for confirmation
- if self.pattern != 2:
+ if self.__pattern != 2:
#check if bi-directional data transmission is enabled
- if self.bi_mode:
+ if self.__bi_mode:
#handle return data
- return_dict = self.msg_socket.recv_json(flags=self.msg_flag)
+ return_dict = self.__msg_socket.recv_json(flags=self.__msg_flag)
return return_dict['data'] if return_dict else None
else:
#otherwise log normally
- recv_confirmation = self.msg_socket.recv()
+ recv_confirmation = self.__msg_socket.recv()
# log confirmation
- if self.logging : self.logger.debug(recv_confirmation)
+ if self.__logging : self.__logger.debug(recv_confirmation)
@@ -739,30 +738,30 @@ def close(self):
"""
Terminates the NetGear processes safely
"""
- if self.logging:
+ if self.__logging:
#log it
- self.logger.debug('Terminating various {} Processes.'.format('Receive Mode' if self.receive_mode else 'Send Mode'))
+ self.__logger.debug('Terminating various {} Processes.'.format('Receive Mode' if self.__receive_mode else 'Send Mode'))
# whether `receive_mode` is enabled or not
- if self.receive_mode:
+ if self.__receive_mode:
# indicate that process should be terminated
- self.terminate = True
+ self.__terminate = True
# check whether queue mode is empty
- if not(self.queue is None):
- self.queue.clear()
+ if not(self.__queue is None):
+ self.__queue.clear()
# call immediate termination
- self.exit_loop = True
+ self.__exit_loop = True
# wait until stream resources are released (producer thread might be still grabbing frame)
- if self.thread is not None:
- self.thread.join()
- self.thread = None
+ if self.__thread is not None:
+ self.__thread.join()
+ self.__thread = None
#properly handle thread exit
# properly close the socket
- self.msg_socket.close(linger=0)
+ self.__msg_socket.close(linger=0)
else:
# indicate that process should be terminated
- self.terminate = True
- if self.multiserver_mode:
+ self.__terminate = True
+ if self.__multiserver_mode:
#check if multiserver_mode
# send termination flag to client with its unique port
term_dict = dict(terminate_flag = True, port = self.port)
@@ -770,13 +769,13 @@ def close(self):
# otherwise send termination flag to client
term_dict = dict(terminate_flag = True)
# otherwise inform client(s) that the termination has been reached
- if self.force_close:
+ if self.__force_close:
#overflow socket with termination signal
- for _ in range(500): self.msg_socket.send_json(term_dict)
+ for _ in range(500): self.__msg_socket.send_json(term_dict)
else:
- self.msg_socket.send_json(term_dict)
+ self.__msg_socket.send_json(term_dict)
#check for confirmation if available
- if self.pattern < 2:
- if self.pattern == 1: self.msg_socket.recv()
+ if self.__pattern < 2:
+ if self.__pattern == 1: self.__msg_socket.recv()
# properly close the socket
- self.msg_socket.close(linger=0)
\ No newline at end of file
+ self.__msg_socket.close(linger=0)
\ No newline at end of file
diff --git a/vidgear/gears/pigear.py b/vidgear/gears/pigear.py
index d5651cb23..5d70c6d9e 100644
--- a/vidgear/gears/pigear.py
+++ b/vidgear/gears/pigear.py
@@ -29,13 +29,11 @@
try:
# import OpenCV Binaries
import cv2
-
# check whether OpenCV Binaries are 3.x+
if parse_version(cv2.__version__) < parse_version('3'):
raise ImportError('[PiGear:ERROR] :: OpenCV library version >= 3.0 is only supported by this library')
-
except ImportError as error:
- raise ImportError('[PiGear:ERROR] :: Failed to detect OpenCV executables, install it with `pip3 install opencv-python` command.')
+ raise ImportError('[PiGear:ERROR] :: Failed to detect correct OpenCV executables, install it with `pip3 install opencv-python` command.')
@@ -46,6 +44,11 @@ class PiGear:
(such as OmniVision OV5647 Camera Module and Sony IMX219 Camera Module). To interface with these
modules correctly, PiGear provides a flexible multi-threaded wrapper around complete picamera
python library and provides us the ability to exploit its various features like `brightness, saturation, sensor_mode`, etc. effortlessly.
+
+ :param (integer) camera_num: selects the camera module index that will be used by API.
+ / Its default value is 0 and shouldn't be altered until unless
+ / if you using Raspberry Pi 3/3+ compute module in your project along with multiple camera modules.
+ / Furthermore, Its value can only be greater than zero, otherwise, it will throw ValueError for any negative value.
:param (tuple) resolution: sets the resolution (width,height). Its default value is (640,480).
@@ -57,7 +60,7 @@ class PiGear:
/ These attribute provides the flexibility to manipulate input raspicam video stream directly.
/ Parameters can be passed using this **option, allows you to pass key worded variable length of arguments to PiGear Class.
- :param (boolean) self.logging: set this flag to enable/disable error logging essential for debugging. Its default value is False.
+ :param (boolean) logging: set this flag to enable/disable error logging essential for debugging. Its default value is False.
:param (integer) time_delay: sets time delay(in seconds) before start reading the frames.
/ This delay is essentially required for camera to warm-up.
@@ -80,21 +83,21 @@ def __init__(self, camera_num = 0, resolution = (640, 480), framerate = 30, colo
raise RuntimeError('[PiGear:ERROR] :: Picamera API failure: {}'.format(error))
# enable logging if specified
- self.logging = False
- self.logger = log.getLogger('PiGear')
- if logging: self.logging = logging
+ self.__logging = False
+ self.__logger = log.getLogger('PiGear')
+ if logging: self.__logging = logging
assert (isinstance(framerate, (int, float)) and framerate > 5.0), "[PiGear:ERROR] :: Input framerate value `{}` is a Invalid! Kindly read docs.".format(framerate)
assert (isinstance(resolution, (tuple, list)) and len(resolution) == 2), "[PiGear:ERROR] :: Input resolution value `{}` is a Invalid! Kindly read docs.".format(resolution)
if not(isinstance(camera_num, int) and camera_num >= 0):
camera_num = 0
- self.logger.warning("Input camera_num value `{}` is invalid, Defaulting to index 0!")
+ self.__logger.warning("Input camera_num value `{}` is invalid, Defaulting to index 0!")
# initialize the picamera stream at given index
- self.camera = PiCamera(camera_num = camera_num)
- self.camera.resolution = tuple(resolution)
- self.camera.framerate = framerate
- if self.logging: self.logger.debug("Activating Pi camera at index: {} with resolution: {} & framerate: {}".format(camera_num, resolution, framerate))
+ self.__camera = PiCamera(camera_num = camera_num)
+ self.__camera.resolution = tuple(resolution)
+ self.__camera.framerate = framerate
+ if self.__logging: self.__logger.debug("Activating Pi camera at index: {} with resolution: {} & framerate: {}".format(camera_num, resolution, framerate))
#initialize framerate variable
self.framerate = framerate
@@ -106,63 +109,61 @@ def __init__(self, camera_num = 0, resolution = (640, 480), framerate = 30, colo
options = {k.strip(): v for k,v in options.items()}
#define timeout variable default value(handles hardware failures)
- self.failure_timeout = 2.0
+ self.__failure_timeout = 2.0
#User-Defined parameter
if options and "HWFAILURE_TIMEOUT" in options:
#for altering timeout variable manually
if isinstance(options["HWFAILURE_TIMEOUT"],(int, float)):
if not(10.0 > options["HWFAILURE_TIMEOUT"] > 1.0): raise ValueError('[PiGear:ERROR] :: `HWFAILURE_TIMEOUT` value can only be between 1.0 ~ 10.0')
- self.failure_timeout = options["HWFAILURE_TIMEOUT"] #assign special parameter
- if self.logging: self.logger.debug("Setting HW Failure Timeout: {} seconds".format(self.failure_timeout))
+ self.__failure_timeout = options["HWFAILURE_TIMEOUT"] #assign special parameter
+ if self.__logging: self.__logger.debug("Setting HW Failure Timeout: {} seconds".format(self.__failure_timeout))
del options["HWFAILURE_TIMEOUT"] #clean
try:
# apply attributes to source if specified
for key, value in options.items():
- setattr(self.camera, key, value)
-
+ setattr(self.__camera, key, value)
# separately handle colorspace value to int conversion
if not(colorspace is None):
self.color_space = capPropId(colorspace.strip())
- if self.logging: self.logger.debug('Enabling `{}` colorspace for this video stream!'.format(colorspace.strip()))
-
+ if self.__logging: self.__logger.debug('Enabling `{}` colorspace for this video stream!'.format(colorspace.strip()))
except Exception as e:
# Catch if any error occurred
- if self.logging: self.logger.exception(str(e))
+ if self.__logging: self.__logger.exception(str(e))
# enable rgb capture array thread and capture stream
- self.rawCapture = PiRGBArray(self.camera, size = resolution)
- self.stream = self.camera.capture_continuous(self.rawCapture,format="bgr", use_video_port=True)
+ self.__rawCapture = PiRGBArray(self.__camera, size = resolution)
+ self.stream = self.__camera.capture_continuous(self.__rawCapture,format="bgr", use_video_port=True)
#frame variable initialization
self.frame = None
try:
stream = next(self.stream)
self.frame = stream.array
- self.rawCapture.seek(0)
- self.rawCapture.truncate()
+ self.__rawCapture.seek(0)
+ self.__rawCapture.truncate()
#render colorspace if defined
if not(self.frame is None and self.color_space is None): self.frame = cv2.cvtColor(self.frame, self.color_space)
except Exception as e:
- self.logger.exception(str(e))
+ self.__logger.exception(str(e))
raise RuntimeError('[PiGear:ERROR] :: Camera Module failed to initialize!')
# applying time delay to warm-up picamera only if specified
if time_delay: time.sleep(time_delay)
#thread initialization
- self.thread = None
+ self.__thread = None
#timer thread initialization(Keeps check on frozen thread)
- self._timer = None
- self.t_elasped = 0.0 #records time taken by thread
+ self.__timer = None
+ self.__t_elasped = 0.0 #records time taken by thread
# catching thread exceptions
- self.exceptions = None
+ self.__exceptions = None
# initialize termination flag
- self.terminate = False
+ self.__terminate = False
@@ -171,39 +172,39 @@ def start(self):
start the thread to read frames from the video stream and initiate internal timer
"""
#Start frame producer thread
- self.thread = Thread(target=self.update, name='PiGear', args=())
- self.thread.daemon = True
- self.thread.start()
+ self.__thread = Thread(target=self.__update, name='PiGear', args=())
+ self.__thread.daemon = True
+ self.__thread.start()
#Start internal timer thread
- self._timer = Thread(target=self._timeit, name='PiTimer', args=())
- self._timer.daemon = True
- self._timer.start()
+ self.__timer = Thread(target=self.__timeit, name='PiTimer', args=())
+ self.__timer.daemon = True
+ self.__timer.start()
return self
- def _timeit(self):
+ def __timeit(self):
"""
Keep checks on Thread excecution timing
"""
#assign current time
- self.t_elasped = time.time()
+ self.__t_elasped = time.time()
#loop until termainated
- while not(self.terminate):
+ while not(self.__terminate):
#check for frozen thread
- if time.time() - self.t_elasped > self.failure_timeout:
+ if time.time() - self.__t_elasped > self.__failure_timeout:
#log failure
- if self.logging: self.logger.critical("Camera Module Disconnected!")
+ if self.__logging: self.__logger.critical("Camera Module Disconnected!")
#prepare for clean exit
- self.exceptions = True
- self.terminate = True #self-terminate
+ self.__exceptions = True
+ self.__terminate = True #self-terminate
- def update(self):
+ def __update(self):
"""
Update frames from stream
"""
@@ -211,24 +212,24 @@ def update(self):
while True:
#check for termination flag
- if self.terminate: break
+ if self.__terminate: break
try:
#Try to iterate next frame from generator
stream = next(self.stream)
except Exception:
#catch and save any exceptions
- self.exceptions = sys.exc_info()
+ self.__exceptions = sys.exc_info()
break #exit
- #update timer
- self.t_elasped = time.time()
+ #__update timer
+ self.__t_elasped = time.time()
# grab the frame from the stream and clear the stream in
# preparation for the next frame
frame = stream.array
- self.rawCapture.seek(0)
- self.rawCapture.truncate()
+ self.__rawCapture.seek(0)
+ self.__rawCapture.truncate()
#apply colorspace if specified
if not(self.color_space is None):
@@ -239,14 +240,14 @@ def update(self):
color_frame = cv2.cvtColor(frame, self.color_space)
else:
self.color_space = None
- if self.logging: self.logger.debug('Colorspace value `{}` is not a valid colorspace!'.format(self.color_space))
+ if self.__logging: self.__logger.warning('Colorspace `{}` is not a valid colorspace!'.format(self.color_space))
except Exception as e:
# Catch if any error occurred
self.color_space = None
- if self.logging:
- self.logger.exception(str(e))
- self.logger.warning('Input colorspace is not a valid Colorspace!')
+ if self.__logging:
+ self.__logger.exception(str(e))
+ self.__logger.warning('Input colorspace is not a valid colorspace!')
if not(color_frame is None):
self.frame = color_frame
@@ -256,12 +257,12 @@ def update(self):
self.frame = frame
# terminate processes
- if not(self.terminate): self.terminate = True
+ if not(self.__terminate): self.__terminate = True
# release picamera resources
self.stream.close()
- self.rawCapture.close()
- self.camera.close()
+ self.__rawCapture.close()
+ self.__camera.close()
@@ -270,8 +271,8 @@ def read(self):
return the frame
"""
#check if there are any thread exceptions
- if not(self.exceptions is None):
- if isinstance(self.exceptions, bool):
+ if not(self.__exceptions is None):
+ if isinstance(self.__exceptions, bool):
#clear frame
self.frame = None
#notify user about hardware failure
@@ -280,8 +281,8 @@ def read(self):
#clear frame
self.frame = None
# re-raise error for debugging
- error_msg = "[PiGear:ERROR] :: Camera Module API failure occured: {}".format(self.exceptions[1])
- raise RuntimeError(error_msg).with_traceback(self.exceptions[2])
+ error_msg = "[PiGear:ERROR] :: Camera Module API failure occured: {}".format(self.__exceptions[1])
+ raise RuntimeError(error_msg).with_traceback(self.__exceptions[2])
# return the frame
return self.frame
@@ -292,27 +293,27 @@ def stop(self):
"""
Terminates the Read process
"""
- if self.logging: self.logger.debug("Terminating PiGear Processes.")
+ if self.__logging: self.__logger.debug("Terminating PiGear Processes.")
# make sure that the threads should be terminated
- self.terminate = True
+ self.__terminate = True
#stop timer thread
- if not(self._timer is None): self._timer.join()
+ if not(self.__timer is None): self.__timer.join()
#handle camera thread
- if not(self.thread is None):
+ if not(self.__thread is None):
#check if hardware failure occured
- if not(self.exceptions is None) and isinstance(self.exceptions, bool):
+ if not(self.__exceptions is None) and isinstance(self.__exceptions, bool):
# force release picamera resources
self.stream.close()
- self.rawCapture.close()
- self.camera.close()
+ self.__rawCapture.close()
+ self.__camera.close()
#properly handle thread exit
- self.thread.terminate()
- self.thread.wait() #wait if still process is still processing some information
- self.thread = None
+ self.__thread.terminate()
+ self.__thread.wait() #wait if still process is still processing some information
+ self.__thread = None
else:
#properly handle thread exit
- self.thread.join()
\ No newline at end of file
+ self.__thread.join()
\ No newline at end of file
diff --git a/vidgear/gears/screengear.py b/vidgear/gears/screengear.py
index d7902185f..d07a0ba2f 100644
--- a/vidgear/gears/screengear.py
+++ b/vidgear/gears/screengear.py
@@ -32,10 +32,9 @@
import cv2
# check whether OpenCV Binaries are 3.x+
if parse_version(cv2.__version__) < parse_version('3'):
- raise ImportError('[ScreenGear:ERROR] :: OpenCV library version >= 3.0 is only supported by this library')
-
+ raise ImportError('[ScreenGear:ERROR] :: OpenCV API version >= 3.0 is only supported by this library.')
except ImportError as error:
- raise ImportError('[ScreenGear:ERROR] :: Failed to detect OpenCV executables, install it with `pip install opencv-python` command.')
+ raise ImportError('[ScreenGear:ERROR] :: Failed to detect correct OpenCV executables, install it with `pip3 install opencv-python` command.')
@@ -69,7 +68,7 @@ class ScreenGear:
def __init__(self, monitor = 1, colorspace = None, logging = False, **options):
#intialize threaded queue mode by default
- self.threaded_queue_mode = True
+ self.__threaded_queue_mode = True
try:
# import mss factory
@@ -81,28 +80,32 @@ def __init__(self, monitor = 1, colorspace = None, logging = False, **options):
raise ImportError('[ScreenGear:ERROR] :: python-mss library not found, install it with `pip install mss` command.')
# enable logging if specified
- self.logging = False
- self.logger = log.getLogger('ScreenGear')
- if logging: self.logging = logging
+ self.__logging = False
+ self.__logger = log.getLogger('ScreenGear')
+ if logging: self.__logging = logging
# create mss object
- self.mss_object = mss()
+ self.__mss_object = mss()
# create monitor instance for the user-defined monitor
monitor_instance = None
if (monitor >= 0):
- monitor_instance = self.mss_object.monitors[monitor]
+ try:
+ monitor_instance = self.__mss_object.monitors[monitor]
+ except Exception as e:
+ self.__logger.exception(str(e))
+ monitor_instance = None
else:
raise ValueError("[ScreenGear:ERROR] :: `monitor` value cannot be negative, Read Docs!")
# Initialize Queue
- self.queue = None
+ self.__queue = None
#import deque
from collections import deque
#define deque and assign it to global var
- self.queue = deque(maxlen=96) #max len 96 to check overflow
+ self.__queue = deque(maxlen=96) #max len 96 to check overflow
#log it
- if logging: self.logger.debug('Enabling Threaded Queue Mode by default for ScreenGear!')
+ if logging: self.__logger.debug('Enabling Threaded Queue Mode by default for ScreenGear!')
#intiate screen dimension handler
screen_dims = {}
@@ -114,48 +117,53 @@ def __init__(self, monitor = 1, colorspace = None, logging = False, **options):
# separately handle colorspace value to int conversion
if not(colorspace is None):
self.color_space = capPropId(colorspace.strip())
- if logging: self.logger.debug('Enabling `{}` colorspace for this video stream!'.format(colorspace.strip()))
+ if logging: self.__logger.debug('Enabling `{}` colorspace for this video stream!'.format(colorspace.strip()))
except Exception as e:
# Catch if any error occurred
- if logging: self.logger.exception(str(e))
+ if logging: self.__logger.exception(str(e))
# intialize mss capture instance
- self.mss_capture_instance = None
+ self.__mss_capture_instance = None
try:
# check whether user-defined dimensions are provided
if screen_dims and len(screen_dims) == 4:
- if logging: self.logger.debug('Setting capture dimensions: {}!'.format(screen_dims))
- self.mss_capture_instance = screen_dims #create instance from dimensions
+ if logging: self.__logger.debug('Setting capture dimensions: {}!'.format(screen_dims))
+ self.__mss_capture_instance = screen_dims #create instance from dimensions
+ elif not(monitor_instance is None):
+ self.__mss_capture_instance = monitor_instance #otherwise create instance from monitor
else:
- self.mss_capture_instance = monitor_instance #otherwise create instance from monitor
+ raise RuntimeError("[ScreenGear:ERROR] :: API Failure occurred!")
# extract global frame from instance
- self.frame = np.asanyarray(self.mss_object.grab(self.mss_capture_instance))
- if self.threaded_queue_mode:
+ self.frame = np.asanyarray(self.__mss_object.grab(self.__mss_capture_instance))
+ if self.__threaded_queue_mode:
#intitialize and append to queue
- self.queue.append(self.frame)
- except ScreenShotError:
- #otherwise catch and log errors
- if logging: self.logger.error(self.mss_object.get_error_details())
- raise ValueError("[ScreenGear:ERROR] :: ScreenShotError caught, Wrong dimensions passed to python-mss, Kindly Refer Docs!")
+ self.__queue.append(self.frame)
+ except Exception as e:
+ if isinstance(e, ScreenShotError):
+ #otherwise catch and log errors
+ if logging: self.__logger.exception(self.__mss_object.get_error_details())
+ raise ValueError("[ScreenGear:ERROR] :: ScreenShotError caught, Wrong dimensions passed to python-mss, Kindly Refer Docs!")
+ else:
+ raise SystemError("[ScreenGear:ERROR] :: Unable to initiate any MSS instance on this system, Are you running headless?")
# thread initialization
- self.thread=None
+ self.__thread=None
# initialize termination flag
- self.terminate = False
+ self.__terminate = False
def start(self):
"""
start the thread to read frames from the video stream
"""
- self.thread = Thread(target=self.update, name='ScreenGear', args=())
- self.thread.daemon = True
- self.thread.start()
+ self.__thread = Thread(target=self.__update, name='ScreenGear', args=())
+ self.__thread.daemon = True
+ self.__thread.start()
return self
- def update(self):
+ def __update(self):
"""
Update frames from stream
"""
@@ -164,27 +172,27 @@ def update(self):
# keep looping infinitely until the thread is terminated
while True:
# if the thread terminate is set, stop the thread
- if self.terminate:
+ if self.__terminate:
break
- if self.threaded_queue_mode:
+ if self.__threaded_queue_mode:
#check queue buffer for overflow
- if len(self.queue) >= 96:
+ if len(self.__queue) >= 96:
#stop iterating if overflowing occurs
time.sleep(0.000001)
continue
try:
- frame = np.asanyarray(self.mss_object.grab(self.mss_capture_instance))
+ frame = np.asanyarray(self.__mss_object.grab(self.__mss_capture_instance))
except ScreenShotError:
- raise RuntimeError(self.mss_object.get_error_details())
- self.terminate = True
+ raise RuntimeError(self.__mss_object.get_error_details())
+ self.__terminate = True
continue
if frame is None or frame.size == 0:
#no frames received, then safely exit
- if self.threaded_queue_mode:
- if len(self.queue) == 0: self.terminate = True
+ if self.__threaded_queue_mode:
+ if len(self.__queue) == 0: self.__terminate = True
else:
- self.terminate = True
+ self.__terminate = True
if not(self.color_space is None):
# apply colorspace to frames
@@ -194,13 +202,13 @@ def update(self):
color_frame = cv2.cvtColor(frame, self.color_space)
else:
self.color_space = None
- if self.logging: self.logger.debug('Colorspace value {} is not a valid Colorspace!'.format(self.color_space))
+ if self.__logging: self.__logger.warning('Colorspace value `{}` is not a valid colorspace!'.format(self.color_space))
except Exception as e:
# Catch if any error occurred
self.color_space = None
- if self.logging:
- self.logger.exception(str(e))
- self.logger.debug('Input Colorspace is not a valid Colorspace!')
+ if self.__logging:
+ self.__logger.exception(str(e))
+ self.__logger.warning('Input colorspace is not a valid colorspace!')
if not(color_frame is None):
self.frame = color_frame
else:
@@ -208,9 +216,9 @@ def update(self):
else:
self.frame = frame
#append to queue
- if self.threaded_queue_mode: self.queue.append(self.frame)
+ if self.__threaded_queue_mode: self.__queue.append(self.frame)
# finally release mss resources
- self.mss_object.close()
+ self.__mss_object.close()
@@ -218,9 +226,9 @@ def read(self):
"""
return the frame
"""
- while self.threaded_queue_mode:
- if len(self.queue)>0:
- return self.queue.popleft()
+ while self.__threaded_queue_mode:
+ if len(self.__queue)>0:
+ return self.__queue.popleft()
return self.frame
@@ -229,15 +237,15 @@ def stop(self):
"""
Terminates the Read process
"""
- if self.logging: self.logger.debug("Terminating ScreenGear Processes.")
+ if self.__logging: self.__logger.debug("Terminating ScreenGear Processes.")
#terminate Threaded queue mode seperately
- if self.threaded_queue_mode and not(self.queue is None):
- self.queue.clear()
- self.threaded_queue_mode = False
+ if self.__threaded_queue_mode and not(self.__queue is None):
+ self.__queue.clear()
+ self.__threaded_queue_mode = False
self.frame = None
# indicate that the thread should be terminated
- self.terminate = True
+ self.__terminate = True
# wait until stream resources are released (producer thread might be still grabbing frame)
- if self.thread is not None:
- self.thread.join()
+ if self.__thread is not None:
+ self.__thread.join()
#properly handle thread exit
\ No newline at end of file
diff --git a/vidgear/gears/stabilizer.py b/vidgear/gears/stabilizer.py
index afc8aa61c..73cd12d0b 100644
--- a/vidgear/gears/stabilizer.py
+++ b/vidgear/gears/stabilizer.py
@@ -51,38 +51,38 @@ class Stabilizer:
def __init__(self, smoothing_radius = 25, border_type = 'black', border_size = 0, crop_n_zoom = False, logging = False):
# initialize deques for handling input frames and its indexes
- self.frame_queue = deque(maxlen=smoothing_radius)
- self.frame_queue_indexes = deque(maxlen=smoothing_radius)
+ self.__frame_queue = deque(maxlen=smoothing_radius)
+ self.__frame_queue_indexes = deque(maxlen=smoothing_radius)
# enable logging if specified
- self.logging = False
- self.logger = log.getLogger('Stabilizer')
- if logging: self.logging = logging
+ self.__logging = False
+ self.__logger = log.getLogger('Stabilizer')
+ if logging: self.__logging = logging
# define and create Adaptive histogram equalization (AHE) object for optimizations
- self.clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
+ self.__clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
# initialize global vars
- self.smoothing_radius = smoothing_radius #averaging window, handles the quality of stabilization at expense of latency and sudden panning
- self.smoothed_path = None #handles the smoothed path with box filter
- self.path = None #handles path i.e cumulative sum of pevious_2_current transformations along a axis
- self.transforms = [] #handles pevious_2_current transformations [dx,dy,da]
- self.frame_transforms_smoothed = None #handles smoothed array of pevious_2_current transformations w.r.t to frames
- self.previous_gray = None #handles previous gray frame
- self.previous_keypoints = None #handles previous detect_GFTTed keypoints w.r.t previous gray frame
- self.frame_height, self.frame_width = (0, 0) #handles width and height of input frames
- self.crop_n_zoom = 0 # handles cropping and zooms frames to reduce the black borders from stabilization being too noticeable.
+ self.__smoothing_radius = smoothing_radius #averaging window, handles the quality of stabilization at expense of latency and sudden panning
+ self.__smoothed_path = None #handles the smoothed path with box filter
+ self.__path = None #handles path i.e cumulative sum of pevious_2_current transformations along a axis
+ self.__transforms = [] #handles pevious_2_current transformations [dx,dy,da]
+ self.__frame_transforms_smoothed = None #handles smoothed array of pevious_2_current transformations w.r.t to frames
+ self.__previous_gray = None #handles previous gray frame
+ self.__previous_keypoints = None #handles previous detect_GFTTed keypoints w.r.t previous gray frame
+ self.__frame_height, self.frame_width = (0, 0) #handles width and height of input frames
+ self.__crop_n_zoom = 0 # handles cropping and zooms frames to reduce the black borders from stabilization being too noticeable.
# if check if crop_n_zoom defined
if crop_n_zoom and border_size:
- self.crop_n_zoom = border_size #crops and zoom frame to original size
- self.border_size = 0 #zero out border size
- self.frame_size = None #handles frame size for zooming
- if logging: self.logger.debug('Setting Cropping margin {} pixels'.format(border_size))
+ self.__crop_n_zoom = border_size #crops and zoom frame to original size
+ self.__border_size = 0 #zero out border size
+ self.__frame_size = None #handles frame size for zooming
+ if logging: self.__logger.debug('Setting Cropping margin {} pixels'.format(border_size))
else:
# Add output borders to frame
- self.border_size = border_size
- if self.logging and border_size: self.logger.debug('Setting Border size {} pixels'.format(border_size))
+ self.__border_size = border_size
+ if self.__logging and border_size: self.__logger.debug('Setting Border size {} pixels'.format(border_size))
# define valid border modes
border_modes = {'black': cv2.BORDER_CONSTANT,'reflect': cv2.BORDER_REFLECT, 'reflect_101': cv2.BORDER_REFLECT_101, 'replicate': cv2.BORDER_REPLICATE, 'wrap': cv2.BORDER_WRAP}
@@ -90,19 +90,19 @@ def __init__(self, smoothing_radius = 25, border_type = 'black', border_size = 0
if border_type in ['black', 'reflect', 'reflect_101', 'replicate', 'wrap']:
if not crop_n_zoom:
#initialize global border mode variable
- self.border_mode = border_modes[border_type]
- if self.logging and border_type != 'black': self.logger.debug('Setting Border type: {}'.format(border_type))
+ self.__border_mode = border_modes[border_type]
+ if self.__logging and border_type != 'black': self.__logger.debug('Setting Border type: {}'.format(border_type))
else:
#log and reset to default
- if self.logging and border_type != 'black': self.logger.debug('Setting border type is disabled if cropping is enabled!')
- self.border_mode = border_modes['black']
+ if self.__logging and border_type != 'black': self.__logger.debug('Setting border type is disabled if cropping is enabled!')
+ self.__border_mode = border_modes['black']
else:
#otherwise log if not
- if logging: self.logger.debug('Invalid input border type!')
- self.border_mode = border_modes['black'] #reset to default mode
+ if logging: self.__logger.debug('Invalid input border type!')
+ self.__border_mode = border_modes['black'] #reset to default mode
# define normalized box filter
- self.box_filter = np.ones(smoothing_radius)/smoothing_radius
+ self.__box_filter = np.ones(smoothing_radius)/smoothing_radius
@@ -118,65 +118,65 @@ def stabilize(self, frame):
return
#save frame size for zooming
- if self.crop_n_zoom and self.frame_size == None:
- self.frame_size = frame.shape[:2]
+ if self.__crop_n_zoom and self.__frame_size == None:
+ self.__frame_size = frame.shape[:2]
# initiate transformations capturing
- if not self.frame_queue:
+ if not self.__frame_queue:
#for first frame
previous_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) #convert to gray
- previous_gray = self.clahe.apply(previous_gray) #optimize gray frame
- self.previous_keypoints = cv2.goodFeaturesToTrack(previous_gray, maxCorners=200, qualityLevel=0.05, minDistance=30.0, blockSize=3 , mask=None, useHarrisDetector=False, k=0.04) #track features using GFTT
- self.frame_height, self.frame_width = frame.shape[:2] #save input frame height and width
- self.frame_queue.append(frame) #save frame to deque
- self.frame_queue_indexes.append(0) #save frame index to deque
- self.previous_gray = previous_gray[:] #save gray frame clone for further processing
-
- elif self.frame_queue_indexes[-1] <= self.smoothing_radius - 1:
+ previous_gray = self.__clahe.apply(previous_gray) #optimize gray frame
+ self.__previous_keypoints = cv2.goodFeaturesToTrack(previous_gray, maxCorners=200, qualityLevel=0.05, minDistance=30.0, blockSize=3 , mask=None, useHarrisDetector=False, k=0.04) #track features using GFTT
+ self.__frame_height, self.frame_width = frame.shape[:2] #save input frame height and width
+ self.__frame_queue.append(frame) #save frame to deque
+ self.__frame_queue_indexes.append(0) #save frame index to deque
+ self.__previous_gray = previous_gray[:] #save gray frame clone for further processing
+
+ elif self.__frame_queue_indexes[-1] <= self.__smoothing_radius - 1:
#for rest of frames
- self.frame_queue.append(frame) #save frame to deque
- self.frame_queue_indexes.append(self.frame_queue_indexes[-1]+1) #save frame index
- self.generate_transformations() #generate transformations
- if self.frame_queue_indexes[-1] == self.smoothing_radius - 1:
+ self.__frame_queue.append(frame) #save frame to deque
+ self.__frame_queue_indexes.append(self.__frame_queue_indexes[-1]+1) #save frame index
+ self.__generate_transformations() #generate transformations
+ if self.__frame_queue_indexes[-1] == self.__smoothing_radius - 1:
#calculate smooth path once transformation capturing is completed
for i in range(3):
#apply normalized box filter to the path
- self.smoothed_path[:,i] = self.box_filter_convolve((self.path[:,i]), window_size=self.smoothing_radius)
+ self.__smoothed_path[:,i] = self.__box_filter_convolve((self.__path[:,i]), window_size=self.__smoothing_radius)
#calculate deviation of path from smoothed path
- deviation = self.smoothed_path - self.path
+ deviation = self.__smoothed_path - self.__path
#save smoothed transformation
- self.frame_transforms_smoothed = self.frame_transform + deviation
+ self.__frame_transforms_smoothed = self.frame_transform + deviation
else:
#start applying transformations
- self.frame_queue.append(frame) #save frame to deque
- self.frame_queue_indexes.append(self.frame_queue_indexes[-1]+1) #save frame index
- self.generate_transformations() #generate transformations
+ self.__frame_queue.append(frame) #save frame to deque
+ self.__frame_queue_indexes.append(self.__frame_queue_indexes[-1]+1) #save frame index
+ self.__generate_transformations() #generate transformations
#calculate smooth path once transformation capturing is completed
for i in range(3):
#apply normalized box filter to the path
- self.smoothed_path[:,i] = self.box_filter_convolve((self.path[:,i]), window_size=self.smoothing_radius)
+ self.__smoothed_path[:,i] = self.__box_filter_convolve((self.__path[:,i]), window_size=self.__smoothing_radius)
#calculate deviation of path from smoothed path
- deviation = self.smoothed_path - self.path
+ deviation = self.__smoothed_path - self.__path
#save smoothed transformation
- self.frame_transforms_smoothed = self.frame_transform + deviation
+ self.__frame_transforms_smoothed = self.frame_transform + deviation
#return transformation applied stabilized frame
- return self.apply_transformations()
+ return self.__apply_transformations()
- def generate_transformations(self):
+ def __generate_transformations(self):
"""
Generate pevious_2_current transformations [dx,dy,da]
"""
- frame_gray = cv2.cvtColor(self.frame_queue[-1], cv2.COLOR_BGR2GRAY) #retrieve current frame and convert to gray
- frame_gray = self.clahe.apply(frame_gray) #optimize it
+ frame_gray = cv2.cvtColor(self.__frame_queue[-1], cv2.COLOR_BGR2GRAY) #retrieve current frame and convert to gray
+ frame_gray = self.__clahe.apply(frame_gray) #optimize it
#calculate optical flow using Lucas-Kanade differential method
- curr_kps, status, error = cv2.calcOpticalFlowPyrLK(self.previous_gray, frame_gray, self.previous_keypoints, None)
+ curr_kps, status, error = cv2.calcOpticalFlowPyrLK(self.__previous_gray, frame_gray, self.__previous_keypoints, None)
#select only valid keypoints
valid_curr_kps = curr_kps[status==1] #current
- valid_previous_keypoints = self.previous_keypoints[status==1] #previous
+ valid_previous_keypoints = self.__previous_keypoints[status==1] #previous
#calculate optimal affine transformation between pevious_2_current keypoints
if check_CV_version() == 3:
@@ -198,22 +198,22 @@ def generate_transformations(self):
dx = dy = da = 0
#save this transformation
- self.transforms.append([dx, dy, da])
+ self.__transforms.append([dx, dy, da])
#calculate path from cumulative transformations sum
- self.frame_transform = np.array(self.transforms, dtype='float32')
- self.path = np.cumsum(self.frame_transform, axis=0)
+ self.frame_transform = np.array(self.__transforms, dtype='float32')
+ self.__path = np.cumsum(self.frame_transform, axis=0)
#create smoothed path from a copy of path
- self.smoothed_path=np.copy(self.path)
+ self.__smoothed_path=np.copy(self.__path)
#re-calculate and save GFTT keypoints for current gray frame
- self.previous_keypoints = cv2.goodFeaturesToTrack(frame_gray, maxCorners=200, qualityLevel=0.05, minDistance=30.0, blockSize=3 , mask=None, useHarrisDetector=False, k=0.04)
+ self.__previous_keypoints = cv2.goodFeaturesToTrack(frame_gray, maxCorners=200, qualityLevel=0.05, minDistance=30.0, blockSize=3 , mask=None, useHarrisDetector=False, k=0.04)
#save this gray frame for further processing
- self.previous_gray = frame_gray[:]
+ self.__previous_gray = frame_gray[:]
- def box_filter_convolve(self, path, window_size):
+ def __box_filter_convolve(self, path, window_size):
"""
applies normalized linear box filter to path w.r.t to averaging window
@@ -223,7 +223,7 @@ def box_filter_convolve(self, path, window_size):
#pad path to size of averaging window
path_padded = np.pad(path, (window_size, window_size), 'median')
#apply linear box filter to path
- path_smoothed = np.convolve(path_padded, self.box_filter, mode='same')
+ path_smoothed = np.convolve(path_padded, self.__box_filter, mode='same')
#crop the smoothed path to original path
path_smoothed = path_smoothed[window_size:-window_size]
#assert if cropping is completed
@@ -233,27 +233,27 @@ def box_filter_convolve(self, path, window_size):
- def apply_transformations(self):
+ def __apply_transformations(self):
"""
Applies affine transformation to the given frame
build from previously calculated transformations
"""
#extract frame and its index from deque
- queue_frame = self.frame_queue.popleft()
- queue_frame_index = self.frame_queue_indexes.popleft()
+ queue_frame = self.__frame_queue.popleft()
+ queue_frame_index = self.__frame_queue_indexes.popleft()
#create border around extracted frame w.r.t border_size
- bordered_frame = cv2.copyMakeBorder(queue_frame, top=self.border_size, bottom=self.border_size, left=self.border_size, right=self.border_size, borderType=self.border_mode, value=[0, 0, 0])
+ bordered_frame = cv2.copyMakeBorder(queue_frame, top=self.__border_size, bottom=self.__border_size, left=self.__border_size, right=self.__border_size, borderType=self.__border_mode, value=[0, 0, 0])
alpha_bordered_frame = cv2.cvtColor(bordered_frame, cv2.COLOR_BGR2BGRA) #create alpha channel
#extract alpha channel
alpha_bordered_frame[:, :, 3] = 0
- alpha_bordered_frame[self.border_size:self.border_size + self.frame_height, self.border_size:self.border_size + self.frame_width, 3] = 255
+ alpha_bordered_frame[self.__border_size:self.__border_size + self.__frame_height, self.__border_size:self.__border_size + self.frame_width, 3] = 255
#extracting Transformations w.r.t frame index
- dx = self.frame_transforms_smoothed[queue_frame_index,0] #x-axis
- dy = self.frame_transforms_smoothed[queue_frame_index,1] #y-axis
- da = self.frame_transforms_smoothed[queue_frame_index,2] #angle
+ dx = self.__frame_transforms_smoothed[queue_frame_index,0] #x-axis
+ dy = self.__frame_transforms_smoothed[queue_frame_index,1] #y-axis
+ da = self.__frame_transforms_smoothed[queue_frame_index,2] #angle
#building 2x3 transformation matrix from extracted transformations
queue_frame_transform = np.zeros((2,3), np.float32)
@@ -265,18 +265,18 @@ def apply_transformations(self):
queue_frame_transform[1,2] = dy
#Applying an affine transformation to the frame
- frame_wrapped = cv2.warpAffine(alpha_bordered_frame, queue_frame_transform, alpha_bordered_frame.shape[:2][::-1], borderMode=self.border_mode)
+ frame_wrapped = cv2.warpAffine(alpha_bordered_frame, queue_frame_transform, alpha_bordered_frame.shape[:2][::-1], borderMode=self.__border_mode)
#drop alpha channel
frame_stabilized = frame_wrapped[:, :, :3]
#crop and zoom
- if self.crop_n_zoom:
+ if self.__crop_n_zoom:
#crop
- frame_cropped = frame_stabilized[self.crop_n_zoom:-self.crop_n_zoom, self.crop_n_zoom:-self.crop_n_zoom]
+ frame_cropped = frame_stabilized[self.__crop_n_zoom:-self.__crop_n_zoom, self.__crop_n_zoom:-self.__crop_n_zoom]
#zoom
interpolation = cv2.INTER_CUBIC if (check_CV_version() == 3) else cv2.INTER_LINEAR_EXACT
- frame_stabilized = cv2.resize(frame_cropped, self.frame_size[::-1], interpolation = interpolation)
+ frame_stabilized = cv2.resize(frame_cropped, self.__frame_size[::-1], interpolation = interpolation)
#finally return stabilized frame
return frame_stabilized
@@ -287,8 +287,8 @@ def clean(self):
clean resources(deque)
"""
# check if deque present
- if self.frame_queue:
+ if self.__frame_queue:
#clear frame deque
- self.frame_queue.clear()
+ self.__frame_queue.clear()
#clear frame indexes deque
- self.frame_queue_indexes.clear()
\ No newline at end of file
+ self.__frame_queue_indexes.clear()
\ No newline at end of file
diff --git a/vidgear/gears/videogear.py b/vidgear/gears/videogear.py
index 58bca1f14..b17f5332a 100644
--- a/vidgear/gears/videogear.py
+++ b/vidgear/gears/videogear.py
@@ -80,14 +80,14 @@ class VideoGear:
def __init__(self, enablePiCamera = False, stabilize = False, source = 0, y_tube = False, backend = 0, colorspace = None, resolution = (640, 480), framerate = 25, logging = False, time_delay = 0, **options):
#initialize stabilizer
- self.stablization_mode = stabilize
+ self.__stablization_mode = stabilize
# enable logging if specified
- self.logging = False
- self.logger = log.getLogger('VideoGear')
- if logging: self.logging = logging
+ self.__logging = False
+ self.__logger = log.getLogger('VideoGear')
+ if logging: self.__logging = logging
- if self.stablization_mode:
+ if self.__stablization_mode:
from .stabilizer import Stabilizer
s_radius, border_size, border_type, crop_n_zoom = (25, 0, 'black', False) #defaults
if options:
@@ -107,8 +107,8 @@ def __init__(self, enablePiCamera = False, stabilize = False, source = 0, y_tube
if isinstance(options["CROP_N_ZOOM"],bool):
crop_n_zoom = options["CROP_N_ZOOM"] #assigsn special parameter
del options["CROP_N_ZOOM"] #clean
- self.stabilizer_obj = Stabilizer(smoothing_radius = s_radius, border_type = border_type, border_size = border_size, crop_n_zoom = crop_n_zoom, logging = logging)
- if self.logging: self.logger.debug('Enabling Stablization Mode for the current video source!') #log info
+ self.__stabilizer_obj = Stabilizer(smoothing_radius = s_radius, border_type = border_type, border_size = border_size, crop_n_zoom = crop_n_zoom, logging = logging)
+ if self.__logging: self.__logger.debug('Enabling Stablization Mode for the current video source!') #log info
if enablePiCamera:
# only import the pigear module only if required
@@ -133,19 +133,19 @@ def start(self):
def read(self):
# return the current frame
- while self.stablization_mode:
+ while self.__stablization_mode:
frame = self.stream.read()
if frame is None:
break
- frame_stab = self.stabilizer_obj.stabilize(frame)
+ frame_stab = self.__stabilizer_obj.stabilize(frame)
if not(frame_stab is None):
return frame_stab
return self.stream.read()
def stop(self):
- if self.logging: self.logger.debug("Terminating VideoGear.")
+ if self.__logging: self.__logger.debug("Terminating VideoGear.")
# stop the thread and release any resources
self.stream.stop()
#clean queue
- if self.stablization_mode: self.stabilizer_obj.clean()
\ No newline at end of file
+ if self.__stablization_mode: self.__stabilizer_obj.clean()
\ No newline at end of file
diff --git a/vidgear/gears/writegear.py b/vidgear/gears/writegear.py
index 96e6b7563..3a22a2f70 100755
--- a/vidgear/gears/writegear.py
+++ b/vidgear/gears/writegear.py
@@ -35,9 +35,9 @@
import cv2
# check whether OpenCV Binaries are 3.x+
if parse_version(cv2.__version__) < parse_version('3'):
- raise ImportError('[WriteGear:ERROR] :: OpenCV library version >= 3.0 is only supported by this library')
+ raise ImportError('[WriteGear:ERROR] :: OpenCV API version >= 3.0 is only supported by this library.')
except ImportError as error:
- raise ImportError('[WriteGear:ERROR] :: Failed to detect OpenCV executables, install it with `pip install opencv-python` command.')
+ raise ImportError('[WriteGear:ERROR] :: Failed to detect correct OpenCV executables, install it with `pip3 install opencv-python` command.')
@@ -92,31 +92,31 @@ class WriteGear:
def __init__(self, output_filename = '', compression_mode = True, custom_ffmpeg = '', logging = False, **output_params):
# assign parameter values to class variables
- self.compression = compression_mode
- self.os_windows = True if os.name == 'nt' else False #checks if machine in-use is running windows os or not
+ self.__compression = compression_mode
+ self.__os_windows = True if os.name == 'nt' else False #checks if machine in-use is running windows os or not
# enable logging if specified
- self.logging = False
- self.logger = log.getLogger('WriteGear')
- if logging: self.logging = logging
+ self.__logging = False
+ self.__logger = log.getLogger('WriteGear')
+ if logging: self.__logging = logging
# initialize various important class variables
- self.output_parameters = {}
- self.inputheight = None
- self.inputwidth = None
- self.inputchannels = None
- self.inputframerate = 0
- self.output_dimensions = None
- self.process = None #handle process to be frames written
- self.DEVNULL = None #handles silent execution of FFmpeg (if logging is disabled)
- self.cmd = '' #handle FFmpeg Pipe command
- self.ffmpeg = '' #handle valid FFmpeg binaries location
- self.initiate = True #initiate one time process for valid process initialization
+ self.__output_parameters = {}
+ self.__inputheight = None
+ self.__inputwidth = None
+ self.__inputchannels = None
+ self.__inputframerate = 0
+ self.__output_dimensions = None
+ self.__process = None #handle process to be frames written
+ self.__DEVNULL = None #handles silent execution of FFmpeg (if logging is disabled)
+ self.__cmd = '' #handle FFmpeg Pipe command
+ self.__ffmpeg = '' #handle valid FFmpeg binaries location
+ self.__initiate = True #initiate one time process for valid process initialization
# handles output file name (if not given)
if not output_filename:
- raise ValueError('[WriteGear:ERROR] :: Kindly provide a valid `output_filename` value, Refer VidGear Docs for more information!')
+ raise ValueError('[WriteGear:ERROR] :: Kindly provide a valid `output_filename` value. Refer Docs for more information.')
elif output_filename and os.path.isdir(output_filename): # check if directory path is given instead
output_filename = os.path.join(output_filename, 'VidGear-{}.mp4'.format(time.strftime("%Y%m%d-%H%M%S"))) # auto-assign valid name and adds it to path
else:
@@ -124,64 +124,63 @@ def __init__(self, output_filename = '', compression_mode = True, custom_ffmpeg
# some definitions and assigning output file absolute path to class variable
_filename = os.path.abspath(output_filename)
- self.out_file = _filename
+ self.__out_file = _filename
basepath, _ = os.path.split(_filename) #extract file base path for debugging ahead
if output_params:
#handle user defined output dimensions(must be a tuple or list)
if output_params and "-output_dimensions" in output_params:
- self.output_dimensions = output_params["-output_dimensions"] #assign special parameter to global variable
+ self.__output_dimensions = output_params["-output_dimensions"] #assign special parameter to global variable
del output_params["-output_dimensions"] #clean
- #cleans and reformat output parameters
+ #cleans and reformat output parameters
try:
- self.output_parameters = {str(k).strip().lower(): str(v).strip() for k,v in output_params.items()}
+ self.__output_parameters = {str(k).strip().lower(): str(v).strip() for k,v in output_params.items()}
except Exception as e:
- if self.logging: self.logger.exception(str(e))
+ if self.__logging: self.__logger.exception(str(e))
raise ValueError('[WriteGear:ERROR] :: Wrong output_params parameters passed to WriteGear class!')
#handles FFmpeg binaries validity tests
- if self.compression:
+ if self.__compression:
- if self.logging:
- self.logger.debug('Compression Mode is enabled therefore checking for valid FFmpeg executables!')
- self.logger.debug(self.output_parameters)
+ if self.__logging:
+ self.__logger.debug('Compression Mode is enabled therefore checking for valid FFmpeg executables.')
+ self.__logger.debug(self.__output_parameters)
# handles where to save the downloaded FFmpeg Static Binaries on Windows(if specified)
ffmpeg_download_path_ = ''
- if self.output_parameters and "-ffmpeg_download_path" in self.output_parameters:
- ffmpeg_download_path_ += self.output_parameters["-ffmpeg_download_path"]
- del self.output_parameters["-ffmpeg_download_path"] #clean
+ if self.__output_parameters and "-ffmpeg_download_path" in self.__output_parameters:
+ ffmpeg_download_path_ += self.__output_parameters["-ffmpeg_download_path"]
+ del self.__output_parameters["-ffmpeg_download_path"] #clean
#handle input framerate if specified
- if self.output_parameters and "-input_framerate" in self.output_parameters:
- self.inputframerate = float(self.output_parameters["-input_framerate"])
- del self.output_parameters["-input_framerate"] #clean
+ if self.__output_parameters and "-input_framerate" in self.__output_parameters:
+ self.__inputframerate = float(self.__output_parameters["-input_framerate"])
+ del self.__output_parameters["-input_framerate"] #clean
#validate the FFmpeg path/binaries and returns valid FFmpeg file executable location(also downloads static binaries on windows)
- actual_command = get_valid_ffmpeg_path(custom_ffmpeg, self.os_windows, ffmpeg_download_path = ffmpeg_download_path_, logging = self.logging)
+ actual_command = get_valid_ffmpeg_path(custom_ffmpeg, self.__os_windows, ffmpeg_download_path = ffmpeg_download_path_, logging = self.__logging)
#check if valid path returned
if actual_command:
- self.ffmpeg += actual_command #assign it to class variable
- if self.logging:
- self.logger.debug('Found valid FFmpeg executables: `{}`'.format(self.ffmpeg))
+ self.__ffmpeg += actual_command #assign it to class variable
+ if self.__logging:
+ self.__logger.debug('Found valid FFmpeg executables: `{}`.'.format(self.__ffmpeg))
else:
#otherwise disable Compression Mode
- if self.logging and not self.os_windows:
- self.logger.debug('Kindly install working FFmpeg or provide a valid custom FFmpeg Path')
- self.logger.debug('Caution: Disabling Video Compression Mode since no valid FFmpeg executables found on this machine!')
- self.compression = False # compression mode disabled
+ self.__logger.warning('Disabling Compression Mode since no valid FFmpeg executables found on this machine!')
+ if self.__logging and not self.__os_windows: self.__logger.debug('Kindly install working FFmpeg or provide a valid custom FFmpeg binary path. See docs for more info.')
+ self.__compression = False # compression mode disabled
#validate this class has the access rights to specified directory or not
- assert os.access(basepath, os.W_OK), "Permission Denied: Cannot write to directory = " + basepath
+ assert os.access(basepath, os.W_OK), "[WriteGear:ERROR] :: Permission Denied: Cannot write to directory: " + basepath
#display confirmation if logging is enabled/disabled
- if self.compression and self.ffmpeg:
- self.DEVNULL = open(os.devnull, 'wb')
- if self.logging: self.logger.debug('Compression Mode is configured properly!')
+ if self.__compression and self.__ffmpeg:
+ self.__DEVNULL = open(os.devnull, 'wb')
+ if self.__logging: self.__logger.debug('Compression Mode is configured properly!')
else:
- if self.logging: self.logger.debug('Compression Mode is disabled, Activating OpenCV In-built Writer!')
+ if self.__logging: self.__logger.debug('Compression Mode is disabled, Activating OpenCV built-in Writer!')
@@ -202,54 +201,54 @@ def write(self, frame, rgb_mode = False):
channels = frame.shape[-1] if frame.ndim == 3 else 1
# assign values to class variables on first run
- if self.initiate:
- self.inputheight = height
- self.inputwidth = width
- self.inputchannels = channels
- if self.logging:
- self.logger.debug('InputFrame => Height:{} Width:{} Channels:{}'.format(self.inputheight, self.inputwidth, self.inputchannels))
+ if self.__initiate:
+ self.__inputheight = height
+ self.__inputwidth = width
+ self.__inputchannels = channels
+ if self.__logging:
+ self.__logger.debug('InputFrame => Height:{} Width:{} Channels:{}'.format(self.__inputheight, self.__inputwidth, self.__inputchannels))
#validate size of frame
- if height != self.inputheight or width != self.inputwidth:
- raise ValueError('[WriteGear:ERROR] :: All frames in a video should have same size')
+ if height != self.__inputheight or width != self.__inputwidth:
+ raise ValueError('[WriteGear:ERROR] :: All frames must have same size!')
#validate number of channels
- if channels != self.inputchannels:
- raise ValueError('[WriteGear:ERROR] :: All frames in a video should have same number of channels')
+ if channels != self.__inputchannels:
+ raise ValueError('[WriteGear:ERROR] :: All frames must have same number of channels!')
- if self.compression:
+ if self.__compression:
# checks if compression mode is enabled
#initiate FFmpeg process on first run
- if self.initiate:
+ if self.__initiate:
#start pre-processing and initiate process
- self.Preprocess(channels, rgb = rgb_mode)
+ self.__Preprocess(channels, rgb = rgb_mode)
# Check status of the process
- assert self.process is not None
+ assert self.__process is not None
#write the frame
try:
- self.process.stdin.write(frame.tostring())
+ self.__process.stdin.write(frame.tostring())
except (OSError, IOError):
# log something is wrong!
- self.logger.error('BrokenPipeError caught: Wrong Values passed to FFmpeg Pipe, Kindly Refer Docs!')
- self.DEVNULL.close()
+ self.__logger.error('BrokenPipeError caught, Wrong values passed to FFmpeg Pipe, Kindly Refer Docs!')
+ self.__DEVNULL.close()
raise ValueError #for testing purpose only
else:
# otherwise initiate OpenCV's VideoWriter Class
- if self.initiate:
+ if self.__initiate:
#start VideoWriter Class process
- self.startCV_Process()
+ self.__startCV_Process()
# Check status of the process
- assert self.process is not None
- if self.logging:
+ assert self.__process is not None
+ if self.__logging:
# log OpenCV warning
- self.logger.warning('RGBA and 16-bit grayscale video frames are not supported by OpenCV yet, switch to `compression_mode` to use them!')
+ self.__logger.info('RGBA and 16-bit grayscale video frames are not supported by OpenCV yet, switch to `compression_mode` to use them!')
#write the frame
- self.process.write(frame)
+ self.__process.write(frame)
- def Preprocess(self, channels, rgb = False):
+ def __Preprocess(self, channels, rgb = False):
"""
pre-processing FFmpeg parameters
@@ -257,16 +256,16 @@ def Preprocess(self, channels, rgb = False):
:param rgb_mode (boolean): set this flag to enable rgb_mode, Its default value is False.
"""
#turn off initiate flag
- self.initiate = False
+ self.__initiate = False
#initialize input parameters
input_parameters = {}
#handle dimensions
dimensions = ''
- if self.output_dimensions is None: #check if dimensions are given
- dimensions += '{}x{}'.format(self.inputwidth, self.inputheight) #auto derive from frame
+ if self.__output_dimensions is None: #check if dimensions are given
+ dimensions += '{}x{}'.format(self.__inputwidth, self.__inputheight) #auto derive from frame
else:
- dimensions += '{}x{}'.format(self.output_dimensions[0],self.output_dimensions[1]) #apply if defined
+ dimensions += '{}x{}'.format(self.__output_dimensions[0],self.__output_dimensions[1]) #apply if defined
input_parameters["-s"] = str(dimensions)
#handles pix_fmt based on channels(HACK)
@@ -279,19 +278,19 @@ def Preprocess(self, channels, rgb = False):
elif channels == 4:
input_parameters["-pix_fmt"] = "rgba" if rgb else "bgra"
else:
- raise ValueError("Handling Frames with (1 > Channels > 4) is not implemented!")
+ raise ValueError("[WriteGear:ERROR] :: Frames with channels, outside range 1-to-4 is not supported!")
- if self.inputframerate > 5:
+ if self.__inputframerate > 5:
#set input framerate - minimum threshold is 5.0
- if self.logging: self.logger.debug("Setting Input FrameRate = {}".format(self.inputframerate))
- input_parameters["-framerate"] = str(self.inputframerate)
+ if self.__logging: self.__logger.debug("Setting Input FrameRate = {}".format(self.__inputframerate))
+ input_parameters["-framerate"] = str(self.__inputframerate)
#initiate FFmpeg process
- self.startFFmpeg_Process(input_params = input_parameters, output_params = self.output_parameters)
+ self.__startFFmpeg_Process(input_params = input_parameters, output_params = self.__output_parameters)
- def startFFmpeg_Process(self, input_params, output_params):
+ def __startFFmpeg_Process(self, input_params, output_params):
"""
Start FFmpeg process
@@ -318,17 +317,17 @@ def startFFmpeg_Process(self, input_params, output_params):
#convert output parameters to list
output_parameters = dict2Args(output_params)
#format command
- cmd = [self.ffmpeg , "-y"] + ["-f", "rawvideo", "-vcodec", "rawvideo"] + input_parameters + ["-i", "-"] + output_parameters + [self.out_file]
+ cmd = [self.__ffmpeg , "-y"] + ["-f", "rawvideo", "-vcodec", "rawvideo"] + input_parameters + ["-i", "-"] + output_parameters + [self.__out_file]
#assign value to class variable
- self.cmd += " ".join(cmd)
+ self.__cmd += " ".join(cmd)
# Launch the FFmpeg process
- if self.logging:
- self.logger.debug('Executing FFmpeg command: `{}`'.format(self.cmd))
+ if self.__logging:
+ self.__logger.debug('Executing FFmpeg command: `{}`'.format(self.__cmd))
# In debugging mode
- self.process = sp.Popen(cmd, stdin=sp.PIPE, stdout=sp.PIPE, stderr=None)
+ self.__process = sp.Popen(cmd, stdin=sp.PIPE, stdout=sp.PIPE, stderr=None)
else:
# In silent mode
- self.process = sp.Popen(cmd, stdin=sp.PIPE, stdout=self.DEVNULL, stderr=sp.STDOUT)
+ self.__process = sp.Popen(cmd, stdin=sp.PIPE, stdout=self.__DEVNULL, stderr=sp.STDOUT)
@@ -340,41 +339,41 @@ def execute_ffmpeg_cmd(self, cmd = None):
"""
#check if valid command
if cmd is None:
- self.logger.warning('Input FFmpeg command is empty, Nothing to execute!')
+ self.__logger.warning('Input FFmpeg command is empty, Nothing to execute!')
return
else:
if not(isinstance(cmd, list)):
- raise ValueError("[WriteGear:ERROR] :: Invalid input FFmpeg command! Kindly read docs.")
+ raise ValueError("[WriteGear:ERROR] :: Invalid input FFmpeg command datatype! Kindly read docs.")
#check if Compression Mode is enabled
- if not(self.compression): raise RuntimeError("[WriteGear:ERROR] :: Compression Mode is disabled, Kindly enable it to access this function!")
+ if not(self.__compression): raise RuntimeError("[WriteGear:ERROR] :: Compression Mode is disabled, Kindly enable it to access this function!")
#add configured FFmpeg path
- cmd = [self.ffmpeg] + cmd
+ cmd = [self.__ffmpeg] + cmd
try:
- if self.logging:
- self.logger.debug('Executing FFmpeg command: `{}`'.format(' '.join(cmd)))
+ #write to pipeline
+ if self.__logging:
+ self.__logger.debug('Executing FFmpeg command: `{}`'.format(' '.join(cmd)))
# In debugging mode
sp.call(cmd, stdin=sp.PIPE, stdout=sp.PIPE, stderr=None)
else:
-
- sp.call(cmd, stdin=sp.PIPE, stdout=self.DEVNULL, stderr=sp.STDOUT)
+ sp.call(cmd, stdin=sp.PIPE, stdout=self.__DEVNULL, stderr=sp.STDOUT)
except (OSError, IOError):
# log something is wrong!
- self.logger.error('BrokenPipeError caught, Wrong command passed to FFmpeg Pipe, Kindly Refer Docs!')
- self.DEVNULL.close()
+ self.__logger.error('BrokenPipeError caught, Wrong command passed to FFmpeg Pipe, Kindly Refer Docs!')
+ self.__DEVNULL.close()
raise ValueError #for testing purpose only
- def startCV_Process(self):
+ def __startCV_Process(self):
"""
Start OpenCV VideoWriter Class process
"""
#turn off initiate flag
- self.initiate = False
+ self.__initiate = False
#initialize essential parameter variables
FPS = 0
@@ -383,18 +382,18 @@ def startCV_Process(self):
COLOR = True
#pre-assign default encoder parameters (if not assigned by user).
- if "-fourcc" not in self.output_parameters:
+ if "-fourcc" not in self.__output_parameters:
FOURCC = cv2.VideoWriter_fourcc(*"MJPG")
- if "-fps" not in self.output_parameters:
+ if "-fps" not in self.__output_parameters:
FPS = 25
#auto assign dimensions
- HEIGHT = self.inputheight
- WIDTH = self.inputwidth
+ HEIGHT = self.__inputheight
+ WIDTH = self.__inputwidth
#assign parameter dict values to variables
try:
- for key, value in self.output_parameters.items():
+ for key, value in self.__output_parameters.items():
if key == '-fourcc':
FOURCC = cv2.VideoWriter_fourcc(*(value.upper()))
elif key == '-fps':
@@ -408,18 +407,18 @@ def startCV_Process(self):
except Exception as e:
# log if something is wrong
- if self.logging: self.logger.exception(str(e))
+ if self.__logging: self.__logger.exception(str(e))
raise ValueError('[WriteGear:ERROR] :: Wrong Values passed to OpenCV Writer, Kindly Refer Docs!')
- if self.logging:
+ if self.__logging:
#log values for debugging
- self.logger.debug('FILE_PATH: {}, FOURCC = {}, FPS = {}, WIDTH = {}, HEIGHT = {}, BACKEND = {}'.format(self.out_file, FOURCC, FPS, WIDTH, HEIGHT, BACKEND))
+ self.__logger.debug('FILE_PATH: {}, FOURCC = {}, FPS = {}, WIDTH = {}, HEIGHT = {}, BACKEND = {}'.format(self.__out_file, FOURCC, FPS, WIDTH, HEIGHT, BACKEND))
#start different process for with/without Backend.
if BACKEND:
- self.process = cv2.VideoWriter(self.out_file, apiPreference = BACKEND, fourcc = FOURCC, fps = FPS, frameSize = (WIDTH, HEIGHT), isColor = COLOR)
+ self.__process = cv2.VideoWriter(self.__out_file, apiPreference = BACKEND, fourcc = FOURCC, fps = FPS, frameSize = (WIDTH, HEIGHT), isColor = COLOR)
else:
- self.process = cv2.VideoWriter(self.out_file, fourcc = FOURCC, fps = FPS, frameSize = (WIDTH, HEIGHT), isColor = COLOR)
+ self.__process = cv2.VideoWriter(self.__out_file, fourcc = FOURCC, fps = FPS, frameSize = (WIDTH, HEIGHT), isColor = COLOR)
@@ -427,27 +426,27 @@ def close(self):
"""
Terminates the Write process
"""
- if self.logging: self.logger.debug("Terminating WriteGear Processes.")
+ if self.__logging: self.__logger.debug("Terminating WriteGear Processes.")
- if self.compression:
+ if self.__compression:
#if Compression Mode is enabled
- if self.process is None:
+ if self.__process is None:
return #no process was initiated at first place
- if self.process.poll() is not None:
+ if self.__process.poll() is not None:
return # process was already dead
- if self.process.stdin:
- self.process.stdin.close() #close `stdin` output
- if self.output_parameters and "-i" in self.output_parameters:
- self.process.terminate()
- self.process.wait() #wait if still process is still processing some information
- self.process = None
- self.DEVNULL.close() #close it
+ if self.__process.stdin:
+ self.__process.stdin.close() #close `stdin` output
+ if self.__output_parameters and "-i" in self.__output_parameters:
+ self.__process.terminate()
+ self.__process.wait() #wait if still process is still processing some information
+ self.__process = None
+ self.__DEVNULL.close() #close it
else:
- self.process.wait() #wait if still process is still processing some information
- self.process = None
- self.DEVNULL.close() #close it
+ self.__process.wait() #wait if still process is still processing some information
+ self.__process = None
+ self.__DEVNULL.close() #close it
else:
#if Compression Mode is disabled
- if self.process is None:
+ if self.__process is None:
return #no process was initiated at first place
- self.process.release() #close it
\ No newline at end of file
+ self.__process.release() #close it
\ No newline at end of file
From 89d71e944eaf34e2ae24df815936ba253ea79901 Mon Sep 17 00:00:00 2001
From: Abhishek
Date: Sat, 21 Dec 2019 12:27:47 +0530
Subject: [PATCH 31/39] Fixed Typo
---
vidgear/gears/camgear.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/vidgear/gears/camgear.py b/vidgear/gears/camgear.py
index 28e665463..fc65323e2 100644
--- a/vidgear/gears/camgear.py
+++ b/vidgear/gears/camgear.py
@@ -53,7 +53,7 @@
-def self.__youtube_url_validation(url):
+def youtube_url_validation(url):
"""
convert Youtube video URLs to a valid address
"""
@@ -124,7 +124,7 @@ def __init__(self, source = 0, y_tube = False, backend = 0, colorspace = None, l
#import pafy and parse youtube stream url
import pafy
# validate
- url = self.__youtube_url_validation(source)
+ url = youtube_url_validation(source)
if url:
source_object = pafy.new(url)
_source = source_object.getbestvideo("any", ftypestrict=False)
From 149b53c53f46c851778588a7aecadbdc777ee47f Mon Sep 17 00:00:00 2001
From: Abhishek
Date: Sat, 21 Dec 2019 17:00:21 +0530
Subject: [PATCH 32/39] Fixes
---
vidgear/tests/test_helper.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/vidgear/tests/test_helper.py b/vidgear/tests/test_helper.py
index 6573e1994..21440983f 100644
--- a/vidgear/tests/test_helper.py
+++ b/vidgear/tests/test_helper.py
@@ -74,7 +74,7 @@ def test_ffmpeg_binaries_download(paths):
if paths != return_static_ffmpeg():
shutil.rmtree(os.path.abspath(os.path.join(file_path ,"../..")))
except Exception as e:
- if paths == 'wrong_test_path' and "Permission Denied:" in str(e):
+ if paths == 'wrong_test_path':
pass
else:
pytest.fail(str(e))
From 604dd5a97c75c37a9b2f6e318874c897c1d1d724 Mon Sep 17 00:00:00 2001
From: Abhishek
Date: Mon, 23 Dec 2019 00:26:41 +0530
Subject: [PATCH 33/39] Increased Codecov and minor fixes for WriteGear API
---
vidgear/gears/writegear.py | 12 ++++++------
.../tests/writer_tests/test_non_compression_mode.py | 2 +-
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/vidgear/gears/writegear.py b/vidgear/gears/writegear.py
index 3a22a2f70..afac3602e 100755
--- a/vidgear/gears/writegear.py
+++ b/vidgear/gears/writegear.py
@@ -278,7 +278,7 @@ def __Preprocess(self, channels, rgb = False):
elif channels == 4:
input_parameters["-pix_fmt"] = "rgba" if rgb else "bgra"
else:
- raise ValueError("[WriteGear:ERROR] :: Frames with channels, outside range 1-to-4 is not supported!")
+ raise ValueError("[WriteGear:ERROR] :: Frames with channels outside range 1-to-4 are not supported!")
if self.__inputframerate > 5:
#set input framerate - minimum threshold is 5.0
@@ -394,14 +394,14 @@ def __startCV_Process(self):
#assign parameter dict values to variables
try:
for key, value in self.__output_parameters.items():
- if key == '-fourcc':
+ if key == '-fourcc' and isinstance(value, str):
FOURCC = cv2.VideoWriter_fourcc(*(value.upper()))
- elif key == '-fps':
+ elif key == '-fps' and isinstance(value, (float, int)):
FPS = float(value)
- elif key =='-backend':
+ elif key =='-backend' and isinstance(value, str):
BACKEND = capPropId(value.upper())
- elif key == '-color':
- COLOR = bool(int(value))
+ elif key == '-color' and isinstance(value, bool):
+ COLOR = value
else:
pass
diff --git a/vidgear/tests/writer_tests/test_non_compression_mode.py b/vidgear/tests/writer_tests/test_non_compression_mode.py
index ac9c9ca95..bf8c7c1d6 100644
--- a/vidgear/tests/writer_tests/test_non_compression_mode.py
+++ b/vidgear/tests/writer_tests/test_non_compression_mode.py
@@ -94,7 +94,7 @@ def test_write(conversion):
('', {}, False),
('Output_twc.avi', {}, True),
(tempfile.gettempdir(), {}, True),
- ('Output_twc.mp4', {"-fourcc":"DIVX"}, True)]
+ ('Output_twc.mp4', {"-fourcc":"DIVX", "-fps": 30, "-backend": "CAP_FFMPEG", "-color":True}, True)]
@pytest.mark.parametrize('f_name, output_params, result', test_data_class)
def test_WriteGear_compression(f_name, output_params, result):
From 225ded106e454e052904626a98439069ec0242e7 Mon Sep 17 00:00:00 2001
From: Abhishek
Date: Mon, 23 Dec 2019 08:56:15 +0530
Subject: [PATCH 34/39] Fixes for CLI test failing
---
vidgear/gears/writegear.py | 12 ++++++------
.../tests/writer_tests/test_non_compression_mode.py | 2 +-
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/vidgear/gears/writegear.py b/vidgear/gears/writegear.py
index afac3602e..2c7ba2b8b 100755
--- a/vidgear/gears/writegear.py
+++ b/vidgear/gears/writegear.py
@@ -394,14 +394,14 @@ def __startCV_Process(self):
#assign parameter dict values to variables
try:
for key, value in self.__output_parameters.items():
- if key == '-fourcc' and isinstance(value, str):
+ if key == '-fourcc':
FOURCC = cv2.VideoWriter_fourcc(*(value.upper()))
- elif key == '-fps' and isinstance(value, (float, int)):
- FPS = float(value)
- elif key =='-backend' and isinstance(value, str):
+ elif key == '-fps':
+ FPS = int(value)
+ elif key =='-backend':
BACKEND = capPropId(value.upper())
- elif key == '-color' and isinstance(value, bool):
- COLOR = value
+ elif key == '-color':
+ COLOR = bool(value)
else:
pass
diff --git a/vidgear/tests/writer_tests/test_non_compression_mode.py b/vidgear/tests/writer_tests/test_non_compression_mode.py
index bf8c7c1d6..8414aa976 100644
--- a/vidgear/tests/writer_tests/test_non_compression_mode.py
+++ b/vidgear/tests/writer_tests/test_non_compression_mode.py
@@ -94,7 +94,7 @@ def test_write(conversion):
('', {}, False),
('Output_twc.avi', {}, True),
(tempfile.gettempdir(), {}, True),
- ('Output_twc.mp4', {"-fourcc":"DIVX", "-fps": 30, "-backend": "CAP_FFMPEG", "-color":True}, True)]
+ ('Output_twc.mp4', {"-fourcc":"DIVX", "-fps": 25, "-backend": "CAP_FFMPEG", "-color":True}, True)]
@pytest.mark.parametrize('f_name, output_params, result', test_data_class)
def test_WriteGear_compression(f_name, output_params, result):
From d13effc9d552d3dde1d6f42a79da8ba4573184d0 Mon Sep 17 00:00:00 2001
From: Abhishek
Date: Mon, 23 Dec 2019 15:53:30 +0530
Subject: [PATCH 35/39] Updated changelog.md
---
changelog.md | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/changelog.md b/changelog.md
index b8b6e32bd..6b8238fca 100644
--- a/changelog.md
+++ b/changelog.md
@@ -56,12 +56,13 @@
### Updates/Improvements:
* Replace `print` logging commands with python's logging module completely.
+ * Implemented encapsulation for class functions and variables on all gears.
* Updated support for screen casting from multiple/all monitors in ScreenGear API.
* Updated ScreenGear API to use *Threaded Queue Mode* by default, thereby removed redundant `THREADED_QUEUE_MODE` param.
* Updated bash script path to download test dataset in `$TMPDIR` rather than `$HOME` directory for downloading testdata.
- * Added support for `REQ/REP` pattern in Multi-Server Mode
- * Added new `camera_num` to support multiple Picameras
- * Moved thread exceptions to the main thread and then re-raised
+ * Added support for `REQ/REP` pattern in Multi-Server Mode.
+ * Added new `camera_num` to support multiple Picameras.
+ * Moved thread exceptions to the main thread and then re-raised.
* Replaced `traceback` with `sys.exc_info`.
* Overall APIs Code and Docs optimizations.
* Updated Code Readability and Wiki Docs.
@@ -76,15 +77,15 @@
* Python 2.7 and 3.4 legacies support dropped from VidGear CLI tests.
### Fixes
- * Reimplemented `Pub/Sub` pattern for smoother performance(#70)
- * Fixed `multiserver_mode` not working properly over some networks
- * Fixed assigned Port address ignored bug (commit 073bca1)
+ * Reimplemented `Pub/Sub` pattern for smoother performance on various networks.
+ * Fixed `multiserver_mode` not working properly over some networks.
+ * Fixed assigned Port address ignored bug (commit 073bca1).
* Fixed several wrong definition bugs from NetGear API(commit 8f7153c).
* Fixed unreliable dataset video URL(rehosted file on `github.com`)
* Removed duplicate code to import MSS(@BoboTiG) from ScreenGear API.
* Fixed code definitions & Typos.
- * Fixed Several bugs related to new `secure_mode` & `multiserver_mode` Modes.
- * Fixed various macOS environment bugs
+ * Fixed several bugs related to `secure_mode` & `multiserver_mode` Modes.
+ * Fixed various macOS environment bugs.
### Pull requests(PR) involved:
* PR #39
@@ -98,6 +99,7 @@
* PR #77
* PR #78
* PR #82
+ * PR #84
:warning: PyPi Release does NOT contain Tests and Scripts!
From df0081ac8c12de4c3dc94a1add491e85bba20e14 Mon Sep 17 00:00:00 2001
From: Abhishek Thakur
Date: Mon, 23 Dec 2019 20:04:28 +0530
Subject: [PATCH 36/39] Update README.md
---
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 75cfa73bf..6831ab107 100644
--- a/README.md
+++ b/README.md
@@ -442,7 +442,7 @@ VidGear releases are available for download as packages in the [latest release][
### Option 3: Clone the Repository
-> Best option for trying **latest patches, Pull requests & updgrades**(_maybe experimental_), or **contributing** to development.
+> Best option for trying **latest patches, Pull requests & upgrades**(_maybe experimental_), or **contributing** to development.
You can clone this repository's `testing` branch for development and thereby can install as follows:
```sh
@@ -593,4 +593,4 @@ External URLs
[numpy]:https://github.com/numpy/numpy
[zmq-pair]:https://learning-0mq-with-pyzmq.readthedocs.io/en/latest/pyzmq/patterns/pair.html
[zmq-req-rep]:https://learning-0mq-with-pyzmq.readthedocs.io/en/latest/pyzmq/patterns/client_server.html
-[zmq-pub-sub]:https://learning-0mq-with-pyzmq.readthedocs.io/en/latest/pyzmq/patterns/pubsub.html
\ No newline at end of file
+[zmq-pub-sub]:https://learning-0mq-with-pyzmq.readthedocs.io/en/latest/pyzmq/patterns/pubsub.html
From 8f1dd8a8e55fae37cb1f824dda4fdfde6a1deb8e Mon Sep 17 00:00:00 2001
From: Abhishek
Date: Wed, 25 Dec 2019 11:23:54 +0530
Subject: [PATCH 37/39] Updated docs
---
README.md | 26 ++++++++++++++++----------
changelog.md | 15 +++++++++++++--
2 files changed, 29 insertions(+), 12 deletions(-)
diff --git a/README.md b/README.md
index 6831ab107..d453d6db8 100644
--- a/README.md
+++ b/README.md
@@ -88,9 +88,15 @@ The following **functional block diagram** clearly depicts the functioning of Vi
# TL;DR
+ #### What is vidgear?
+
> ***"VidGear is an [ultrafast➶][ultrafast-wiki], compact, flexible and easy-to-adapt complete Video Processing Python Library."***
- *Built with simplicity in mind, VidGear lets programmers and software developers to easily integrate and perform complex Video Processing tasks in their existing or new applications, without going through various underlying library's documentation and using just a few lines of code. Beneficial for both, if you're new to programming with Python language or already a pro at it.*
+ #### What does it do?
+ > ***"VidGear can read, write, process, send & receive video frames from various devices in real-time."***
+
+ #### What is its purpose?
+ > ***"Built with simplicity in mind, VidGear lets programmers and software developers to easily integrate and perform complex Video Processing tasks in their existing or new applications, without going through various underlying library's documentation and using just a few lines of code. Beneficial for both, if you're new to programming with Python language or already a pro at it."***
**For more advanced information, see the [*Wiki Documentation ➶*][wiki].**
@@ -100,7 +106,7 @@ The following **functional block diagram** clearly depicts the functioning of Vi
# Gears:
-> **VidGear is built with **multi-threaded APIs** *(a.k.a Gears)* each with some unique function/mechanism.**
+> **VidGear is built with various **Multi-Threaded APIs** *(a.k.a Gears)* each with some unique function/mechanism.**
Each of these API is designed exclusively to handle/control different device-specific video streams, network streams, and media encoders. These APIs provides an easy-to-use, highly extensible, and a multi-threaded wrapper around various underlying libraries to exploit their features and functions directly while providing robust error-handling.
@@ -125,7 +131,7 @@ Each of these API is designed exclusively to handle/control different device-spe
## CamGear
-> *CamGear can grab ultrafast frames from diverse range of VideoStreams, which includes almost any IP/USB Cameras, multimedia video file format ([_upto 4k tested_][test-4k]), various network stream protocols such as `http(s), rtp, rstp, rtmp, mms, etc.`, plus support for live Gstreamer's stream pipeline and YouTube video/livestreams URLs.*
+> *CamGear can grab ultra-fast frames from diverse range of devices/streams, which includes almost any IP/USB Cameras, multimedia video file format ([_upto 4k tested_][test-4k]), various network stream protocols such as `http(s), rtp, rstp, rtmp, mms, etc.`, plus support for live Gstreamer's stream pipeline and YouTube video/livestreams URLs.*
CamGear provides a flexible, high-level multi-threaded wrapper around `OpenCV's` [VideoCapture class][opencv-vc] with access almost all of its available parameters and also employs [`pafy`][pafy] python APIs for live [YouTube streaming][youtube-wiki]. Furthermore, CamGear implements exclusively on [**Threaded Queue mode**][TQM-wiki] for ultra-fast, error-free and synchronized frame handling.
@@ -217,7 +223,7 @@ stream_stab.stop()
PiGear provides a flexible multi-threaded wrapper around complete [**picamera**][picamera] python library to interface with these modules correctly, and also grants the ability to exploit its various features like `brightness, saturation, sensor_mode, etc.` effortlessly.
-Best of all, PiGear API provides excellent Error-Handling with features like a threaded internal timer that keeps active track of any frozen threads and handles hardware failures/frozen threads robustly thereby will exit safely if any failure occurs. So if you accidently pulled your camera cable out when running PiGear API in your script, instead of going into possible kernel panic due to IO error, it will exit safely to save resources.
+Best of all, PiGear API provides excellent Error-Handling with features like a threaded internal timer that keeps active track of any frozen threads and handles hardware failures/frozen threads robustly thereby will exit safely if any failure occurs. So now if you accidently pulled your camera module cable out when running PiGear API in your script, instead of going into possible kernel panic/frozen threads, API exit safely to save resources.
**Following simplified functional block diagram depicts PiGear API:**
@@ -233,7 +239,7 @@ Best of all, PiGear API provides excellent Error-Handling with features like a t
## ScreenGear
-> *ScreenGear act as Screen Recorder, that can grab frames from your monitor in real-time either by define an area on the computer screen or fullscreen at the expense of insignificant latency. It also provide seemless support for capturing frames from multiple monitors.*
+> *ScreenGear API act as Screen Recorder, that can grab frames from your monitor in real-time either by define an area on the computer screen or fullscreen at the expense of insignificant latency. It also provide seemless support for capturing frames from multiple monitors.*
ScreenGear provides a high-level multi-threaded wrapper around [**python-mss**][mss] python library API and also supports a easy and flexible direct internal parameter manipulation.
@@ -325,7 +331,7 @@ NetGear also introduces real-time frame Encoding/Decoding compression capabiliti
For security, NetGear also supports easy access to ZeroMQ's powerful, smart & secure Security Layers, that enables strong encryption on data, and unbreakable authentication between the Server and the Client with the help of custom certificates/keys and brings easy, standardized privacy and authentication for distributed systems over the network.
-Best of all, NetGear can robustly handle Multiple Servers at once, thereby providing access to seamless Live Streams of the various device in a network at the same time.
+Best of all, NetGear can robustly handle Multiple Servers devices at once, thereby providing access to seamless Live Streaming of the multiple device in a network at the same time.
**NetGear as of now seamlessly supports three ZeroMQ messaging patterns:**
@@ -379,7 +385,7 @@ Best of all, NetGear can robustly handle Multiple Servers at once, thereby provi
# Installation
-## Prerequisites
+## Prerequisites:
Before installing VidGear, you must verify that the following dependencies are met:
@@ -421,7 +427,7 @@ Before installing VidGear, you must verify that the following dependencies are m
-## Available Installation Options
+## Available Installation Options:
### Option 1: PyPI Install
@@ -442,7 +448,7 @@ VidGear releases are available for download as packages in the [latest release][
### Option 3: Clone the Repository
-> Best option for trying **latest patches, Pull requests & upgrades**(_maybe experimental_), or **contributing** to development.
+> Best option for trying **latest patches, Pull requests & updgrades**(_maybe experimental_), or **contributing** to development.
You can clone this repository's `testing` branch for development and thereby can install as follows:
```sh
@@ -593,4 +599,4 @@ External URLs
[numpy]:https://github.com/numpy/numpy
[zmq-pair]:https://learning-0mq-with-pyzmq.readthedocs.io/en/latest/pyzmq/patterns/pair.html
[zmq-req-rep]:https://learning-0mq-with-pyzmq.readthedocs.io/en/latest/pyzmq/patterns/client_server.html
-[zmq-pub-sub]:https://learning-0mq-with-pyzmq.readthedocs.io/en/latest/pyzmq/patterns/pubsub.html
+[zmq-pub-sub]:https://learning-0mq-with-pyzmq.readthedocs.io/en/latest/pyzmq/patterns/pubsub.html
\ No newline at end of file
diff --git a/changelog.md b/changelog.md
index 6b8238fca..73c15bbc7 100644
--- a/changelog.md
+++ b/changelog.md
@@ -11,6 +11,7 @@
* Implemented Robust Multi-Server support for NetGear API:
* Enables Multiple Servers messaging support with a single client.
* Added exclusive `multiserver_mode` param for enabling it.
+ * Added support for `REQ/REP` & `PUB/SUB` patterns for this mode.
* Added ability to send additional data of any datatype along with the frame in realtime in this mode.
* Introducing exclusive Bi-Directional Mode for bidirectional data transmission:
* Added new `return_data` parameter to `recv()` function.
@@ -60,7 +61,11 @@
* Updated support for screen casting from multiple/all monitors in ScreenGear API.
* Updated ScreenGear API to use *Threaded Queue Mode* by default, thereby removed redundant `THREADED_QUEUE_MODE` param.
* Updated bash script path to download test dataset in `$TMPDIR` rather than `$HOME` directory for downloading testdata.
- * Added support for `REQ/REP` pattern in Multi-Server Mode.
+ * Implemented better error handling of colorspace in various videocapture APIs.
+ * Updated bash scripts, Moved FFmpeg static binaries to `github.com`.
+ * Updated bash scripts, Added additional flag to support un-secure apt sources.
+ * CamGear API will now throw `RuntimeError` if source provided is invalid.
+ * Updated threaded Queue mode in CamGear API for more robust performance.
* Added new `camera_num` to support multiple Picameras.
* Moved thread exceptions to the main thread and then re-raised.
* Replaced `traceback` with `sys.exc_info`.
@@ -78,11 +83,17 @@
### Fixes
* Reimplemented `Pub/Sub` pattern for smoother performance on various networks.
+ * Fixed Assertion error in CamGear API during colorspace manipulation.
+ * Fixed random freezing in `Secure Mode` and several related performance updates
* Fixed `multiserver_mode` not working properly over some networks.
* Fixed assigned Port address ignored bug (commit 073bca1).
* Fixed several wrong definition bugs from NetGear API(commit 8f7153c).
- * Fixed unreliable dataset video URL(rehosted file on `github.com`)
+ * Fixed unreliable dataset video URL(rehosted file on `github.com`).
+ * Disabled `overwrite_cert` for client-end in NetGear API.
+ * Disabled Universal Python wheel builds in `setup.cfg `file.
* Removed duplicate code to import MSS(@BoboTiG) from ScreenGear API.
+ * Eliminated unused redundant code blocks from library.
+ * Fixed Code indentation in `setup.py` and updated new release information.
* Fixed code definitions & Typos.
* Fixed several bugs related to `secure_mode` & `multiserver_mode` Modes.
* Fixed various macOS environment bugs.
From 4e5ff369d07af4ef8bb5132e182025edbe31bceb Mon Sep 17 00:00:00 2001
From: Abhishek
Date: Fri, 27 Dec 2019 14:53:23 +0530
Subject: [PATCH 38/39] Updated Docs
---
README.md | 31 +++++++++++++++++++++++--------
setup.py | 8 ++++----
2 files changed, 27 insertions(+), 12 deletions(-)
diff --git a/README.md b/README.md
index d453d6db8..9e272832f 100644
--- a/README.md
+++ b/README.md
@@ -63,7 +63,7 @@ The following **functional block diagram** clearly depicts the functioning of Vi
* [**WriteGear**](#writegear)
* [**NetGear**](#netgear)
-[**Installation Options**](#installation)
+[**Installation**](#installation)
* [**Prerequisites**](#prerequisites)
* [**1 - PyPI Install**](#option-1-pypi-install)
* [**2 - Release Archive Download**](#option-2-release-archive-download)
@@ -80,6 +80,7 @@ The following **functional block diagram** clearly depicts the functioning of Vi
**Additional Info**
* [**Supported Python legacies**](#supported-python-legacies)
* [**Changelog**](#changelog)
+ * [**Citing**](#citing)
* [**License**](#license)
@@ -104,7 +105,7 @@ The following **functional block diagram** clearly depicts the functioning of Vi
-# Gears:
+# Gears
> **VidGear is built with various **Multi-Threaded APIs** *(a.k.a Gears)* each with some unique function/mechanism.**
@@ -323,13 +324,13 @@ In addition to this, WriteGear also provides flexible access to [**OpenCV's Vide
> *NetGear is exclusively designed to transfer video frames synchronously and asynchronously between interconnecting systems over the network in real-time.*
-NetGear implements a high-level wrapper around [**PyZmQ**][pyzmq] python library that contains python bindings for [ZeroMQ](http://zeromq.org/) - a high-performance asynchronous distributed messaging library that aim to be used in distributed or concurrent applications. It provides a message queue, but unlike message-oriented middleware, a ZeroMQ system can run without a dedicated message broker.
+NetGear implements a high-level wrapper around [**PyZmQ**][pyzmq] python library that contains python bindings for [ZeroMQ](http://zeromq.org/) - a high-performance asynchronous distributed messaging library that aim to be used in distributed or concurrent applications. It provides a message queue, but unlike message-oriented middleware, a ZeroMQ system can run without a dedicated message broker.
NetGear provides seamless support for bidirectional data transmission between receiver(client) and sender(server) through bi-directional synchronous messaging patterns such as zmq.PAIR _(ZMQ Pair Pattern)_ & zmq.REQ/zmq.REP _(ZMQ Request/Reply Pattern)_.
-NetGear also introduces real-time frame Encoding/Decoding compression capabilities for optimizing performance while sending the frames of large size directly over the network by encoding the frame before sending it and decoding it on the client's end automatically all in real-time.
+NetGear also supports real-time frame Encoding/Decoding compression capabilities for optimizing performance while sending the frames directly over the network, by encoding the frame before sending it and decoding it on the client's end automatically in real-time.
-For security, NetGear also supports easy access to ZeroMQ's powerful, smart & secure Security Layers, that enables strong encryption on data, and unbreakable authentication between the Server and the Client with the help of custom certificates/keys and brings easy, standardized privacy and authentication for distributed systems over the network.
+For security, NetGear implements easy access to ZeroMQ's powerful, smart & secure Security Layers, that enables strong encryption on data, and unbreakable authentication between the Server and the Client with the help of custom certificates/keys and brings easy, standardized privacy and authentication for distributed systems over the network.
Best of all, NetGear can robustly handle Multiple Servers devices at once, thereby providing access to seamless Live Streaming of the multiple device in a network at the same time.
@@ -448,7 +449,7 @@ VidGear releases are available for download as packages in the [latest release][
### Option 3: Clone the Repository
-> Best option for trying **latest patches, Pull requests & updgrades**(_maybe experimental_), or **contributing** to development.
+> Best option for trying **latest patches(_maybe experimental_), Pull Requests**, or **contributing** to development.
You can clone this repository's `testing` branch for development and thereby can install as follows:
```sh
@@ -487,7 +488,7 @@ The full documentation for all VidGear classes and functions can be found in the
```sh
chmod +x scripts/bash/prepare_dataset.sh
- .scripts/bash/prepare_dataset.sh #for windows, use `sh scripts/pre_install.sh`
+ .scripts/bash/prepare_dataset.sh #for Windows, use `sh scripts/bash/prepare_dataset.sh`
```
* **Run Tests:** Then various VidGear tests can be run with `pytest`(*in VidGear's root folder*) as below:
@@ -515,7 +516,21 @@ See [**contributing.md**](contributing.md).
See [**changelog.md**](changelog.md)
-
+
+
+# Citing
+
+**Here is a Bibtex entry you can use to cite this project in a publication:**
+
+```tex
+@misc{vidgear,
+ Title = {vidgear},
+ Author = {Abhishek Thakur},
+ howpublished = {\url{https://github.com/abhiTronix/vidgear}}
+ }
+```
+
+
# License
diff --git a/setup.py b/setup.py
index 7e3a03971..12ef63a6c 100644
--- a/setup.py
+++ b/setup.py
@@ -52,16 +52,16 @@ def test_opencv():
version='0.1.6',
description='Most Powerful multi-threaded Video Processing Python framework powerpacked with unique trailblazing features.',
license='Apache License 2.0',
- author='abhiTronix',
- install_requires = ["pafy", "mss", "youtube-dl", "requests","pyzmq"]
- + (["opencv-contrib-python"] if test_opencv() else [])
+ author='Abhishek Thakur',
+ install_requires = ["pafy", "mss", "youtube-dl", "requests", "pyzmq"]
+ + (["opencv-python"] if test_opencv() else [])
+ (["picamera"] if ("arm" in platform.uname()[4][:3]) else []),
long_description=long_description,
long_description_content_type="text/markdown",
author_email='abhi.una12@gmail.com',
url='https://github.com/abhiTronix/vidgear',
download_url='https://github.com/abhiTronix/vidgear/releases/download/vidgear-0.1.6/vidgear-0.1.6.tar.gz',
- keywords=['opencv', 'multithreading', 'FFmpeg', 'picamera', 'mss', 'pyzmq', 'pafy', 'Video Processing', 'Video Stablization', 'Computer Vision'],
+ keywords=['OpenCV', 'multithreading', 'FFmpeg', 'picamera', 'mss', 'pyzmq', 'pafy', 'Video Processing', 'Video Stablization', 'Computer Vision', 'raspberrypi', 'youtube'],
classifiers=[
'Development Status :: 5 - Production/Stable',
'Operating System :: POSIX',
From f75d377f8630528e6a48c2f75f1ebb04716b70f7 Mon Sep 17 00:00:00 2001
From: Abhishek
Date: Mon, 30 Dec 2019 00:56:52 +0530
Subject: [PATCH 39/39] New Updates
* Added alternate github mirror for FFmpeg static binaries auto-installation on windows oses.
* Added `colorlog` python module for presentable colored logging.
* Updated docs
---
changelog.md | 2 ++
setup.py | 2 +-
vidgear/gears/helper.py | 62 +++++++++++++++++++++++++++++++++--------
3 files changed, 53 insertions(+), 13 deletions(-)
diff --git a/changelog.md b/changelog.md
index 73c15bbc7..08d7b84c7 100644
--- a/changelog.md
+++ b/changelog.md
@@ -68,6 +68,8 @@
* Updated threaded Queue mode in CamGear API for more robust performance.
* Added new `camera_num` to support multiple Picameras.
* Moved thread exceptions to the main thread and then re-raised.
+ * Added alternate github mirror for FFmpeg static binaries auto-installation on windows oses.
+ * Added `colorlog` python module for presentable colored logging.
* Replaced `traceback` with `sys.exc_info`.
* Overall APIs Code and Docs optimizations.
* Updated Code Readability and Wiki Docs.
diff --git a/setup.py b/setup.py
index 12ef63a6c..1cb80c51b 100644
--- a/setup.py
+++ b/setup.py
@@ -53,7 +53,7 @@ def test_opencv():
description='Most Powerful multi-threaded Video Processing Python framework powerpacked with unique trailblazing features.',
license='Apache License 2.0',
author='Abhishek Thakur',
- install_requires = ["pafy", "mss", "youtube-dl", "requests", "pyzmq"]
+ install_requires = ["pafy", "mss", "youtube-dl", "requests", "pyzmq", "colorlog"]
+ (["opencv-python"] if test_opencv() else [])
+ (["picamera"] if ("arm" in platform.uname()[4][:3]) else []),
long_description=long_description,
diff --git a/vidgear/gears/helper.py b/vidgear/gears/helper.py
index 1bf56fecb..51edf76eb 100644
--- a/vidgear/gears/helper.py
+++ b/vidgear/gears/helper.py
@@ -26,9 +26,39 @@
import numpy as np
from pkg_resources import parse_version
import logging as log
-
-
-log.basicConfig(format='%(name)s :: %(levelname)s :: %(message)s', level=log.DEBUG)
+import logging.config
+
+#logging formatter
+logging.config.dictConfig({
+ 'version': 1,
+ 'formatters': {
+ 'colored': {
+ '()': 'colorlog.ColoredFormatter',
+ 'log_colors': {
+ 'DEBUG': 'bold_green',
+ 'WARNING': 'bold_yellow',
+ 'ERROR': 'bold_red',
+ 'CRITICAL': 'bold_red,bg_white',
+ },
+ 'format':
+ "%(bold_blue)s%(name)s%(reset)s :: %(log_color)s%(levelname)s%(reset)s :: %(message)s",
+ }
+ },
+ 'handlers': {
+ 'stream': {
+ 'class': 'logging.StreamHandler',
+ 'formatter': 'colored',
+ 'level': 'DEBUG'
+ },
+ },
+ 'loggers': {
+ '': {
+ 'handlers': ['stream'],
+ 'level': 'DEBUG',
+ },
+ },
+})
+#logger
logger = log.getLogger('Helper')
@@ -158,17 +188,25 @@ def download_ffmpeg_binaries(path, os_windows = False):
if os.path.isfile(file_path):
final_path += file_path #skip download if does
else:
+ #import libs
import requests
import zipfile
- #check if given pth has write access
- assert os.access(path, os.W_OK), "[Helper:ERROR] :: Permission Denied, Cannot write ffmpeg binaries to directory = " + path
- #remove leftovers
- if os.path.isfile(file_name):
- os.remove(file_name)
+ #check if given path has write access
+ assert os.access(path, os.W_OK), "[Helper:ERROR] :: Permission Denied, Cannot write binaries to directory = " + path
+ #remove leftovers if exists
+ if os.path.isfile(file_name): os.remove(file_name)
#download and write file to the given path
with open(file_name, "wb") as f:
- logger.debug("No Custom FFmpeg path provided, Auto-Downloading binaries for Windows. Please wait...")
- response = requests.get(file_url, stream=True)
+ logger.debug("No Custom FFmpeg path provided. Auto-Installing FFmpeg static binaries now. Please wait...")
+ try:
+ response = requests.get(file_url, stream=True, timeout=2)
+ response.raise_for_status()
+ except Exception as e:
+ logger.exception(str(e))
+ logger.warning("Downloading Failed. Trying GitHub mirror now!")
+ file_url = 'https://raw.githubusercontent.com/abhiTronix/ffmpeg-static-builds/master/windows/ffmpeg-latest-{}-static.zip'.format(windows_bit, windows_bit)
+ response = requests.get(file_url, stream=True, timeout=2)
+ response.raise_for_status()
total_length = response.headers.get('content-length')
if total_length is None: # no content length header
f.write(response.content)
@@ -181,12 +219,12 @@ def download_ffmpeg_binaries(path, os_windows = False):
done = int(50 * dl / total_length)
sys.stdout.write("\r[{}{}]{}{}".format('=' * done, ' ' * (50-done), done * 2, '%') )
sys.stdout.flush()
- logger.debug("\nExtracting executables, Please Wait...")
+ logger.debug("Extracting executables.")
with zipfile.ZipFile(file_name, "r") as zip_ref:
zip_ref.extractall(base_path)
#perform cleaning
os.remove(file_name)
- logger.debug("FFmpeg binaries for Windows Configured Successfully!")
+ logger.debug("FFmpeg binaries for Windows configured successfully!")
final_path += file_path
#return final path
return final_path