From 778de536e830f438652c06b911acce42b4e38d88 Mon Sep 17 00:00:00 2001 From: abhiTronix Date: Sat, 22 Jul 2023 16:42:47 +0530 Subject: [PATCH 01/38] =?UTF-8?q?=F0=9F=94=96=20Bumped=20version=20to=20`0?= =?UTF-8?q?.3.2`.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vidgear/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vidgear/version.py b/vidgear/version.py index 260c070a8..f9aa3e110 100644 --- a/vidgear/version.py +++ b/vidgear/version.py @@ -1 +1 @@ -__version__ = "0.3.1" +__version__ = "0.3.2" From c6b659ad871215159c683ff3ca10107886b1ff18 Mon Sep 17 00:00:00 2001 From: abhiTronix Date: Tue, 25 Jul 2023 19:21:41 +0530 Subject: [PATCH 02/38] =?UTF-8?q?=F0=9F=9A=A7=20Setup.py:=20Removed=20supp?= =?UTF-8?q?ort=20for=20python-3.7=20legacies=20(Fixes=20#369)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - πŸ“Œ Raised `python_requires` to `>=3.8`. Thereby python `3.7` and any before legacy are no longer supported. - πŸ”₯ Removed `3.7` legacy from Programming Language metadata. - 🩹 Readded latest patch to `uvicorn`, `starlette`, `mss`, `pyzmq` dependencies. Docs: - πŸ“ Updated minimum python to version `3.8` while installing vidgear. - 🎨 Updated API-specific dependencies. - ✏️ Fixed hyperlinks. --- docs/installation.md | 6 +++--- docs/installation/pip_install.md | 6 +++--- docs/installation/source_install.md | 6 +++--- setup.py | 16 ++++++++-------- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/docs/installation.md b/docs/installation.md index f28ccd451..16e72c879 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -29,7 +29,7 @@ limitations under the License. ## Supported Systems -VidGear is well-tested and supported on the following systems(but not limited to), with [python 3.7+](https://www.python.org/downloads/) and [pip](https://pip.pypa.io/en/stable/installing/#do-i-need-to-install-pip) installed: +VidGear is well-tested and supported on the following systems(but not limited to), with [python 3.8+](https://www.python.org/downloads/) and [pip](https://pip.pypa.io/en/stable/installing/#do-i-need-to-install-pip) installed: * Any :material-linux: Linux distro released in 2016 or later * :fontawesome-brands-windows: Windows 7 or later @@ -41,9 +41,9 @@ VidGear is well-tested and supported on the following systems(but not limited to !!! warning "Depreciation Notice" - Python-3.6 legacies support has been dropped from Vidgear. + Python-3.7 legacies support has been dropped from Vidgear. -:fontawesome-brands-python: [**Python 3.7+**](https://www.python.org/downloads/) are only supported legacies for installing Vidgear v0.2.5 and above. +:fontawesome-brands-python: [**Python 3.8+**](https://www.python.org/downloads/) are only supported legacies for installing Vidgear `v0.3.1` and above.   diff --git a/docs/installation/pip_install.md b/docs/installation/pip_install.md index 2ee73b8fe..ba51b9181 100644 --- a/docs/installation/pip_install.md +++ b/docs/installation/pip_install.md @@ -262,7 +262,7 @@ When installing VidGear with [pip](https://pip.pypa.io/en/stable/installing/), y | CamGear | `pafy`, `yt_dlp`, `streamlink` | | PiGear | `picamera` | | VideoGear | *Based on CamGear or PiGear backend in use* | - | ScreenGear | `dxcam`, `mss`, `pyscreenshot`, `Pillow` | + | ScreenGear | `mss`, `pyscreenshot`, `Pillow` | | WriteGear | **FFmpeg:** See [this doc ➢](../../gears/writegear/compression/advanced/ffmpeg_install/#ffmpeg-installation-instructions) | | StreamGear | **FFmpeg:** See [this doc ➢](../../gears/streamgear/ffmpeg_install/#ffmpeg-installation-instructions) | | NetGear | `pyzmq`, `simplejpeg` | @@ -329,10 +329,10 @@ pip install git+git://github.com/abhiTronix/vidgear@master#egg=vidgear[asyncio] ```sh # Install latest stable release with all Core dependencies -pip install vidgear-0.3.1-py3-none-any.whl[core] +pip install vidgear-0.3.2-py3-none-any.whl[core] # Or Install latest stable release with all Core & Asyncio dependencies -pip install vidgear-0.3.1-py3-none-any.whl[asyncio] +pip install vidgear-0.3.2-py3-none-any.whl[asyncio] ```   diff --git a/docs/installation/source_install.md b/docs/installation/source_install.md index ab2300e91..88d49ef82 100644 --- a/docs/installation/source_install.md +++ b/docs/installation/source_install.md @@ -161,9 +161,9 @@ When installing VidGear from source, FFmpeg is the only API specific prerequisit | CamGear | `yt_dlp` | | PiGear | `picamera` | | VideoGear | *Based on CamGear or PiGear backend in use* | - | ScreenGear | `mss`, `pyscreenshot`, `Pillow` | - | WriteGear | **FFmpeg:** See [this doc ➢](https://abhitronix.github.io/vidgear/v0.2.2-dev/gears/writegear/compression/advanced/ffmpeg_install/#ffmpeg-installation-instructions) | - | StreamGear | **FFmpeg:** See [this doc ➢](https://abhitronix.github.io/vidgear/v0.2.2-dev/gears/streamgear/ffmpeg_install/#ffmpeg-installation-instructions) | + | ScreenGear | `dxcam`, `mss`, `pyscreenshot`, `Pillow` | + | WriteGear | **FFmpeg:** See [this doc ➢](https://abhitronix.github.io/vidgear/dev/gears/writegear/compression/advanced/ffmpeg_install/#ffmpeg-installation-instructions) | + | StreamGear | **FFmpeg:** See [this doc ➢](https://abhitronix.github.io/vidgear/dev/gears/streamgear/ffmpeg_install/#ffmpeg-installation-instructions) | | NetGear | `pyzmq`, `simplejpeg` | | WebGear | `starlette`, `jinja2`, `uvicorn`, `simplejpeg` | | WebGear_RTC | `aiortc`, `starlette`, `jinja2`, `uvicorn` | diff --git a/setup.py b/setup.py index 70e1715d0..aab347730 100644 --- a/setup.py +++ b/setup.py @@ -109,10 +109,10 @@ def latest_version(package_name): # API specific deps "core": [ "yt_dlp{}".format(latest_version("yt_dlp")), - "pyzmq==24.0.1", + "pyzmq{}".format(latest_version("pyzmq")), "Pillow", "simplejpeg{}".format(latest_version("simplejpeg")), - "mss==7.0.1", # TODO temporary solution, needs to be addressed + "mss{}".format(latest_version("mss")), "pyscreenshot{}".format(latest_version("pyscreenshot")), ] + (["picamera"] if ("arm" in platform.uname()[4][:3]) else []) @@ -124,17 +124,17 @@ def latest_version(package_name): # API specific + Asyncio deps "asyncio": [ "yt_dlp{}".format(latest_version("yt_dlp")), - "pyzmq==24.0.1", + "pyzmq{}".format(latest_version("pyzmq")), "simplejpeg{}".format(latest_version("simplejpeg")), - "mss==7.0.1", # TODO temporary solution, needs to be addressed + "mss{}".format(latest_version("mss")), "Pillow", "pyscreenshot{}".format(latest_version("pyscreenshot")), - "starlette", + "starlette{}".format(latest_version("starlette")), "jinja2", "msgpack{}".format(latest_version("msgpack")), "msgpack_numpy{}".format(latest_version("msgpack_numpy")), "aiortc{}".format(latest_version("aiortc")), - "uvicorn", + "uvicorn{}".format(latest_version("uvicorn")), ] + (["picamera"] if ("arm" in platform.uname()[4][:3]) else []) + ( @@ -184,12 +184,12 @@ def latest_version(package_name): "Intended Audience :: Science/Research", "Intended Audience :: Education", "License :: OSI Approved :: Apache Software License", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", ], - python_requires=">=3.7", + python_requires=">=3.8", scripts=[], project_urls={ "Bug Reports": "https://github.com/abhiTronix/vidgear/issues", From 394296497b449049817569e28bb29e934aec57de Mon Sep 17 00:00:00 2001 From: abhiTronix Date: Tue, 25 Jul 2023 19:43:44 +0530 Subject: [PATCH 03/38] =?UTF-8?q?=F0=9F=91=B7=20CI:=20Updated=20Azure=20Pi?= =?UTF-8?q?peline=20workflow.=20(Fixes=20#369)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ✨ Added python 3.11 legacy support for MacOS environments. - πŸ”₯ Deprecated python 3.7 legacy support. --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 28b2112b2..23cbde702 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -35,14 +35,14 @@ pool: strategy: matrix: - Python37: - python.version: "3.7" Python38: python.version: "3.8" Python39: python.version: "3.9" Python310: python.version: "3.10" + Python311: + python.version: "3.11" steps: - task: UsePythonVersion@0 From dfb493d690bcf3cab0c9d38af516a8e725addfa1 Mon Sep 17 00:00:00 2001 From: abhiTronix Date: Tue, 25 Jul 2023 19:45:09 +0530 Subject: [PATCH 04/38] =?UTF-8?q?=F0=9F=91=B7=20CI:=20Updated=20Appveyor?= =?UTF-8?q?=20Pipeline=20workflow.=20(Fixes=20#369)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ✨ Added python 3.11 legacy support for Windows OS environments. - πŸ”₯ Deprecated python 3.7 legacy support. --- appveyor.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 779f0e4e6..4b5e528c2 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -16,10 +16,6 @@ image: Visual Studio 2019 environment: matrix: - - PYTHON: "C:\\Python37-x64" - PYTHON_VERSION: "3.7.x" - PYTHON_ARCH: "64" - - PYTHON: "C:\\Python38-x64" PYTHON_VERSION: "3.8.x" PYTHON_ARCH: "64" @@ -32,6 +28,10 @@ environment: PYTHON_VERSION: "3.10.x" PYTHON_ARCH: "64" + - PYTHON: "C:\\Python311-x64" + PYTHON_VERSION: "3.11.x" + PYTHON_ARCH: "64" + build: off version: "{branch}-{build}" @@ -70,6 +70,5 @@ install: test_script: - cmd: python -m pytest --verbose --capture=no --cov-report term-missing --cov=vidgear vidgear/tests/ - after_test: - - cmd: python -m codecov \ No newline at end of file + - cmd: python -m codecov From 61e956194195e10b4bb0a7e45eb634d813eec264 Mon Sep 17 00:00:00 2001 From: abhiTronix Date: Tue, 25 Jul 2023 19:50:48 +0530 Subject: [PATCH 05/38] =?UTF-8?q?=F0=9F=91=B7=20CI:=20Updated=20GitHub=20C?= =?UTF-8?q?I=20Pipeline=20workflow.=20(Fixes=20#369)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ✨ Added python 3.11 legacy support for Linux OS environments. - πŸ”₯ Deprecated python 3.7 legacy support. - 🚚 Migrated python version to 3.9 in deploy_docs.yml workflow. --- .github/workflows/ci_linux.yml | 2 +- .github/workflows/deploy_docs.yml | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci_linux.yml b/.github/workflows/ci_linux.yml index 6c748cefa..0e6d0c0c1 100644 --- a/.github/workflows/ci_linux.yml +++ b/.github/workflows/ci_linux.yml @@ -39,7 +39,7 @@ jobs: runs-on: ubuntu-20.04 strategy: matrix: - python-version: ["3.7", "3.8", "3.9", "3.10"] + python-version: ["3.8", "3.9", "3.10", "3.11"] steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 diff --git a/.github/workflows/deploy_docs.yml b/.github/workflows/deploy_docs.yml index 86aafbc09..de74a6c73 100644 --- a/.github/workflows/deploy_docs.yml +++ b/.github/workflows/deploy_docs.yml @@ -23,7 +23,7 @@ on: types: [published] env: - PYTHON_VERSION: 3.7 + PYTHON_VERSION: 3.9 GIT_TOKEN: ${{ secrets.GIT_TOKEN }} GIT_NAME: ${{ secrets.GIT_NAME }} GIT_EMAIL: ${{ secrets.GIT_EMAIL }} @@ -65,7 +65,7 @@ jobs: - name: mike deploy docs release run: | echo "${{ env.NAME_RELEASE }}" - mike deploy --push --update-aliases --no-redirect ${{ env.NAME_RELEASE }} ${{ env.RELEASE_NAME }} --title=${{ env.RELEASE_NAME }} + mike deploy --push --update-aliases --no-redirect ${{ env.NAME_RELEASE }} ${{ env.RELEASE_NAME }} --title=${{ env.RELEASE_NAME }} env: NAME_RELEASE: "v${{ env.RELEASE_NAME }}-release" if: success() @@ -112,7 +112,6 @@ jobs: NAME_STABLE: "v${{ env.RELEASE_NAME }}-stable" if: success() - deploy-docs-dev: name: Deploy Development Docs if: github.event_name == 'push' && github.ref == 'refs/heads/testing' From 10a58b3b9c10f20ef4e181abb449aad4cf852f51 Mon Sep 17 00:00:00 2001 From: abhiTronix Date: Tue, 25 Jul 2023 19:52:44 +0530 Subject: [PATCH 06/38] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20Maintenance:=20Added?= =?UTF-8?q?=20GitHub=20sponsors=20and=20dropped=20liberapay=20from=20Fundi?= =?UTF-8?q?ng.yml.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/FUNDING.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 566711e4a..970fe1ca5 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,2 +1,2 @@ ko_fi: abhitronix -liberapay: abhiTronix \ No newline at end of file +github: abhiTronix From dba0767b851c62b921e86ec4e9aefc07ab436fbc Mon Sep 17 00:00:00 2001 From: Abhishek Thakur Date: Tue, 25 Jul 2023 22:56:16 +0530 Subject: [PATCH 07/38] =?UTF-8?q?=F0=9F=93=8C=20Setup.py:=20Pinned=20`pyzm?= =?UTF-8?q?q=3D=3D24.0.1`.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index aab347730..81be564b5 100644 --- a/setup.py +++ b/setup.py @@ -109,7 +109,7 @@ def latest_version(package_name): # API specific deps "core": [ "yt_dlp{}".format(latest_version("yt_dlp")), - "pyzmq{}".format(latest_version("pyzmq")), + "pyzmq==24.0.1", "Pillow", "simplejpeg{}".format(latest_version("simplejpeg")), "mss{}".format(latest_version("mss")), @@ -124,7 +124,7 @@ def latest_version(package_name): # API specific + Asyncio deps "asyncio": [ "yt_dlp{}".format(latest_version("yt_dlp")), - "pyzmq{}".format(latest_version("pyzmq")), + "pyzmq==24.0.1", "simplejpeg{}".format(latest_version("simplejpeg")), "mss{}".format(latest_version("mss")), "Pillow", From 09ea4819e66aecbba43ad93558cc3dd5d1a1f73e Mon Sep 17 00:00:00 2001 From: abhiTronix Date: Wed, 26 Jul 2023 22:23:48 +0530 Subject: [PATCH 08/38] =?UTF-8?q?=F0=9F=92=9A=20CI:=20Temporary=20fix=20fo?= =?UTF-8?q?r=20AST=20constructor=20depth=20mismatch=20in=20pytest=20on=20p?= =?UTF-8?q?ython=203.11.x?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - More information: https://github.com/pytest-dev/pytest/issues/10874 --- .../tests/streamer_tests/asyncio_tests/test_webgear_rtc.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/vidgear/tests/streamer_tests/asyncio_tests/test_webgear_rtc.py b/vidgear/tests/streamer_tests/asyncio_tests/test_webgear_rtc.py index 5bbe4f5dc..e14c98057 100644 --- a/vidgear/tests/streamer_tests/asyncio_tests/test_webgear_rtc.py +++ b/vidgear/tests/streamer_tests/asyncio_tests/test_webgear_rtc.py @@ -412,6 +412,10 @@ async def test_webpage_reload(options): ] +@pytest.mark.skipif( + (platform.system() == "Windows" and platform.python_version_tuple()[:2] >= (3, 11)), + reason="Random Failures!", +) @pytest.mark.asyncio @pytest.mark.parametrize("stream_class, result", test_stream_classes) async def test_webgear_rtc_custom_stream_class(stream_class, result): From d903c1e0c661fd2a3671bb840f292bb42ef3f9b1 Mon Sep 17 00:00:00 2001 From: abhiTronix Date: Wed, 26 Jul 2023 22:35:58 +0530 Subject: [PATCH 09/38] =?UTF-8?q?=F0=9F=A9=B9=20Asyncio:=20Formatted=20`Te?= =?UTF-8?q?mplateResponse`=20class=20parameters=20w.r.t=20new=20changes.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vidgear/gears/asyncio/webgear.py | 10 +++------- vidgear/gears/asyncio/webgear_rtc.py | 10 +++------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/vidgear/gears/asyncio/webgear.py b/vidgear/gears/asyncio/webgear.py index 64a7214a8..d68e51293 100755 --- a/vidgear/gears/asyncio/webgear.py +++ b/vidgear/gears/asyncio/webgear.py @@ -496,7 +496,7 @@ async def __homepage(self, request): Returns an HTML index page. """ return ( - self.__templates.TemplateResponse("index.html", {"request": request}) + self.__templates.TemplateResponse(request, "index.html") if not self.__skip_generate_webdata else JSONResponse( {"detail": "WebGear Data-Files Auto-Generation WorkFlow is disabled!"}, @@ -509,9 +509,7 @@ async def __not_found(self, request, exc): Returns an HTML 404 page. """ return ( - self.__templates.TemplateResponse( - "404.html", {"request": request}, status_code=404 - ) + self.__templates.TemplateResponse(request, "404.html", status_code=404) if not self.__skip_generate_webdata else JSONResponse( {"detail": "WebGear Data-Files Auto-Generation WorkFlow is disabled!"}, @@ -524,9 +522,7 @@ async def __server_error(self, request, exc): Returns an HTML 500 page. """ return ( - self.__templates.TemplateResponse( - "500.html", {"request": request}, status_code=500 - ) + self.__templates.TemplateResponse(request, "500.html", status_code=500) if not self.__skip_generate_webdata else JSONResponse( {"detail": "WebGear Data-Files Auto-Generation WorkFlow is disabled!"}, diff --git a/vidgear/gears/asyncio/webgear_rtc.py b/vidgear/gears/asyncio/webgear_rtc.py index 33e622fe1..62729559c 100644 --- a/vidgear/gears/asyncio/webgear_rtc.py +++ b/vidgear/gears/asyncio/webgear_rtc.py @@ -596,23 +596,19 @@ async def __homepage(self, request): """ Return an HTML index page. """ - return self.__templates.TemplateResponse("index.html", {"request": request}) + return self.__templates.TemplateResponse(request, "index.html") async def __not_found(self, request, exc): """ Return an HTML 404 page. """ - return self.__templates.TemplateResponse( - "404.html", {"request": request}, status_code=404 - ) + return self.__templates.TemplateResponse(request, "404.html", status_code=404) async def __server_error(self, request, exc): """ Return an HTML 500 page. """ - return self.__templates.TemplateResponse( - "500.html", {"request": request}, status_code=500 - ) + return self.__templates.TemplateResponse(request, "500.html", status_code=500) async def __reset_connections(self, request): """ From 53aa84d186cab712c1e80500d312bb98e8955ca4 Mon Sep 17 00:00:00 2001 From: abhiTronix Date: Wed, 26 Jul 2023 22:37:56 +0530 Subject: [PATCH 10/38] =?UTF-8?q?=F0=9F=92=9A=20CI:=20Made=20temporary=20f?= =?UTF-8?q?ix=20platform=20independent.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Temporary fix for AST constructor depth mismatch in pytest on python 3.11.x, More information: https://github.com/pytest-dev/pytest/issues/10874 --- vidgear/tests/streamer_tests/asyncio_tests/test_webgear_rtc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vidgear/tests/streamer_tests/asyncio_tests/test_webgear_rtc.py b/vidgear/tests/streamer_tests/asyncio_tests/test_webgear_rtc.py index e14c98057..65c9d213f 100644 --- a/vidgear/tests/streamer_tests/asyncio_tests/test_webgear_rtc.py +++ b/vidgear/tests/streamer_tests/asyncio_tests/test_webgear_rtc.py @@ -413,7 +413,7 @@ async def test_webpage_reload(options): @pytest.mark.skipif( - (platform.system() == "Windows" and platform.python_version_tuple()[:2] >= (3, 11)), + platform.python_version_tuple()[:2] >= (3, 11), reason="Random Failures!", ) @pytest.mark.asyncio From 1ad2e3d221a862ad09cf86445ce031972a2b1201 Mon Sep 17 00:00:00 2001 From: abhiTronix Date: Wed, 26 Jul 2023 22:58:19 +0530 Subject: [PATCH 11/38] =?UTF-8?q?=F0=9F=92=9A=20CI:=20Fixed=20TypeError=20?= =?UTF-8?q?in=20`skipif`.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vidgear/tests/streamer_tests/asyncio_tests/test_webgear_rtc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vidgear/tests/streamer_tests/asyncio_tests/test_webgear_rtc.py b/vidgear/tests/streamer_tests/asyncio_tests/test_webgear_rtc.py index 65c9d213f..6cb818be7 100644 --- a/vidgear/tests/streamer_tests/asyncio_tests/test_webgear_rtc.py +++ b/vidgear/tests/streamer_tests/asyncio_tests/test_webgear_rtc.py @@ -413,7 +413,7 @@ async def test_webpage_reload(options): @pytest.mark.skipif( - platform.python_version_tuple()[:2] >= (3, 11), + platform.python_version_tuple()[:2] >= ("3", "11"), reason="Random Failures!", ) @pytest.mark.asyncio From 182ca412d607a8f2258f66b077bfacb08fc6f722 Mon Sep 17 00:00:00 2001 From: abhiTronix Date: Thu, 27 Jul 2023 00:47:47 +0530 Subject: [PATCH 12/38] =?UTF-8?q?=F0=9F=92=9A=20CI:=20Extended=20fix=20to?= =?UTF-8?q?=20more=20tests.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Temporary fix for AST constructor depth mismatch in pytest on python 3.11.x, More information: https://github.com/pytest-dev/pytest/issues/10874 --- .../tests/streamer_tests/asyncio_tests/test_webgear_rtc.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/vidgear/tests/streamer_tests/asyncio_tests/test_webgear_rtc.py b/vidgear/tests/streamer_tests/asyncio_tests/test_webgear_rtc.py index 6cb818be7..7d17c3678 100644 --- a/vidgear/tests/streamer_tests/asyncio_tests/test_webgear_rtc.py +++ b/vidgear/tests/streamer_tests/asyncio_tests/test_webgear_rtc.py @@ -466,6 +466,10 @@ async def test_webgear_rtc_custom_stream_class(stream_class, result): ] +@pytest.mark.skipif( + platform.python_version_tuple()[:2] >= ("3", "11"), + reason="Random Failures!", +) @pytest.mark.asyncio @pytest.mark.parametrize("middleware, result", test_data_class) async def test_webgear_rtc_custom_middleware(middleware, result): From a8a3abb3906ef3045b4ab66679df4ae920cd5b5b Mon Sep 17 00:00:00 2001 From: abhiTronix Date: Thu, 27 Jul 2023 08:16:49 +0530 Subject: [PATCH 13/38] =?UTF-8?q?=F0=9F=92=9A=20CI:=20Extended=20fix=20to?= =?UTF-8?q?=20more=20Webgear=5FRTC=20tests.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Temporary fix for AST constructor depth mismatch in pytest on python 3.11.x, More information: https://github.com/pytest-dev/pytest/issues/10874 --- .../streamer_tests/asyncio_tests/test_webgear_rtc.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/vidgear/tests/streamer_tests/asyncio_tests/test_webgear_rtc.py b/vidgear/tests/streamer_tests/asyncio_tests/test_webgear_rtc.py index 7d17c3678..845081c12 100644 --- a/vidgear/tests/streamer_tests/asyncio_tests/test_webgear_rtc.py +++ b/vidgear/tests/streamer_tests/asyncio_tests/test_webgear_rtc.py @@ -330,7 +330,13 @@ async def test_webgear_rtc_options(options): ] -@pytest.mark.skipif((platform.system() == "Windows"), reason="Random Failures!") +@pytest.mark.skipif( + ( + platform.system() == "Windows" + or platform.python_version_tuple()[:2] >= ("3", "11") + ), + reason="Random Failures!", +) @pytest.mark.asyncio @pytest.mark.parametrize("options", test_data) async def test_webpage_reload(options): From ec4c797419feba54004142b776aacefe12c20308 Mon Sep 17 00:00:00 2001 From: abhiTronix Date: Thu, 27 Jul 2023 08:34:23 +0530 Subject: [PATCH 14/38] =?UTF-8?q?=E2=98=82=EF=B8=8F=20CI:=20Increased=20co?= =?UTF-8?q?de=20coverage.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../asyncio_tests/test_webgear_rtc.py | 39 ++++++++++++++++--- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/vidgear/tests/streamer_tests/asyncio_tests/test_webgear_rtc.py b/vidgear/tests/streamer_tests/asyncio_tests/test_webgear_rtc.py index 845081c12..5950d0d47 100644 --- a/vidgear/tests/streamer_tests/asyncio_tests/test_webgear_rtc.py +++ b/vidgear/tests/streamer_tests/asyncio_tests/test_webgear_rtc.py @@ -189,23 +189,46 @@ def stop(self): self.running = False -class Invalid_Custom_Stream_Class: +class Invalid_Custom_Channel_Class: """ Custom Invalid WebGear_RTC Server """ - def __init__(self, source=0): + def __init__(self): # define running flag self.running = True + # define stream + self.stream = Custom_Grayscale_class() + + def read(self): + # return non supported channeled frame + return self.stream.read(size=(480, 640, 5)) + def stop(self): # don't forget this function!!! # flag that we're not running self.running = False + self.stream.stop() + + +class Invalid_Custom_Stream_Class: + """ + Custom Invalid WebGear_RTC Server + """ + + def __init__(self): + # define running flag + self.running = True + + def stop(self): + # flag that we're not running + self.running = False test_data = [ + (None, False, None, 0), (return_testvideo_path(), True, None, 0), (return_testvideo_path(), False, "COLOR_BGR2HSV", 1), ] @@ -248,7 +271,7 @@ async def test_webgear_rtc_class(source, stabilize, colorspace, time_delay): await offer_pc.close() web.shutdown() except Exception as e: - if not isinstance(e, MediaStreamError): + if source is None or not isinstance(e, MediaStreamError): pytest.fail(str(e)) @@ -412,9 +435,15 @@ async def test_webpage_reload(options): test_stream_classes = [ (None, False), (Custom_Stream_Class(source=return_testvideo_path()), True), - (VideoGear(source=return_testvideo_path(), logging=True), True), + ( + VideoGear( + source=return_testvideo_path(), colorspace="COLOR_BGR2BGRA", logging=True + ), + True, + ), (Custom_Grayscale_class(), False), - (Invalid_Custom_Stream_Class(source=return_testvideo_path()), False), + (Invalid_Custom_Channel_Class(), False), + (Invalid_Custom_Stream_Class(), False), ] From 3ed1fd2548402728ddb84ab79e561283e5a7b43f Mon Sep 17 00:00:00 2001 From: abhiTronix Date: Thu, 27 Jul 2023 08:37:06 +0530 Subject: [PATCH 15/38] =?UTF-8?q?=F0=9F=92=9A=20CI:=20Extended=20fix=20to?= =?UTF-8?q?=20all=20Webgear=5FRTC=20tests.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Temporary fix for AST constructor depth mismatch in pytest on python 3.11.x, More information: https://github.com/pytest-dev/pytest/issues/10874 --- .../asyncio_tests/test_webgear_rtc.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/vidgear/tests/streamer_tests/asyncio_tests/test_webgear_rtc.py b/vidgear/tests/streamer_tests/asyncio_tests/test_webgear_rtc.py index 5950d0d47..ad041b016 100644 --- a/vidgear/tests/streamer_tests/asyncio_tests/test_webgear_rtc.py +++ b/vidgear/tests/streamer_tests/asyncio_tests/test_webgear_rtc.py @@ -234,6 +234,10 @@ def stop(self): ] +@pytest.mark.skipif( + platform.python_version_tuple()[:2] >= ("3", "11"), + reason="Random Failures!", +) @pytest.mark.asyncio @pytest.mark.parametrize("source, stabilize, colorspace, time_delay", test_data) async def test_webgear_rtc_class(source, stabilize, colorspace, time_delay): @@ -300,6 +304,10 @@ async def test_webgear_rtc_class(source, stabilize, colorspace, time_delay): ] +@pytest.mark.skipif( + platform.python_version_tuple()[:2] >= ("3", "11"), + reason="Random Failures!", +) @pytest.mark.asyncio @pytest.mark.parametrize("options", test_data) async def test_webgear_rtc_options(options): @@ -525,6 +533,10 @@ async def test_webgear_rtc_custom_middleware(middleware, result): pytest.xfail(str(e)) +@pytest.mark.skipif( + platform.python_version_tuple()[:2] >= ("3", "11"), + reason="Random Failures!", +) @pytest.mark.asyncio async def test_webgear_rtc_routes(): """ @@ -570,6 +582,10 @@ async def test_webgear_rtc_routes(): pytest.fail(str(e)) +@pytest.mark.skipif( + platform.python_version_tuple()[:2] >= ("3", "11"), + reason="Random Failures!", +) @pytest.mark.asyncio async def test_webgear_rtc_routes_validity(): """ From c861b178be610c532ac8d7677a8c4b5ecd550d25 Mon Sep 17 00:00:00 2001 From: Abhishek Thakur Date: Thu, 27 Jul 2023 13:44:25 +0530 Subject: [PATCH 16/38] =?UTF-8?q?=F0=9F=92=9A=20CI:=20Fixed=20condition=20?= =?UTF-8?q?logic=20bug.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vidgear/tests/streamer_tests/asyncio_tests/test_webgear_rtc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vidgear/tests/streamer_tests/asyncio_tests/test_webgear_rtc.py b/vidgear/tests/streamer_tests/asyncio_tests/test_webgear_rtc.py index ad041b016..5477ce796 100644 --- a/vidgear/tests/streamer_tests/asyncio_tests/test_webgear_rtc.py +++ b/vidgear/tests/streamer_tests/asyncio_tests/test_webgear_rtc.py @@ -275,7 +275,7 @@ async def test_webgear_rtc_class(source, stabilize, colorspace, time_delay): await offer_pc.close() web.shutdown() except Exception as e: - if source is None or not isinstance(e, MediaStreamError): + if source and not isinstance(e, MediaStreamError): pytest.fail(str(e)) From 5479aa57d5ef25da303ad9bb06591251a118cd61 Mon Sep 17 00:00:00 2001 From: abhiTronix Date: Sat, 5 Aug 2023 17:27:40 +0530 Subject: [PATCH 17/38] =?UTF-8?q?=E2=9A=A1=EF=B8=8FNetGear:=20Added=20`kil?= =?UTF-8?q?l`=20argument=20to=20`close()`=20method.=20(Fixes=20#361)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 🚩 Implemented new `kill` argument to `close()` method to forcefully kill ZMQ context instead of graceful exit only in the `receive` mode. - πŸ”Š Updated logging. --- vidgear/gears/netgear.py | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/vidgear/gears/netgear.py b/vidgear/gears/netgear.py index 2765f8ba7..4b4f423c8 100644 --- a/vidgear/gears/netgear.py +++ b/vidgear/gears/netgear.py @@ -119,7 +119,6 @@ def __init__( logging=False, **options ): - """ This constructor method initializes the object state and attributes of the NetGear class. @@ -403,7 +402,6 @@ def __init__( # Handle Secure mode if self.__secure_mode: - # activate and log if overwriting is enabled if overwrite_cert: if not receive_mode: @@ -525,7 +523,6 @@ def __init__( # check whether `receive_mode` is enabled if self.__receive_mode: - # define connection address if address is None: address = "*" # define address @@ -612,6 +609,7 @@ def __init__( # define exclusive socket options for patterns if self.__pattern == 2: self.__msg_socket.setsockopt_string(zmq.SUBSCRIBE, "") + self.__msg_socket.setsockopt(zmq.LINGER, 0) # if multiserver_mode is enabled, then assign port addresses to zmq socket if self.__multiserver_mode: @@ -721,7 +719,6 @@ def __init__( logger.debug("Receive Mode is now activated.") else: - # otherwise default to `Send Mode` # define connection address @@ -931,7 +928,6 @@ def __init__( ) def __recv_handler(self): - """ A threaded receiver handler, that keep iterating data from ZMQ socket to a internally monitored deque, until the thread is terminated, or socket disconnects. @@ -941,7 +937,6 @@ def __recv_handler(self): # keep looping infinitely until the thread is terminated while not self.__terminate: - # check queue buffer for overflow if len(self.__queue) >= 96: # stop iterating if overflowing occurs @@ -1302,7 +1297,6 @@ def send(self, frame, message=None): if self.__pattern < 2: # check if Bidirectional data transmission is enabled if self.__bi_mode or self.__multiclient_mode: - # handles return data recvd_data = None @@ -1452,9 +1446,12 @@ def send(self, frame, message=None): # log confirmation self.__logging and logger.debug(recv_confirmation) - def close(self): + def close(self, kill=False): """ Safely terminates the threads, and NetGear resources. + + Parameters: + kill (bool): Kills ZMQ context instead of graceful exiting in receive mode. """ # log it self.__logging and logger.debug( @@ -1469,20 +1466,29 @@ def close(self): self.__queue.clear() # call immediate termination self.__terminate = True - # wait until stream resources are released (producer thread might be still grabbing frame) + # properly close the socket + self.__logging and logger.debug("Terminating. Please wait...") + # wait until stream resources are released + # (producer thread might be still grabbing frame) if self.__thread is not None: # properly handle thread exit - self.__thread.join() + if self.__thread.is_alive() and kill: + # force close if still alive + logger.warning("Thread still running...Killing it forcefully!") + self.__msg_context.destroy() + self.__thread.join() + else: + self.__thread.join() + self.__msg_socket.close(linger=0) self.__thread = None - self.__logging and logger.debug("Terminating. Please wait...") - # properly close the socket - self.__msg_socket.close(linger=0) self.__logging and logger.debug("Terminated Successfully!") - else: # indicate that process should be terminated self.__terminate = True - + # log if kill enabled + kill and logger.warning( + "`kill` parmeter is only available in the receive mode." + ) # check if all attempts of reconnecting failed, then skip to closure if (self.__pattern < 2 and not self.__max_retries) or ( self.__multiclient_mode and not self.__port_buffer From 5a502cd7e1d051e1f3df14c3de66628300e725fe Mon Sep 17 00:00:00 2001 From: abhiTronix Date: Sat, 5 Aug 2023 17:30:16 +0530 Subject: [PATCH 18/38] =?UTF-8?q?=F0=9F=91=B7=20CI:=20Added=20`kill`=20arg?= =?UTF-8?q?ument=20to=20`close()`=20method=20in=20various=20NetGear=20test?= =?UTF-8?q?s.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vidgear/tests/network_tests/test_netgear.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/vidgear/tests/network_tests/test_netgear.py b/vidgear/tests/network_tests/test_netgear.py index 42aecf4f1..f70b98f85 100644 --- a/vidgear/tests/network_tests/test_netgear.py +++ b/vidgear/tests/network_tests/test_netgear.py @@ -167,9 +167,9 @@ def test_patterns(pattern): if not (stream is None): stream.release() if not (server is None): - server.close() + server.close(kill=True) if not (client is None): - client.close() + client.close(kill=True) @pytest.mark.parametrize( @@ -239,9 +239,9 @@ def test_compression(options_server): if not (stream is None): stream.stop() if not (server is None): - server.close() + server.close(kill=True) if not (client is None): - client.close() + client.close(kill=True) test_data_class = [ @@ -304,9 +304,9 @@ def test_secure_mode(pattern, security_mech, custom_cert_location, overwrite_cer if not (stream is None): stream.release() if not (server is None): - server.close() + server.close(kill=True) if not (client is None): - client.close() + client.close(kill=True) @pytest.mark.parametrize( @@ -418,9 +418,9 @@ def test_bidirectional_mode(pattern, target_data, options): if not (stream is None): stream.stop() if not (server is None): - server.close() + server.close(kill=True) if not (client is None): - client.close() + client.close(kill=True) @pytest.mark.parametrize( From 2eaa5fa9052a2ad9998f7c494a4d905136a69ff7 Mon Sep 17 00:00:00 2001 From: abhiTronix Date: Mon, 28 Aug 2023 23:57:55 +0530 Subject: [PATCH 19/38] =?UTF-8?q?=F0=9F=90=9B=20ScreenGear:=20Fixed=20swap?= =?UTF-8?q?ped=20region=20dimensions=20bug=20with=20`dxcam`=20backend.=20(?= =?UTF-8?q?Fixes=20#372)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 🩹 Fixed "mss" backend disabled when `monitor` parameter is not defined. Stabilizer Class: - πŸ”₯ Removed redundant code - ✏️ Fixed typos. --- vidgear/gears/screengear.py | 12 ++++++------ vidgear/gears/stabilizer.py | 30 +++++++++--------------------- 2 files changed, 15 insertions(+), 27 deletions(-) diff --git a/vidgear/gears/screengear.py b/vidgear/gears/screengear.py index c5aa17318..64ab630ee 100644 --- a/vidgear/gears/screengear.py +++ b/vidgear/gears/screengear.py @@ -106,7 +106,11 @@ def __init__( } # check whether user-defined dimensions are provided if screen_dims and len(screen_dims) == 4: - key_order = ("top", "left", "width", "height") + key_order = ( + ("top", "left", "width", "height") + if self.__backend != "dxcam" + else ("left", "top", "width", "height") + ) screen_dims = OrderedDict((k, screen_dims[k]) for k in key_order) logging and logger.debug( "Setting Capture-Area dimensions: {}".format(json.dumps(screen_dims)) @@ -165,11 +169,7 @@ def __init__( # raise error(s) for critical Class imports import_dependency_safe("pyscreenshot" if pysct is None else "") # reset backend if not provided - self.__backend = ( - "pil" - if self.__backend is None or self.__backend == "mss" - else self.__backend - ) + self.__backend = "pil" if self.__backend is None else self.__backend # check if valid backend assert ( self.__backend in pysct.backends() diff --git a/vidgear/gears/stabilizer.py b/vidgear/gears/stabilizer.py index 1bbe557e8..5bd998ab2 100644 --- a/vidgear/gears/stabilizer.py +++ b/vidgear/gears/stabilizer.py @@ -59,7 +59,6 @@ def __init__( crop_n_zoom=False, logging=False, ): - """ This constructor method initializes the object state and attributes of the Stabilizer class. @@ -67,7 +66,7 @@ def __init__( smoothing_radius (int): alter averaging window size. border_type (str): changes the extended border type. border_size (int): enables and set the value for extended border size to reduce the black borders. - crop_n_zoom (bool): enables croping and zooming of frames(to original size) to reduce the black borders. + crop_n_zoom (bool): enables cropping and zooming of frames(to original size) to reduce the black borders. logging (bool): enables/disables logging. """ # print current version @@ -88,9 +87,9 @@ def __init__( # 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.__path = None # handles path i.e cumulative sum of previous_2_current transformations along a axis + self.__transforms = [] # handles previous_2_current transformations [dx,dy,da] + self.__frame_transforms_smoothed = None # handles smoothed array of previous_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 @@ -193,24 +192,13 @@ def stabilize(self, frame): : ] # save gray frame clone for further processing - elif self.__frame_queue_indexes[-1] <= self.__smoothing_radius - 1: + 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: - # 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 - ) - # calculate deviation of path from smoothed path - deviation = self.__smoothed_path - self.__path - # save smoothed transformation - self.__frame_transforms_smoothed = self.frame_transform + deviation else: # start applying transformations self.__frame_queue.append(frame) # save frame to deque @@ -253,7 +241,7 @@ def __generate_transformations(self): status == 1 ] # previous - # calculate optimal affine transformation between pevious_2_current key-points + # calculate optimal affine transformation between previous_2_current key-points if self.__cv2_version == 3: # backward compatibility with OpenCV3 transformation = cv2.estimateRigidTransform( @@ -270,11 +258,11 @@ def __generate_transformations(self): # check if transformation is not None if not (transformation is None): - # pevious_2_current translation in x direction + # previous_2_current translation in x direction dx = transformation[0, 2] - # pevious_2_current translation in y direction + # previous_2_current translation in y direction dy = transformation[1, 2] - # pevious_2_current rotation in angle + # previous_2_current rotation in angle da = np.arctan2(transformation[1, 0], transformation[0, 0]) else: # otherwise zero it From 31ac907c5b1959be64b39e0b7575826bd5cdfba3 Mon Sep 17 00:00:00 2001 From: "ahmadibtsam07@gmail.com" Date: Fri, 1 Sep 2023 00:44:14 +0500 Subject: [PATCH 20/38] ffmpeg subprocess creation flag for exe files --- vidgear/gears/writegear.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/vidgear/gears/writegear.py b/vidgear/gears/writegear.py index 8c537d8a8..fc99d8e82 100644 --- a/vidgear/gears/writegear.py +++ b/vidgear/gears/writegear.py @@ -81,6 +81,7 @@ def __init__( compression_mode=True, custom_ffmpeg="", logging=False, + ffmpeg_subprocess_creation_window=True, **output_params ): @@ -92,6 +93,7 @@ def __init__( compression_mode (bool): selects the WriteGear's Primary Mode of Operation. custom_ffmpeg (str): assigns the location of custom path/directory for custom FFmpeg executables. logging (bool): enables/disables logging. + ffmpeg_subprocess_creation_window: enables/disables creating window for ffmpeg subprocess output_params (dict): provides the flexibility to control supported internal parameters and FFmpeg properities. """ # print current version @@ -122,6 +124,7 @@ def __init__( self.__initiate_process = ( True # handles initiate one-time process for generating pipeline ) + self.ffmpeg_subprocess_creation_window = ffmpeg_subprocess_creation_window self.__out_file = None # handles output gstpipeline_mode = False # handles GStreamer Pipeline Mode @@ -591,12 +594,25 @@ def __start_FFProcess(self, input_params, output_params): # log command in logging mode logger.debug("Executing FFmpeg command: `{}`".format(" ".join(cmd))) # In logging mode - self.__process = sp.Popen(cmd, stdin=sp.PIPE, stdout=sp.PIPE, stderr=None) + if platform.system() == "Windows" and not self.ffmpeg_subprocess_creation_window: + # this prevents ffmpeg creation window from opening when building exe files with pyinstaller on windows + self.__process = sp.Popen(cmd, stdin=sp.PIPE, stdout=sp.PIPE, stderr=None, creationflags=sp.DETACHED_PROCESS) + else : + 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=sp.DEVNULL, stderr=sp.STDOUT - ) + if platform.system() == "Windows" and not self.ffmpeg_subprocess_creation_window: + # this prevents ffmpeg creation window from opening when building exe files with pyinstaller on windows + self.__process = sp.Popen( + cmd, stdin=sp.PIPE, stdout=sp.DEVNULL, stderr=sp.STDOUT, creationflags=sp.DETACHED_PROCESS + ) + else: + self.__process = sp.Popen( + cmd, stdin=sp.PIPE, stdout=sp.DEVNULL, stderr=sp.STDOUT + ) + def __enter__(self): """ From a8d7d7ac159c1d91e30cda3ee33153e3fd4497a1 Mon Sep 17 00:00:00 2001 From: abhiTronix Date: Sat, 2 Sep 2023 17:52:48 +0530 Subject: [PATCH 21/38] =?UTF-8?q?=F0=9F=93=9D=20Docs:=20Updated=20Supporte?= =?UTF-8?q?d=20Dimensional=20Attributes=20in=20ScreenGear=20docs=20(Fixes?= =?UTF-8?q?=20#372)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - πŸ“ Updated information related to Supported Dimensional Attributes in ScreenGear docs. - ✏️ Fixed typos and context. - 🍱 Added new asset `screengear_region.png`. - πŸ“„ Fixed missing `compression_mode` flags in WriteGear API docs. --- docs/gears/screengear/params.md | 23 +++++++++--------- docs/gears/screengear/usage.md | 14 +++++++++++ .../gears/writegear/non_compression/params.md | 6 ++--- .../assets/images/screengear_region.png | Bin 0 -> 114860 bytes 4 files changed, 28 insertions(+), 15 deletions(-) create mode 100644 docs/overrides/assets/images/screengear_region.png diff --git a/docs/gears/screengear/params.md b/docs/gears/screengear/params.md index 2202c912d..c12f96ad5 100644 --- a/docs/gears/screengear/params.md +++ b/docs/gears/screengear/params.md @@ -131,17 +131,18 @@ ScreenGear(colorspace="COLOR_BGR2HSV") This parameter provides the flexibility to manually set the dimensions of capture screen area. -!!! info "Supported Dimensional Parameters" +!!! info "Supported Dimensional Attributes" - Supported Dimensional Parameters are as follows: + ScreenGear API takes `left`, `top`, `width`, `height` coordinates of the bounding box of capture screen area(ROI), similar to [PIL.ImageGrab.grab](https://pillow.readthedocs.io/en/stable/reference/ImageGrab.html), defined below: + +

+ ScreenGear ROI region +

* **`left`:** the x-coordinate of the upper-left corner of the region * **`top`:** the y-coordinate of the upper-left corner of the region - * **`width`:** the width of the region - * **`height`:** the height of the region - -!!! note "Additional Exclusive Attribute such as [`THREAD_TIMEOUT`](../../camgear/advanced/source_params/#exclusive-camgear-parameters) is also supported for this parameter." - + * **`width`:** the width of the complete region from left to the bottom-right corner of the region. + * **`height`:** the height of the complete region from top to the bottom-right corner of the region. **Data-Type:** Dictionary @@ -149,15 +150,13 @@ This parameter provides the flexibility to manually set the dimensions of captur **Usage:** -The desired dimensional parameters can be passed to ScreenGear API by formatting them as attributes, as follows: - -!!! tip "More information about screen dimensioning can be found [here ➢](https://python-mss.readthedocs.io/api.html#mss.tools.mss.base.MSSMixin.monitors)" +The desired dimensional coordinates parameters can be passed to ScreenGear API by formatting them as attributes, as follows: ```python # formatting dimensional parameters as dictionary attributes options = {'top': 40, 'left': 0, 'width': 100, 'height': 100} -# assigning it w.r.t monitor=1 -ScreenGear(monitor=1, **options) +# assigning it +ScreenGear(**options) ```   diff --git a/docs/gears/screengear/usage.md b/docs/gears/screengear/usage.md index 8400dddfe..52bb5f8a8 100644 --- a/docs/gears/screengear/usage.md +++ b/docs/gears/screengear/usage.md @@ -77,6 +77,20 @@ stream.stop() ScreenGear API provides us the flexibility to directly set the dimensions of capturing-area of the screen. These dimensions can be easily applied to ScreenGear API through its [`options`](../params/#options) dictionary parameter by formatting them as its attributes. + +??? info "Supported Dimensional Attributes" + + ScreenGear API takes `left`, `top`, `width`, `height` coordinates of the bounding box of capture screen area(ROI), similar to [PIL.ImageGrab.grab](https://pillow.readthedocs.io/en/stable/reference/ImageGrab.html), defined below: + +

+ ScreenGear ROI region +

+ + * **`left`:** the x-coordinate of the upper-left corner of the region + * **`top`:** the y-coordinate of the upper-left corner of the region + * **`width`:** the width of the complete region from left to the bottom-right corner of the region. + * **`height`:** the height of the complete region from top to the bottom-right corner of the region. + The complete usage example is as follows: diff --git a/docs/gears/writegear/non_compression/params.md b/docs/gears/writegear/non_compression/params.md index b9ae65f2a..034e7b351 100644 --- a/docs/gears/writegear/non_compression/params.md +++ b/docs/gears/writegear/non_compression/params.md @@ -149,7 +149,7 @@ To assign desired parameters in Non-Compression Mode, you can format it as dicti # format parameter as dictionary attribute output_params = {"-fps":30} # and then, assign it -WriteGear(output = 'output.mp4', **output_params) +WriteGear(output = 'output.mp4', compression_mode=False, **output_params) ``` !!! example "Its usage example can be found [here ➢](../usage/#using-non-compression-mode-with-videocapture-gears)." @@ -170,7 +170,7 @@ To select desired FOURCC codec in Non-Compression Mode, you can format it as dic # format codec as dictionary attribute output_params = {"-fourcc":"MJPG"} # and then, assign it -WriteGear(output = 'output.mp4', **output_params) +WriteGear(output = 'output.mp4', compression_mode=False, **output_params) ``` !!! example "Its usage example can be found [here ➢](../usage/#using-non-compression-mode-with-videocapture-gears)." @@ -188,7 +188,7 @@ This parameter enables logging _(if `True`)_, essential for debugging. **Usage:** ```python -WriteGear(output = 'output.mp4', logging=True) +WriteGear(output = 'output.mp4', compression_mode=False, logging=True) ```   diff --git a/docs/overrides/assets/images/screengear_region.png b/docs/overrides/assets/images/screengear_region.png new file mode 100644 index 0000000000000000000000000000000000000000..a884fb261befb1042cb788fec3f9b82c6f5382c6 GIT binary patch literal 114860 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 Date: Sat, 2 Sep 2023 22:59:58 +0530 Subject: [PATCH 22/38] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20NetGear:=20Added=20t?= =?UTF-8?q?imeout=20support=20for=20`pattern=3D2`=20in=20`reciever=5Fmode`?= =?UTF-8?q?.=20(Fixes=20#374)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ✨ Added new `subscriber_timeout` integer optional parameter to support timeout with `pattern=2` or PUBLISHER-SUBSCRIBER pattern. - πŸ§‘β€πŸ’» Receiver will exit safely if timeout defined(any value(in milliseconds) > 0), and timeout occurs in Receiver Mode with `pattern=2`. - 🎨 Note: Default behavior still is to block the thread till infinite time. - πŸ”Š Updated logging. --- vidgear/gears/netgear.py | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/vidgear/gears/netgear.py b/vidgear/gears/netgear.py index 4b4f423c8..3ef639a84 100644 --- a/vidgear/gears/netgear.py +++ b/vidgear/gears/netgear.py @@ -240,6 +240,9 @@ def __init__( self.__max_retries = 3 # request timeout self.__request_timeout = 4000 # 4 secs + else: + # subscriber timeout + self.__subscriber_timeout = None # Handle user-defined options dictionary values # reformat dictionary @@ -383,6 +386,7 @@ def __init__( self.__max_retries = value else: logger.warning("Invalid `max_retries` value skipped!") + # assign request timeout in synchronous patterns elif key == "request_timeout" and isinstance(value, int) and pattern < 2: if value >= 4: @@ -390,6 +394,15 @@ def __init__( else: logger.warning("Invalid `request_timeout` value skipped!") + # assign subscriber timeout + elif ( + key == "subscriber_timeout" and isinstance(value, int) and pattern == 2 + ): + if value > 0: + self.__subscriber_timeout = value * 1000 # covert to milliseconds + else: + logger.warning("Invalid `request_timeout` value skipped!") + # handle ZMQ flags elif key == "flag" and isinstance(value, int): self.__msg_flag = value @@ -606,10 +619,15 @@ def __init__( # enable CURVE connection for this socket self.__msg_socket.curve_server = True - # define exclusive socket options for patterns + # define exclusive socket options for `patterns=2` if self.__pattern == 2: self.__msg_socket.setsockopt_string(zmq.SUBSCRIBE, "") - self.__msg_socket.setsockopt(zmq.LINGER, 0) + self.__subscriber_timeout and self.__msg_socket.setsockopt( + zmq.RCVTIMEO, self.__subscriber_timeout + ) + self.__subscriber_timeout and self.__msg_socket.setsockopt( + zmq.LINGER, 0 + ) # if multiserver_mode is enabled, then assign port addresses to zmq socket if self.__multiserver_mode: @@ -638,12 +656,17 @@ def __init__( ) self.__msg_pattern = msg_pattern[1] self.__poll.register(self.__msg_socket, zmq.POLLIN) - self.__logging and logger.debug( "Reliable transmission is enabled for this pattern with max-retries: {} and timeout: {} secs.".format( self.__max_retries, self.__request_timeout / 1000 ) ) + else: + self.__logging and self.__subscriber_timeout and logger.debug( + "Timeout: {} secs is enabled for this system.".format( + self.__subscriber_timeout / 1000 + ) + ) except Exception as e: # otherwise log and raise error @@ -981,7 +1004,14 @@ def __recv_handler(self): continue else: - msg_json = self.__msg_socket.recv_json(flags=self.__msg_flag) + try: + msg_json = self.__msg_socket.recv_json(flags=self.__msg_flag) + except zmq.ZMQError as e: + if e.errno == zmq.EAGAIN: + logger.critical("Connection Timeout. Exiting!") + self.__terminate = True + self.__queue.append(None) + break # check if terminate_flag` received if msg_json["terminate_flag"]: From 5d9c5a31c40c5da12519200ddb03a3884553ede3 Mon Sep 17 00:00:00 2001 From: abhiTronix Date: Sun, 3 Sep 2023 23:32:17 +0530 Subject: [PATCH 23/38] =?UTF-8?q?=F0=9F=A7=B1=20WriteGear:=20Replaced=20cl?= =?UTF-8?q?ass=20parameter=20with=20optional=20flag.=20(Fixes=20#377)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ✨ Added new `-disable_ffmpeg_window` optional Boolean flag to enable patch that prevents FFmpeg creation window from opening when building .exe files on Windows OS. - ⚑️Note: Now `-disable_ffmpeg_window` optional Boolean flag is only available on Windows OS with logging disabled(`logging=False`) in compression mode. - 🚩 Removed `ffmpeg_subprocess_creation_window` class parameter. - πŸ₯… Disabled this patch for logging mode. - πŸ”₯ Removed redundant code. - πŸ”Š Updated logging. --- vidgear/gears/writegear.py | 53 +++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/vidgear/gears/writegear.py b/vidgear/gears/writegear.py index fc99d8e82..370fcea47 100644 --- a/vidgear/gears/writegear.py +++ b/vidgear/gears/writegear.py @@ -81,10 +81,8 @@ def __init__( compression_mode=True, custom_ffmpeg="", logging=False, - ffmpeg_subprocess_creation_window=True, **output_params ): - """ This constructor method initializes the object state and attributes of the WriteGear class. @@ -93,7 +91,7 @@ def __init__( compression_mode (bool): selects the WriteGear's Primary Mode of Operation. custom_ffmpeg (str): assigns the location of custom path/directory for custom FFmpeg executables. logging (bool): enables/disables logging. - ffmpeg_subprocess_creation_window: enables/disables creating window for ffmpeg subprocess + ffmpeg_subprocess_creation_window: output_params (dict): provides the flexibility to control supported internal parameters and FFmpeg properities. """ # print current version @@ -124,7 +122,9 @@ def __init__( self.__initiate_process = ( True # handles initiate one-time process for generating pipeline ) - self.ffmpeg_subprocess_creation_window = ffmpeg_subprocess_creation_window + self.__ffmpeg_window_disabler_patch = ( + False # handles disabling window for ffmpeg subprocess on Windows + ) self.__out_file = None # handles output gstpipeline_mode = False # handles GStreamer Pipeline Mode @@ -247,6 +247,22 @@ def __init__( True if ("-i" in self.__output_parameters) else False ) + # handles disabling window for ffmpeg subprocess on Windows OS (only for Compression mode) + # this patch prevents ffmpeg creation window from opening when building exe files + ffmpeg_window_disabler_patch = self.__output_parameters.pop( + "-disable_ffmpeg_window", False + ) + # check if value is valid + if not self.__os_windows or logging: + logging.warning( + "Optional `-disable_ffmpeg_window` flag is only available on Windows OS with `logging=False`. Discarding!" + ) + elif isinstance(ffmpeg_window_disabler_patch, bool): + self.__ffmpeg_window_disabler_patch = ffmpeg_window_disabler_patch + else: + # handle improper values + self.__ffmpeg_window_disabler_patch = False + # validate the FFmpeg path/binaries and returns valid executable FFmpeg # location/path (also auto-downloads static binaries on Windows OS) self.__ffmpeg = get_valid_ffmpeg_path( @@ -355,7 +371,6 @@ def __init__( ) def write(self, frame, rgb_mode=False): - """ Pipelines `ndarray` frames to respective API _(**FFmpeg** in Compression Mode & **OpenCV's VideoWriter API** in Non-Compression Mode)_. @@ -519,7 +534,6 @@ def __PreprocessFFParams(self, channels, dtype=None, rgb=False): ) def __start_FFProcess(self, input_params, output_params): - """ An Internal method that launches FFmpeg subprocess pipeline in Compression Mode for pipelining frames to `stdin`. @@ -594,25 +608,18 @@ def __start_FFProcess(self, input_params, output_params): # log command in logging mode logger.debug("Executing FFmpeg command: `{}`".format(" ".join(cmd))) # In logging mode - if platform.system() == "Windows" and not self.ffmpeg_subprocess_creation_window: - # this prevents ffmpeg creation window from opening when building exe files with pyinstaller on windows - self.__process = sp.Popen(cmd, stdin=sp.PIPE, stdout=sp.PIPE, stderr=None, creationflags=sp.DETACHED_PROCESS) - else : - 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 - if platform.system() == "Windows" and not self.ffmpeg_subprocess_creation_window: - # this prevents ffmpeg creation window from opening when building exe files with pyinstaller on windows - self.__process = sp.Popen( - cmd, stdin=sp.PIPE, stdout=sp.DEVNULL, stderr=sp.STDOUT, creationflags=sp.DETACHED_PROCESS - ) - else: - self.__process = sp.Popen( - cmd, stdin=sp.PIPE, stdout=sp.DEVNULL, stderr=sp.STDOUT - ) - + self.__process = sp.Popen( + cmd, + stdin=sp.PIPE, + stdout=sp.DEVNULL, + stderr=sp.STDOUT, + creationflags=( # this prevents ffmpeg creation window from opening when building exe files on Windows + sp.DETACHED_PROCESS if self.__ffmpeg_window_disabler_patch else 0 + ), + ) def __enter__(self): """ From 003fe76d429b4b069aa324838184f4998af3a6b7 Mon Sep 17 00:00:00 2001 From: abhiTronix Date: Mon, 4 Sep 2023 00:48:47 +0530 Subject: [PATCH 24/38] =?UTF-8?q?=F0=9F=93=9D=20Docs:=20Added=20doc=20for?= =?UTF-8?q?=20`subscriber=5Ftimeout`=20optional=20Integer=20parameter=20in?= =?UTF-8?q?=20NetGear.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/gears/netgear/params.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/gears/netgear/params.md b/docs/gears/netgear/params.md index c9562e362..59ddd3186 100644 --- a/docs/gears/netgear/params.md +++ b/docs/gears/netgear/params.md @@ -169,7 +169,9 @@ This parameter provides the flexibility to alter various NetGear API's internal * **`max_retries`**(_integer_): This internal attribute controls the maximum retries before Server/Client exit itself, if it's unable to get any response/reply from the socket before a certain amount of time, when synchronous messaging patterns like (`zmq.PAIR` & `zmq.REQ/zmq.REP`) are being used. It's value can anything greater than `0`, and its default value is `3`. - * **`request_timeout`**(_integer_): This internal attribute controls the timeout value _(in seconds)_, after which the Server/Client exit itself if it's unable to get any response/reply from the socket, when synchronous messaging patterns like (`zmq.PAIR` & `zmq.REQ/zmq.REP`) are being used. It's value can anything greater than `0`, and its default value is `10` seconds. + * **`request_timeout`**(_integer_): This internal attribute controls the timeout value _(in seconds)_, after which the Server/Client exit itself with `Nonetype` value if it's unable to get any response/reply from the socket, when synchronous messaging patterns like (`zmq.PAIR` & `zmq.REQ/zmq.REP`) are being used. It's value can anything greater than `0`, and its default value is `10` seconds. + + * **`subscriber_timeout`**(_integer_): Similar to `request_timeout`, this internal attribute also controls the timeout value _(in seconds)_ but for non-synchronous `zmq.PU/zmq.SUB` pattern in compression mode, after which the Client(Subscriber) exit itself with `Nonetype` value if it's unable to get any response from the socket. It's value can anything greater than `0`, and its disabled by default _(meaning the client will wait forever for response)_. * **`flag`**(_integer_): This PyZMQ attribute value can be either `0` or `zmq.NOBLOCK`_( i.e. 1)_. More information can be found [here ➢](https://pyzmq.readthedocs.io/en/latest/api/zmq.html). From 37e82e93b2956d64785a2cd7c7f1d8a8941415e9 Mon Sep 17 00:00:00 2001 From: abhiTronix Date: Mon, 4 Sep 2023 00:57:49 +0530 Subject: [PATCH 25/38] =?UTF-8?q?=F0=9F=93=9D=20Docs:=20Added=20doc=20for?= =?UTF-8?q?=20`disable=5Fffmpeg=5Fwindow`=20optional=20Boolean=20parameter?= =?UTF-8?q?=20in=20WriteGear.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/gears/writegear/compression/params.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/gears/writegear/compression/params.md b/docs/gears/writegear/compression/params.md index f81393aa3..d2949d451 100644 --- a/docs/gears/writegear/compression/params.md +++ b/docs/gears/writegear/compression/params.md @@ -176,6 +176,17 @@ This parameter allows us to exploit almost all FFmpeg supported parameters effor "-ffpreheaders": ["-re"], # executes as `ffmpeg -re ` } ``` + + * **`-disable_ffmpeg_window`** _(bool)_: sets a special flag to enable detached subprocess creation on Windows OS, and can be useful while creating an `.exe` file for a python script that uses WriteGear API. On Windows, in certain cases, even after creating the `.exe` file in windowed mode or no-console mode, the FFmpeg commandline window would pop up while its being used by WriteGear API. Its usage is as follows: + + ??? new "New in v0.3.2" + This feature was added in `v0.3.2`. + + !!! warning "`-disable_ffmpeg_window` is only available on Windows OS with logging disabled(`logging=False`) in compression mode." + + ```python + output_params = {"-disable_ffmpeg_window": True} # disables FFmpeg creation window + ``` * **`-disable_force_termination`** _(bool)_: sets a special flag to manually disable the default forced-termination behaviour in WriteGear API when `-i` FFmpeg parameter is used _(For more details, see issue: #149)_. Its usage is as follows: From be2ba3970c1f835e3f6454d9efa534c2b464e46d Mon Sep 17 00:00:00 2001 From: abhiTronix Date: Mon, 4 Sep 2023 01:02:06 +0530 Subject: [PATCH 26/38] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Docs:=20Fixed=20typo?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/gears/netgear/params.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/gears/netgear/params.md b/docs/gears/netgear/params.md index 59ddd3186..949069b2f 100644 --- a/docs/gears/netgear/params.md +++ b/docs/gears/netgear/params.md @@ -171,7 +171,7 @@ This parameter provides the flexibility to alter various NetGear API's internal * **`request_timeout`**(_integer_): This internal attribute controls the timeout value _(in seconds)_, after which the Server/Client exit itself with `Nonetype` value if it's unable to get any response/reply from the socket, when synchronous messaging patterns like (`zmq.PAIR` & `zmq.REQ/zmq.REP`) are being used. It's value can anything greater than `0`, and its default value is `10` seconds. - * **`subscriber_timeout`**(_integer_): Similar to `request_timeout`, this internal attribute also controls the timeout value _(in seconds)_ but for non-synchronous `zmq.PU/zmq.SUB` pattern in compression mode, after which the Client(Subscriber) exit itself with `Nonetype` value if it's unable to get any response from the socket. It's value can anything greater than `0`, and its disabled by default _(meaning the client will wait forever for response)_. + * **`subscriber_timeout`**(_integer_): Similar to `request_timeout`, this internal attribute also controls the timeout value _(in seconds)_ but for non-synchronous `zmq.PUB/zmq.SUB` pattern in compression mode, after which the Client(Subscriber) exit itself with `Nonetype` value if it's unable to get any response from the socket. It's value can anything greater than `0`, and its disabled by default _(meaning the client will wait forever for response)_. * **`flag`**(_integer_): This PyZMQ attribute value can be either `0` or `zmq.NOBLOCK`_( i.e. 1)_. More information can be found [here ➢](https://pyzmq.readthedocs.io/en/latest/api/zmq.html). From 99e781369edd69d84ef24d9a1013184d92c99841 Mon Sep 17 00:00:00 2001 From: abhiTronix Date: Mon, 4 Sep 2023 09:59:54 +0530 Subject: [PATCH 27/38] =?UTF-8?q?=F0=9F=91=B7=20CI:=20Updated=20tests=20fo?= =?UTF-8?q?r=20`subscriber=5Ftimeout`=20optional=20Integer=20parameter=20i?= =?UTF-8?q?n=20NetGear.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vidgear/tests/network_tests/test_netgear.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/vidgear/tests/network_tests/test_netgear.py b/vidgear/tests/network_tests/test_netgear.py index f70b98f85..9cf09d18f 100644 --- a/vidgear/tests/network_tests/test_netgear.py +++ b/vidgear/tests/network_tests/test_netgear.py @@ -133,7 +133,13 @@ def test_patterns(pattern): Testing NetGear different messaging patterns """ # define parameters - options = {"flag": 0, "copy": False, "track": False, "jpeg_compression": False} + options = { + "flag": 0, + "copy": False, + "track": False, + "jpeg_compression": False, + "subscriber_timeout": 5, + } # initialize frame_server = None stream = None @@ -345,6 +351,7 @@ def test_secure_mode(pattern, security_mech, custom_cert_location, overwrite_cer { "bidirectional_mode": True, "jpeg_compression": True, + "subscriber_timeout": 0, }, ), ], @@ -455,6 +462,7 @@ def test_bidirectional_mode(pattern, target_data, options): { "multiserver_mode": True, "ssh_tunnel_mode": "new@sdf.org", + "subscriber_timeout": 5, }, ), ], From bae90f204eb10e07e019a988136bbbfe8a344dfb Mon Sep 17 00:00:00 2001 From: abhiTronix Date: Mon, 4 Sep 2023 10:01:44 +0530 Subject: [PATCH 28/38] =?UTF-8?q?=F0=9F=92=9A=20CI:=20Fixed=20NetGear=20te?= =?UTF-8?q?sts.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vidgear/tests/network_tests/test_netgear.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vidgear/tests/network_tests/test_netgear.py b/vidgear/tests/network_tests/test_netgear.py index 9cf09d18f..cab91ea9a 100644 --- a/vidgear/tests/network_tests/test_netgear.py +++ b/vidgear/tests/network_tests/test_netgear.py @@ -351,7 +351,7 @@ def test_secure_mode(pattern, security_mech, custom_cert_location, overwrite_cer { "bidirectional_mode": True, "jpeg_compression": True, - "subscriber_timeout": 0, + "subscriber_timeout": 5, }, ), ], @@ -462,7 +462,7 @@ def test_bidirectional_mode(pattern, target_data, options): { "multiserver_mode": True, "ssh_tunnel_mode": "new@sdf.org", - "subscriber_timeout": 5, + "subscriber_timeout": 0, }, ), ], From 5c554c4b63e99cadd183f8eb570cf7457dae46d6 Mon Sep 17 00:00:00 2001 From: abhiTronix Date: Mon, 4 Sep 2023 10:07:36 +0530 Subject: [PATCH 29/38] =?UTF-8?q?=F0=9F=91=B7=20CI:=20Updated=20tests=20fo?= =?UTF-8?q?r=20`disable=5Fffmpeg=5Fwindow`=20optional=20Boolean=20paramete?= =?UTF-8?q?r=20in=20WriteGear.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vidgear/tests/writer_tests/test_compression_mode.py | 10 +++++++++- .../tests/writer_tests/test_non_compression_mode.py | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/vidgear/tests/writer_tests/test_compression_mode.py b/vidgear/tests/writer_tests/test_compression_mode.py index aac1fa472..262877ded 100644 --- a/vidgear/tests/writer_tests/test_compression_mode.py +++ b/vidgear/tests/writer_tests/test_compression_mode.py @@ -229,6 +229,7 @@ def test_output_dimensions(): output_params = { "-output_dimensions": dimensions, "-ffmpeg_download_path": tempfile.gettempdir(), + "-disable_ffmpeg_window": True, } else: output_params = {"-output_dimensions": dimensions} @@ -264,7 +265,13 @@ def test_output_dimensions(): ( "Output2.mp4", "", - {"-vcodec": "libx264", "-crf": 0, "-preset": "fast", "-ffpreheaders": False}, + { + "-vcodec": "libx264", + "-crf": 0, + "-preset": "fast", + "-ffpreheaders": False, + "-disable_ffmpeg_window": True, + }, True, ), ( @@ -276,6 +283,7 @@ def test_output_dimensions(): "-crf": 0, "-preset": "veryfast", "-ffpreheaders": ["-re"], + "-disable_ffmpeg_window": "Invalid", }, True, ), diff --git a/vidgear/tests/writer_tests/test_non_compression_mode.py b/vidgear/tests/writer_tests/test_non_compression_mode.py index 69f555e60..c9c45bfd2 100644 --- a/vidgear/tests/writer_tests/test_non_compression_mode.py +++ b/vidgear/tests/writer_tests/test_non_compression_mode.py @@ -134,6 +134,7 @@ def test_write(conversion): "-fourcc": "DIVX", "-fps": 25, "-backend": "CAP_FFMPEG", + "-disable_ffmpeg_window": True, "-color": True, "-gst_pipeline_mode": False, }, From e065ecfc33aeef19d83070ff30d4cffdf0d5e143 Mon Sep 17 00:00:00 2001 From: abhiTronix Date: Mon, 4 Sep 2023 18:23:37 +0530 Subject: [PATCH 30/38] =?UTF-8?q?=F0=9F=90=9B=20WriteGear:=20Fixed=20typo?= =?UTF-8?q?=20bug.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vidgear/gears/writegear.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vidgear/gears/writegear.py b/vidgear/gears/writegear.py index 370fcea47..1d041d7a6 100644 --- a/vidgear/gears/writegear.py +++ b/vidgear/gears/writegear.py @@ -254,7 +254,7 @@ def __init__( ) # check if value is valid if not self.__os_windows or logging: - logging.warning( + logger.warning( "Optional `-disable_ffmpeg_window` flag is only available on Windows OS with `logging=False`. Discarding!" ) elif isinstance(ffmpeg_window_disabler_patch, bool): From d6c56871ec94668644a802a906a85bc5d99380e2 Mon Sep 17 00:00:00 2001 From: abhiTronix Date: Tue, 5 Sep 2023 00:30:11 +0530 Subject: [PATCH 31/38] =?UTF-8?q?=F0=9F=9A=9A=20Bash=20Script:=20Moved=20F?= =?UTF-8?q?Fmpeg=20static=20binaries=20from=20GitHub=20to=20Gitlab.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/bash/prepare_dataset.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/bash/prepare_dataset.sh b/scripts/bash/prepare_dataset.sh index e7fcdac8b..8701ef4e1 100644 --- a/scripts/bash/prepare_dataset.sh +++ b/scripts/bash/prepare_dataset.sh @@ -26,7 +26,7 @@ mkdir -p "$TMPFOLDER"/Downloads mkdir -p "$TMPFOLDER"/Downloads/{FFmpeg_static,Test_videos} # Acknowledging machine architecture -MACHINE_BIT=$(uname -m) +# MACHINE_BIT=$(uname -m) #Defining alternate ffmpeg static binaries date/version ALTBINARIES_DATE="12-07-2022" @@ -48,12 +48,12 @@ msys*) esac #Download and Configure FFmpeg Static -cd "$TMPFOLDER"/Downloads/FFmpeg_static +cd "$TMPFOLDER"/Downloads/FFmpeg_static || exit if [ $OS_NAME = "linux" ]; then echo "Downloading Linux64 Static FFmpeg Binaries..." - curl -LO https://github.com/abhiTronix/ffmpeg-static-builds/raw/master/$ALTBINARIES_DATE/linux/ffmpeg-git-amd64-static.tar.xz + curl -LO https://gitlab.com/abhiTronix/ffmpeg-static-builds/-/raw/master/$ALTBINARIES_DATE/linux/ffmpeg-git-amd64-static.tar.xz tar -xJf ffmpeg-git-amd64-static.tar.xz rm *.tar.* mv ffmpeg* ffmpeg @@ -61,7 +61,7 @@ if [ $OS_NAME = "linux" ]; then elif [ $OS_NAME = "windows" ]; then echo "Downloading Win64 Static FFmpeg Binaries..." - curl -LO https://github.com/abhiTronix/ffmpeg-static-builds/raw/master/$ALTBINARIES_DATE/windows/ffmpeg-latest-win64-static.zip + curl -LO https://gitlab.com/abhiTronix/ffmpeg-static-builds/-/raw/master/$ALTBINARIES_DATE/windows/ffmpeg-latest-win64-static.zip unzip -qq ffmpeg-latest-win64-static.zip rm ffmpeg-latest-win64-static.zip mv ffmpeg-latest-win64-static ffmpeg @@ -69,7 +69,7 @@ elif [ $OS_NAME = "windows" ]; then else echo "Downloading MacOS64 Static FFmpeg Binary..." - curl -LO https://github.com/abhiTronix/ffmpeg-static-builds/raw/master/$ALTBINARIES_DATE/macOS/ffmpeg-latest-macos64-static.zip + curl -LO https://gitlab.com/abhiTronix/ffmpeg-static-builds/-/raw/master/$ALTBINARIES_DATE/macOS/ffmpeg-latest-macos64-static.zip unzip -qq ffmpeg-latest-macos64-static.zip rm ffmpeg-latest-macos64-static.zip mv ffmpeg-latest-macos64-static ffmpeg From c488c6c91c5e13cd936d86fa5c1d1dfaf75f1a1c Mon Sep 17 00:00:00 2001 From: abhiTronix Date: Tue, 5 Sep 2023 15:50:54 +0530 Subject: [PATCH 32/38] =?UTF-8?q?=E2=98=82=EF=B8=8F=20CI:=20Increased=20co?= =?UTF-8?q?de=20coverage.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vidgear/tests/network_tests/test_netgear.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vidgear/tests/network_tests/test_netgear.py b/vidgear/tests/network_tests/test_netgear.py index cab91ea9a..6875dc76a 100644 --- a/vidgear/tests/network_tests/test_netgear.py +++ b/vidgear/tests/network_tests/test_netgear.py @@ -351,7 +351,6 @@ def test_secure_mode(pattern, security_mech, custom_cert_location, overwrite_cer { "bidirectional_mode": True, "jpeg_compression": True, - "subscriber_timeout": 5, }, ), ], @@ -655,6 +654,7 @@ def test_multiclient_mode(pattern): }, {"max_retries": 2, "request_timeout": 4, "multiclient_mode": True}, {"max_retries": 2, "request_timeout": -1, "multiserver_mode": True}, + {"subscriber_timeout": 4}, ], ) def test_client_reliablity(options): @@ -666,7 +666,7 @@ def test_client_reliablity(options): try: # define params client = NetGear( - pattern=1, + pattern=2 if "subscriber_timeout" in options.keys() else 1, port=[5587] if "multiserver_mode" in options.keys() else 6657, receive_mode=True, logging=True, @@ -755,8 +755,8 @@ def test_server_reliablity(options): @pytest.mark.parametrize( "server_ports, client_ports, options", [ - (0, 5555, {"multiserver_mode": True}), - (5555, 0, {"multiclient_mode": True}), + (None, 5555, {"multiserver_mode": True}), + (5555, None, {"multiclient_mode": True}), ], ) @pytest.mark.xfail(raises=ValueError) From 917228bf4b1529d77993b8a9238c6f4333374b5c Mon Sep 17 00:00:00 2001 From: abhiTronix Date: Sun, 10 Sep 2023 09:20:10 +0530 Subject: [PATCH 33/38] =?UTF-8?q?=F0=9F=93=9D=20Docs:=20Updated=20Zenodo?= =?UTF-8?q?=20badge.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 12 ++++++------ docs/index.md | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 9fc6418d4..c5b8e8b71 100644 --- a/README.md +++ b/README.md @@ -650,7 +650,7 @@ It is something I am doing with my own free time. But so much more needs to be d Here is a Bibtex entry you can use to cite this project in a publication: -[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.7571405.svg)](https://doi.org/10.5281/zenodo.7571405) +[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.8174694.svg)](https://doi.org/10.5281/zenodo.8174694) ```BibTeX @software{vidgear, @@ -665,13 +665,13 @@ Here is a Bibtex entry you can use to cite this project in a publication: Benjamin Lowe and MickaΓ«l Schoentgen and Renaud Bouckenooghe}, - title = {abhiTronix/vidgear: VidGear v0.3.0}, - month = jan, + title = {abhiTronix/vidgear: VidGear v0.3.1}, + month = jul, year = 2023, publisher = {Zenodo}, - version = {vidgear-0.3.0}, - doi = {10.5281/zenodo.7571405}, - url = {https://doi.org/10.5281/zenodo.7571405} + version = {vidgear-0.3.1}, + doi = {10.5281/zenodo.8174694}, + url = {https://doi.org/10.5281/zenodo.8174694} } ``` diff --git a/docs/index.md b/docs/index.md index 201fad8dc..a52c5346c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -127,7 +127,7 @@ It is something I am doing with my own free time. But so much more needs to be d Here is a Bibtex entry you can use to cite this project in a publication: -[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.7571405.svg)](https://doi.org/10.5281/zenodo.7571405) +[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.8174694.svg)](https://doi.org/10.5281/zenodo.8174694) ```BibTeX @software{vidgear, @@ -142,13 +142,13 @@ Here is a Bibtex entry you can use to cite this project in a publication: Benjamin Lowe and MickaΓ«l Schoentgen and Renaud Bouckenooghe}, - title = {abhiTronix/vidgear: VidGear v0.3.0}, - month = jan, + title = {abhiTronix/vidgear: VidGear v0.3.1}, + month = jul, year = 2023, publisher = {Zenodo}, - version = {vidgear-0.3.0}, - doi = {10.5281/zenodo.7571405}, - url = {https://doi.org/10.5281/zenodo.7571405} + version = {vidgear-0.3.1}, + doi = {10.5281/zenodo.8174694}, + url = {https://doi.org/10.5281/zenodo.8174694} } ``` From be983574c345fdb05d71e92452b134f6d881ecbc Mon Sep 17 00:00:00 2001 From: abhiTronix Date: Sun, 10 Sep 2023 09:25:14 +0530 Subject: [PATCH 34/38] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Setup.py:=20Fixed=20?= =?UTF-8?q?typo=20in=20metadata.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 81be564b5..465d60516 100644 --- a/setup.py +++ b/setup.py @@ -165,7 +165,7 @@ def latest_version(package_name): "dash", "hls", "Video Processing", - "Video Stablization", + "Video Stabilization", "Computer Vision", "Video Streaming", "raspberrypi", From 81a9cd68ad2dfd4b507ac78fb54d3d033cd9c026 Mon Sep 17 00:00:00 2001 From: abhiTronix Date: Sun, 10 Sep 2023 16:37:04 +0530 Subject: [PATCH 35/38] =?UTF-8?q?=F0=9F=93=9D=20Docs:=20Updated=20changelo?= =?UTF-8?q?g.md=20and=20fixed=20minor=20typos.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/changelog.md | 78 ++++++++++++++++++++++++++++++++++++++ vidgear/gears/writegear.py | 5 +-- 2 files changed, 80 insertions(+), 3 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index dfa655e79..0f2eb118f 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -20,6 +20,84 @@ limitations under the License. # Release Notes +## v0.3.2 (2023-09-10) + +??? tip "New Features" + - [x] **NetGear:** + * Added new `kill` parameter to `close()` method to forcefully kill ZMQ context instead of graceful exit only in the `receive` mode. + * Added new `subscriber_timeout` integer optional parameter to support timeout with `pattern=2` or PUBLISHER-SUBSCRIBER pattern. + + Receiver will exit safely if timeout defined(any value(in milliseconds) > 0), and timeout occurs in Receiver Mode with `pattern=2`. + + πŸ’¬ Note: Default behavior still is to block the thread till infinite time. + - [x] **WriteGear:** + * Added new `-disable_ffmpeg_window` optional Boolean flag to enable patch that prevents FFmpeg creation window from opening when building `.exe` files on Windows OS. _(PR by @ibtsam3301)_ + + πŸ’¬ Note: `-disable_ffmpeg_window` optional Boolean flag is only available on Windows OS with logging disabled(`logging=False`) in compression mode. + + Use Case: This flag can be useful while creating an `.exe` file for a python script that uses WriteGear API. On windows even after creating the `.exe` file in windowed mode or no-console mode, the `ffmpeg.exe` command line window would pop up while its being used by WriteGear API. + - [x] **Setup.py** + * Added official support for python `3.11.x` legacies. + * Bumped version to `0.3.1`. + - [x] **Docs** + * Added doc for `subscriber_timeout` optional Integer parameter in NetGear. + * Added doc for `disable_ffmpeg_window` optional Boolean parameter in WriteGear. + * Added new asset `screengear_region.png`. + - [x] **CI** + * Added python 3.11 legacy support for MacOS, Windows and Linux environments. + * Added kill argument to close() method in various NetGear tests. + +??? success "Updates/Improvements" + - [x] Asyncio: + * Formatted TemplateResponse class parameters w.r.t new changes in backend Starlette API. + - [x] Setup.py: + * Readded latest patch to `uvicorn`, `starlette`, `pyzmq` dependencies. + * Removed `3.7` legacy from Programming Language metadata. + - [x] Maintenance: + * Added GitHub sponsors and dropped liberapay from `Funding.yml`. + * Removed redundant code. + - [x] Docs: + * Updated information related to Supported Dimensional Attributes in ScreenGear docs. + * Updated minimum python to version `3.8` while installing vidgear in docs. + * Updated API-specific dependencies in docs. + * Updated changelog.md + - [x] CI: + * Updated Azure Pipeline workflow. + * Updated Appveyor Pipeline workflow. + * Updated GitHub Actions Pipeline workflow. + * Migrated python version to `3.9` in `deploy_docs.yml` workflow. + * Removed deprecated python `3.7` legacy support. + * Increased code coverage by updating tests. + * Updated tests for `subscriber_timeout` optional Integer parameter in NetGear. + * Updated tests for `disable_ffmpeg_window` optional Boolean parameter in WriteGear. + +??? danger "Breaking Updates/Changes" + - [ ] Setup.py: + * Removed support for python-3.7 legacies + + Raised `python_requires` to `>=3.8`. Thereby python `3.7` and any before legacy are no longer supported. + +??? bug "Bug-fixes" + - [x] ScreenGear: + * Fixed swapped region dimensions bug with dxcam backend. + * Fixed "mss" backend disabled when `monitor` parameter is not defined. + - [x] Setup.py: + * Starting from version `8.0.0`, the python-mss library dropped support for Python `3.7`, so as a temporary measure, `mss` dependency has been pinned to version `7.0.1`. + - [x] Docs: + * Fixed missing `compression_mode` flags in WriteGear API docs. + * Fixed missing hyperlinks. + * Fixed typos and context. + - [x] CI: + * Temporary fix for AST constructor depth mismatch in pytest on python `3.11.x`, More information: pytest-dev/pytest#10874 + + Made temporary fix platform independent. + + Extended fix to all Webgear_RTC tests. + * Fixed NetGear tests bugs. + * Fixed condition logic bug. + +??? question "Pull Requests" + * PR #378 + * PR #375 + * PR #370 + +  + +  + ## v0.3.1 (2023-07-22) ??? tip "New Features" diff --git a/vidgear/gears/writegear.py b/vidgear/gears/writegear.py index 1d041d7a6..ff42a812a 100644 --- a/vidgear/gears/writegear.py +++ b/vidgear/gears/writegear.py @@ -53,7 +53,7 @@ class WriteGear: WriteGear handles various powerful Video-Writer Tools that provide us the freedom to do almost anything imaginable with multimedia data. WriteGear API provides a complete, flexible, and robust wrapper around FFmpeg, a leading multimedia framework. WriteGear can process real-time frames into a lossless - compressed video-file with any suitable specification (such asbitrate, codec, framerate, resolution, subtitles, etc.). It is powerful enough to perform complex tasks such as + compressed video-file with any suitable specification (such as bitrate, codec, framerate, resolution, subtitles, etc.). It is powerful enough to perform complex tasks such as Live-Streaming (such as for Twitch) and Multiplexing Video-Audio with real-time frames in way fewer lines of code. Best of all, WriteGear grants users the complete freedom to play with any FFmpeg parameter with its exclusive Custom Commands function without relying on any @@ -91,8 +91,7 @@ def __init__( compression_mode (bool): selects the WriteGear's Primary Mode of Operation. custom_ffmpeg (str): assigns the location of custom path/directory for custom FFmpeg executables. logging (bool): enables/disables logging. - ffmpeg_subprocess_creation_window: - output_params (dict): provides the flexibility to control supported internal parameters and FFmpeg properities. + output_params (dict): provides the flexibility to control supported internal parameters and FFmpeg properties. """ # print current version logcurr_vidgear_ver(logging=logging) From 21b153a0234ac4d9845be5ac5f1fb041685efde1 Mon Sep 17 00:00:00 2001 From: abhiTronix Date: Sun, 10 Sep 2023 16:38:40 +0530 Subject: [PATCH 36/38] =?UTF-8?q?=F0=9F=93=9D=20Docs:=20Updated=20changelo?= =?UTF-8?q?g.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/changelog.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 0f2eb118f..f2979ac37 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -76,8 +76,6 @@ limitations under the License. - [x] ScreenGear: * Fixed swapped region dimensions bug with dxcam backend. * Fixed "mss" backend disabled when `monitor` parameter is not defined. - - [x] Setup.py: - * Starting from version `8.0.0`, the python-mss library dropped support for Python `3.7`, so as a temporary measure, `mss` dependency has been pinned to version `7.0.1`. - [x] Docs: * Fixed missing `compression_mode` flags in WriteGear API docs. * Fixed missing hyperlinks. From c58e37c0736d08d8210a8f5ea6a9c68254bdd000 Mon Sep 17 00:00:00 2001 From: abhiTronix Date: Sun, 10 Sep 2023 16:41:31 +0530 Subject: [PATCH 37/38] =?UTF-8?q?=F0=9F=93=9D=20Docs:=20Updated=20changelo?= =?UTF-8?q?g.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/changelog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index f2979ac37..916bf42a8 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -22,10 +22,10 @@ limitations under the License. ## v0.3.2 (2023-09-10) -??? tip "New Features" +???+ tip "New Features" - [x] **NetGear:** * Added new `kill` parameter to `close()` method to forcefully kill ZMQ context instead of graceful exit only in the `receive` mode. - * Added new `subscriber_timeout` integer optional parameter to support timeout with `pattern=2` or PUBLISHER-SUBSCRIBER pattern. + * Added new `subscriber_timeout` integer optional parameter to support timeout with `pattern=2` _(or Publisher-Subscriber)_ pattern. + Receiver will exit safely if timeout defined(any value(in milliseconds) > 0), and timeout occurs in Receiver Mode with `pattern=2`. + πŸ’¬ Note: Default behavior still is to block the thread till infinite time. - [x] **WriteGear:** From 5087055ce4eab5b99568adf39d29f61fde6694f7 Mon Sep 17 00:00:00 2001 From: abhiTronix Date: Sun, 10 Sep 2023 17:54:44 +0530 Subject: [PATCH 38/38] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Maintenance:=20Fixed?= =?UTF-8?q?=20typos.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vidgear/gears/camgear.py | 2 -- vidgear/gears/streamgear.py | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/vidgear/gears/camgear.py b/vidgear/gears/camgear.py index 613792629..096de933b 100644 --- a/vidgear/gears/camgear.py +++ b/vidgear/gears/camgear.py @@ -220,7 +220,6 @@ def __init__( time_delay=0, **options ): - """ This constructor method initializes the object state and attributes of the CamGear class. @@ -449,7 +448,6 @@ def __update(self): # or frames runs out # if the thread indicator variable is set, stop the thread while not self.__terminate.is_set(): - # stream not read yet self.__stream_read.clear() diff --git a/vidgear/gears/streamgear.py b/vidgear/gears/streamgear.py index 7d95c9191..315d66feb 100644 --- a/vidgear/gears/streamgear.py +++ b/vidgear/gears/streamgear.py @@ -79,8 +79,9 @@ def __init__( format (str): select the adaptive HTTP streaming format(DASH and HLS). custom_ffmpeg (str): assigns the location of custom path/directory for custom FFmpeg executables. logging (bool): enables/disables logging. - stream_params (dict): provides the flexibility to control supported internal parameters and FFmpeg properities. + stream_params (dict): provides the flexibility to control supported internal parameters and FFmpeg properties. """ + # print current version logcurr_vidgear_ver(logging=logging) @@ -873,7 +874,6 @@ def __generate_dash_stream(self, input_params, output_params): return (input_params, output_params) def __Build_n_Execute(self, input_params, output_params): - """ An Internal function that launches FFmpeg subprocess and pipelines commands.