Skip to content

Commit

Permalink
⚡️Core: APIs Bug Fixes and Enhancements
Browse files Browse the repository at this point in the history
#### WebGear_RTC:
- Optimized peer connection closure to avoid redundant closures.
- Reduced unnecessary logging by only logging ICE connection state changes when they are not in a "failed" state.

#### StreamGear:
- Enhanced stream copy support in Single Source mode (Fixes #396)
  - Ignored stream copy parameter in Real-time Frames Mode or Custom Streams with appropriate warnings.
  - Updated `-acodec` handling:
    - Default to `aac` for Custom Streams.
    - Use stream copy (`-acodec copy`) for input video’s audio when Custom Streams are disabled.
  - Refined `-livestream` parameter usage to Real-time Frames Mode only.
  - Adjusted video and audio bitrate assignment to skip when stream copy is enabled.
  - Improved log message for `-clear_prev_assets` parameter.
- Moved handle streaming format to beginning to fix 'StreamGear' object has no attribute '_StreamGear__format' bug.

#### CamGear:
- Removed GStreamer support check.
- Improved readability of livestream warning logs.

#### WriterGear:
- Improved error handling in `execute_ffmpeg_cmd` method:
  - Raised `ValueError` with descriptive messages for `BrokenPipeError` or `IOError`.
  - Updated error handling per PEP 409 to preserve original exception context or suppress it based on logging settings.

#### NetGear: Updated parameters and documentation (Fixes #390)
- Added warning log for potential issues with `flag=1` (NOBLOCK).
- Noted that `track` option is ignored when `copy=True`.

### Core: 
- Improved exception handling for module imports. 
  - Updated `import_dependency_safe` in `helper.py`:
    - dded specific handling for `ModuleNotFoundError`.
    - Included original exception in `ImportError` for better error tracing.
    - Enhanced logging to include exception traceback when error is set to "log".
  - Enhanced `import_core_dependency` in `__init__.py`:
    - Added specific handling for `ModuleNotFoundError`.
    - Included original exception in `ImportError` for better error tracing.

#### Maintenance:
- Refactored colorspace handling in videocapture gears
  - Logged a warning and discarded invalid colorspace values instead of raising an exception.
  - Consolidated colorspace logging into a single line using a ternary operation.
- Simplified code using short-circuiting.
- Corrected a typo in comments.
- Removed unnecessary parentheses and type checks.
- Removed unused imports.

#### Docs:
- Enhanced StreamGear documentation:
  - Added a tip box on benefits of using stream copy (`-vcodec copy`) for faster HLS/DASH transcoding.
  - Highlighted limitations of stream copy, including incompatibility with Real-time Frames Mode and Custom Streams.
  - Clarified automatic audio stream copy (`-acodec copy`) usage with input video’s audio stream.
- Changed default value of `copy` to `True` in NetGear API documentation.
- Fixed typos, formatting, code highlighting, and grammar issues.

#### CI:
- Enhanced WebGear RTC tests
- Updated NetGear unit tests to reflect the new default for `copy`.
- Fixed simplejpeg and opencv not compatible with `numpy==2.x.x` versions.
  - Pinned `numpy<2.0.0` in all CI envs.
- Added test cases for import_dependency_safe function to validate different scenarios and error handling in `import_dependency_safe`.
  - Ensured coverage for `raise`, `log`, `silent`, and unknown error types.
- Fixed invalid escape sequence in testcase string.
- Fixed python environment bugs in `appveyor.yml`.
- Removed pinned `cryptography==38.0.4` dependency.
  • Loading branch information
abhiTronix authored Jun 19, 2024
2 parents d4243ab + 0d9071e commit e533553
Show file tree
Hide file tree
Showing 27 changed files with 312 additions and 252 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci_linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,12 @@ jobs:
chmod +x scripts/bash/install_opencv.sh
- name: install pip_dependencies
run: |
pip install -U pip wheel numpy
pip install -U pip wheel
pip install "numpy<2.0.0"
pip install -U .[asyncio]
pip uninstall opencv-python -y
pip install -U flake8 six codecov httpx pytest pytest-asyncio pytest-cov yt_dlp mpegdash paramiko m3u8 async-asgi-testclient
pip install -U deffcode
pip install cryptography==38.0.4
if: success()
- name: run prepare_dataset_script
run: bash scripts/bash/prepare_dataset.sh
Expand Down
5 changes: 3 additions & 2 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,10 @@ install:
- "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
- "python --version"
- "python -m pip install --upgrade pip wheel"
- "python -m pip install .[asyncio] six codecov httpx pytest-cov pytest-asyncio yt_dlp aiortc paramiko m3u8 async-asgi-testclient"
- cmd: python -m pip install "numpy<2.0.0"
- "python -m pip install .[asyncio] six codecov httpx yt_dlp aiortc paramiko"
- "python -m pip install pytest pytest-cov pytest-asyncio m3u8 async-asgi-testclient"
- "python -m pip install --upgrade deffcode"
- "python -m pip install cryptography==38.0.4"
- "python -m pip install https://github.com/abhiTronix/python-mpegdash/releases/download/0.3.0-dev2/mpegdash-0.3.0.dev2-py3-none-any.whl"
- cmd: chmod +x scripts/bash/prepare_dataset.sh
- cmd: bash scripts/bash/prepare_dataset.sh
Expand Down
4 changes: 2 additions & 2 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,11 @@ steps:
displayName: "Prepare dataset"
- script: |
python -m pip install -U pip wheel numpy
python -m pip install -U pip wheel
python -m pip install "numpy<2.0.0"
python -m pip install -U .[asyncio]
python -m pip install -U six codecov httpx pytest pytest-asyncio pytest-cov yt_dlp mpegdash paramiko m3u8 async-asgi-testclient
python -m pip install -U deffcode
python -m pip install cryptography==38.0.4
displayName: "Install pip dependencies"
- script: |
Expand Down
12 changes: 7 additions & 5 deletions docs/gears/netgear/params.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,11 +173,13 @@ This parameter provides the flexibility to alter various NetGear API's internal

* **`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).
* **`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#zmq.Socket.recv).

!!! warning "With flags=1 (i.e. `NOBLOCK`), NetGear raises `ZMQError` if no messages have arrived; otherwise, this waits until a message arrives."

* **`copy`**(_boolean_): This PyZMQ attribute selects if message be received in a copying or non-copying manner. If `False` a object is returned, if `True` a string copy of the message is returned.

* **`track`**(_boolean_): This PyZMQ attribute check if the message is tracked for notification that ZMQ has finished with it. (_ignored if copy=True_).
* **`track`**(_boolean_): This PyZMQ attribute check if the message is tracked for notification that ZMQ has finished with it. _(ignored if `copy=True`)_.


The desired attributes can be passed to NetGear API as follows:
Expand All @@ -188,9 +190,9 @@ options = {
"secure_mode": 2,
"custom_cert_location": "/home/foo/foo1/foo2",
"overwrite_cert": True,
"flag": 0,
"copy": False,
"track": False,
"flag": 0,
"copy": True,
"track": False
}
# assigning it
NetGear(logging=True, **options)
Expand Down
12 changes: 6 additions & 6 deletions docs/gears/netgear/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ from vidgear.gears import NetGear
import cv2

# define various tweak flags
options = {"flag": 0, "copy": False, "track": False}
options = {"flag": 0, "copy": True, "track": False}

# Define Netgear Client at given IP address and define parameters
# !!! change following IP address '192.168.x.xxx' with yours !!!
Expand Down Expand Up @@ -198,7 +198,7 @@ from vidgear.gears import VideoGear
from vidgear.gears import NetGear

# define various tweak flags
options = {"flag": 0, "copy": False, "track": False}
options = {"flag": 0, "copy": True, "track": False}

# Open live video stream on webcam at first index(i.e. 0) device
stream = VideoGear(source=0).start()
Expand Down Expand Up @@ -260,7 +260,7 @@ from vidgear.gears import NetGear
import cv2

# define tweak flags
options = {"flag": 0, "copy": False, "track": False}
options = {"flag": 0, "copy": True, "track": False}

# Define Netgear Client at given IP address and define parameters
# !!! change following IP address '192.168.x.xxx' with yours !!!
Expand Down Expand Up @@ -318,7 +318,7 @@ import cv2
stream = cv2.VideoCapture(0)

# define tweak flags
options = {"flag": 0, "copy": False, "track": False}
options = {"flag": 0, "copy": True, "track": False}

# Define Netgear Client at given IP address and define parameters
# !!! change following IP address '192.168.x.xxx' with yours !!!
Expand Down Expand Up @@ -377,7 +377,7 @@ from vidgear.gears import NetGear
import cv2

# define various tweak flags
options = {"flag": 0, "copy": False, "track": False}
options = {"flag": 0, "copy": True, "track": False}

# Define Netgear Client at given IP address and define parameters
# !!! change following IP address '192.168.x.xxx' with yours !!!
Expand Down Expand Up @@ -432,7 +432,7 @@ from vidgear.gears import ScreenGear
from vidgear.gears import NetGear

# define various tweak flags
options = {"flag": 0, "copy": False, "track": False}
options = {"flag": 0, "copy": True, "track": False}

# Start capturing live Monitor screen frames with default settings
stream = ScreenGear().start()
Expand Down
3 changes: 0 additions & 3 deletions docs/gears/pigear/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,21 +55,18 @@ Following is the bare-minimum code you need to get started with PiGear API:
=== ":material-linux: Linux"

```sh
# path to file
export LIBCAMERA_LOG_LEVELS=2
```

=== ":fontawesome-brands-windows: Windows (Powershell)"

```powershell
# path to file
$Env:LIBCAMERA_LOG_LEVELS=2
```

=== ":material-apple: MacOS"
```sh
# path to file
export LIBCAMERA_LOG_LEVELS=2
```

Expand Down
17 changes: 12 additions & 5 deletions docs/gears/streamgear/params.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ StreamGear API provides some exclusive internal parameters to easily generate St
# set video source as `/home/foo/bar.mp4`
stream_params = {"-video_source": "/home/foo/bar.mp4"}
```

* **Video URL**: Valid URL of a network video stream as follows:

!!! danger "Ensure the given video URL uses a protocol supported by the installed FFmpeg _(verify with `ffmpeg -protocols` terminal command)_."
Expand Down Expand Up @@ -269,7 +270,7 @@ StreamGear API provides some exclusive internal parameters to easily generate St

&ensp;

* **`-livestream`** _(bool)_: ***(optional)*** specifies whether to enable **Low-latency Live-Streaming :material-video-wireless-outline:** in Real-time Frames Mode only, where chunks will contain information for new frames only and forget previous ones, or not. The default value is `False`. It can be used as follows:
* **`-livestream`** _(bool)_: ***(optional)*** specifies whether to enable **Low-latency Live-Streaming :material-video-wireless-outline:** in [**Real-time Frames Mode**](../rtfm/overview) only, where chunks will contain information for new frames only and forget previous ones, or not. The default value is `False`. It can be used as follows:

!!! warning "The `-livestream` optional parameter is **NOT** supported in [Single-Source mode](../ssm/overview)."

Expand Down Expand Up @@ -338,9 +339,9 @@ StreamGear API provides some exclusive internal parameters to easily generate St

&ensp;

* **`-clear_prev_assets`** _(bool)_: ***(optional)*** This parameter specifies whether to force-delete any previous copies of StreamGear assets _(i.e., manifest (`mpd`), playlist (`mu38`), and streaming chunks (`.m4s`), etc. files)_ present at the path specified by the [`output`](#output) parameter. The default value is `False`. It can be used as follows:
* **`-clear_prev_assets`** _(bool)_: ***(optional)*** This parameter specifies whether to remove/delete all previous copies of StreamGear assets files for selected [`format`](#format) _(i.e., manifest (`mpd`) in DASH, playlist (`mu38`) in HLS, and respective streaming chunks (`.ts`,`.m4s`), etc.)_ present at the path specified by the [`output`](#output) parameter. The default value is `False`. It can be enabled as follows:

!!! info "Additional segments _(such as `.webm`, `.mp4` chunks)_ are also cleared automatically."
!!! info "Additional segments _(such as `.webm`, `.mp4` chunks)_ are also removed automatically."

```python
# delete all previous assets
Expand Down Expand Up @@ -381,9 +382,13 @@ stream_params = {"-vcodec":"libx264", "-crf": 0, "-preset": "fast", "-tune": "ze

All encoders and decoders compiled with the FFmpeg in use are supported by the StreamGear API. You can check the compiled encoders by running the following command in your terminal:

!!! warning "Stream copy (`-vcodec copy`) is not compatible with Real-time Frames Mode as this mode requires re-encoding of incoming frames."
???+ tip "Faster Transcoding with Stream Copy in Single Source Mode"

For faster transcoding of input video, utilize Stream copy (`-vcodec copy`) as the input video encoder in the [**Single-Source Mode**](../ssm/overview) for creating HLS/DASH chunks of the primary stream efficiently. However, consider the following points:

!!! info "Similarly, supported audio/video demuxers and filters depend on the FFmpeg binaries in use."
- :warning: Stream copy is **NOT** compatible with [**Real-time Frames Mode**](../rtfm/overview), as this mode necessitates re-encoding of incoming frames. Therefore, the `-vcodec copy` parameter will be ignored.
- :warning: Stream copying **NOT** compatible with Custom Streams ([`-streams`](#a-exclusive-parameters)), which also require re-encoding for each additional stream. Consequently, the `-vcodec copy` parameter will be ignored.
- When using the audio stream from the input video, the Audio Stream copy (`-acodec copy`) encoder will be automatically applied.

```sh
# for checking encoder
Expand All @@ -392,6 +397,8 @@ ffmpeg -encoders # use `ffmpeg.exe -encoders` on windows
ffmpeg -decoders # use `ffmpeg.exe -decoders` on windows
```

!!! info "Similarly, supported audio/video demuxers and filters depend on the FFmpeg binaries in use."

&nbsp;

## **`logging`**
Expand Down
14 changes: 6 additions & 8 deletions docs/gears/streamgear/rtfm/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ limitations under the License.
- [x] StreamGear API **MUST** requires FFmpeg executables for its core operations. Follow these dedicated [Platform specific Installation Instructions ➶](../../ffmpeg_install/) for its installation. API will throw **RuntimeError**, if it fails to detect valid FFmpeg executables on your system.
- [x] In this mode, ==API by default generates a primary stream _(at the index `0`)_ of same resolution as the input frames and at default framerate[^1].==
- [x] In this mode, API **DOES NOT** automatically maps video-source audio to generated streams. You need to manually assign separate audio-source through [`-audio`](../../params/#a-exclusive-parameters) attribute of `stream_params` dictionary parameter.
- [x] In this mode, Stream copy (`-vcodec copy`) is not compatible as this mode requires re-encoding of incoming frames.
- [x] In this mode, Stream copy (`-vcodec copy`) encoder is unsupported as it requires re-encoding of incoming frames.
- [x] Always use `close()` function at the very end of the main code.

???+ danger "DEPRECATION NOTICES for `v0.3.3` and above"
??? danger "DEPRECATION NOTICES for `v0.3.3` and above"

- [ ] The `terminate()` method in StreamGear is now deprecated and will be removed in a future release. Developers should use the new [`close()`](../../../../bonus/reference/streamgear/#vidgear.gears.streamgear.StreamGear.close) method instead, as it offers a more descriptive name, similar to the WriteGear API, for safely terminating StreamGear processes.
- [ ] The `rgb_mode` parameter in [`stream()`](../../../bonus/reference/streamgear/#vidgear.gears.streamgear.StreamGear.stream) method, which earlier used to support RGB frames in Real-time Frames Mode is now deprecated, and will be removed in a future version. Only BGR format frames will be supported going forward. Please update your code to handle BGR format frames.
Expand Down Expand Up @@ -515,7 +515,7 @@ To generate Secondary Streams, add each desired resolution and bitrate/framerate

=== "DASH"

```python linenums="1" hl_lines="12-14"
```python linenums="1" hl_lines="11-15"
# import required libraries
from vidgear.gears import CamGear
from vidgear.gears import StreamGear
Expand Down Expand Up @@ -571,7 +571,7 @@ To generate Secondary Streams, add each desired resolution and bitrate/framerate

=== "HLS"

```python linenums="1" hl_lines="12-14"
```python linenums="1" hl_lines="11-15"
# import required libraries
from vidgear.gears import CamGear
from vidgear.gears import StreamGear
Expand Down Expand Up @@ -1038,11 +1038,9 @@ In this example, we will be using `h264_vaapi` as our Hardware Encoder and speci

!!! danger "This example is just conveying the idea of how to use FFmpeg's hardware encoders with the StreamGear API in Real-time Frames Mode, which MAY OR MAY NOT suit your system. Please use suitable parameters based on your supported system and FFmpeg configurations only."

!!! warning "Stream copy (`-vcodec copy`) is not compatible with this Mode as it requires re-encoding of incoming frames."
???+ info "Checking VAAPI Support for Hardware Encoding"

??? info "Check VAAPI support"

To use `h264_vaapi` encoder, remember to check if its available and your FFmpeg compiled with VAAPI support. You can easily do this by executing following one-liner command in your terminal, and observing if output contains something similar as follows:
To use **VAAPI** (Video Acceleration API) as a hardware encoder in this example, follow these steps to ensure your FFmpeg supports VAAPI:

```sh
ffmpeg -hide_banner -encoders | grep vaapi
Expand Down
12 changes: 9 additions & 3 deletions docs/gears/streamgear/ssm/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,17 @@ limitations under the License.
- [x] In this mode, if input video-source _(i.e. `-video_source`)_ contains any audio stream/channel, then it automatically gets mapped to all generated streams.
- [x] Always use `close()` function at the very end of the main code.

???+ danger "DEPRECATION NOTICES for `v0.3.3` and above"
??? danger "DEPRECATION NOTICES for `v0.3.3` and above"

- [ ] The `terminate()` method in StreamGear is now deprecated and will be removed in a future release. Developers should use the new [`close()`](../../../../bonus/reference/streamgear/#vidgear.gears.streamgear.StreamGear.close) method instead, as it offers a more descriptive name, similar to the WriteGear API, for safely terminating StreamGear processes.
- [ ] The [`-livestream`](../../params/#a-exclusive-parameters) optional parameter is NOT supported in this Single-Source Mode.

??? tip "Faster Transcoding of Primary Stream with Stream Copy in Single Source Mode"

For faster transcoding of input video in this mode, utilize Stream copy (`-vcodec copy`) as the input video encoder for creating HLS/DASH chunks of the primary stream efficiently. However, consider the following points:

- :warning: Stream copying **NOT** compatible with Custom Streams ([`-streams`](../../params/#a-exclusive-parameters)), which require re-encoding for each additional stream. Therefore, the `-vcodec copy` parameter will be ignored.
- When using the audio stream from the input video, the Audio Stream copy (`-acodec copy`) encoder will be automatically applied.

!!! example "After going through following Usage Examples, Checkout more of its advanced configurations [here ➶](../../../help/streamgear_ex/)"

Expand Down Expand Up @@ -106,7 +112,7 @@ To generate Secondary Streams, add each desired resolution and bitrate/framerate

=== "DASH"

```python linenums="1" hl_lines="6-12"
```python linenums="1" hl_lines="7-12"
# import required libraries
from vidgear.gears import StreamGear

Expand All @@ -130,7 +136,7 @@ To generate Secondary Streams, add each desired resolution and bitrate/framerate

=== "HLS"

```python linenums="1" hl_lines="6-12"
```python linenums="1" hl_lines="7-12"
# import required libraries
from vidgear.gears import StreamGear

Expand Down
4 changes: 2 additions & 2 deletions docs/help/screengear_ex.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ from vidgear.gears import WriteGear
import cv2

# define various tweak flags
options = {"flag": 0, "copy": False, "track": False}
options = {"flag": 0, "copy": True, "track": False}

# Define Netgear Client at given IP address and define parameters
# !!! change following IP address '192.168.x.xxx' with yours !!!
Expand Down Expand Up @@ -106,7 +106,7 @@ options = {"top": 40, "left": 0, "width": 100, "height": 100}
stream = ScreenGear(logging=True, **options).start()

# define various netgear tweak flags
options = {"flag": 0, "copy": False, "track": False}
options = {"flag": 0, "copy": True, "track": False}

# Define Netgear server at given IP address and define parameters
# !!! change following IP address '192.168.x.xxx' with client's IP address !!!
Expand Down
13 changes: 8 additions & 5 deletions vidgear/gears/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,14 @@ def import_core_dependency(
# try importing dependency
try:
module = importlib.import_module(name)
if sub_class:
module = getattr(module, sub_class)
except ImportError:
# raise
raise ImportError(msg) from None
module = getattr(module, sub_class) if sub_class else module
except Exception as e:
if isinstance(e, ModuleNotFoundError):
# raise message
raise ModuleNotFoundError(msg) from None
else:
# raise error+message
raise ImportError(msg) from e

# check if minimum required version
if not (version) is None:
Expand Down
11 changes: 8 additions & 3 deletions vidgear/gears/asyncio/webgear_rtc.py
Original file line number Diff line number Diff line change
Expand Up @@ -559,14 +559,15 @@ async def __offer(self, request):
# track ICE connection state changes
@pc.on("iceconnectionstatechange")
async def on_iceconnectionstatechange():
logger.debug("ICE connection state is %s" % pc.iceConnectionState)
if pc.iceConnectionState == "failed":
logger.error("ICE connection state failed.")
# check if Live Broadcasting is enabled
if self.__relay is None:
# if not, close connection.
await pc.close()
self.__pcs.discard(pc)
else:
logger.debug("ICE connection state is %s" % pc.iceConnectionState)

# Change the remote description associated with the connection.
await pc.setRemoteDescription(offer)
Expand Down Expand Up @@ -628,7 +629,9 @@ async def __reset_connections(self, request):
logger.critical("Resetting Server")
# close old peer connections
if parameter != 0: # disable if specified explicitly
coros = [pc.close() for pc in self.__pcs]
coros = [
pc.close() for pc in self.__pcs if pc.iceConnectionState != "closed"
]
await asyncio.gather(*coros)
self.__pcs.clear()
await self.__default_rtc_server.reset()
Expand All @@ -645,7 +648,9 @@ async def __lifespan(self, context):
# close Video Server
self.shutdown()
# collects peer RTC connections
coros = [pc.close() for pc in self.__pcs]
coros = [
pc.close() for pc in self.__pcs if pc.iceConnectionState != "closed"
]
await asyncio.gather(*coros)
self.__pcs.clear()

Expand Down
Loading

0 comments on commit e533553

Please sign in to comment.