diff --git a/dev/changelog/index.html b/dev/changelog/index.html index 48d6ebd8..4d56caf1 100644 --- a/dev/changelog/index.html +++ b/dev/changelog/index.html @@ -1,4 +1,4 @@ - Release Notes - VidGear
Skip to content
We're excited to announce our new Deffcode library, which will be integrated with VidGear soon. We value your feedback and would love to hear your thoughts!

Release Notes

v0.3.2 (2023-09-10)

New Features
  • 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.
  • 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.
  • Setup.py
    • Added official support for python 3.11.x legacies.
    • Bumped version to 0.3.1.
  • 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.
  • CI
    • Added python 3.11 legacy support for MacOS, Windows and Linux environments.
    • Added kill argument to close() method in various NetGear tests.
Updates/Improvements
  • Asyncio:
    • Formatted TemplateResponse class parameters w.r.t new changes in backend Starlette API.
  • Setup.py:
    • Readded latest patch to uvicorn, starlette, pyzmq dependencies.
    • Removed 3.7 legacy from Programming Language metadata.
  • Maintenance:
    • Added GitHub sponsors and dropped liberapay from Funding.yml.
    • Removed redundant code.
  • 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
  • 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.
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-fixes
  • ScreenGear:
    • Fixed swapped region dimensions bug with dxcam backend.
    • Fixed "mss" backend disabled when monitor parameter is not defined.
  • Docs:
    • Fixed missing compression_mode flags in WriteGear API docs.
    • Fixed missing hyperlinks.
    • Fixed typos and context.
  • 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.
Pull Requests
New Contributors

 

 

v0.3.1 (2023-07-22)

New Features
  • WebGear:
    • Added an option to add a custom video endpoint path.
      • Users can now change the video endpoint path from "/video" to any alphanumeric string.
      • Added the custom_video_endpoint optional string attribute for this purpose.
      • Only alphanumeric strings with no spaces in between are allowed as its value.
  • ScreenGear:
    • Added dxcam support for Windows machines.
      • Implemented a complete end-to-end workflow for the dxcam backend.
      • dxcam is now the default backend for Windows machines when no backend is defined.
      • Added support for variable screen dimensions to capture an area from the screen.
      • Added the optional flag dxcam_target_fps to control the target fps in dxcam. Defaults to 0 (disabled).
      • RGB frames from dxcam are automatically converted into BGR.
      • For better performance, video_mode is enabled by default in dxcam backend.
      • Added necessary imports.
    • Added support for tuple values in the monitor parameter to specify device and output indexes as (int[device_idx], int[output_idx]) in the dxcam backend only.
      • Default int index is also allowed as a value for selecting device index.
  • Helper
    • Added multiple servers support for downloading assets.
      • Added GitHub server to the generate_webdata method to make it more robust for rate limits and other shortcomings.
      • Now, the generate_webdata method will retry a different server when one fails.
  • Setup.py
    • Added dxcam dependency in core and asyncio extra requires.
    • Bumped version to 0.3.1.
  • Docs
    • Added dxcam API specific prerequisites for ScreenGear API when installing on Windows via pip.
    • Added documentation for the custom_video_endpoint optional string attribute.
    • Added documentation for controlling Chunk size in HLS stream.
    • Added new hyperlinks for dxcam dependency.
  • CI
    • Added a test case for ndim==3 grayscale frames.
      • Added the Custom_Grayscale_class to generate ndim==3 grayscale frames.
    • Added test cases for the custom_video_endpoint optional string attribute.
Updates/Improvements
  • WebGear:
    • Improved the conditions logic to check if non-empty values are assigned to optional parameters.
  • WebGear_RTC:
    • Improved the handling of the format parameter when constructing a VideoFrame from ndarray frames.
  • ScreenGear:
    • Enforced dxcam backend (if installed) when monitor is defined on Windows machines.
    • Refactored code blocks to ensure backward compatibility.
  • Maintenance:
    • Cleaned up unused imports and code blocks.
    • Cleaned redundant code.
    • Improved logging.
    • Implemented short-circuiting.
    • Fixed comment typos.
    • Updated comments.
  • Docs:
    • Updated ScreenGear API usage example docs, added new relevant information, updated requirements for dxcam support in Windows machines.
    • Refactored monitor and backend parameters docs of ScreenGear.
    • Updated class and class parameters descriptions in ScreenGear docs.
    • Updated a new description for ScreenGear API.
    • Updated Zenodo badge and the BibTeX entry.
    • Relocated some docs for a better context.
    • Removed ScreenGear name from Threaded Queue Mode doc.
    • Updated ScreenGear FAQs.
    • Updated changelog.md
  • CI:
    • Updated the test_webgear_rtc_custom_stream_class method.
    • Updated the test_webgear_options method.
    • Updated the test_webgear_routes test to validate the new custom endpoint.
    • Increased code coverage by updating tests.
Breaking Updates/Changes
  • ScreenGear:
    • Previously enforced threaded queue mode is now completely removed, resulting in a potential performance boost.
      • 💬 Reason: The IO is automatically blocked by the screen refresh rate, so adding the overhead of maintaining a separate queue is pointless.
    • Removed the THREAD_TIMEOUT optional flag.
Bug-fixes
  • WebGear_RTC:
    • Fixed a bug caused by PyAV's error when ndim==3 grayscale frames are encountered.
      • The API will now drop the third dimension if ndim==3 grayscale frames are detected.
  • ScreenGear:
    • Fixed backend not defined while logging.
  • 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.
  • Docs:
    • Fixed context and added separate code for controlling chunk size in HLS and DASH streams in StreamGear docs.
    • Fixed naming conventions for the recently added DXcam backend in ScreenGear docs.
    • Fixed missing hyperlinks.
  • CI:
    • Fixed m3u8 module failing to recognize Windows paths in ScreenGear tests.
    • Fixed a path bug by replacing the absolute file path with the decoded file content as a string in its loads()
Pull Requests

 

 

v0.3.0 (2023-01-26)

New Features
  • WriteGear:
    • Added support for user-defined and higher than 8-bit depth input frames pixel-format.
      • Added support for higher than 8-bit depth frames with datatypes of unsigned integer(uint) kind and element size 2.
      • Added dtype parameter to internal Preprocess method for passing input frames datatype.
      • Implemented auto-calculation of input pixel-format based on number of channels in higher than 8-bit depth frames.
      • Added various known working pixel-formats(based on number of channels), supported by all prominent computer vision libraries.
      • Added support for up to 1-channel(gray16-le/be) to all the way up to 4-channels(bgra64-le/be) in input frames.
      • Added endianness little(le) or big(be) at the suffix of pixel-format based on byte-order of input frames datatypes.
      • Extended support for higher RGB 8-bit depth frames through RGB mode.
    • Added support for user-defined custom input pixel-format.
      • Added new -input_pixfmt attribute to output_params dictionary parameter for easily specifying custom input pixel-format.
      • Added newly implemented get_supported_pixfmts method import for verifying user-defined input pixel-format against Installed FFmpeg supported pixel-formats. Unsupported values will be discarded.
      • Implemented runtime datatype validation check, such that all input frames must have same datatype.
    • Added support for Context Managers for proper handling of resources via with statement for allocating and releasing resources precisely. (Suggested by @sueskind)
      • Implement the __enter__() and __exit__() methods.
      • Added __enter__ method that returns reference to the WriteGear Class.
      • Added __exit__ method that automatically executes close() for performing the cleanup operations and handling exception gracefully.
  • StreamGear:
    • Added support for Context Managers for proper handling of resources via with statement for allocating and releasing resources precisely. (Suggested by @sueskind)
      • Implement the __enter__() and __exit__() methods.
      • Added __enter__ method that returns reference to the StreamGear Class.
      • Added __exit__ method that automatically executes close() for performing the cleanup operations and handling exception gracefully.
  • WebGear:
    • Added way to completely disable Data-Files Auto-Generation WorkFlow.
      • Added new skip_generate_webdata boolean optional attribute(False by default) to completely disable Data-Files Auto-Generation WorkFlow.
      • This flag enables only /video route for disabled Data-Files Auto-Generation WorkFlow.
      • Implemented JSONResponse as placeholder response instead of Index, 404 and 500 HTML pages, when workflow is disabled. (Note: Index HTML page will throw 404 status code.)
      • Added necessary imports.
  • Helper:
    • Added more robust implementation of validate_audio method.
      • Added new more robust regex pattern for extracting audio-samplerate.
      • Added new validate_audio method for calculating accurate bitrate(in kbps) from audio samplerate, channels, bit-depth values.
      • Implemented new patterns and logic for accurately extracting audio channels and bit-depth from given metadata.
    • Added support for Linux video device path (such as /dev/video0).
  • Maintenance:
    • Logging current vidgear version when vidgear APIs are called, not at import.
      • Added logcurr_vidgear_ver helper function to facilitate logging current vidgear version, when called within a API.
      • Implemented ver_is_logged global variable in helper to log version only once, which can modifiable with logcurr_vidgear_ver method only. Followed recommendation given in official python docs: https://docs.python.org/3/faq/programming.html#how-do-i-share-global-variables-across-modules
      • Current version can only be logged by VidGear APIs with the logging turned on (i.e. logging=True).
  • Docs:
    • Added new WriteGear Bonus Example:
      • Added "Using WriteGear's Compression Mode with v4l2loopback Virtual Cameras bonus python example.
      • Added related prerequisites and dependencies for creating v4l2loopback Virtual Cameras on Linux machines.
      • Added both With/Without-Audio cases for "Using WriteGear's Compression Mode for YouTube-Live Streaming".
    • Added content.code.copy and content.tabs.link features.
    • Added docs related to skip_generate_webdata optional attribute.
    • Added feedback features to mkdocs.yml.
    • Added 404.html static template to mkdocs.yml.
  • CI:
    • Added v4l2loopback support for testing /dev/video0 device on Linux machines.
    • Added test cases for newer implementation of validate_audio method.
    • Added test_skip_generate_webdata to test skip_generate_webdata optional attribute.
    • Added tests for user-defined and higher than 8-bit depth input frames pixel-format.
Updates/Improvements
  • WriteGear:
    • Completely revamped code structure and comments.
      • Updated comments, description, and logging messages to more sensible and developer friendly.
      • Implemented operator short-circuiting to cleanup code as much as possible.
      • Renamed startFFmpeg_Process internal class method to start_FFProcess.
      • Renamed Preprocess internal class method to PreprocessFFParams.
      • Renamed startCV_Process internal class method to start_CVProcess.
      • Renamed initiate internal class parameter to initiate_process.
      • Renamed force_termination internal class parameter to forced_termination.
      • Enabled output_params parameters logging in both modes.
      • Improved compression and logging parameters boolean value handling.
      • Implemented stdout closing to cleanup pipeline before terminating.
  • Helper:
    • Updated validate_audio method with improved and more robust regex patterns for identifying audio bitrate in ay audio file.
  • Setup.py:
    • Bumped version to 0.3.0.
    • Replaced >= comparison operator with more flexible ~=.
    • Replaced distutils.version.LooseVersion with pkg_resources.parse_version.
  • Maintenance:
    • Replaced depreciated LooseVersion with parse_version.
    • Updated Retry package to be imported from requests.adapters.
    • Moved terminal and python code text area to Question GitHub Form Schema.
    • Removed unnecessary imports.
    • Removed redundant code.
    • Improved logging messages.
    • Updated code comments.
    • Updated method descriptions.
    • Refactored code.
    • Increased coverage.
  • Bash Script:
    • Updated FFmpeg Static Binaries links to latest date/version tag to 12-07-2022.
    • Removed depreciated binaries download links and code.
  • Docs:
    • Replaced all raw.githubusercontent.com GIF URLs with user-images.githubusercontent.com.
    • Reformatted custom.css and added missing comments.
    • Updated sponsor block.
    • Enabled Code Highlights.
    • Updated announcement bar.
    • Updated changelog.md.
    • Reduced webgear_rtc.gif size.
    • Updated Zenodo badge and the BibTeX entry.
  • CI:
    • Added more flexible formats to return_testvideo_path function.
    • Updated test_write test for higher than 8-bit depth input frames pixel-format in WriteGear's Compression Mode.
    • Updated actions/checkout to v3.
    • Updated actions/setup-python to v4.
    • Updated codecov/codecov-action to v3.
    • Moved test_colorspaces test to CamGear tests.
    • Added deffcode library import.
  • Re-stuctured yaml code.
Breaking Updates/Changes
  • WriteGear:
    • Renamed output_filename string parameter to output.
      • Since WriteGear API accepts all sorts of streams (such as valid filename/path/URL) for encoding, thereby changing parameter name to output will be more true to its purpose.
      • Renaming output_filename to output in WriteGear API will also help user to not accidentally assume WriteGear supports only encoding of video files.
      • It matches the output parameter in StreamGear which basically does the same thing.
    • Renamed cmd parameter in execute_ffmpeg_cmd() class method to more sensible command.
    • ValueError will be raised if datatype of input frames mismatches Writegear API
Bug-fixes
  • Camgear:
    • Fixed CamGear.read() blocked unnecessarily.
      • 💬 When THREADED_QUEUE_MODE is enabled CamGear.read() blocks for an excessive duration when attempting to read past the end of a stream.
      • Added None frame to the queue at the end to signal we're done.
      • Added terminate Event check before continuing.
    • Fixed deadlock on exit.
      • 💬 The deadlock is due to self.__queue.get(timeout=self.__thread_timeout) line in read() method, which still waits for timeout(thread_timeout) to happen when main update() thread was already terminated on exit and queue was empty. Since there was no way to signal queue that stream is already ended, the blocking queue.get() keeps on waiting until timeout occurs.
      • The solution was to signal queue.get() that stream is already ended by putting None in queue on exiting the main update() thread.
  • ScreenGear:
    • Fixed ScreenGear.read() blocked during cold startup.
    • 💬 During startup, ScreenGear.read() doesn't checks if queue is empty before continuing.
  • WriteGear:
    • Fixed gstpipeline_mode not activating when wrongly assuming output value as valid path.
    • Fixed name 'compression' is not defined bug.
    • Fixed AttributeError.
  • Helper:
    • Fixed fltp keyword in regex pattern causing non-ftlp streams to be not recognized.
    • Fixed response.headers returning content-length as Nonetype since it may not necessarily have the Content-Legth header set.
    • Fixed Linux video device paths still not working.
      • Moved helper.py logic to WriteGear and StreamGear APIs resp.
    • Fixed KeyError for empty metadata.
  • Setup:
    • Pinned pyzmq==24.0.1 working version.
    • Removed redundant patch for the issue.
  • Maintaince:
    • Fixed missing pkg name import_dependency_safe functions calls.
  • Bash Script:
    • Fixed gstreamer installation.
  • CI:
    • Fixed missing v4l2loopback apt dependency on Linux envs.
    • Added fix for RTCPeerConnection fails to create RTCDtlsTransport (Related issue: aiortc/aiortc#804)
      • Pinned cryptography==38.0.4 in dependencies.
    • Pinned Linux image to ubuntu-20.04 in github actions.
    • Fixed No module named 'httpx' bug.
      • Added httpx library import.
    • Fixed F821 undefined name bug.
    • Fixed Gstreamer bug.
  • Docs:
    • Fixed hyperlinks to new GitHub's form schemas.
    • Fixed non-rendering images in README.md
      • Replaced all relative image/gifs paths with absolute URLs in README.md.
    • Fixed badges/shields#8671 badge issue in README.md
    • Fixed GitLab CDN links throwing blocked by CORS policy bug.
      • Replaced gitlab GitHack CDN links with with bitbucket.
    • Fixed DASH playback failing by setting the maxAttempts to Infinity.
    • Removed x-sign glow-text effect CSS.
    • Fixed several typos (suggested by @timgates42)
    • Fixed coverage badge.
Pull Requests
New Contributors

 

 

v0.2.6 (2022-07-05)

New Features
  • Docs:
    • Added new bonus example for RTSP/RTP Live-Streaming using WriteGear's Compression Mode.
    • Added "How to resolve zmq.error.ZMQError" FAQ for NetGear API.(PR by @iandol)
    • Added new ko-fi button to README.md
    • Added new contributors block to changelog.md
  • Maintenance:
    • Added new patterns to .gitignore to ignore pypi's build directory and egg-info files.
  • CI:
    • Switched to new Issue GitHub's form schema using YAML
      • Added new bug_report.yaml.
      • Added new question.yaml.
      • Added new proposal.yaml.
      • Deleted depreciated markdown files.
      • Polished forms.
Updates/Improvements
  • Setup.py:
    • Bumped version to 0.2.6.
    • Updated logic operators and dependency.
      • Replaced >= comparsion operator with more flexible ~=.
      • Replaced distutils.version.LooseVersion with pkg_resources.parse_version.
  • Docs:
    • Updated Site Navigation.
      • Added new notices to inform users more effectively about bonus examples.
      • Added new Bonus section to navigation and moved suitable pages under it.
      • Updated headings and URLs.
    • Redesigned and Rewritten Donation and Contribution section to README.md
    • Updated Zenodo badge and Bibtex entry.
    • Updated Admonition Icon, FAQs and site-links.
    • Reformatted code and its comments.
    • Updated changelog.md.
  • API:
  • CI:
    • Added more paths and files to skip commits.
Breaking Updates/Changes
  • -input_framerate parameter now accepts any positive value for WriteGear and StreamGear APIs.
Bug-fixes
  • API:
    • Fixed -input_framerate less than 5 does not get used in WriteGear and StreamGear APIs.(PR by @freol35241)
  • CamGear: Fixed Yt-dlp generated HTTP DASH Segments URLs not supported by OpenCV's VideoCapture(PR by @DynamiteC)
  • StreamGear:
    • Fixed hls_segment_type not working bug. (PR by @enarche-ahn)
    • Fixed critical logging parameter bug
      • Fixed debug logs even when logging=False in StreamGear's Real-time Mode. (patch suggested by @enarche-ahn)
      • Added length check to -video_source attribute to correctly infers it as empty(or invalid).
  • CI:
    • Xfailed RTSP CamGear CI test.
    • Fixed pinned version syntax bug in docs_deployer workflow.
    • Fixed typos in Github forms and its context.
    • Added missing dependency.
  • Docs:
    • Fixed jinja2 3.1.0 or above breaks mkdocs.
      • jinja2>=3.1.0 breaks mkdocs (mkdocs/mkdocs#2799), therefore pinned jinja2 version to <3.1.0.
    • Fixed support for new mkdocstring versions
      • Replaced rendering sub-value with options.
      • Removed pinned mkdocstrings==0.17.0 version.
    • Fixed Netgear+Webgear bonus example code bugs.(PR by @iandol)
      • Added a missing import.
      • Removed self. typo.
      • Replaced the return value with break in the async as it triggers an error.
    • Fixed external bug that causing "Home" tab to irresponsive randomly when accessed from other tabs.
    • Fixed indentation and spacing.
    • Fixed typos and updated context.
    • Removed dead code.
Pull Requests
New Contributors

 

 

v0.2.5 (2021-02-11)

New Features
  • WriteGear:
    • Add support for GStreamer pipeline in WriteGear API's Non-Compression mode:
      • Implemented GStreamer Pipeline Mode to accept GStreamer pipeline as string to its output_filename parameter.
      • Added new special -gst_pipeline_mode attribute for its output_params parameter.
      • This feature provides flexible way to directly write video frames into GStreamer Pipeline with controlled bitrate.
      • Added new docs and updated existing docs with related changes.
    • Added new -ffpreheaders special attribute to WriteGear's options parameter:
      • This attribute is specifically required to set special FFmpeg parameters in Compression Mode that are present at the starting of command(such as -re).
      • This attribute only accepts list datatype as value.
      • Added related docs.
  • NetGear:
    • Added bidirectional data transfer support by extending Bidirectional mode support to exclusive Multi-Clients and Multi-Servers modes:
      • Users will now able to send data bidirectionally in both Multi-Clients and Multi-Servers exclusive modes.
      • Bidirectional mode will no longer disables automatically when Multi-Clients and Multi-Servers modes already enabled.
      • Added new docs and updated existing docs with related changes.
  • Maintenance:
    • Added official support for Python-3.10 legacies.
    • Added float value support to THREAD_TIMEOUT optional parameter.
    • Added info about dropped support for Python-3.6 legacies through announcement bar.
    • Added config.md file for Issue templates.
    • Added title to Issue templates.
  • Docs:
    • Added new Code Annotations
    • Added new icons to headings.
    • Added Advanced VideoGear usage example with CamGear backend.
Updates/Improvements
  • Setup.py:
    • Dropped support for Python-3.6 and below legacies.
    • Updated logging formatting.
    • Updated python_requires to >=3.7.
    • Bumped version to 0.2.5.
  • Helper:
    • Vidgear will now report current version on every run.
  • Docs:
    • Updated SSH tunneling docs context.
    • Excluded docs directory from CI envs.
    • Updated Zenodo badge and BibTeX entry.
    • Updated dark theme hue to 260.
    • Updated Admonitions.
    • Additional warnings against pushing PR against VidGear's testing branch only.
    • Updated code comments.
  • CI:
    • Removed support for Python-3.6 legacies from all workflows.
    • Updated NetGear's Exclusive Mode tests.
    • Added GStreamer Pipeline Mode tests.
  • Maintenance:
    • Updated Issue and PR templates.
    • Updated metadata.
Breaking Updates/Changes
  • Dropped support for Python-3.6 legacies from vidgear.
Bug-fixes
  • NetGear: Fixed bidirectional mode overriding multi-clients mode's data.
  • WriteGear:
    • Fixed wrongly defined ffmpeg_preheaders.
    • Fixed condition logic bugs.
    • Fixed UnboundLocalError bug.
  • Setup: Fixed uvicorn and aiortc dropped support for Python-3.6 legacies.
  • CI:
    • Fixed GitHub Actions interprets 3.10 as 3.1 if used without strings.
    • Fixed naming error in azure YAML.
  • Docs:
    • Fixed codecov badge URL in README.md
    • Fixed hyperlinks in README.
    • Fixed indentation and spacing.
    • Fixed typos and updated context.
    • Removed dead code.
  • Maintenance:
    • Removed depreciated condition checks.
Pull Requests

 

 

v0.2.4 (2021-12-05)

New Features
  • CamGear:
    • Added a new YT_backend Internal Class with YT-DLP backend:
      • Implemented YT_backend a new CamGear's Internal YT-DLP backend class for extracting metadata from Streaming URLs.
      • Added support for pipeling (live) video-frames from all yt-dlp supported streaming sites: https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md#supported-sites
      • Implemented algorithm from scratch for auto-extracting resolution specific streamable URLs for pipelineing.
      • Implemented logic for auto-calculating best and worst resolutions.
      • Added new ytv_metadata global parameter to CamGear for accessing video's metadata(such as duration, title, description) on-the-go.
      • ⚠️ Playlists are still unsupported.
  • WebGear_RTC:
    • Implemented a new easy way of defining Custom Streaming Class with suitable source(such as OpenCV):
      • Added new custom_stream attribute with WebGear_RTC options parameter that allows you to easily define your own Custom Streaming Class with suitable source(such as OpenCV).
      • This implementation supports repeated Auto-Reconnection or Auto-Refresh out-of-the-box.
      • This implementation is more user-friendly and easy to integrate within complex APIs.
      • This implementation requires at-least read() and stop() methods implemented within Custom Streaming Class, otherwise WebGear_RTC will throw ValueError.
      • This implementation supports all vidgear's VideoCapture APIs readily as input.
  • Maintenance:
    • Added new .gitignore for specifying intentionally untracked files to ignore
      • Added more files entries to .gitignore.
    • Added new .gitattributes to manage how Git reads line endings.
      • Enabled auto default behavior, in case people don't have core.autocrlf set.
      • Enforced LF line-endings for selective files types.
      • Added Binary data files that specifies they are not text, and git should not try to change them.
      • Added Language aware diff headers.
      • Added Linguist language overrides.
  • Docs:
    • Added bonus example to add real-time file audio encoding with VideoGear and Stabilizer class.
    • Added complete usage docs with new CamGear's Internal Class with YT-DLP backend.
    • Added instructions to extract video's metadata in CamGear.
    • Added donation link in page footer with bouncing heart animation through pure CSS.
    • Added info about critical changes in v0.2.4 and above installation through new announcement bar.
    • Added related usage docs for new WebGear_RTC custom streaming class.
    • Added changes for upgrading mkdocs-material from v7.x to newer v8.x.
    • Added outdated version warning block.
Updates/Improvements
  • CamGear:
    • Added is_livestream global YT_backend parameters.
    • Added default options for yt-dlp for extracting info_dict(metadata) of the video as a single JSON line.
    • Completely removed old logic for extracting streams using pafy.
    • Removed all dead code related to streamlink backend.
  • Setup.py:
    • Moved all API specific dependencies to extra_requires under the name "core". [PR #268 by @zpapakipos]
    • Added rule to replace GitHub heading links in description.
    • Updated extra_require dependencies.
    • Removed streamlink dependency.
    • Removed pafy dependency.
    • Removed pyzmq from latest_version group.
    • Updated SEO Keywords.
  • Docs:
    • Re-written pip and source installation docs.
    • Added warning for using -disable_force_termination flag for short duration videos.
    • Added permalink_title entry to mkdocs.yml.
    • Updated CamGear parameters.
    • Updated Admonitions with related information.
    • Updated Functional Block Diagram(gears_fbd.png) image.
    • Updated installation instructions.
    • Updated Advanced examples using WebGear_RTC's custom streaming class.
    • Updated code highlighting.
    • Updated zenodo badge.
    • Updated BibTex for project citation.
    • Replaced incorrect API parameter docs.
    • Updated WebGear_RTC parameters.
  • CI:
    • Updated CI tests for new WebGear_RTC custom streaming class.
    • Restored test_stream_mode CamGear test.
    • Updated Streaming Sites test links.
    • Added more tests cases.
  • Maintenance:
    • Updated spacing in logger formatting.
    • Renamed Asyncio Helper logger name.
    • Changed logging colors.
    • Updated logging messages.
Breaking Updates/Changes
  • Installation command with pip has been changed in v0.2.4:
    • The legacy pip install vidgear command now installs critical bare-minimum dependencies only. Therefore in order to automatically install all the API specific dependencies as previous versions, use pip install vidgear[core] command instead.
  • CamGear:
    • Removed streamlink backend support from stream_mode in favor of more reliable CamGear's Internal YT-DLP backend class for extracting metadata from Streaming URLs.
      • CamGear will raise ValueError if streaming site URL is unsupported by yt-dlp backend.
      • CamGear will raise ValueError if yt-dlp isn't installed and stream_mode is enabled.
    • Removed automatic enforcing of GStreamer backend for YouTube-livestreams and made it optional.
      • The CamGear will not raise ValueError if GStreamer support is missing in OpenCV backends.
  • WebGear_RTC:
    • Removed support for assigning Custom Media Server Class(inherited from aiortc's VideoStreamTrack) in WebGear_RTC through its config global parameter.
    • WebGear_RTC API will now throws ValueError if source parameter is NoneType as well as custom_stream attribute is undefined.
  • Helper:
    • Removed restore_levelnames method.
    • Removed youtube_url_validator helper method.
Bug-fixes
  • CamGear:
    • Fixed KeyError Bug for missing attributed in meta_data json in some streaming sites.
  • Helper:
    • Removed unused imports.
  • Docs:
    • Removed slugify from mkdocs which was causing invalid hyperlinks in docs.
    • Fixed GitHub hyperlinks in README.md.
    • Fixed hyperlink in announcement bar.
    • Fixed content tabs failing to work.
    • Fixed line-endings and usage example code.
    • Removed any pafy and streamlink references.
    • Fixed context and typos.
  • CI:
    • Fixed NameError bugs in WebGear_RTC CI test.
  • Maintenance:
    • Removed dead logger code causing Python's Built-in logging module to hide logs.
    • Removed unused logging import.
    • Updated code comments.
Pull Requests
New Contributors

 

 

v0.2.3 (2021-10-27)

New Features
  • CamGear:
    • Added support for 4K Streaming URLs.
  • Helper:
    • Implemented logging ColorFormatter string alignment.
      • Center aligned logging Level-name and Class-name.
      • Changed % formatting style with modern {.
      • Re-added asctime value to Formatter string.
      • Re-arranged parameter positions in Formatter string.
  • Maintenance:
    • Added new .gitignore for specifying intentionally untracked files to ignore
      • Added more files entries to .gitignore.
    • Added new .gitattributes to manage how Git reads line endings.
      • Enabled auto default behavior, in case people don't have core.autocrlf set.
      • Enforced LF line-endings for selective files types.
      • Added Binary data files that specifies they are not text, and git should not try to change them.
      • Added Language aware diff headers.
      • Added Linguist language overrides.
  • Docs:
    • Added new ScreenGear with WebGear_RTC API bonus example.
    • Added support for hl_lines argument for highlighting specific code lines.
    • Added drop-shadow effects for its slate theme to improve visibility.
Updates/Improvements
  • CamGear:
    • Replaced youtube-dl with yt-dlp as pafy backend for YouTube videos pipelining.
      • Implemented hack to trick pafy into assuming yt-dlp as youtube-dl.
      • Using sys.modules to present yt-dlp as youtube-dl.
      • yt-dlp python API functions exactly similar to youtube-dl.
      • Replaced youtube-dl dependency with yt-dlp.
      • Replaced youtube-dl imports with yt-dlp.
  • StreamGear:
    • Updated default stream_count internal dict key value to 1.
  • Maintenance:
    • Introduced python short-circuiting for handling logging logic.
    • Enabled logging for check_WriteAccess method in WriteGear, StreamGear and NetGear APIs.
  • Docs:
    • Added warning for ScreenGear outputting RGBA frames instead of default BGR frames with mss backend.
    • Added warnings for properly formatting output_params when assigning external audio-source in WriteGear.
    • Added depreciation notice for Python 3.6 legacies.
    • Restructured docs to make it more user-friendly.
    • Updated, Extended and Improved context.
    • Improved code comments.
    • Updated docs admonitions.
    • Updated Zenodo badge.
  • CI:
    • Migrated to new Codecov Uploader in Azure Pipelines.
    • Replaced env with export in ci_linux.yml.
    • Replaced bubkoo/needs-more-info@v1 with wow-actions/needs-more-info@v1.
    • Added codecov secret token through env variable.
    • Added wildcard to skip CI tests for doc(.md) files.
    • Added .md files to Codecov ignore list.
    • Update vidgear's banner image.
Breaking Updates/Changes
  • check_WriteAccess will now return as invalid path if writing directory does not exists. This will effect output file handling in WriteGear and StreamGear APIs.
Bug-fixes
  • StreamGear:
    • Fixed StreamGear Malformed URI Error with HLS Segments [PR #243 by @Vboivin]
      • Removed the extra '%' character from the naming convention for segment files.
      • Used stream_count internal dict variable to alter template for HLS segment filenames.
  • WriteGear:
    • Fixed bug in disable_force_termination logic which accidentally disables force termination.
  • WebGear_RTC:
    • Fixed name 'VideoStreamTrack' is not defined bug.
  • Setup.py:
    • Fixed TypeError bug.
    • Fixed invalid latest_version retrieval.
  • Helper:
    • Fixed check_WriteAccess failing to recognize correct permission for writing the output file on windows platform.
      • Implemented separate logic for Windows and *nix platforms.
      • Added new stat import.
      • Improved warnings and error handling.
      • Added logging parameter to check_WriteAccess.
    • Fixed bug in check_WriteAccess that throws OSError while handling URLs.
  • Docs:
    • Fixed bugs in WriteGear's Compression Mode with Live Audio Input example.
    • Fixed "drop-shadow" property via filter function conflicting with sidecard button.
      • Added new CSS classes for image, admonitions and code highlight in dark theme.
    • Several internal and external webpage links typos fixed.
    • Fixed several language typos.
  • CI:
    • Fixed Azure Pipeline coverage upload bugs.
    • Fixed random errors in CamGear stream_mode test.
  • Bash:
    • Removed the Windows carriage returns from the shell scripts to be able to execute them on Linux.
  • Fixed logging comments.
Pull Requests
New Contributors

 

 

v0.2.2 (2021-09-02)

New Features
  • StreamGear:
    • Native Support for Apple HLS Multi-Bitrate Streaming format:
      • Added support for new Apple HLS (HTTP Live Streaming) HTTP streaming format in StreamGear.
      • Implemented default workflow for auto-generating primary HLS stream of same resolution and framerate as source.
      • Added HLS support in Single-Source and Real-time Frames Modes.
      • Implemented inherit support for fmp4 and mpegts HLS segment types.
      • Added adequate default parameters required for trans-coding HLS streams.
      • Added native support for HLS live-streaming.
      • Added "hls" value to format parameter for easily selecting HLS format.
      • Added HLS support in -streams attribute for transcoding additional streams.
      • Added support for .m3u8 and .ts extensions in clear_prev_assets workflow.
      • Added validity check for .m3u8 extension in output when HLS format is used.
      • Separated DASH and HLS command handlers.
      • Created HLS format exclusive parameters.
      • Implemented -hls_base_url FFMpeg parameter support.
    • Added support for audio input from external device:
      • Implemented support for audio input from external device.
      • Users can now easily add audio device and decoder by formatting them as python list.
      • Modified -audio parameter to support list data type as value.
      • Modified validate_audio helper function to validate external audio devices.
    • Added -seg_duration to control segment duration.
  • NetGear:
    • New SSH Tunneling Mode for remote connection:
      • New SSH Tunneling Mode for connecting ZMQ sockets across machines via SSH tunneling.
      • Added new ssh_tunnel_mode attribute to enable ssh tunneling at provide address at server end only.
      • Implemented new check_open_port helper method to validate availability of host at given open port.
      • Added new attributes ssh_tunnel_keyfile and ssh_tunnel_pwd to easily validate ssh connection.
      • Extended this feature to be compatible with bi-directional mode and auto-reconnection.
      • Disabled support for exclusive Multi-Server and Multi-Clients modes.
      • Implemented logic to automatically enable paramiko support if installed.
      • Reserved port-47 for testing.
    • Additional colorspace support for input frames with Frame-Compression enabled:
      • Allowed to manually select colorspace on-the-fly with JPEG frame compression.
      • Updated jpeg_compression dict parameter to support colorspace string values.
      • Added all supported colorspace values by underline simplejpeg library.
      • Server enforced frame-compression colorspace on client(s).
      • Enable "BGR" colorspace by default.
      • Added Example for changing incoming frames colorspace with NetGear's Frame Compression.
      • Updated Frame Compression parameters in NetGear docs.
      • Updated existing CI tests to cover new frame compression functionality.
  • NetGear_Async:
    • New exclusive Bidirectional Mode for bidirectional data transfer:
      • NetGear_Async's first-ever exclusive Bidirectional mode with pure asyncio implementation.
      • Bidirectional mode is only available with User-defined Custom Source(i.e. source=None)
      • Added support for PAIR & REQ/REP bidirectional patterns for this mode.
      • Added powerful asyncio.Queues for handling user data and frames in real-time.
      • Implemented new transceive_data method to Transmit (in Recieve mode) and Receive (in Send mode) data in real-time.
      • Implemented terminate_connection internal asyncio method to safely terminate ZMQ connection and queues.
      • Added msgpack automatic compression encoding and decoding of data and frames in bidirectional mode.
      • Added support for np.ndarray video frames.
      • Added new bidirectional_mode attribute for enabling this mode.
      • Added 8-digit random alphanumeric id generator for each device.
      • NetGear_Async will throw RuntimeError if bidirectional mode is disabled at server or client but not both.
    • Added new disable_confirmation used to force disable termination confirmation from client in terminate_connection.
    • Added task_done() method after every get() call to gracefully terminate queues.
    • Added new secrets and string imports.
  • WebGear:
    • Updated JPEG Frame compression with simplejpeg:
      • Implemented JPEG compression algorithm for 4-5% performance boost at cost of minor loss in quality.
      • Utilized encode_jpeg and decode_jpeg methods to implement turbo-JPEG transcoding with simplejpeg.
      • Added new options to control JPEG frames quality, enable fastest dct, fast upsampling to boost performance.
      • Added new jpeg_compression, jpeg_compression_quality, jpeg_compression_fastdct, jpeg_compression_fastupsample attributes.
      • Enabled fast dct by default with JPEG frames at 90%.
      • Incremented default frame reduction to 25%.
      • Implemented automated grayscale colorspace frames handling.
      • Updated old and added new usage examples.
      • Dropped support for depreciated attributes from WebGear and added new attributes.
    • Added new WebGear Theme: (Checkout at https://github.com/abhiTronix/vidgear-vitals)
      • Added responsive image scaling according to screen aspect ratios.
      • Added responsive text scaling.
      • Added rounded border and auto-center to image tag.
      • Added bootstrap css properties to implement auto-scaling.
      • Removed old resize() hack.
      • Improved text spacing and weight.
      • Integrated toggle full-screen to new implementation.
      • Hide Scrollbar both in WebGear_RTC and WebGear Themes.
      • Beautify files syntax and updated files checksum.
      • Refactor files and removed redundant code.
      • Bumped theme version to v0.1.2.
  • WebGear_RTC:
    • Added native support for middlewares:
      • Added new global middleware variable for easily defining Middlewares as list.
      • Added validity check for Middlewares.
      • Added tests for middlewares support.
      • Added example for middlewares support.
      • Extended middlewares support to WebGear API too.
      • Added related imports.
    • Added new WebGear_RTC Theme: (Checkout at https://github.com/abhiTronix/vidgear-vitals)
      • Implemented new responsive video scaling according to screen aspect ratios.
      • Added bootstrap CSS properties to implement auto-scaling.
      • Removed old resize() hack.
      • Beautify files syntax and updated files checksum.
      • Refactored files and removed redundant code.
      • Bumped theme version to v0.1.2
  • Helper:
    • New automated interpolation selection for gears:
      • Implemented retrieve_best_interpolation method to automatically select best available interpolation within OpenCV.
      • Added support for this method in WebGear, WebGear_RTC and Stabilizer Classes/APIs.
      • Added new CI tests for this feature.
    • Implemented get_supported_demuxers method to get list of supported demuxers.
  • CI:
    • Added new no-response work-flow for stale issues.
    • Added new CI tests for SSH Tunneling Mode.
    • Added paramiko to CI dependencies.
    • Added support for "hls" format in existing CI tests.
    • Added new functions check_valid_m3u8 and extract_meta_video for validating HLS files.
    • Added new m3u8 dependency to CI workflows.
    • Added complete CI tests for NetGear_Async's new Bidirectional Mode:
      • Implemented new exclusive Custom_Generator class for testing bidirectional data dynamically on server-end.
      • Implemented new exclusive client_dataframe_iterator method for testing bidirectional data on client-end.
      • Implemented test_netgear_async_options and test_netgear_async_bidirectionalmode two new tests.
      • Added timeout value on server end in CI tests.
  • Setup.py:
    • Added new cython and msgpack dependency.
    • Added msgpack and msgpack_numpy to auto-install latest.
  • BASH:
    • Added new temp_m3u8 folder for generating M3U8 assets in CI tests.
  • Docs:
    • Added docs for new Apple HLS StreamGear format:
      • Added StreamGear HLS transcoding examples for both StreamGear modes.
      • Updated StreamGear parameters to w.r.t new HLS configurations.
      • Added open-sourced "Sintel" - project Durian Teaser Demo with StreamGear's HLS stream using Clappr and raw.githack.com.
      • Added new HLS chunks at https://github.com/abhiTronix/vidgear-docs-additionals for StreamGear
      • Added support for HLS video in Clappr within custom.js using HlsjsPlayback plugin.
      • Added support for Video Thumbnail preview for HLS video in Clappr within custom.js
      • Added hlsjs-playback.min.js JS script and suitable configuration for HlsjsPlayback plugin.
      • Added custom labels for quality levels selector in custom.js.
      • Added new docs content related to new Apple HLS format.
      • Updated DASH chunk folder at https://github.com/abhiTronix/vidgear-docs-additionals.
      • Added example for audio input support from external device in StreamGear.
      • Added steps for using -audio attribute on different OS platforms in StreamGear.
    • Added usage examples for NetGear_Async's Bidirectional Mode:
      • Added new Usage examples and Reference doc for NetGear_Async's Bidirectional Mode.
      • Added new image asset for NetGear_Async's Bidirectional Mode.
      • Added NetGear_Async's option parameter reference.
      • Updated NetGear_Async definition in docs.
      • Changed font size for Helper methods.
      • Renamed Bonus section to References in mkdocs.yml.
    • Added Gitter sidecard embed widget:
      • Imported gitter-sidecar script to main.html.
      • Updated custom.js to set global window option.
      • Updated Sidecard UI in custom.css.
    • Added bonus examples to help section:
      • Implemented a curated list of more advanced examples with unusual configuration for each API.
    • Added several new contents and updated context.
    • Added support for search suggestions, search highlighting and search sharing (i.e. deep linking)
    • Added more content to docs to make it more user-friendly.
    • Added warning that JPEG Frame-Compression is disabled with Custom Source in WebGear.
    • Added steps for identifying and specifying sound card on different OS platforms in WriteGear.
    • Added Zenodo DOI badge and its reference in BibTex citations.
    • Added extra.homepage parameter, which allows for setting a dedicated URL for site_url.
    • Added pymdownx.striphtml plugin for stripping comments.
    • Added complete docs for SSH Tunneling Mode.
    • Added complete docs for NetGear's SSH Tunneling Mode.
    • Added pip upgrade related docs.
    • Added docs for installing vidgear with only selective dependencies
    • Added new advance/experiment admonition with new background color.
    • Added new icons SVGs for advance and warning admonition.
    • Added new usage example and related information.
    • Added new image assets for ssh tunneling example.
    • Added new admonitions
    • Added new FAQs.
Updates/Improvements
  • VidGear Core:
    • New behavior to virtually isolate optional API specific dependencies by silencing ImportError on all VidGear's APIs import.
    • Implemented algorithm to cache all imports on startup but silence any ImportError on missing optional dependency.
    • Now ImportError will be raised only any certain API specific dependency is missing during given API's initialization.
    • New import_dependency_safe to imports specified dependency safely with importlib module.
    • Replaced all APIs imports with import_dependency_safe.
    • Added support for relative imports in import_dependency_safe.
    • Implemented error parameter to by default ImportError with a meaningful message if a dependency is missing, Otherwise if error = log a warning will be logged and on error = silent everything will be quit. But If a dependency is present, but older than specified, an error is raised if specified.
    • Implemented behavior that if a dependency is present, but older than min_version specified, an error is raised always.
    • Implemented custom_message to display custom message on error instead of default one.
    • Implemented separate import_core_dependency function to import and check for specified core dependency.
    • ImportError will be raised immediately if core dependency not found.
  • StreamGear:
    • Replaced depreciated -min_seg_duration flag with -seg_duration.
    • Removed redundant -re flag from RTFM.
    • Improved Live-Streaming performance by disabling SegmentTimline
    • Improved DASH assets detection for removal by using filename prefixes.
  • NetGear:
    • Replaced np.newaxis with np.expand_dims.
    • Replaced random module with secrets while generating system ID.
    • Update array indexing with np.copy.
  • NetGear_Async:
    • Improved custom source handling.
    • Removed deprecated loop parameter from asyncio methods.
    • Re-implemented skip_loop parameter in close() method.
    • run_until_complete will not used if skip_loop is enabled.
    • skip_loop now will create asyncio task instead and will enable disable_confirmation by default.
    • Replaced create_task with ensure_future to ensure backward compatibility with python-3.6 legacies.
    • Simplified code for transceive_data method.
  • WebGear_RTC:
    • Improved handling of failed ICE connection.
    • Made is_running variable globally available for internal use.
  • Helper:
    • Added 4320p resolution support to dimensions_to_resolutions method.
    • Implemented new delete_file_safe to safely delete files at given path.
    • Replaced os.remove calls with delete_file_safe.
    • Added support for filename prefixes in delete_ext_safe method.
    • Improved and simplified create_blank_frame functions frame channels detection.
    • Added logging parameter to capPropId function to forcefully discard any error(if required).
  • Setup.py:
  • Asyncio:
    • Changed asyncio.sleep value to 0.
      • The amount of time sleep is irrelevant; the only purpose await asyncio.sleep() serves is to force asyncio to suspend execution to the event loop, and give other tasks a chance to run. Also, await asyncio.sleep(0) will achieve the same effect. https://stackoverflow.com/a/55782965/10158117
  • License:
    • Dropped publication year range to avoid confusion. (Signed and Approved by @abhiTronix)
    • Updated Vidgear license's year of first publication of the work in accordance with US copyright notices defined by Title 17, Chapter 4(Visually perceptible copies): https://www.copyright.gov/title17/92chap4.html
    • Reflected changes in all copyright notices.
  • CI:
    • Updated macOS VM Image to latest in azure devops.
    • Updated VidGear Docs Deployer Workflow.
    • Updated WebGear_RTC CI tests.
    • Removed redundant code from CI tests.
    • Updated tests to increase coverage.
    • Enabled Helper tests for python 3.8+ legacies.
    • Enabled logging in validate_video method.
    • Added -hls_base_url to streamgear tests.
    • Update mpegdash dependency to 0.3.0-dev2 version in Appveyor.
    • Updated CI tests for new HLS support
    • Updated CI tests from scratch for new native HLS support in StreamGear.
    • Updated test patch for StreamGear.
    • Added exception for RunTimeErrors in NetGear CI tests.
    • Added more directories to Codecov ignore list.
    • Imported relative logger_handler for asyncio tests.
  • Docs:
    • Re-positioned few docs comments at bottom for easier detection during stripping.
    • Updated to new extra analytics parameter in Material Mkdocs.
    • Updated dark theme to dark orange.
    • Changed fonts => text: Muli & code: Fira Code
    • Updated fonts to Source Sans Pro.
    • Updated setup.py update-link for modules.
    • Re-added missing StreamGear Code docs.
    • Several minor tweaks and typos fixed.
    • Updated 404.html page.
    • Updated admonitions colors and beautified custom.css.
    • Replaced VideoGear & CamGear with OpenCV in CPU intensive examples.
    • Updated mkdocs.yml with new changes and URLs.
    • Moved FAQ examples to bonus examples.
    • Moved StreamGear primary modes to separate sections for better readability.
    • Implemented separate overview and usage example pages for StreamGear primary modes.
    • Improved StreamGear docs context and simplified language.
    • Renamed StreamGear overview page to introduction.
    • Re-written Threaded-Queue-Mode from scratch with elaborated functioning.
    • Replace Paypal with Liberpay in FUNDING.yml.
    • Updated FFmpeg Download links.
    • Reverted UI change in CSS.
    • Updated changelog.md and fixed clutter.
    • Updated README.md and mkdocs.yml with new additions
    • Updated context for CamGear example.
    • Restructured and added more content to docs.
    • Updated comments in source code.
    • Removed redundant data table tweaks from custom.css.
    • Re-aligned badges in README.md.
    • Beautify custom.css.
    • Updated mkdocs.yml.
    • Updated context and fixed typos.
    • Added missing helper methods in Reference.
    • Updated Admonitions.
    • Updates images assets.
    • Bumped CodeCov.
  • Logging:
    • Improved logging level-names.
    • Updated logging messages.
  • Minor tweaks to needs-more-info template.
  • Updated issue templates and labels.
  • Removed redundant imports.
Breaking Updates/Changes
  • Virtually isolated all API specific dependencies, Now ImportError for API-specific dependencies will be raised only when any of them is missing at API's initialization.
  • Renamed delete_safe to delete_ext_safe.
  • Dropped support for frame_jpeg_quality, frame_jpeg_optimize, frame_jpeg_progressive attributes from WebGear.
Bug-fixes
  • CamGear:
    • Hot-fix for Live Camera Streams:
      • Added new event flag to keep check on stream read.
      • Implemented event wait for read() to block it when source stream is busy.
      • Added and Linked THREAD_TIMEOUT with event wait timout.
      • Improved backward compatibility of new additions.
    • Enforced logging for YouTube live.
  • NetGear:
    • Fixed Bidirectional Video-Frame Transfer broken with frame-compression:
      • Fixed return_data interfering with return JSON-data in receive mode.
      • Fixed logic.
    • Fixed color-subsampling interfering with colorspace.
    • Patched external simplejpeg bug. Issue: https://gitlab.com/jfolz/simplejpeg/-/issues/11
      • Added np.squeeze to drop grayscale frame's 3rd dimension on Client's end.
    • Fixed bug that cause server end frame dimensions differ from client's end when frame compression enabled.
  • NetGear_Async:
    • Fixed bug related asyncio queue freezing on calling join().
    • Fixed ZMQ connection bugs in bidirectional mode.
    • Fixed several critical bugs in event loop handling.
    • Fixed several bugs in bidirectional mode implementation.
    • Fixed missing socket termination in both server and client end.
    • Fixed timeout parameter logic.
    • Fixed typos in error messages.
  • WebGear_RTC:
    • Fixed stream freezes after web-page reloading:
      • Implemented new algorithm to continue stream even when webpage is reloaded.
      • Inherit and modified next_timestamp VideoStreamTrack method for generating accurate timestamps.
      • Implemented reset_connections callable to reset all peer connections and recreate Video-Server timestamps. (Implemented by @kpetrykin)
      • Added close_connection endpoint in JavaScript to inform server page refreshing.(Thanks to @kpetrykin)
      • Added exclusive reset connection node /close_connection in routes.
      • Added reset() method to Video-Server class for manually resetting timestamp clock.
      • Added reset_enabled flag to keep check on reloads.
      • Fixed premature webpage auto-reloading.
      • Added additional related imports.
    • Fixed web-page reloading bug after stream ended:
      • Disable webpage reload behavior handling for Live broadcasting.
      • Disable reload CI test on Windows machines due to random failures.
      • Improved handling of failed ICE connection.
    • Fixed Assertion error bug:
      • Source must raise MediaStreamError when stream ends instead of returning None-type.
  • WebGear
    • Removed format specific OpenCV decoding and encoding support for WebGear.
  • Helper:
    • Regex bugs fixed:
      • New improved regex for discovering supported encoders in get_supported_vencoders.
      • Re-implemented check for extracting only valid output protocols in is_valid_url.
      • Minor tweaks for better regex compatibility.
    • Bugfix related to OpenCV import:
      • Bug fixed for OpenCV import comparison test failing with Legacy versions and throwing ImportError.
      • Replaced packaging.parse_version with more robust distutils.version.
    • Fixed bug with create_blank_frame that throws error with gray frames:
      • Implemented automatic output channel correction inside create_blank_frame function.
      • Extended automatic output channel correction support to asyncio package.
    • Implemented RTSP protocol validation as demuxer, since it's not a protocol but a demuxer.
    • Removed redundant logger_handler, mkdir_safe, retrieve_best_interpolation, capPropId helper functions from asyncio package. Relatively imported helper functions from non-asyncio package.
    • Removed unused aiohttp dependency.
    • Removed asctime formatting from logging.
  • StreamGear:
    • Fixed Multi-Bitrate HLS VOD streams:
      • Re-implemented complete workflow for Multi-Bitrate HLS VOD streams.
      • Extended support to both Single-Source and Real-time Frames Modes.
    • Fixed bugs with audio-video mapping.
    • Fixed master playlist not generating in output.
    • Fixed improper -seg_duration value resulting in broken pipeline.
    • Fixed expected aspect ratio not calculated correctly for additional streams.
    • Fixed stream not terminating when provided input from external audio device.
    • Fixed bugs related to external audio not mapped correctly in HLS format.
    • Fixed OPUS audio fragments not supported with MP4 video in HLS.
    • Fixed unsupported high audio bit-rate bug.
  • Setup.py:
    • Fixed latest_version returning incorrect version for some PYPI packages.
    • Removed latest_version variable support from simplejpeg.
    • Fixed streamlink only supporting requests==2.25.1 on Windows.
    • Removed all redundant dependencies like colorama, aiofiles, aiohttp.
    • Fixed typos in dependencies.
  • Setup.cfg:
    • Replaced dashes with underscores to remove warnings.
  • CI:
    • Replaced buggy starlette.TestClient with async-asgi-testclient in WebGear_RTC
    • Removed run() method and replaced with pure asyncio implementation.
    • Added new async-asgi-testclient CI dependency.
    • Fixed fake_picamera class logger calling vidgear imports prematurely before importing picamera class in tests.
      • Implemented new fake_picamera class logger inherently with logging module.
      • Moved sys.module logic for faking to init.py.
      • Added __init__.py to ignore in Codecov.
    • Fixed event loop closing prematurely while reloading:
      • Internally disabled suspending event loop while reloading.
    • Event Policy Loop patcher added for WebGear_RTC tests.
    • Fixed return_assets_path path bug.
    • Fixed typo in TimeoutError exception import.
    • Fixed eventloop is already closed bug.
    • Fixed eventloop bugs in Helper CI tests.
    • Fixed several minor bugs related to new CI tests.
    • Fixed bug in PiGear tests.
  • Docs:
    • Fixed 404 page does not work outside the site root with mkdocs.
    • Fixed markdown files comments not stripped when converted to HTML.
    • Fixed missing heading in VideoGear.
    • Typos in links and code comments fixed.
    • Several minor tweaks and typos fixed.
    • Fixed improper URLs/Hyperlinks and related typos.
    • Fixed typos in usage examples.
    • Fixed redundant properties in CSS.
    • Fixed bugs in mkdocs.yml.
    • Fixed docs contexts and typos.
    • Fixed stream.release() missing in docs.
    • Fixed several typos in code comments.
    • Removed dead code from docs.
  • Refactored Code and reduced redundancy.
  • Fixed shutdown in main.py.
  • Fixed logging comments.
Pull Requests
New Contributors

 

 

v0.2.1 (2021-04-25)

New Features
  • WebGear_RTC:
    • A new API that is similar to WeGear API in all aspects but utilizes WebRTC standard instead of Motion JPEG for streaming.
    • Now it is possible to share data and perform teleconferencing peer-to-peer, without requiring that the user install plugins or any other third-party software.
    • Added a flexible backend for aiortc - a python library for Web Real-Time Communication (WebRTC).
    • Integrated all functionality and parameters of WebGear into WebGear_RTC API.
    • Implemented JSON Response with a WebRTC Peer Connection of Video Server.
    • Added a internal RTC_VideoServer server on WebGear_RTC, a inherit-class to aiortc's VideoStreamTrack API.
    • New Standalone UI Default theme v0.1.1 for WebGear_RTC from scratch without using 3rd-party assets. (by @abhiTronix)
    • New custom.js and custom.css for custom responsive behavior.
    • Added WebRTC support to custom.js and ensured compatibility with WebGear_RTC.
    • Added example support for ICE framework and STUN protocol like WebRTC features to custom.js.
    • Added resize() function to custom.js to automatically adjust video & img tags for smaller screens.
    • Added WebGear_RTC support in main.py for easy access through terminal using --mode flag.
    • Integrated all WebGear_RTC enhancements to WebGear Themes.
    • Added CI test for WebGear_RTC.
    • Added complete docs for WebGear_RTC API.
    • Added bare-minimum as well as advanced examples usage code.
    • Added new theme images.
    • Added Reference and FAQs.
  • CamGear API:
    • New Improved Pure-Python Multiple-Threaded Implementation:
      • Optimized Threaded-Queue-Mode Performance. (PR by @bml1g12)
      • Replaced regular queue.full checks followed by sleep with implicit sleep with blocking queue.put.
      • Replaced regular queue.empty checks followed by queue.
      • Replaced nowait_get with a blocking queue.get natural empty check.
      • Up-to 2x performance boost than previous implementations.
    • New THREAD_TIMEOUT attribute to prevent deadlocks:
      • Added support for THREAD_TIMEOUT attribute to its options parameter.
      • Updated CI Tests and docs.
  • WriteGear API:
    • New more robust handling of default video-encoder in compression mode:
      • Implemented auto-switching of default video-encoder automatically based on availability.
      • API now selects Default encoder based on priority: "libx264" > "libx265" > "libxvid" > "mpeg4".
      • Added get_supported_vencoders Helper method to enumerate Supported Video Encoders.
      • Added common handler for -c:v and -vcodec flags.
  • NetGear API:
    • New Turbo-JPEG compression with simplejpeg
      • Implemented JPEG compression algorithm for 4-5% performance boost at cost of minor loss in quality.
      • Utilized encode_jpeg and decode_jpeg methods to implement turbo-JPEG transcoding with simplejpeg.
      • Added options to control JPEG frames quality, enable fastest dct, fast upsampling to boost performance.
      • Added new jpeg_compression, jpeg_compression_quality, jpeg_compression_fastdct, jpeg_compression_fastupsample attributes.
      • Enabled fast dct by default with JPEG frames at 90%.
      • Added Docs for JPEG Frame Compression.
  • WebGear API:
    • New modular and flexible configuration for Custom Sources:
      • Implemented more convenient approach for handling custom source configuration.
      • Added new config global variable for this new behavior.
      • Now None-type source parameter value is allowed for defining own custom sources.
      • Added new Example case and Updates Docs for this feature.
      • Added new CI Tests.
    • New Browser UI Updates:
      • New Standalone UI Default theme v0.1.0 for browser (by @abhiTronix)
      • Completely rewritten theme from scratch with only local resources.
      • New custom.js and custom.css for custom responsive behavior.
      • New sample glow effect with css.
      • New sample click to full-screen behavior with javascript.
      • Removed all third-party theme dependencies.
      • Update links to new github server abhiTronix/vidgear-vitals
      • Updated docs with new theme's screenshots.
    • Added enable_infinite_frames attribute for enabling infinite frames.
    • Added New modular and flexible configuration for Custom Sources.
    • Bumped WebGear Theme Version to v0.1.1.
    • Updated Docs and CI tests.
  • ScreenGear API:
    • Implemented Improved Pure-Python Multiple-Threaded like CamGear.
    • Added support for THREAD_TIMEOUT attribute to its options parameter.
  • StreamGear API:
    • Enabled pseudo live-streaming flag re for live content.
  • Docs:
    • Added new native docs versioning to mkdocs-material.
    • Added new examples and few visual tweaks.
    • Updated Stylesheet for versioning.
    • Added new DASH video chunks at https://github.com/abhiTronix/vidgear-docs-additionals for StreamGear and Stabilizer streams.
    • Added open-sourced "Tears of Steel" * project Mango Teaser video chunks.
    • Added open-sourced "Subspace Video Stabilization" http://web.cecs.pdx.edu/~fliu/project/subspace_stabilization/ video chunks.
    • Added support for DASH Video Thumbnail preview in Clappr within custom.js.
    • Added responsive clappr DASH player with bootstrap's embed-responsive.
    • Added new permalink icon and slugify to toc.
    • Added "back-to-top" button for easy navigation.
  • Helper:
    • New GitHub Mirror with latest Auto-built FFmpeg Static Binaries:
      • Replaced new GitHub Mirror abhiTronix/FFmpeg-Builds in helper.py
      • New CI maintained Auto-built FFmpeg Static Binaries.
      • Removed all 3rd-party and old links for better compatibility and Open-Source reliability.
      • Updated Related CI tests.
    • Added auto-font-scaling for create_blank_frame method.
    • Added c_name parameter to generate_webdata and download_webdata to specify class.
    • A more robust Implementation of Downloading Artifacts:
      • Added a custom HTTP TimeoutHTTPAdapter Adapter with a default timeout for all HTTP calls based on this GitHub comment.
      • Implemented http client and the send() method to ensure that the default timeout is used if a timeout argument isn't provided.
      • Implemented Requests sessionwith block to exit properly even if there are unhandled exceptions.
      • Add a retry strategy to custom TimeoutHTTPAdapter Adapter with max 3 retries and sleep(backoff_factor=1) between failed requests.
    • Added create_blank_frame method to create bland frames with suitable text.
  • [CI] Continuous Integration:
    • Added new fake frame generated for fake picamera class with numpy.
    • Added new create_bug parameter to fake picamera class for emulating various artificial bugs.
    • Added float/int instance check on time_delay for camgear and pigear.
    • Added EXIT_CODE to new timeout implementation for pytests to upload codecov report when no timeout.
    • Added auxiliary classes to fake picamera for facilitating the emulation.
    • Added new CI tests for PiGear Class for testing on all platforms.
    • Added shutdown() function to gracefully terminate WebGear_RTC API.
    • Added new coreutils brew dependency.
    • Added handler for variable check on exit and codecov upload.
    • Added is_running flag to WebGear_RTC to exit safely.
  • Setup:
    • New automated latest version retriever for packages:
      • Implemented new latest_version method to automatically retrieve latest version for packages.
      • Added Some Dependencies.
    • Added simplejpeg package for all platforms.
Updates/Improvements
  • Added exception for RunTimeErrors in NetGear CI tests.
  • WriteGear: Critical file write access checking method:
    • Added new check_WriteAccess Helper method.
    • Implemented a new robust algorithm to check if given directory has write-access.
    • Removed old behavior which gives irregular results.
  • Helper: Maintenance Updates
    • Added workaround for Python bug.
    • Added safe_mkdir to check_WriteAccess to automatically create non-existential parent folder in path.
    • Extended check_WriteAccess Patch to StreamGear.
    • Simplified check_WriteAccess to handle Windows envs easily.
    • Updated FFmpeg Static Download URL for WriteGear.
    • Implemented fallback option for auto-calculating bitrate from extracted audio sample-rate in validate_audio method.
  • Docs: General UI Updates
    • Updated Meta tags for og site and twitter cards.
    • Replaced Custom dark theme toggle with mkdocs-material's official Color palette toggle
    • Added example for external audio input and creating segmented MP4 video in WriteGear FAQ.
    • Added example for YouTube streaming with WriteGear.
    • Removed custom dark-material.js and header.html files from theme.
    • Added blogpost link for detailed information on Stabilizer Working.
    • Updated mkdocs.yml and custom.css configuration.
    • Remove old hack to resize clappr DASH player with css.
    • Updated Admonitions.
    • Improved docs contexts.
    • Updated CSS for version-selector-button.
    • Adjusted files to match new themes.
    • Updated welcome-bot message for typos.
    • Removed redundant FAQs from NetGear Docs.
    • Updated Assets Images.
    • Updated spacing.
  • CI:
    • Removed unused github.ref from yaml.
    • Updated OpenCV Bash Script for Linux envs.
    • Added timeout-minutes flag to github-actions workflow.
    • Added timeout flag to pytest.
    • Replaced Threaded Gears with OpenCV VideoCapture API.
    • Moved files and Removed redundant code.
    • Replaced grayscale frames with color frames for WebGear tests.
    • Updated pytest timeout value to 15mins.
    • Removed aiortc automated install on Windows platform within setup.py.
    • Added new timeout logic to continue to run on external timeout for GitHub Actions Workflows.
    • Removed unreliable old timeout solution from WebGear_RTC.
    • Removed timeout_decorator and asyncio_timeout dependencies for CI.
    • Removed WebGear_RTC API exception from codecov.
    • Implemented new fake picamera class to CI utils for emulating RPi Camera-Module Real-time capabilities.
    • Implemented new get_RTCPeer_payload method to receive WebGear_RTC peer payload.
    • Removed PiGear from Codecov exceptions.
    • Disable Frame Compression in few NetGear tests failing on frame matching.
    • Updated NetGear CI tests to support new attributes
    • Removed warnings and updated yaml
      • Added pytest.ini to address multiple warnings.
      • Updated azure workflow condition syntax.
    • Update mike settings for mkdocs versioning.
    • Updated codecov configurations.
    • Minor logging and docs updates.
    • Implemented pytest timeout for azure pipelines for macOS envs.
    • Added aiortc as external dependency in appveyor.yml.
    • Re-implemented WebGear_RTC improper offer-answer handshake in CI tests.
    • WebGear_RTC CI Updated with VideoTransformTrack to test stream play.
    • Implemented fake AttributeError for fake picamera class.
    • Updated PiGear CI tests to increment codecov.
    • Update Tests docs and other minor tweaks to increase overall coverage.
    • Enabled debugging and disabled exit 1 on error in azure pipeline.
    • Removed redundant benchmark tests.
  • Helper: Added missing RTSP URL scheme to is_valid_url method.
  • NetGear_Async: Added fix for uvloop only supporting python>=3.7 legacies.
  • Extended WebGear's Video-Handler scope to https.
  • CI: Remove all redundant 32-bit Tests from Appveyor:
  • Setup: Removed latest_version behavior from some packages.
  • NetGear_Async: Revised logic for handling uvloop for all platforms and legacies.
  • Setup: Updated logic to install uvloop-"v0.14.0" for python-3.6 legacies.
  • Removed any redundant code from webgear.
  • StreamGear:
    • Replaced Ordinary dict with Ordered Dict to use move_to_end method.
    • Moved external audio input to output parameters dict.
    • Added additional imports.
    • Updated docs to reflect changes.
  • Numerous Updates to Readme and mkdocs.yml.
  • Updated font to FONT_HERSHEY_SCRIPT_COMPLEX and enabled logging in create_blank_frame.
  • Separated channels for downloading and storing theme files for WebGear and WebGear_RTC APIs.
  • Removed logging condition to always inform user in a event of FFmpeg binary download failure.
  • WebGear_RTC:
    • Improved auto internal termination.
    • More Performance updates through setCodecPreferences.
    • Moved default Video RTC video launcher to __offer.
  • NetGear_Async: Added timeout to client in CI tests.
  • Reimplemented and updated changelog.md.
  • Updated code comments.
  • Setup: Updated keywords and classifiers.
  • Bumped codecov.
Breaking Updates/Changes
  • WriteGear will automatically switch video encoder to default if specified encoder not found.
  • WriteGear will throw RuntimeError if no suitable default encoder found!
  • Removed format specific OpenCV decoding and encoding support for NetGear.
  • Dropped support for compression_format, compression_param attributes from NetGear.
  • Non-existent parent folder in output_filename value will no longer be considered as invalid in StreamGear and WriteGear APIs.
  • None-type source parameter value is allowed for WebGear and NetGear_Async for defining custom sources.
Bug-fixes
  • CamGear: Fixed F821 undefined name 'queue' bug.
  • NetGear_Async: Fixed source parameter missing None as default value.
  • Fixed uvloops only supporting python>=3.7 in NetGear_Async.
  • Helper:
  • Docs:
    • Invalid site_url bug patched in mkdocs.yml
    • Remove redundant mike theme support and its files.
    • Fixed video not centered when DASH video in fullscreen mode with clappr.
    • Fixed Incompatible new mkdocs-docs theme.
    • Fixed missing hyperlinks.
  • CI:
    • Fixed NetGear Address bug
    • Fixed bugs related to termination in WebGear_RTC.
    • Fixed random CI test failures and code cleanup.
    • Fixed string formating bug in Helper.py.
    • Fixed F821 undefined name bugs in WebGear_RTC tests.
    • NetGear_Async Tests fixes.
    • Fixed F821 undefined name bugs.
    • Fixed typo bugs in main.py.
    • Fixed Relative import bug in PiGear.
    • Fixed regex bug in warning filter.
    • Fixed WebGear_RTC frozen threads on exit.
    • Fixed bugs in codecov bash uploader setting for azure pipelines.
    • Fixed False-positive picamera import due to improper sys.module settings.
    • Fixed Frozen Threads on exit in WebGear_RTC API.
    • Fixed deploy error in VidGear Docs Deployer workflow
    • Fixed low timeout bug.
    • Fixed bugs in PiGear tests.
    • Patched F821 undefined name bug.
  • StreamGear:
    • Fixed StreamGear throwing Picture size 0x0 is invalid bug with external audio.
    • Fixed default input framerate value getting discarded in Real-time Frame Mode.
    • Fixed internal list-formatting bug.
  • Fixed E999 SyntaxError bug in main.py.
  • Fixed Typo in bash script.
  • Fixed WebGear freeze on reloading bug.
  • Fixed anomalies in install_opencv bash script.
  • Helper: Bug Fixed in download_ffmpeg_binaries method.
  • Helper: Fixed OSError bug in check_WriteAccess method.
  • Helper: Fixed Input Audio stream bitrate test failing to detect audio-bitrate in certain videos with validate_audio method.
  • Fixed bugs in requests module's function arguments.
  • Fixed None-type stream bug in WebGear.
  • Fixed random crashes in WebGear.
  • Fixed numerous CI test bugs.
  • Fixed several typos.
Pull Requests
New Contributors

 

 

v0.2.0 (2021-01-01)

New Features
  • CamGear API:
    • Support for various Live-Video-Streaming services:
      • Added seamless support for live video streaming sites like Twitch, LiveStream, Dailymotion etc.
      • Implemented flexible framework around streamlink python library with easy control over parameters and quality.
      • Stream Mode can now automatically detects whether source belong to YouTube or elsewhere, and handles it with appropriate API.
    • Re-implemented YouTube URLs Handler:
      • Re-implemented CamGear's YouTube URLs Handler completely from scratch.
      • New Robust Logic to flexibly handing video and video-audio streams.
      • Intelligent stream selector for selecting best possible stream compatible with OpenCV.
      • Added support for selecting stream qualities and parameters.
      • Implemented new get_supported_quality helper method for handling specified qualities
      • Fixed Live-Stream URLs not supported by OpenCV's Videocapture and its FFmpeg.
    • Added additional STREAM_QUALITY and STREAM_PARAMS attributes.
  • ScreenGear API:
    • Multiple Backends Support:
      • Added new multiple backend support with new pyscreenshot python library.
      • Made pyscreenshot the default API for ScreenGear, replaces mss.
      • Added new backend parameter for this feature while retaining previous behavior.
      • Added native automated RGB to BGR conversion for default PIL backend.
      • Kept support for old mss for old compatibility and multi-screen support.
      • Added native dimensional support for multi-screen.
      • Added support all input from all multiple screens.
      • Updated ScreenGear Docs.
      • Updated ScreenGear CI tests.
  • StreamGear API:
    • Changed default behaviour to support complete video transcoding.
    • Added -livestream attribute to support live-streaming.
    • Added additional parameters for -livestream attribute functionality.
    • Updated StreamGear Tests.
    • Updated StreamGear docs.
  • Stabilizer Class:
    • New Robust Error Handling with Blank Frames:
      • Elegantly handles all crashes due to Empty/Blank/Dark frames.
      • Stabilizer throws Warning with this new behavior instead of crashing.
      • Updated CI test for this feature.
  • Docs:
    • Automated Docs Versioning:
      • Implemented Docs versioning through mike API.
      • Separate new workflow steps to handle different versions.
      • Updated docs deploy worflow to support release and dev builds.
      • Added automatic version extraction from github events.
      • Added version-select.js and version-select.css files.
    • Toggleable Dark-White Docs Support:
      • Toggle-button to easily switch dark, white and preferred theme.
      • New Updated Assets for dark backgrounds
      • New css, js files/content to implement this behavior.
      • New material icons for button.
      • Updated scheme to slate in mkdocs.yml.
    • New Theme and assets:
      • New purple theme with dark-purple accent color.
      • New images assets with updated transparent background.
      • Support for both dark and white theme.
      • Increased rebufferingGoal for dash videos.
      • New updated custom 404 page for docs.
    • Issue and PR automated-bots changes
      • New need_info.yml YAML Workflow.
      • New needs-more-info.yml Request-Info template.
      • Replaced Request-Info templates.
      • Improved PR and Issue welcome formatting.
    • Added custom HTML pages.
    • Added show_root_heading flag to disable headings in References.
    • Added new inserAfter function to version-select.js.
    • Adjusted hue for dark-theme for better contrast.
    • New usage examples and FAQs.
    • Added gitmoji for commits.
  • Continuous Integration:
    • Maintenance Updates:
      • Added support for new VIDGEAR_LOGFILE environment variable in Travis CI.
      • Added missing CI tests.
      • Added logging for helper functions.
    • Azure-Pipeline workflow for MacOS envs
      • Added Azure-Pipeline Workflow for testing MacOS environment.
      • Added codecov support.
    • GitHub Actions workflow for Linux envs
      • Added GitHub Action work-flow for testing Linux environment.
    • New YAML to implement GitHub Action workflow for python 3.6, 3.7, 3,8 & 3.9 matrices.
    • Added Upload coverage to Codecov GitHub Action workflow.
    • New codecov-bash uploader for Azure Pipelines.
  • Logging:
    • Added file support
      • Added VIDGEAR_LOGFILE environment variable to manually add file/dir path.
      • Reworked logger_handler() Helper methods (in asyncio too).
      • Added new formatter and Filehandler for handling logger files.
    • Added restore_levelnames auxiliary method for restoring logging levelnames.
  • Added auto version extraction from package version.py in setup.py.
Updates/Improvements
  • Added missing Lazy-pirate auto-reconnection support for Multi-Servers and Multi-Clients Mode in NetGear API.
  • Added new FFmpeg test path to Bash-Script and updated README broken links.
  • Asset Cleanup:
    • Removed all third-party javascripts from projects.
    • Linked all third-party javascript directly.
    • Cleaned up necessary code from CSS and JS files.
    • Removed any copyrighted material or links.
  • Rewritten Docs from scratch:
    • Improved complete docs formatting.
    • Simplified language for easier understanding.
    • Fixed mkdocstrings showing root headings.
    • Included all APIs methods to mkdocstrings docs.
    • Removed unnecessary information from docs.
    • Corrected Spelling and typos.
    • Fixed context and grammar.
    • Removed motivation.md.
    • Renamed many terms.
    • Fixed hyper-links.
    • Reformatted missing or improper information.
    • Fixed context and spellings in Docs files.
    • Simplified language for easy understanding.
    • Updated image sizes for better visibility.
  • Bash Script: Updated to Latest OpenCV Binaries version and related changes
  • Docs: Moved version-selector to header and changed default to alias.
  • Docs: Updated deploy_docs.yml for releasing dev, stable, and release versions.
  • Re-implemented overridden material theme.
  • Updated docs with all new additions and examples.
  • CamGear: CI Stream Mode test updated.
  • Updated ReadMe.md badges.
  • Updated CI tests.
  • Updated setup.py with new features.
  • Updated contributing.md and ReadMe.md.
  • Updated OpenCV version to 4.5.1-dev in bash scripts
  • Updated changelog.md.
  • Moved WebGear API to Streaming Gears.
  • Bumped Codecov.
  • UI changes to version-select.js
  • Docs: Retitle the versions and mkdocs.yml formatting updated.
  • Docs: Version Selector UI reworked and other minor changes.
Breaking Updates/Changes
  • y_tube parameter renamed as stream_mode in CamGear API!
  • Removed Travis support and travis.yml deleted.
Bug-fixes
  • Fixed StreamGear API Limited Segments Bug
  • Fixed Missing links in docs and bump up version.
  • CI: Fixed Appveyor need newer VM image to support Python 3.9.x matrix.
  • ScreenGear BugFix: Fixed Error Handling and updated CI Tests.
  • Fixed improper mkdocs.yml variables.
  • Fixed GStreamer plugin support in bash scripts.
  • Fixed typos in YAMLs and docs.
  • Docs: Fixed Docs Deployer YAML bug for CI envs.
  • Fixed wrong import in YAML.
  • Fixed visible hyperlink on hover in dark-toggle button.
  • Docs: Deployer YAML bug fixed.
  • Docs YAML: issue jimporter/mike#33 patched and fixed fetch-depth=0.
  • Docs: version-select.js bug fixed.
  • Docs: UI Bugs Fixed.
  • CI: Codecov bugfixes.
  • Azure-Pipelines Codecov BugFixes.
  • Fixed version.json not detecting properly in version-select.js.
  • Fixed images not centered inside <figure> tag.
  • Fixed Asset Colors.
  • Fixed failing CI tests.
  • Fixed Several logging bugs.
Pull Requests

 

 

v0.1.9 (2020-08-31)

New Features
  • StreamGear API:
    • New API that automates transcoding workflow for generating Ultra-Low Latency, High-Quality, Dynamic & Adaptive Streaming Formats.
    • Implemented multi-platform , standalone, highly extensible and flexible wrapper around FFmpeg for generating chunked-encoded media segments of the media, and easily accessing almost all of its parameters.
    • API automatically transcodes videos/audio files & real-time frames into a sequence of multiple smaller chunks/segments and also creates a Manifest file.
    • Added initial support for MPEG-DASH (Dynamic Adaptive Streaming over HTTP, ISO/IEC 23009-1).
    • Constructed default behavior in StreamGear, for auto-creating a Primary Stream of same resolution and framerate as source.
    • Added TQDM progress bar in non-debugged output for visual representation of internal processes.
    • Implemented several internal methods for preprocessing FFmpeg and internal parameters for producing streams.
    • Several standalone internal checks to ensure robust performance.
    • New terminate() function to terminate StremGear Safely.
    • New StreamGear Dual Modes of Operation:
      • Implemented Single-Source and Real-time Frames like independent Transcoding Modes.
      • Linked -video_source attribute for activating these modes
      • Single-Source Mode, transcodes entire video/audio file (as opposed to frames by frame) into a sequence of multiple smaller segments for streaming
      • Real-time Frames Mode, directly transcodes video-frames (as opposed to a entire file), into a sequence of multiple smaller segments for streaming
      • Added separate functions, stream() for Real-time Frame Mode and transcode_source() for Single-Source Mode for easy transcoding.
      • Included auto-colorspace detection and RGB Mode like features (extracted from WriteGear), into StreamGear.
    • New StreamGear Parameters:
      • Developed several new parameters such as:
        • output: handles assets directory
        • formats: handles adaptive HTTP streaming format.
        • custom_ffmpeg: handles custom FFmpeg location.
        • stream_params: handles internal and FFmpeg parameter seamlessly.
        • logging: turns logging on or off.
      • New stream_params parameter allows us to exploit almost all FFmpeg parameters and flexibly change its internal settings, and seamlessly generating high-quality streams with its attributes:
        • -streams (list of dictionaries) for building additional streams with -resolution, -video_bitrate & -framerate like sub-attributes.
        • -audio for specifying external audio.
        • -video_source for specifying Single-Source Mode source.
        • -input_framerate for handling input framerate in Real-time Frames Mode.
        • -bpp attribute for handling bits-per-pixels used to auto-calculate video-bitrate.
        • -gop to manually specify GOP length.
        • -ffmpeg_download_path to handle custom FFmpeg download path on windows.
        • -clear_prev_assets to remove any previous copies of SteamGear Assets.
    • New StreamGear docs, MPEG-DASH demo, and recommended DASH players list:
      • Added new StreamGear docs, usage examples, parameters, references, new FAQs.
      • Added Several StreamGear usage examples w.r.t Mode of Operation.
      • Implemented Clappr based on Shaka-Player, as Demo Player.
      • Added Adaptive-dimensional behavior for Demo-player, purely in css.
      • Hosted StreamGear generated DASH chunks on GitHub and served with raw.githack.com.
      • Introduced variable quality level-selector plugin for Clapper Player.
      • Provide various required javascripts and implemented additional functionality for player in extra.js.
      • Recommended tested Online, Command-line and GUI Adaptive Stream players.
      • Implemented separate FFmpeg installation doc for StreamGear API.
      • Reduced rebufferingGoal for faster response.
    • New StreamGear CI tests:
      • Added IO and API initialization CI tests for its Modes.
      • Added various mode Streaming check CI tests.
  • NetGear_Async API:
    • Added new send_terminate_signal internal method.
    • Added WindowsSelectorEventLoopPolicy() for windows 3.8+ envs.
    • Moved Client auto-termination to separate method.
    • Implemented graceful termination with signal API on UNIX machines.
    • Added new timeout attribute for controlling Timeout in Connections.
    • Added missing termination optimizer (linger=0) flag.
    • Several ZMQ Optimizer Flags added to boost performance.
  • WriteGear API:
    • Added support for adding duplicate FFmpeg parameters to output_params:
      • Added new -clones attribute in output_params parameter for handing this behavior..
      • Support to pass FFmpeg parameters as list, while maintaining the exact order it was specified.
      • Built support for zmq.REQ/zmq.REP and zmq.PUB/zmq.SUB patterns in this mode.
      • Added new CI tests debugging this behavior.
      • Updated docs accordingly.
    • Added support for Networks URLs in Compression Mode:
      • output_filename parameter supports Networks URLs in compression modes only
      • Added automated handling of non path/file Networks URLs as input.
      • Implemented new is_valid_url helper method to easily validate assigned URLs value.
      • Validates whether the given URL value has scheme/protocol supported by assigned/installed ffmpeg or not.
      • WriteGear will throw ValueError if -output_filename is not supported.
      • Added related CI tests and docs.
    • Added disable_force_termination attribute in WriteGear to disable force-termination.
  • NetGear API:
    • Added option to completely disable Native Frame-Compression:
      • Checks if any Incorrect/Invalid value is assigned on compression_format attribute.
      • Completely disables Native Frame-Compression.
      • Updated docs accordingly.
  • CamGear API:
    • Added new and robust regex for identifying YouTube URLs.
    • Moved youtube_url_validator to Helper.
  • New helper.py methods:
    • Added validate_video function to validate video_source.
    • Added extract_time Extract time from give string value.
    • Added get_video_bitrate to calculate video birate from resolution, framerate, bits-per-pixels values.
    • Added delete_safe to safely delete files of given extension.
    • Added validate_audio to validate audio source.
    • Added new Helper CI tests.
      • Added new check_valid_mpd function to test MPD files validity.
      • Added mpegdash library to CI requirements.
  • Deployed New Docs Upgrades:
    • Added new assets like images, gifs, custom scripts, javascripts fonts etc. for achieving better visual graphics in docs.
    • Added clappr.min.js, dash-shaka-playback.js, clappr-level-selector.min.js third-party javascripts locally.
    • Extended Overview docs Hyperlinks to include all major sub-pages (such as Usage Examples, Reference, FAQs etc.).
    • Replaced GIF with interactive MPEG-DASH Video Example in Stabilizer Docs.
    • Added new pymdownx.keys to replace [Ctrl+C]/[⌘+C] formats.
    • Added new custom.css stylescripts variables for fluid animations in docs.
    • Overridden announce bar and added donation button.
    • Lossless WEBP compressed all PNG assets for faster loading.
    • Enabled lazy-loading for GIFS and Images for performance.
    • Reimplemented Admonitions contexts and added new ones.
    • Added StreamGear and its different modes Docs Assets.
    • Added patch for images & unicodes for PiP flavored markdown in setup.py.
  • Added Request Info and Welcome GitHub Apps to automate PR and issue workflow
    • Added new config.yml for customizations.
    • Added various suitable configurations.
  • Added new -clones attribute to handle FFmpeg parameter clones in StreamGear and WriteGear API.
  • Added new Video-only and Audio-Only sources in bash script.
  • Added new paths in bash script for storing StreamGear & WriteGear assets temporarily.
Updates/Improvements
  • Added patch for NotImplementedError in NetGear_Async API on Windows 3.8+ envs.
  • Check for valid output file extension according to format selected in StreamGear.
  • Completed migration to travis.com.
  • Created new temp_write temp directory for WriteGear Assets in bash script.
  • Deleted old Redundant assets and added new ones.
  • Employed isort library to sort and group imports in Vidgear APIs.
  • Enabled exception for list, tuple, int, float in WriteGear API's output_params dict.
  • Enabled missing support for frame-compression in its primary Receive Mode.
  • Enforced pixel formats for streams.
  • Improved check for valid system path detection in WriteGear API.
  • Overrided pytest-asyncio fixture in NetGear_Async API.
  • Quoted Gear Headline for understanding each gear easily.
  • Re-Positioned Gear's banner images in overview for better readability.
  • Reduced redundant try-except blocks in NetGear Async.
  • Reformatted and Simplified Docs context.
  • Reimplemented return_testvideo_path CI function with variable streams.
  • Reimplemented skip_loop in NetGear_Async to fix asyncio.CancelledError.
  • Reimplemented buggy audio handler in StreamGear.
  • Reimplemented images with <figure> and <figurecaption> like tags.
  • Removed Python < 3.8 condition from all CI tests.
  • Removed or Grouped redundant code for increasing codecov.
  • Removed redundant code and simplified algorithmic complexities in Gears.
  • Replaced ;nbsp with ;thinsp and ;emsp.
  • Replaced IOError with more reliable RuntimeError in StreamGear Pipelines.
  • Replaced del with pop in dicts.
  • Replaced all Netgear CI tests with more reliable try-except-final blocks.
  • Replaced simple lists with pymdownx.tasklist.
  • Replaced subprocess call() with run() for better error handling in execute_ffmpeg_cmd function.
  • Resized over-sized docs images.
  • Simplified delete_safe Helper function.
  • Simplified default audio-bitrate logic in StreamGear
  • Updated CI tests and cleared redundant code from NetGear_Async API.
  • Updated CI with new tests and Bumped Codecov.
  • Updated Issue and PR templates.
  • Updated Licenses for new files and shrink images dimensions.
  • Updated Missing Helpful tips and increased logging.
  • Updated PR guidelines for more clarity.
  • Updated WebGear examples addresses from 0.0.0.0 to localhost.
  • Updated WriteGear and StreamGear CI tests for not supporting temp directory.
  • Updated README.md and changelog.md with new changes.
  • Updated check_output and added force_retrieve_stderr support to **kwargs to extract stderr output even on FFmpeg error.
  • Updated dicts2args to support internal repeated coreX FFmpeg parameters for StreamGear.
  • Updated mkdocs.yml, changelog.md and README.md with latest changes.
  • Updated validate_audio Helper function will now retrieve audio-bitrate for validation.
  • Updated buggy mpegdash dependency with custom dev fork for Windows machines.
  • Updated core parameters for audio handling.
  • Updated logging for debugging selected eventloops in NetGear_Async API.
  • Updated termination linger to zero at Server's end.
Breaking Updates/Changes
  • Changed Webgear API default address to localhost for cross-compatibility between different platforms.
  • In Netgear_Async API, source value can now be NoneType for a custom frame-generator at Server-end only.
  • Temp (such as /tmp in linux) is now not a valid directory for WriteGear & StreamGear API outputs.
  • Moved vidgear docs assets (i.e images, gifs, javascripts and stylescripts) to override directory.
Bug-fixes
  • Added workaround for system path not handle correctly.
  • Fixed Bug: URL Audio format not being handled properly.
  • Fixed Critical Bug in NetGear_Async throwing ValueError with None-type Source.
  • Fixed Critical StreamGear Bug: FFmpeg pipeline terminating prematurely in Single-Source Mode.
  • Fixed Critical external audio handler bug: moved audio-input to input_parameters.
  • Fixed Frozen-threads bug in CI tests.
  • Fixed Mkdocs only accepting Relative paths.
  • Fixed OSError in WriteGear's compression mode.
  • Fixed StreamGear CI bugs for Windows and CI envs.
  • Fixed Typos and Indentation bugs in NetGear API.
  • Fixed ZMQ throwing error on termination if all max-tries exhausted.
  • Fixed NameError bug in NetGear API and CI tests.
  • Fixed TimeoutError bug in NetGear_Async CI tests.
  • Fixed get_valid_ffmpeg_path throwing TypeError with non-string values.
  • Fixed broken links in docs.
  • Fixed critical duplicate logging bug.
  • Fixed default gop value not handle correctly.
  • Fixed handling of incorrect paths detection.
  • Fixed incorrect definitions in NetGear_Async.
  • Fixed left-over attribute bug in WriteGear.
  • Fixed logic and indentation bugs in CI tests.
  • Fixed logic for handling output parameters in WriteGear API.
  • Fixed missing definitions and logic bug in StreamGear.
  • Fixed missing import and incorrect CI definitions.
  • Fixed missing source dimensions from extract_resolutions output in StreamGear API.
  • Fixed missing support for compression parameters in Multi-Clients Mode.
  • Fixed round off error in FPS.
  • Fixed several CI bugs and updated extract_resolutions method.
  • Fixed several bugs from CI Bidirectional Mode tests.
  • Fixed several typos in docs usage examples.
  • Fixed various AttributeError with wrong attribute names and definition in CI Helper functions.
  • Fixed wrong and missing definitions in docs.
  • Fixed wrong logic for extracting OpenCV frames.
  • Fixed wrong type bug in StreamGear API.
  • Fixed wrong type error bug in WriteGear API.
  • Fixed wrong variable assignments bug in WriteGear API.
  • Fixes to CLI tests and missing docs imports.
  • Many minor typos and wrong definitions.
Pull Requests

 

 

v0.1.8 (2020-06-12)

New Features
  • NetGear API:
    • Multiple Clients support:
      • Implemented support for handling any number of Clients simultaneously with a single Server in this mode.
      • Added new multiclient_mode attribute for enabling this mode easily.
      • Built support for zmq.REQ/zmq.REP and zmq.PUB/zmq.SUB patterns in this mode.
      • Implemented ability to receive data from all Client(s) along with frames with zmq.REQ/zmq.REP pattern only.
      • Updated related CI tests
    • Support for robust Lazy Pirate pattern(auto-reconnection) in NetGear API for both server and client ends:
      • Implemented a algorithm where NetGear rather than doing a blocking receive, will now:
        • Poll the socket and receive from it only when it's sure a reply has arrived.
        • Attempt to reconnect, if no reply has arrived within a timeout period.
        • Abandon the connection if there is still no reply after several requests.
      • Implemented its default support for REQ/REP and PAIR messaging patterns internally.
      • Added new max_retries and request_timeout(in seconds) for handling polling.
      • Added DONTWAIT flag for interruption-free data receiving.
      • Both Server and Client can now reconnect even after a premature termination.
    • Performance Updates:
      • Added default Frame Compression support for Bidirectional frame transmission in Bidirectional mode.
      • Added support for Reducer() function in Helper.py to aid reducing frame-size on-the-go for more performance.
      • Added small delay in recv() function at client's end to reduce system load.
      • Reworked and Optimized NetGear termination, and also removed/changed redundant definitions and flags.
  • Docs: Migration to Mkdocs
    • Implemented a beautiful, static documentation site based on MkDocs which will then be hosted on GitHub Pages.
    • Crafted base mkdocs with third-party elegant & simplistic mkdocs-material theme.
    • Implemented new mkdocs.yml for Mkdocs with relevant data.
    • Added new docs folder to handle markdown pages and its assets.
    • Added new Markdown pages(.md) to docs folder, which are carefully crafted documents - [x] based on previous Wiki's docs, and some completely new additions.
    • Added navigation under tabs for easily accessing each document.
    • New Assets:
    • Added Required Plugins and Extensions:
      • Added support for all pymarkdown-extensions.
      • Added support for some important admonition, attr_list, codehilite, def_list, footnotes, meta, and toc like Mkdocs extensions.
      • Enabled search, minify and git-revision-date-localized plugins support.
      • Added various VidGear's social links to yaml.
      • Added support for en (English) language.
    • Auto-Build API Reference with mkdocstrings:
      • Added support for mkdocstrings plugin for auto-building each VidGear's API references.
      • Added python handler for parsing python source-code to mkdocstrings.
    • Auto-Deploy Docs with GitHub Actions:
      • Implemented Automated Docs Deployment on gh-pages through GitHub Actions workflow.
      • Added new workflow yaml with minimal configuration for automated docs deployment.
      • Added all required python dependencies and environment for this workflow.
      • Added master branch on Ubuntu machine to build matrix.
Updates/Improvements
  • Added in-built support for bidirectional frames(NDarray) transfer in Bidirectional mode.
  • Added support for User-Defined compression params in Bidirectional frames transfer.
  • Added workaround for address already in use bug at client's end.
  • Unified Bidirectional and Multi-Clients mode for client's return data transmission.
  • Replaced ValueError with more suitable RuntimeError.
  • Updated logging for better readability.
  • Added CI test for Multi-Clients mode.
  • Reformatted and grouped imports in VidGear.
  • Added Reducer Helper function CI test.
  • Added Reliability tests for both Server and Client end.
  • Disabled reliable reconnection for Multi-Clients mode.
  • Replaced os.devnull with suprocess's inbuilt function.
  • Updated README.md, Issue and PR templates with new information and updates.
  • Moved changelog.md to /docs and updated contribution guidelines.
  • Improved source-code docs for compatibility with mkdocstrings.
  • Added additional dependency mkdocs-exclude, for excluding files from Mkdocs builds.
  • Updated license and compressed images/diagrams.
  • Added new CI tests and Bumped Codecov.
  • Changed YouTube video URL for CI tests to Creative Commons(CC) video.
  • Removed redundant code.
Breaking Updates/Changes
  • VidGear Docs moved to GitHub Pages, Now Available at https://abhitronix.github.io/vidgear.
  • Removed filter attribute from options parameter in NetGear API.
  • Removed force_terminate parameter support from NetGear API.
  • Disabled additional data of datatype numpy.ndarray for Server end in Bidirectional Mode.
Bug-fixes
  • Fixed 'NoneType' object is not subscriptable bug.
  • Fixed bugs related to delayed termination in NetGear API.
  • Reduced default request_timeout value to 4 and also lowered cut-off limit for the same.
  • Removed redundant ZMQ context termination and similar variables.
  • Added missing VidGear installation in workflow.
  • Excluded conflicting assets README.md from Mkdocs builds.
  • Fixed pattern value check bypassed if wrong value is assigned.
  • Fixed incorrect handling of additional data transferred in synchronous mode at both Server and Client end.
  • Replaced Netgear CI test with more reliable try-except-final blocks.
  • Updated termination linger to zero at Server's end.
  • Fixed NameError bug in NetGear API.
  • Fixed missing support for compression parameters in Multi-Clients Mode.
  • Fixed ZMQ throwing error on termination if all max-tries exhausted.
  • Enabled missing support for frame compression in its primary receive mode.
  • Fixed several bugs from CI Bidirectional Mode tests.
  • Removed or Grouped redundant code for increasing codecov.
  • Fixed Mkdocs only accepting Relative paths.
  • Fixed broken links in docs.
  • Fixed round off error in FPS.
  • Many small typos and bugs fixes.
Pull Requests

 

 

v0.1.7 (2020-04-29)

New Features
  • WebGear API:
    • Added a robust Live Video Server API that can transfer live video frames to any web browser on the network in real-time.
    • Implemented a flexible asyncio wrapper around starlette ASGI Application Server.
    • Added seamless access to various starlette's Response classes, Routing tables, Static Files, Template engine(with Jinja2), etc.
    • Added a special internal access to VideoGear API and all its parameters.
    • Implemented a new Auto-Generation Work-flow to generate/download & thereby validate WebGear API data files from its GitHub server automatically.
    • Added on-the-go dictionary parameter in WebGear to tweak performance, Route Tables and other internal properties easily.
    • Added new simple & elegant default Bootstrap Cover Template for WebGear Server.
    • Added __main__.py to directly run WebGear Server through the terminal.
    • Added new gif and related docs for WebGear API.
    • Added and Updated various CI tests for this API.
  • NetGear_Async API:
    • Designed NetGear_Async asynchronous network API built upon ZeroMQ's asyncio API.
    • Implemented support for state-of-the-art asyncio event loop uvloop at its backend.
    • Achieved Unmatchable high-speed and lag-free video streaming over the network with minimal resource constraint.
    • Added exclusive internal wrapper around VideoGear API for this API.
    • Implemented complete server-client handling and options to use variable protocols/patterns for this API.
    • Implemented support for all four ZeroMQ messaging patterns: i.e zmq.PAIR, zmq.REQ/zmq.REP, zmq.PUB/zmq.SUB, and zmq.PUSH/zmq.PULL.
    • Implemented initial support for tcp and ipc protocols.
    • Added new Coverage CI tests for NetGear_Async Network Gear.
    • Added new Benchmark tests for benchmarking NetGear_Async against NetGear.
  • Asynchronous Enhancements:
    • Added asyncio package to for handling asynchronous APIs.
    • Moved WebGear API(webgear.py) to asyncio and created separate asyncio helper.py for it.
    • Various Performance tweaks for Asyncio APIs with concurrency within a single thread.
    • Moved __main__.py to asyncio for easier access to WebGear API through the terminal.
    • Updated setup.py with new dependencies and separated asyncio dependencies.
  • General Enhancements:
    • Added new highly-precise Threaded FPS class for accurate benchmarking with time.perf_counter python module.
    • Added a new Gitter community channel.
    • Added a new Reducer function to reduce the frame size on-the-go.
    • Add Flake8 tests to Travis CI to find undefined names. (PR by @cclauss)
    • Added a new unified logging handler helper function for vidgear.
Updates/Improvements
  • Re-implemented and simplified logic for NetGear Async server-end.
  • Added new dependencies for upcoming asyncio updates to setup.py.
  • Added retry function and replaced wget with curl for Linux test envs.
  • Bumped OpenCV to latest 4.2.0-dev for Linux test envs.
  • Updated YAML files to reflect new changes to different CI envs.
  • Separated each API logger with a common helper method to avoid multiple copies.
  • Limited Importing OpenCV API version check's scope to helper.py only.
  • Implemented case for incorrect color_space value in ScreenGear API.
  • Removed old conflicting logging formatter with a common method and expanded logging.
  • Improved and added shutdown function for safely stopping frame producer threads in WebGear API.
  • Re-implemented and simplified all CI tests with maximum code-coverage in mind.
  • Replaced old mkdir function with new mkdir_safe helper function for creating directories safely.
  • Updated ReadMe.md with updated diagrams, gifs and information.
  • Improve, structured and Simplified the Contribution Guidelines.
  • Bundled CI requirements in a single command.(Suggested by @cclauss)
  • Replaced line endings CRLF with LF endings.
  • Added dos2unix for Travis OSX envs.
  • Bumped Codecov to maximum.
Breaking Updates/Changes
  • Dropped support for Python 3.5 and below legacies. (See issue #99)
  • Dropped and replaced Python 3.5 matrices with new Python 3.8 matrices in all CI environments.
  • Implemented PEP-8 Styled Black formatting throughout the source-code.
  • Limited protocols support to tcp and ipc only, in NetGear API.
Bug-fixes
  • Fixed Major NetGear_Async bug where __address and __port are not set in async mode.(PR by @otter-in-a-suit)
  • Fixed Major PiGear Color-space Conversion logic bug.
  • Workaround for CAP_IMAGES error in YouTube Mode.
  • Replaced incorrect terminate() with join() in PiGear.
  • Removed uvloop for windows as still NOT yet supported.
  • Refactored Asynchronous Package name async to asyncio, since it is used as Keyword in python>=3.7 (raises SyntaxError).
  • Fixed unfinished close of event loops bug in WebGear API.
  • Fixed NameError in helper.py.
  • Added fix for OpenCV installer failure on Linux test envs.
  • Fixed undefined NameError in helper.py context. (@cclauss)
  • Fixed incorrect logic while pulling frames from ScreenGear API.
  • Fixed missing functions in __main__.py.
  • Fixed Typos and definitions in docs.
  • Added missing camera_num parameter to VideoGear.
  • Added OpenSSL's [SSL: CERTIFICATE_VERIFY_FAILED] bug workaround for macOS envs.
  • Removed download_url meta from setup.py.
  • Removed PiGear from CI completely due to hardware emulation limitation.
  • Removed VideoCapture benchmark tests for macOS envs.
  • Removed trivial __main__.py from codecov.
  • Removed several redundant try-catch loops.
  • Renamed youtube_url_validation as youtube_url_validator.
  • Several minor wrong/duplicate variable definitions and various bugs fixed.
  • Fixed, Improved & removed many Redundant CI tests for various APIs.
Pull Requests
New Contributors

 

 

v0.1.6 (2020-01-01)

New Features
  • NetGear API:
    • Added powerful ZMQ Authentication & Data Encryption features for NetGear API:
      • Added exclusive secure_mode param for enabling it.
      • Added support for two most powerful Stonehouse & Ironhouse ZMQ security mechanisms.
      • Added smart auth-certificates/key generation and validation features.
    • Implemented Robust Multi-Servers support for NetGear API:
      • Enables Multiple Servers messaging support with a single client.
      • Added exclusive multiserver_mode param for enabling it.
      • Added support for REQ/REP & PUB/SUB patterns for this mode.
      • Added ability to send additional data of any datatype along with the frame in realtime in this mode.
    • Introducing exclusive Bidirectional Mode for bidirectional data transmission:
      • Added new return_data parameter to recv() function.
      • Added new bidirectional_mode attribute for enabling this mode.
      • Added support for PAIR & REQ/REP patterns for this mode
      • Added support for sending data of any python datatype.
      • Added support for message parameter for non-exclusive primary modes for this mode.
    • Implemented compression support with on-the-fly flexible frame encoding for the Server-end:
      • Added initial support for JPEG, PNG & BMP encoding formats .
      • Added exclusive options attribute compression_format & compression_param to tweak this feature.
      • Client-end will now decode frame automatically based on the encoding as well as support decoding flags.
    • Added force_terminate attribute flag for handling force socket termination at the Server-end if there's latency in the network.
    • Implemented new Publish/Subscribe(zmq.PUB/zmq.SUB) pattern for seamless Live Streaming in NetGear API.
  • PiGear API:
    • Added new threaded internal timing function for PiGear to handle any hardware failures/frozen threads.
    • PiGear will not exit safely with SystemError if Picamera ribbon cable is pulled out to save resources.
    • Added support for new user-defined HWFAILURE_TIMEOUT options attribute to alter timeout.
  • VideoGear API:
    • Added framerate global variable and removed redundant function.
    • Added CROP_N_ZOOM attribute in Videogear API for supporting Crop and Zoom stabilizer feature.
  • WriteGear API:
    • Added new execute_ffmpeg_cmd function to pass a custom command to its FFmpeg pipeline.
  • Stabilizer class:
    • Added new Crop and Zoom feature.
      • Added crop_n_zoom param for enabling this feature.
    • Updated docs.
  • CI & Tests updates:
    • Replaced python 3.5 matrices with latest python 3.8 matrices in Linux environment.
    • Added full support for Codecov in all CI environments.
    • Updated OpenCV to v4.2.0-pre(master branch).
    • Added various Netgear API tests.
    • Added initial Screengear API test.
    • More test RTSP feeds added with better error handling in CamGear network test.
    • Added tests for ZMQ authentication certificate generation.
    • Added badge and Minor doc updates.
  • Added VidGear's official native support for MacOS environments.
Updates/Improvements
  • Replace print logging commands with python's logging module completely.
  • Implemented encapsulation for class functions and variables on all gears.
  • Updated support for screen casting from multiple/all monitors in ScreenGear API.
  • Updated ScreenGear API to use Threaded Queue Mode by default, thereby removed redundant THREADED_QUEUE_MODE param.
  • Updated bash script path to download test dataset in $TMPDIR rather than $HOME directory for downloading testdata.
  • Implemented better error handling of colorspace in various videocapture APIs.
  • Updated bash scripts, Moved FFmpeg static binaries to github.com.
  • Updated bash scripts, Added additional flag to support un-secure apt sources.
  • CamGear API will now throw RuntimeError if source provided is invalid.
  • Updated threaded Queue mode in CamGear API for more robust performance.
  • Added new camera_num to support multiple Picameras.
  • Moved thread exceptions to the main thread and then re-raised.
  • Added alternate github mirror for FFmpeg static binaries auto-installation on windows oses.
  • Added colorlog python module for presentable colored logging.
  • Replaced traceback with sys.exc_info.
  • Overall APIs Code and Docs optimizations.
  • Updated Code Readability and Wiki Docs.
  • Updated ReadMe & Changelog with the latest changes.
  • Updated Travis CI Tests with support for macOS environment.
  • Reformatted & implemented necessary MacOS related changes and dependencies in travis.yml.
Breaking Updates/Changes
  • Python 2.7 legacy support dropped completely.
  • Source-code Relicensed to Apache 2.0 License.
  • Python 3+ are only supported legacies for installing v0.1.6 and above.
  • Python 2.7 and 3.4 legacies support dropped from CI tests.
Bug-fixes
  • Reimplemented Pub/Sub pattern for smoother performance on various networks.
  • Fixed Assertion error in CamGear API during colorspace manipulation.
  • Fixed random freezing in Secure Mode and several related performance updates
  • Fixed multiserver_mode not working properly over some networks.
  • Fixed assigned Port address ignored bug (commit 073bca1).
  • Fixed several wrong definition bugs from NetGear API(commit 8f7153c).
  • Fixed unreliable dataset video URL(rehosted file on github.com).
  • Disabled overwrite_cert for client-end in NetGear API.
  • Disabled Universal Python wheel builds in setup.cfgfile.
  • Removed duplicate code to import MSS(@BoboTiG) from ScreenGear API.
  • Eliminated unused redundant code blocks from library.
  • Fixed Code indentation in setup.py and updated new release information.
  • Fixed code definitions & Typos.
  • Fixed several bugs related to secure_mode & multiserver_mode Modes.
  • Fixed various macOS environment bugs.
Pull Requests
New Contributors

 

 

v0.1.5 (2019-07-24)

New Features
  • Added new ScreenGear API, supports Live ScreenCasting.
  • Added new NetGear API, aids real-time frame transfer through messaging(ZmQ) over network.
  • Added new new Stabilizer Class, for minimum latency Video Stabilization with OpenCV.
  • Added Option to use API's standalone.
  • Added Option to use VideoGear API as internal wrapper around Stabilizer Class.
  • Added new parameter stabilize to API, to enable or disable Video Stabilization.
  • Added support for **option dict attributes to update VidGear's video stabilizer parameters directly.
  • Added brand new logo and functional block diagram (.svg) in readme.md
  • Added new pictures and GIFs for improving readme.md readability
  • Added new contributing.md and changelog.md for reference.
  • Added collections.deque import in Threaded Queue Mode for performance consideration
  • Added new install_opencv.sh bash scripts for Travis cli, to handle OpenCV installation.
  • Added new Project Issue & PR Templates
  • Added new Sponsor Button(FUNDING.yml)
Updates/Improvements
  • Updated New dependencies: mss, pyzmq and rejected redundant ones.
  • Revamped and refreshed look for readme.md and added new badges.
  • Updated Releases Documentation completely.
  • Updated CI tests for new changes
  • Updated Code Documentation.
  • Updated bash scripts and removed redundant information
  • Updated Youtube video URL in tests
  • Completely Reformatted and Updated Wiki Docs with new changes.
Breaking Updates/Changes
  • Implemented experimental Threaded Queue Mode(a.k.a Blocking Mode) for fast, synchronized, error-free multi-threading.
  • Renamed bash script pre-install.sh to prepare_dataset.sh - [x] downloads opensourced test datasets and static FFmpeg binaries for debugging.
  • Changed script folder location to bash/script.
  • Python 3.4 removed from Travis CI tests.
Bug-fixes
  • Temporarily fixed Travis CI bug: Replaced opencv-contrib-python with OpenCV built from scratch as dependency.
  • Fixed CI Timeout Bug: Disable Threaded Queue Mode for CI Tests
  • Fixes** sys.stderr.close() throws ValueError bug: Replaced sys.close() with DEVNULL.close()
  • Fixed Youtube Live Stream bug that return NonType frames in CamGear API.
  • Fixed NoneType frames bug in PiGear class on initialization.
  • Fixed Wrong function definitions
  • Removed /xe2 unicode bug from Stabilizer class.
  • Fixed **output_params KeyError bug in WriteGear API
  • Fixed subprocess not closing properly on exit in WriteGear API.
  • Fixed bugs in ScreenGear: Non-negative monitor values
  • Fixed missing import, typos, wrong variable definitions
  • Removed redundant hack from setup.py
  • Fixed Minor YouTube playback Test CI Bug
  • Fixed new Twitter Intent
  • Fixed bug in bash script that not working properly due to changes at server end.
Pull Requests

 

 

v0.1.4 (2019-05-11)

New Features
  • Added new WriteGear API: for enabling lossless video encoding and compression(built around FFmpeg and OpenCV Video Writer)
  • Added YouTube Mode for direct Video Pipelining from YouTube in CamGear API
  • Added new y_tube to access YouTube Mode in CamGear API.
  • Added flexible Output file Compression control capabilities in compression-mode(WriteGear).
  • Added -output_dimensions special parameter to WriteGear API.
  • Added new helper.py to handle special helper functions.
  • Added feature to auto-download and configure FFmpeg Static binaries(if not found) on Windows platforms.
  • Added -input_framerate special parameter to WriteGear class to change/control output constant framerate in compression mode(WriteGear).
  • Added new Direct Video colorspace Conversion capabilities in CamGear and PiGear API.
  • Added new framerate class variable for CamGear API, to retrieve input framerate.
  • Added new parameter backend - [x] changes the backend of CamGear's API
  • Added automatic required prerequisites installation ability, when installation from source.
  • Added Travis CI Complete Integration for Linux-based Testing for VidGear.
  • Added and configured travis.yml
  • Added Appveyor CI Complete Integration for Windows-based Testing in VidGear.
  • Added and configured new appveyor.yml
  • Added new bash script pre-install.sh to download opensourced test datasets and static FFmpeg binaries for debugging.
  • Added several new Tests(including Benchmarking Tests) for each API for testing with pytest.
  • Added license to code docs.
  • Added Say Thank you! badge to Readme.md.
Updates/Improvements
  • Removed redundant dependencies
  • Updated youtube-dl as a dependency, as required by pafy's backend.
  • Updated common VideoGear API with new parameter.
  • Update robust algorithm to auto-detect FFmpeg executables and test them, if failed, auto fallback to OpenCV's VideoWriter API.
  • Improved system previously installed OpenCV detection in setup.py.
  • Updated setup.py with hack to remove bullets from pypi description.
  • Updated Code Documentation
  • Reformatted & Modernized readme.md with new badges.
  • Reformatted and Updated Wiki Docs.
Breaking Updates/Changes
  • Removed -height and -width parameter from CamGear API.
  • Replaced dependency opencv-python with opencv-contrib-python completely
Bug-fixes
  • Windows Cross-Platform fix: replaced dependency os with platform in setup.py.
  • Fixed Bug: Arises due to spaces in input **options/**output_param dictionary keys.
  • Fixed several wrong/missing variable & function definitions.
  • Fixed code uneven indentation.
  • Fixed several typos in docs.
Pull Requests

 

 

v0.1.3 (2019-04-07)

Bug-fixes
  • Patched Major PiGear Bug: Incorrect import of PiRGBArray function in PiGear Class
  • Several Fixes for backend picamera API handling during frame capture(PiGear)
  • Fixed missing frame variable initialization.
  • Fixed minor typos
Pull Requests

 

 

v0.1.2 (2019-03-27)

New Features
  • Added easy Source manipulation feature in CamGear API, to control features like resolution, brightness, framerate etc.
  • Added new **option parameter to CamGear API, provides the flexibility to manipulate input stream directly.
  • Added new parameters for Camgear API for time delay and logging.
  • Added new Logo to readme.md
  • Added new Wiki Documentation.
Updates/Improvements
  • Reformatted readme.md.
  • Updated Wiki Docs with new changes.
Bug-fixes
  • Improved Error Handling in CamGear & PiGear API.
  • Fixed minor typos in docs.
Pull Requests

 

 

v0.1.1 (2019-03-24)

New Features
  • Release ViGear binaries on the Python Package Index (PyPI)
  • Added new and configured setup.py & setup.cfg
Bug-fixes
  • Fixed PEP bugs: added and configured properly __init__.py in each folder
  • Fixed PEP bugs: improved code Indentation
  • Fixed wrong imports: replaced distutils.core with setuptools
  • Fixed readme.md

 

 

v0.1.0 (2019-03-17)

New Features
  • Initial Release
  • Converted my imutils PR into Python Project.
  • Renamed conventions and reformatted complete source-code from scratch.
  • Added support for both python 2.7 and 3 legacies
  • Added new multi-threaded CamGear, PiGear, and VideoGear APIs
  • Added multi-platform compatibility
  • Added robust & flexible control over the source in PiGear API.
\ No newline at end of file +-->

Release Notes

v0.3.3 (2024-06-22)

New Features
  • PiGear:
    • ⚡️ Official Support for Picamera2 API backend. (Fixes #342)
      • This massive update brings official support for the new Picamera2 API, unlocking powerful features for Raspberry Pi Camera Modules and limited USB camera support.
      • Seamless Python wrapper: A robust wrapper around Picamera2 API library simplifies integration with minimal code changes for existing PiGear users.
      • Enhanced camera control: Leverages libcamera API under the hood for Raspberry Pi Camera Modules.
      • Existing compatibility: Maintains compatibility with PiGear's existing super-charged multi-threaded and colorspace manipulation framework.
      • Proper Resource management: Ensures proper resource release during PiGear termination.
      • USB camera support (limited): Provides basic functionality for USB webcams. PiGear could accurately differentiates between USB and Raspberry Pi cameras using metadata.
      • Backward compatibility: Seamlessly switches to the legacy Picamera library backend if Picamera2 is unavailable.
      • Standalone functionalities: Standalone functionalities for both legacy picamera and newer picamera2 backends for clarity.
      • Advanced optional parameters handling: Handles camera configurational parameters and user-defined settings for various camera types.
        • New optional configurational parameters: Currently Supports sensor, format, controls, transform, stride, buffer_count, and queue with sanity checks.
        • New user-defined optional parameters: Such as auto_align_output_config, enable_verbose_logs, and more.
  • StreamGear:
    • Introduced new -enable_force_termination attribute for immediate FFmpeg process termination.
  • Helper:
    • Added support for SRTP/RTSPS in is_valid_url function (Fixes #410)
      • Enhanced is_valid_url in helper.py to recognize and support both rtsp and rtsps protocols. (Suggested by @jonra1993)
      • SRTP/RTSPS extends RTSP/RTP to encrypt video and audio data using the same ciphers as HTTPS, typically AES with a 128-bit key length.
    • Added a custom deprecated decorator to mark deprecated functions and parameters to display a warning message when a deprecated one is used.
  • Docs:
    • Overhauled mkdocs material theme:
      • Added unrecognized_links: ignore to mkdocs.yml for validations.
      • Added custom admonition icons.
      • Added new git-authors plugin.
      • Added new tables markdown extension.
      • Added custom fences to pymdownx.superfences markdown extension.
      • Added line_spans: __span and pygments_lang_class: true parameters to pymdownx.highlight markdown extension.
      • Added normalize_issue_symbols: true to pymdownx.magiclink markdown extension.
      • Added new mkdocs feature dependency mkdocs-git-authors-plugin.
    • Added the use of new -enable_force_termination parameter.
    • Added a new FAQ entry about the deprecated rgb_mode parameter.
    • Added new screengear_error11.png asset.
  • CI:
    • Added test cases for import_dependency_safe function to validate different scenarios and error handling in import_dependency_safe.
Updates/Improvements
  • Core:
    • Improved exception handling for module imports:
      • Updated import_dependency_safe in helper.py:
        • Added 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.
    • Improved 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.
  • Asyncio:
    • Replaced deprecated Starlette's on_shutdown parameter with an async context manager lifespan in WebGear and WebGear_RTC APIs. (Fixes #397)
      • Moved shutdown logic for VideoGear and peer RTC connections to this new lifespan context manager.
      • Added new contextlib import for using asynccontextmanager.
  • NetGear_Async API:
    • Modified __init__ method to handle event loop more robustly:
      • Try to get the running event loop using asyncio.get_running_loop()
      • If no running event loop found, create a new one with asyncio.new_event_loop()
      • Log if creating a new event loop
    • Changed launch method to use self.loop.create_task() instead of asyncio.ensure_future()
      • Ensures the task is created using the correct event loop instance.
    • Moved the event loop initialization code to an earlier point before setting event loop policy to ensure it is set up correctly before selecting WindowsSelectorEventLoop policy.
      • On Windows, vidgear requires the WindowsSelectorEventLoop, but Python 3.8 and above defaults to the ProactorEventLoop which is not compatible.
    • Removed redundant python version check to set WindowsSelectorEventLoop policy, as minimum supported version is already 3.8.
    • Move event loop setup and policy assignment to the beginning of __init__ before zmq Context creation.
    • Refactored return data handling.
  • StreamGear:
    • Updated close() methods for handling gracefully signal interruptions based on different operating systems with device audio streams.
    • Deprecated terminate() method, introducing close() for safer process termination.
    • Enhanced stream copy support in Single Source mode (Fixes #396).
      • Moved settings for "-vf" and "-aspect" inside conditional blocks.
      • Added warnings and discarded these parameters in stream copy mode.
      • 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.
    • Restricted -livestream parameter to Real-time Frames Mode only.
      • Disabled live streaming for video files and updated relevant logging.
    • Enhanced warning messages and clarified description.
  • PiGear:
    • Logging optimization with warning for common libcamera messages.
    • Lowered framerate minimum value to 0.0.
    • Moved sensor optional parameter to commonly supported picamera2 configurational parameters.
    • Removed unsupported bit_depth optional parameters.
    • Updated PiGear API tagline and introduction.
  • NetGear:
    • Enhanced logging and error handling for secure mode.
    • Logged Authenticator start/stop events.
    • Handled socket session expiration more gracefully in recv_handler.
    • Ensured proper termination of the ZMQ context and socket when closing the NetGear instance.
  • WebGear:
    • Enhanced error messages for WebGear auto-generation workflow (Fixes #403)
      • Updated homepage, not_found, and server_error methods to include more detailed JSON error messages.
      • Added specific error and message prefixes to improve clarity.
  • 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.
  • WriteGear:
    • Simplified the logic for formatting output parameters.
    • 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.
  • CamGear:
    • Removed GStreamer support check.
    • Improved readability of livestream warning logs.
  • Setup.py:
    • Dropped legacy picamera dependency in setup.py.
    • Updated setup.py to use the latest pyzmq version to address installation issues (Fixes #399).
  • Helper:
    • Added patch for substring index bug in get_supported_demuxers helper method.
    • Updated extract_time helper function regex to handle milliseconds.
  • Docs:
    • Update StreamGear documentation:
      • Updated documentation to deprecated terminate() method, and introducing close() for safer process termination.
      • Improved the overview section's description and wording.
      • Updated usage examples for both Single-Source Mode and Real-time Frames Mode.
      • Updated StreamGear usage examples for device audio input.
      • Refactored sections for Live Streaming usage.
        • Added warning for unsupported -livestream parameter in Single-Source Mode.
      • 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.
      • Updated usage example for device video source.
      • Addressed deprecation of the terminate() method in favor of the new close() method.
      • Updated respective notices for the deprecated terminate() method and rgb_mode parameter.
      • Added a deprecation warning admonition for the rgb_mode parameter in the stream() method.
      • Removed the obsolete usage example for deprecation RGB mode with StreamGear.
      • Added documentation and usage of the new -enable_force_termination parameter.
      • Modified the warning message to mention that forced termination can cause corrupted output in certain scenarios.
      • Updated the docstring for the stream() method and transcode_source() method.
      • Refactored the StreamGear API Parameters documentation to enhance clarity and readability.
      • Refined the description of the -streams attribute of the StreamGear API.
    • Update PiGear documentation:
      • Added a warning advising users to disable common libcamera messages when logging is disabled.
      • Updated Picamera2 installation instructions (including apt, pip, pre-installation on Raspberry Pi images, and compatibility warnings)
      • Moved legacy Picamera library installation instructions to an admonition.
      • Removed Importing section from overview to avoid confusion.
    • Update NetGear documentation:
      • Added Admonition for warning users about the Client's end must run before the Server's end to establish a secure connection in Secure Mode.
      • Added warning log for potential issues with flag=1 (NOBLOCK). (Fixes #390)
        • Changed default value of copy to True in NetGear API documentation.
      • Noted that track option is ignored when copy=True.
    • Update WriteGear documentation:
      • Updated the documentation for the -disable_force_termination parameter.
    • Update README.md:
      • Replaced deprecated options (frame_jpeg_quality, frame_jpeg_optimize, frame_jpeg_progressive) with their newer equivalents (jpeg_compression_quality, jpeg_compression_fastdct, jpeg_compression_fastupsample) in WebGear usage example.
    • Update mkdocs.yml:
      • Set edit_uri for GitHub edit links.
      • Add new theme features like content actions, tooltips, etc.
      • Update palette settings for light/dark mode.
      • Enable new markdown extensions.
      • Add custom javascript hook support.
      • Migrated to new Google Analytics 4.
      • Replaced depreciated materialxwith supported emoji extension.
      • Replaced permalink icon with default one.
      • Change system mode toggle icon and name in mkdocs.yml.
    • Improved overall documentation quality by added detailed explanations, practical examples, following best practices, and clearer usage patterns.
    • Updated sections, code examples, admonitions, and comments for better readability, consistency, and precision.
    • Added missing version contributors to changelog.md.
    • Added new icons to make headings more readable.
    • Replaced unsupported admonitions with supported ones.
    • Removed all custom admonition icons and color CSS from custom.css.
    • Removed Twitter section from help and docs site metadata.
    • Updated Zenodo badge and BibTeX entry.
    • Added workaround for 'AttributeError: 'DXCamera' object has no attribute 'is_capturing'' error on Windows.
    • Remove script tags from main.html and use a custom hook for adding javascripts on certain pages.
    • Refactored all APIs and bonus examples to use linenums and hl_lines which makes it easier to highlight specific lines in code blocks.
    • Removed Gitter community chat sidecard javascript file.
    • Redefined spacing between sections.
    • Add failure warning in various docs about picamera incompatibility on 64-bit OS.
    • Update announcement icon in main.html.
    • Remove site.webmanifest file.
  • Maintenance:
    • Improved logging, parameter validation, and added descriptive dialogs across various APIs.
    • Moved logging enablement before version logging for consistency in vidgear APIs.
    • Removed redundant boolean assignment for various APIs internal logging.
    • Simplified conditional statements and assignments using short-circuiting, Boolean operations, and ternary operators across various APIs and tests.
    • Refactored vidgear code to improve readability, maintainability, and performance.
    • Added .cache directory to .gitignore,
    • Updated vidgear library version to v0.3.3.
    • Improved code efficiency with short-circuiting and formatting.
    • Updated logging practices to be more developer-friendly.
    • Removed unnecessary parentheses and type checks.
    • Removed unused imports.
    • Updated code comments.
  • CI:
    • Temporarily removed PiGear API from code coverage due to hardware limitations.
    • Deprecated custom event_loop fixture overrides in WebGear_RTC and NetGear_Async tests
      • Removed redundant pytest.mark.asyncio decorators from several test functions.
    • Add a new event_loop_policy fixture for pytest to override the event loop policy:
      • Added new recommended approach of using pytest.mark.asyncio(scope="module") to mark all WebGear_RTC and NetGear_Async tests as asynchronous and utilize the same event loop throughout the module.
      • Log the event loop being used for debugging.
    • Updated NetGear unit tests to reflect the new default for copy.
    • Ensured coverage for raise, log, silent, and unknown error types.
    • Improved parameterized test cases to support floating point values.
    • Updated StreamGear tests to use the new close() method instead of the deprecated terminate() method.
    • Updated tests of various APIs for better coverage and reliability.
    • Enabled kill=True in close() in NetGear Tests.
    • Removed pinned cryptography==38.0.4 dependency.
    • Remove unused imports and code cleanup.
    • Rearranged the dependencies.
Breaking Updates/Changes
  • StreamGear:
    • Deprecated terminate() method and introduce close() method.
      • The terminate() method in StreamGear is now deprecated and will be removed in a future release. Developers should use the new close() method instead, which provides a more descriptive name like in WriteGear API for terminating StreamGear processes safely.
    • Deprecated rgb_mode parameter in stream() method.
      • This parameter will be removed in a future version, and only BGR format frames will be supported.
    • Restricted -livestream parameter to Real-time Frames Mode only.
      • Live streaming is intended for low-latency streaming of real-time frames, where chunks contain only the most recent frames. It doesn't make sense when streaming from a video file, as the entire file can be streamed normally without the need for live streaming.
Bug-fixes
  • PiGear:
    • Modify PiGear class behavior when enforce_legacy_picamera=True on unsupported system
      • Instead of silently disabling picamera2 API directly, PiGear now raises an error if picamera is unavailable or unsupported
      • Prevented incorrect initialization of PiGear class on unsupported 64-bit OS systems.
    • Fixed UnboundLocalError bug for 'picamera2' variable assigment.
    • Fixed UnboundLocalError bug for 'queue' variable assignment.
    • Fixed colorspace typo bug.
  • StreamGear:
    • Fixed incompatibility of stream copy with Real-time Frames Mode.
      • Added warnings and discarded -vcodec copy when using this mode.
    • Removed non-essential aspect ratio parameter to prevent unwanted distortions (Fixes #385).
    • Moved handle streaming format to beginning to fix 'StreamGear' object has no attribute '_StreamGear__format' bug.
  • NetGear:
    • Fixed Secure Mode failing to work on conflicting ZMQ Contexts:
      • Handled "Address in use" error more gracefully and disabled secure mode if errors occur.
      • Improved handling of ZMQ Authenticator and Certificates.
    • Fixed msg_json undefined when terminating context in the recv_handler method.
  • CamGear:
    • Fixed logging condition for yt-dlp (Fixes #394)
      • Updated no_warnings parameter in CamGear to be False when logging is enabled and True otherwise.
  • Docs:
    • Replaced buggy kofi widget with a button image in index.md.
    • Fixed Ko-fi sponsorship heart hover effect in footer
      • Replaced twemoji heart emoji with heart-pulse fontawesome SVG
    • Fixed titles and indentations in various admonitions.
    • Fixed various issues in code comments, and hyperlinks URLs.
    • Fixed typos, formatting, code highlighting, and grammar inconsistencies.
    • Fixed minor typo in js_hook.py.
  • CI:
    • Fixed simplejpeg and opencv not compatible with numpy==2.x.x versions.
      • Pinned numpy<2.0.0 in all CI envs.
    • Fixed expected duration value in parameterized test case from 8 to 8.44 since test_extract_time function now supports floating point values.
    • Fixed test_secure_mode NetGear test:
      • Added "127.0.0.1" address to allow common endpoint for connection.
      • Added "jpeg_compression":False to disable frame compression, allowing frame to be the same while assertion.
    • Fixed pip install hash bug in Azure Pipelines CI.
    • Fixed various typos and code issues in tests.
    • Fixed invalid escape sequence in test case string.
    • Fixed python environment bugs in appveyor.yml.
Pull Requests
New Contributors

 

 

v0.3.2 (2023-09-10)

New Features
  • 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.
  • 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.
  • Setup.py
    • Added official support for python 3.11.x legacies.
    • Bumped version to 0.3.1.
  • 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.
  • CI
    • Added python 3.11 legacy support for MacOS, Windows and Linux environments.
    • Added kill argument to close() method in various NetGear tests.
Updates/Improvements
  • Asyncio:
    • Formatted TemplateResponse class parameters w.r.t new changes in backend Starlette API.
  • Setup.py:
    • Readded latest patch to uvicorn, starlette, pyzmq dependencies.
    • Removed 3.7 legacy from Programming Language metadata.
  • Maintenance:
    • Added GitHub sponsors and dropped liberapay from Funding.yml.
    • Removed redundant code.
  • 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
  • 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.
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-fixes
  • ScreenGear:
    • Fixed swapped region dimensions bug with dxcam backend.
    • Fixed "mss" backend disabled when monitor parameter is not defined.
  • Docs:
    • Fixed missing compression_mode flags in WriteGear API docs.
    • Fixed missing hyperlinks.
    • Fixed typos and context.
  • 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.
Pull Requests
New Contributors

 

 

v0.3.1 (2023-07-22)

New Features
  • WebGear:
    • Added an option to add a custom video endpoint path.
      • Users can now change the video endpoint path from "/video" to any alphanumeric string.
      • Added the custom_video_endpoint optional string attribute for this purpose.
      • Only alphanumeric strings with no spaces in between are allowed as its value.
  • ScreenGear:
    • Added dxcam support for Windows machines.
      • Implemented a complete end-to-end workflow for the dxcam backend.
      • dxcam is now the default backend for Windows machines when no backend is defined.
      • Added support for variable screen dimensions to capture an area from the screen.
      • Added the optional flag dxcam_target_fps to control the target fps in dxcam. Defaults to 0 (disabled).
      • RGB frames from dxcam are automatically converted into BGR.
      • For better performance, video_mode is enabled by default in dxcam backend.
      • Added necessary imports.
    • Added support for tuple values in the monitor parameter to specify device and output indexes as (int[device_idx], int[output_idx]) in the dxcam backend only.
      • Default int index is also allowed as a value for selecting device index.
  • Helper
    • Added multiple servers support for downloading assets.
      • Added GitHub server to the generate_webdata method to make it more robust for rate limits and other shortcomings.
      • Now, the generate_webdata method will retry a different server when one fails.
  • Setup.py
    • Added dxcam dependency in core and asyncio extra requires.
    • Bumped version to 0.3.1.
  • Docs
    • Added dxcam API specific prerequisites for ScreenGear API when installing on Windows via pip.
    • Added documentation for the custom_video_endpoint optional string attribute.
    • Added documentation for controlling Chunk size in HLS stream.
    • Added new hyperlinks for dxcam dependency.
  • CI
    • Added a test case for ndim==3 grayscale frames.
      • Added the Custom_Grayscale_class to generate ndim==3 grayscale frames.
    • Added test cases for the custom_video_endpoint optional string attribute.
Updates/Improvements
  • WebGear:
    • Improved the conditions logic to check if non-empty values are assigned to optional parameters.
  • WebGear_RTC:
    • Improved the handling of the format parameter when constructing a VideoFrame from ndarray frames.
  • ScreenGear:
    • Enforced dxcam backend (if installed) when monitor is defined on Windows machines.
    • Refactored code blocks to ensure backward compatibility.
  • Maintenance:
    • Cleaned up unused imports and code blocks.
    • Cleaned redundant code.
    • Improved logging.
    • Implemented short-circuiting.
    • Fixed comment typos.
    • Updated comments.
  • Docs:
    • Updated ScreenGear API usage example docs, added new relevant information, updated requirements for dxcam support in Windows machines.
    • Refactored monitor and backend parameters docs of ScreenGear.
    • Updated class and class parameters descriptions in ScreenGear docs.
    • Updated a new description for ScreenGear API.
    • Updated Zenodo badge and the BibTeX entry.
    • Relocated some docs for a better context.
    • Removed ScreenGear name from Threaded Queue Mode doc.
    • Updated ScreenGear FAQs.
    • Updated changelog.md
  • CI:
    • Updated the test_webgear_rtc_custom_stream_class method.
    • Updated the test_webgear_options method.
    • Updated the test_webgear_routes test to validate the new custom endpoint.
    • Increased code coverage by updating tests.
Breaking Updates/Changes
  • ScreenGear:
    • Previously enforced threaded queue mode is now completely removed, resulting in a potential performance boost.
      • 💬 Reason: The IO is automatically blocked by the screen refresh rate, so adding the overhead of maintaining a separate queue is pointless.
    • Removed the THREAD_TIMEOUT optional flag.
Bug-fixes
  • WebGear_RTC:
    • Fixed a bug caused by PyAV's error when ndim==3 grayscale frames are encountered.
      • The API will now drop the third dimension if ndim==3 grayscale frames are detected.
  • ScreenGear:
    • Fixed backend not defined while logging.
  • 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.
  • Docs:
    • Fixed context and added separate code for controlling chunk size in HLS and DASH streams in StreamGear docs.
    • Fixed naming conventions for the recently added DXcam backend in ScreenGear docs.
    • Fixed missing hyperlinks.
  • CI:
    • Fixed m3u8 module failing to recognize Windows paths in ScreenGear tests.
    • Fixed a path bug by replacing the absolute file path with the decoded file content as a string in its loads()
Pull Requests

 

 

v0.3.0 (2023-01-26)

New Features
  • WriteGear:
    • Added support for user-defined and higher than 8-bit depth input frames pixel-format.
      • Added support for higher than 8-bit depth frames with datatypes of unsigned integer(uint) kind and element size 2.
      • Added dtype parameter to internal Preprocess method for passing input frames datatype.
      • Implemented auto-calculation of input pixel-format based on number of channels in higher than 8-bit depth frames.
      • Added various known working pixel-formats(based on number of channels), supported by all prominent computer vision libraries.
      • Added support for up to 1-channel(gray16-le/be) to all the way up to 4-channels(bgra64-le/be) in input frames.
      • Added endianness little(le) or big(be) at the suffix of pixel-format based on byte-order of input frames datatypes.
      • Extended support for higher RGB 8-bit depth frames through RGB mode.
    • Added support for user-defined custom input pixel-format.
      • Added new -input_pixfmt attribute to output_params dictionary parameter for easily specifying custom input pixel-format.
      • Added newly implemented get_supported_pixfmts method import for verifying user-defined input pixel-format against Installed FFmpeg supported pixel-formats. Unsupported values will be discarded.
      • Implemented runtime datatype validation check, such that all input frames must have same datatype.
    • Added support for Context Managers for proper handling of resources via with statement for allocating and releasing resources precisely. (Suggested by @sueskind)
      • Implement the __enter__() and __exit__() methods.
      • Added __enter__ method that returns reference to the WriteGear Class.
      • Added __exit__ method that automatically executes close() for performing the cleanup operations and handling exception gracefully.
  • StreamGear:
    • Added support for Context Managers for proper handling of resources via with statement for allocating and releasing resources precisely. (Suggested by @sueskind)
      • Implement the __enter__() and __exit__() methods.
      • Added __enter__ method that returns reference to the StreamGear Class.
      • Added __exit__ method that automatically executes close() for performing the cleanup operations and handling exception gracefully.
  • WebGear:
    • Added way to completely disable Data-Files Auto-Generation WorkFlow.
      • Added new skip_generate_webdata boolean optional attribute(False by default) to completely disable Data-Files Auto-Generation WorkFlow.
      • This flag enables only /video route for disabled Data-Files Auto-Generation WorkFlow.
      • Implemented JSONResponse as placeholder response instead of Index, 404 and 500 HTML pages, when workflow is disabled. (Note: Index HTML page will throw 404 status code.)
      • Added necessary imports.
  • Helper:
    • Added more robust implementation of validate_audio method.
      • Added new more robust regex pattern for extracting audio-samplerate.
      • Added new validate_audio method for calculating accurate bitrate(in kbps) from audio samplerate, channels, bit-depth values.
      • Implemented new patterns and logic for accurately extracting audio channels and bit-depth from given metadata.
    • Added support for Linux video device path (such as /dev/video0).
  • Maintenance:
    • Logging current vidgear version when vidgear APIs are called, not at import.
      • Added logcurr_vidgear_ver helper function to facilitate logging current vidgear version, when called within a API.
      • Implemented ver_is_logged global variable in helper to log version only once, which can modifiable with logcurr_vidgear_ver method only. Followed recommendation given in official python docs: https://docs.python.org/3/faq/programming.html#how-do-i-share-global-variables-across-modules
      • Current version can only be logged by VidGear APIs with the logging turned on (i.e. logging=True).
  • Docs:
    • Added new WriteGear Bonus Example:
      • Added "Using WriteGear's Compression Mode with v4l2loopback Virtual Cameras bonus python example.
      • Added related prerequisites and dependencies for creating v4l2loopback Virtual Cameras on Linux machines.
      • Added both With/Without-Audio cases for "Using WriteGear's Compression Mode for YouTube-Live Streaming".
    • Added content.code.copy and content.tabs.link features.
    • Added docs related to skip_generate_webdata optional attribute.
    • Added feedback features to mkdocs.yml.
    • Added 404.html static template to mkdocs.yml.
  • CI:
    • Added v4l2loopback support for testing /dev/video0 device on Linux machines.
    • Added test cases for newer implementation of validate_audio method.
    • Added test_skip_generate_webdata to test skip_generate_webdata optional attribute.
    • Added tests for user-defined and higher than 8-bit depth input frames pixel-format.
Updates/Improvements
  • WriteGear:
    • Completely revamped code structure and comments.
      • Updated comments, description, and logging messages to more sensible and developer friendly.
      • Implemented operator short-circuiting to cleanup code as much as possible.
      • Renamed startFFmpeg_Process internal class method to start_FFProcess.
      • Renamed Preprocess internal class method to PreprocessFFParams.
      • Renamed startCV_Process internal class method to start_CVProcess.
      • Renamed initiate internal class parameter to initiate_process.
      • Renamed force_termination internal class parameter to forced_termination.
      • Enabled output_params parameters logging in both modes.
      • Improved compression and logging parameters boolean value handling.
      • Implemented stdout closing to cleanup pipeline before terminating.
  • Helper:
    • Updated validate_audio method with improved and more robust regex patterns for identifying audio bitrate in ay audio file.
  • Setup.py:
    • Bumped version to 0.3.0.
    • Replaced >= comparison operator with more flexible ~=.
    • Replaced distutils.version.LooseVersion with pkg_resources.parse_version.
  • Maintenance:
    • Replaced depreciated LooseVersion with parse_version.
    • Updated Retry package to be imported from requests.adapters.
    • Moved terminal and python code text area to Question GitHub Form Schema.
    • Removed unnecessary imports.
    • Removed redundant code.
    • Improved logging messages.
    • Updated code comments.
    • Updated method descriptions.
    • Refactored code.
    • Increased coverage.
  • Bash Script:
    • Updated FFmpeg Static Binaries links to latest date/version tag to 12-07-2022.
    • Removed depreciated binaries download links and code.
  • Docs:
    • Replaced all raw.githubusercontent.com GIF URLs with user-images.githubusercontent.com.
    • Reformatted custom.css and added missing comments.
    • Updated sponsor block.
    • Enabled Code Highlights.
    • Updated announcement bar.
    • Updated changelog.md.
    • Reduced webgear_rtc.gif size.
    • Updated Zenodo badge and the BibTeX entry.
  • CI:
    • Added more flexible formats to return_testvideo_path function.
    • Updated test_write test for higher than 8-bit depth input frames pixel-format in WriteGear's Compression Mode.
    • Updated actions/checkout to v3.
    • Updated actions/setup-python to v4.
    • Updated codecov/codecov-action to v3.
    • Moved test_colorspaces test to CamGear tests.
    • Added deffcode library import.
  • Re-stuctured yaml code.
Breaking Updates/Changes
  • WriteGear:
    • Renamed output_filename string parameter to output.
      • Since WriteGear API accepts all sorts of streams (such as valid filename/path/URL) for encoding, thereby changing parameter name to output will be more true to its purpose.
      • Renaming output_filename to output in WriteGear API will also help user to not accidentally assume WriteGear supports only encoding of video files.
      • It matches the output parameter in StreamGear which basically does the same thing.
    • Renamed cmd parameter in execute_ffmpeg_cmd() class method to more sensible command.
    • ValueError will be raised if datatype of input frames mismatches Writegear API
Bug-fixes
  • Camgear:
    • Fixed CamGear.read() blocked unnecessarily.
      • 💬 When THREADED_QUEUE_MODE is enabled CamGear.read() blocks for an excessive duration when attempting to read past the end of a stream.
      • Added None frame to the queue at the end to signal we're done.
      • Added terminate Event check before continuing.
    • Fixed deadlock on exit.
      • 💬 The deadlock is due to self.__queue.get(timeout=self.__thread_timeout) line in read() method, which still waits for timeout(thread_timeout) to happen when main update() thread was already terminated on exit and queue was empty. Since there was no way to signal queue that stream is already ended, the blocking queue.get() keeps on waiting until timeout occurs.
      • The solution was to signal queue.get() that stream is already ended by putting None in queue on exiting the main update() thread.
  • ScreenGear:
    • Fixed ScreenGear.read() blocked during cold startup.
    • 💬 During startup, ScreenGear.read() doesn't checks if queue is empty before continuing.
  • WriteGear:
    • Fixed gstpipeline_mode not activating when wrongly assuming output value as valid path.
    • Fixed name 'compression' is not defined bug.
    • Fixed AttributeError.
  • Helper:
    • Fixed fltp keyword in regex pattern causing non-ftlp streams to be not recognized.
    • Fixed response.headers returning content-length as Nonetype since it may not necessarily have the Content-Legth header set.
    • Fixed Linux video device paths still not working.
      • Moved helper.py logic to WriteGear and StreamGear APIs resp.
    • Fixed KeyError for empty metadata.
  • Setup:
    • Pinned pyzmq==24.0.1 working version.
    • Removed redundant patch for the issue.
  • Maintaince:
    • Fixed missing pkg name import_dependency_safe functions calls.
  • Bash Script:
    • Fixed gstreamer installation.
  • CI:
    • Fixed missing v4l2loopback apt dependency on Linux envs.
    • Added fix for RTCPeerConnection fails to create RTCDtlsTransport (Related issue: aiortc/aiortc#804)
      • Pinned cryptography==38.0.4 in dependencies.
    • Pinned Linux image to ubuntu-20.04 in github actions.
    • Fixed No module named 'httpx' bug.
      • Added httpx library import.
    • Fixed F821 undefined name bug.
    • Fixed Gstreamer bug.
  • Docs:
    • Fixed hyperlinks to new GitHub's form schemas.
    • Fixed non-rendering images in README.md
      • Replaced all relative image/gifs paths with absolute URLs in README.md.
    • Fixed badges/shields#8671 badge issue in README.md
    • Fixed GitLab CDN links throwing blocked by CORS policy bug.
      • Replaced gitlab GitHack CDN links with with bitbucket.
    • Fixed DASH playback failing by setting the maxAttempts to Infinity.
    • Removed x-sign glow-text effect CSS.
    • Fixed several typos (suggested by @timgates42)
    • Fixed coverage badge.
Pull Requests
New Contributors

 

 

v0.2.6 (2022-07-05)

New Features
  • Docs:
    • Added new bonus example for RTSP/RTP Live-Streaming using WriteGear's Compression Mode.
    • Added "How to resolve zmq.error.ZMQError" FAQ for NetGear API.(PR by @iandol)
    • Added new ko-fi button to README.md
    • Added new contributors block to changelog.md
  • Maintenance:
    • Added new patterns to .gitignore to ignore pypi's build directory and egg-info files.
  • CI:
    • Switched to new Issue GitHub's form schema using YAML
      • Added new bug_report.yaml.
      • Added new question.yaml.
      • Added new proposal.yaml.
      • Deleted depreciated markdown files.
      • Polished forms.
Updates/Improvements
  • Setup.py:
    • Bumped version to 0.2.6.
    • Updated logic operators and dependency.
      • Replaced >= comparsion operator with more flexible ~=.
      • Replaced distutils.version.LooseVersion with pkg_resources.parse_version.
  • Docs:
    • Updated Site Navigation.
      • Added new notices to inform users more effectively about bonus examples.
      • Added new Bonus section to navigation and moved suitable pages under it.
      • Updated headings and URLs.
    • Redesigned and Rewritten Donation and Contribution section to README.md
    • Updated Zenodo badge and Bibtex entry.
    • Updated Admonition Icon, FAQs and site-links.
    • Reformatted code and its comments.
    • Updated changelog.md.
  • API:
  • CI:
    • Added more paths and files to skip commits.
Breaking Updates/Changes
  • -input_framerate parameter now accepts any positive value for WriteGear and StreamGear APIs.
Bug-fixes
  • API:
    • Fixed -input_framerate less than 5 does not get used in WriteGear and StreamGear APIs.(PR by @freol35241)
  • CamGear: Fixed Yt-dlp generated HTTP DASH Segments URLs not supported by OpenCV's VideoCapture(PR by @DynamiteC)
  • StreamGear:
    • Fixed hls_segment_type not working bug. (PR by @enarche-ahn)
    • Fixed critical logging parameter bug
      • Fixed debug logs even when logging=False in StreamGear's Real-time Mode. (patch suggested by @enarche-ahn)
      • Added length check to -video_source attribute to correctly infers it as empty(or invalid).
  • CI:
    • Xfailed RTSP CamGear CI test.
    • Fixed pinned version syntax bug in docs_deployer workflow.
    • Fixed typos in Github forms and its context.
    • Added missing dependency.
  • Docs:
    • Fixed jinja2 3.1.0 or above breaks mkdocs.
      • jinja2>=3.1.0 breaks mkdocs (mkdocs/mkdocs#2799), therefore pinned jinja2 version to <3.1.0.
    • Fixed support for new mkdocstring versions
      • Replaced rendering sub-value with options.
      • Removed pinned mkdocstrings==0.17.0 version.
    • Fixed Netgear+Webgear bonus example code bugs.(PR by @iandol)
      • Added a missing import.
      • Removed self. typo.
      • Replaced the return value with break in the async as it triggers an error.
    • Fixed external bug that causing "Home" tab to irresponsive randomly when accessed from other tabs.
    • Fixed indentation and spacing.
    • Fixed typos and updated context.
    • Removed dead code.
Pull Requests
New Contributors

 

 

v0.2.5 (2021-02-11)

New Features
  • WriteGear:
    • Add support for GStreamer pipeline in WriteGear API's Non-Compression mode:
      • Implemented GStreamer Pipeline Mode to accept GStreamer pipeline as string to its output_filename parameter.
      • Added new special -gst_pipeline_mode attribute for its output_params parameter.
      • This feature provides flexible way to directly write video frames into GStreamer Pipeline with controlled bitrate.
      • Added new docs and updated existing docs with related changes.
    • Added new -ffpreheaders special attribute to WriteGear's options parameter:
      • This attribute is specifically required to set special FFmpeg parameters in Compression Mode that are present at the starting of command(such as -re).
      • This attribute only accepts list datatype as value.
      • Added related docs.
  • NetGear:
    • Added bidirectional data transfer support by extending Bidirectional mode support to exclusive Multi-Clients and Multi-Servers modes:
      • Users will now able to send data bidirectionally in both Multi-Clients and Multi-Servers exclusive modes.
      • Bidirectional mode will no longer disables automatically when Multi-Clients and Multi-Servers modes already enabled.
      • Added new docs and updated existing docs with related changes.
  • Maintenance:
    • Added official support for Python-3.10 legacies.
    • Added float value support to THREAD_TIMEOUT optional parameter.
    • Added info about dropped support for Python-3.6 legacies through announcement bar.
    • Added config.md file for Issue templates.
    • Added title to Issue templates.
  • Docs:
    • Added new Code Annotations
    • Added new icons to headings.
    • Added Advanced VideoGear usage example with CamGear backend.
Updates/Improvements
  • Setup.py:
    • Dropped support for Python-3.6 and below legacies.
    • Updated logging formatting.
    • Updated python_requires to >=3.7.
    • Bumped version to 0.2.5.
  • Helper:
    • Vidgear will now report current version on every run.
  • Docs:
    • Updated SSH tunneling docs context.
    • Excluded docs directory from CI envs.
    • Updated Zenodo badge and BibTeX entry.
    • Updated dark theme hue to 260.
    • Updated Admonitions.
    • Additional warnings against pushing PR against VidGear's testing branch only.
    • Updated code comments.
  • CI:
    • Removed support for Python-3.6 legacies from all workflows.
    • Updated NetGear's Exclusive Mode tests.
    • Added GStreamer Pipeline Mode tests.
  • Maintenance:
    • Updated Issue and PR templates.
    • Updated metadata.
Breaking Updates/Changes
  • Dropped support for Python-3.6 legacies from vidgear.
Bug-fixes
  • NetGear: Fixed bidirectional mode overriding multi-clients mode's data.
  • WriteGear:
    • Fixed wrongly defined ffmpeg_preheaders.
    • Fixed condition logic bugs.
    • Fixed UnboundLocalError bug.
  • Setup: Fixed uvicorn and aiortc dropped support for Python-3.6 legacies.
  • CI:
    • Fixed GitHub Actions interprets 3.10 as 3.1 if used without strings.
    • Fixed naming error in azure YAML.
  • Docs:
    • Fixed codecov badge URL in README.md
    • Fixed hyperlinks in README.
    • Fixed indentation and spacing.
    • Fixed typos and updated context.
    • Removed dead code.
  • Maintenance:
    • Removed depreciated condition checks.
Pull Requests

 

 

v0.2.4 (2021-12-05)

New Features
  • CamGear:
    • Added a new YT_backend Internal Class with YT-DLP backend:
      • Implemented YT_backend a new CamGear's Internal YT-DLP backend class for extracting metadata from Streaming URLs.
      • Added support for pipeling (live) video-frames from all yt-dlp supported streaming sites: https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md#supported-sites
      • Implemented algorithm from scratch for auto-extracting resolution specific streamable URLs for pipelineing.
      • Implemented logic for auto-calculating best and worst resolutions.
      • Added new ytv_metadata global parameter to CamGear for accessing video's metadata(such as duration, title, description) on-the-go.
      • ⚠️ Playlists are still unsupported.
  • WebGear_RTC:
    • Implemented a new easy way of defining Custom Streaming Class with suitable source(such as OpenCV):
      • Added new custom_stream attribute with WebGear_RTC options parameter that allows you to easily define your own Custom Streaming Class with suitable source(such as OpenCV).
      • This implementation supports repeated Auto-Reconnection or Auto-Refresh out-of-the-box.
      • This implementation is more user-friendly and easy to integrate within complex APIs.
      • This implementation requires at-least read() and stop() methods implemented within Custom Streaming Class, otherwise WebGear_RTC will throw ValueError.
      • This implementation supports all vidgear's VideoCapture APIs readily as input.
  • Maintenance:
    • Added new .gitignore for specifying intentionally untracked files to ignore
      • Added more files entries to .gitignore.
    • Added new .gitattributes to manage how Git reads line endings.
      • Enabled auto default behavior, in case people don't have core.autocrlf set.
      • Enforced LF line-endings for selective files types.
      • Added Binary data files that specifies they are not text, and git should not try to change them.
      • Added Language aware diff headers.
      • Added Linguist language overrides.
  • Docs:
    • Added bonus example to add real-time file audio encoding with VideoGear and Stabilizer class.
    • Added complete usage docs with new CamGear's Internal Class with YT-DLP backend.
    • Added instructions to extract video's metadata in CamGear.
    • Added donation link in page footer with bouncing heart animation through pure CSS.
    • Added info about critical changes in v0.2.4 and above installation through new announcement bar.
    • Added related usage docs for new WebGear_RTC custom streaming class.
    • Added changes for upgrading mkdocs-material from v7.x to newer v8.x.
    • Added outdated version warning block.
Updates/Improvements
  • CamGear:
    • Added is_livestream global YT_backend parameters.
    • Added default options for yt-dlp for extracting info_dict(metadata) of the video as a single JSON line.
    • Completely removed old logic for extracting streams using pafy.
    • Removed all dead code related to streamlink backend.
  • Setup.py:
    • Moved all API specific dependencies to extra_requires under the name "core". [PR #268 by @zpapakipos]
    • Added rule to replace GitHub heading links in description.
    • Updated extra_require dependencies.
    • Removed streamlink dependency.
    • Removed pafy dependency.
    • Removed pyzmq from latest_version group.
    • Updated SEO Keywords.
  • Docs:
    • Re-written pip and source installation docs.
    • Added warning for using -disable_force_termination flag for short duration videos.
    • Added permalink_title entry to mkdocs.yml.
    • Updated CamGear parameters.
    • Updated Admonitions with related information.
    • Updated Functional Block Diagram(gears_fbd.png) image.
    • Updated installation instructions.
    • Updated Advanced examples using WebGear_RTC's custom streaming class.
    • Updated code highlighting.
    • Updated zenodo badge.
    • Updated BibTex for project citation.
    • Replaced incorrect API parameter docs.
    • Updated WebGear_RTC parameters.
  • CI:
    • Updated CI tests for new WebGear_RTC custom streaming class.
    • Restored test_stream_mode CamGear test.
    • Updated Streaming Sites test links.
    • Added more tests cases.
  • Maintenance:
    • Updated spacing in logger formatting.
    • Renamed Asyncio Helper logger name.
    • Changed logging colors.
    • Updated logging messages.
Breaking Updates/Changes
  • Installation command with pip has been changed in v0.2.4:
    • The legacy pip install vidgear command now installs critical bare-minimum dependencies only. Therefore in order to automatically install all the API specific dependencies as previous versions, use pip install vidgear[core] command instead.
  • CamGear:
    • Removed streamlink backend support from stream_mode in favor of more reliable CamGear's Internal YT-DLP backend class for extracting metadata from Streaming URLs.
      • CamGear will raise ValueError if streaming site URL is unsupported by yt-dlp backend.
      • CamGear will raise ValueError if yt-dlp isn't installed and stream_mode is enabled.
    • Removed automatic enforcing of GStreamer backend for YouTube-livestreams and made it optional.
      • The CamGear will not raise ValueError if GStreamer support is missing in OpenCV backends.
  • WebGear_RTC:
    • Removed support for assigning Custom Media Server Class(inherited from aiortc's VideoStreamTrack) in WebGear_RTC through its config global parameter.
    • WebGear_RTC API will now throws ValueError if source parameter is NoneType as well as custom_stream attribute is undefined.
  • Helper:
    • Removed restore_levelnames method.
    • Removed youtube_url_validator helper method.
Bug-fixes
  • CamGear:
    • Fixed KeyError Bug for missing attributed in meta_data json in some streaming sites.
  • Helper:
    • Removed unused imports.
  • Docs:
    • Removed slugify from mkdocs which was causing invalid hyperlinks in docs.
    • Fixed GitHub hyperlinks in README.md.
    • Fixed hyperlink in announcement bar.
    • Fixed content tabs failing to work.
    • Fixed line-endings and usage example code.
    • Removed any pafy and streamlink references.
    • Fixed context and typos.
  • CI:
    • Fixed NameError bugs in WebGear_RTC CI test.
  • Maintenance:
    • Removed dead logger code causing Python's Built-in logging module to hide logs.
    • Removed unused logging import.
    • Updated code comments.
Pull Requests
New Contributors

 

 

v0.2.3 (2021-10-27)

New Features
  • CamGear:
    • Added support for 4K Streaming URLs.
  • Helper:
    • Implemented logging ColorFormatter string alignment.
      • Center aligned logging Level-name and Class-name.
      • Changed % formatting style with modern {.
      • Re-added asctime value to Formatter string.
      • Re-arranged parameter positions in Formatter string.
  • Maintenance:
    • Added new .gitignore for specifying intentionally untracked files to ignore
      • Added more files entries to .gitignore.
    • Added new .gitattributes to manage how Git reads line endings.
      • Enabled auto default behavior, in case people don't have core.autocrlf set.
      • Enforced LF line-endings for selective files types.
      • Added Binary data files that specifies they are not text, and git should not try to change them.
      • Added Language aware diff headers.
      • Added Linguist language overrides.
  • Docs:
    • Added new ScreenGear with WebGear_RTC API bonus example.
    • Added support for hl_lines argument for highlighting specific code lines.
    • Added drop-shadow effects for its slate theme to improve visibility.
Updates/Improvements
  • CamGear:
    • Replaced youtube-dl with yt-dlp as pafy backend for YouTube videos pipelining.
      • Implemented hack to trick pafy into assuming yt-dlp as youtube-dl.
      • Using sys.modules to present yt-dlp as youtube-dl.
      • yt-dlp python API functions exactly similar to youtube-dl.
      • Replaced youtube-dl dependency with yt-dlp.
      • Replaced youtube-dl imports with yt-dlp.
  • StreamGear:
    • Updated default stream_count internal dict key value to 1.
  • Maintenance:
    • Introduced python short-circuiting for handling logging logic.
    • Enabled logging for check_WriteAccess method in WriteGear, StreamGear and NetGear APIs.
  • Docs:
    • Added warning for ScreenGear outputting RGBA frames instead of default BGR frames with mss backend.
    • Added warnings for properly formatting output_params when assigning external audio-source in WriteGear.
    • Added depreciation notice for Python 3.6 legacies.
    • Restructured docs to make it more user-friendly.
    • Updated, Extended and Improved context.
    • Improved code comments.
    • Updated docs admonitions.
    • Updated Zenodo badge.
  • CI:
    • Migrated to new Codecov Uploader in Azure Pipelines.
    • Replaced env with export in ci_linux.yml.
    • Replaced bubkoo/needs-more-info@v1 with wow-actions/needs-more-info@v1.
    • Added codecov secret token through env variable.
    • Added wildcard to skip CI tests for doc(.md) files.
    • Added .md files to Codecov ignore list.
    • Update vidgear's banner image.
Breaking Updates/Changes
  • check_WriteAccess will now return as invalid path if writing directory does not exists. This will effect output file handling in WriteGear and StreamGear APIs.
Bug-fixes
  • StreamGear:
    • Fixed StreamGear Malformed URI Error with HLS Segments [PR #243 by @Vboivin]
      • Removed the extra '%' character from the naming convention for segment files.
      • Used stream_count internal dict variable to alter template for HLS segment filenames.
  • WriteGear:
    • Fixed bug in disable_force_termination logic which accidentally disables force termination.
  • WebGear_RTC:
    • Fixed name 'VideoStreamTrack' is not defined bug.
  • Setup.py:
    • Fixed TypeError bug.
    • Fixed invalid latest_version retrieval.
  • Helper:
    • Fixed check_WriteAccess failing to recognize correct permission for writing the output file on windows platform.
      • Implemented separate logic for Windows and *nix platforms.
      • Added new stat import.
      • Improved warnings and error handling.
      • Added logging parameter to check_WriteAccess.
    • Fixed bug in check_WriteAccess that throws OSError while handling URLs.
  • Docs:
    • Fixed bugs in WriteGear's Compression Mode with Live Audio Input example.
    • Fixed "drop-shadow" property via filter function conflicting with sidecard button.
      • Added new CSS classes for image, admonitions and code highlight in dark theme.
    • Several internal and external webpage links typos fixed.
    • Fixed several language typos.
  • CI:
    • Fixed Azure Pipeline coverage upload bugs.
    • Fixed random errors in CamGear stream_mode test.
  • Bash:
    • Removed the Windows carriage returns from the shell scripts to be able to execute them on Linux.
  • Fixed logging comments.
Pull Requests
New Contributors

 

 

v0.2.2 (2021-09-02)

New Features
  • StreamGear:
    • Native Support for Apple HLS Multi-Bitrate Streaming format:
      • Added support for new Apple HLS (HTTP Live Streaming) HTTP streaming format in StreamGear.
      • Implemented default workflow for auto-generating primary HLS stream of same resolution and framerate as source.
      • Added HLS support in Single-Source and Real-time Frames Modes.
      • Implemented inherit support for fmp4 and mpegts HLS segment types.
      • Added adequate default parameters required for trans-coding HLS streams.
      • Added native support for HLS live-streaming.
      • Added "hls" value to format parameter for easily selecting HLS format.
      • Added HLS support in -streams attribute for transcoding additional streams.
      • Added support for .m3u8 and .ts extensions in clear_prev_assets workflow.
      • Added validity check for .m3u8 extension in output when HLS format is used.
      • Separated DASH and HLS command handlers.
      • Created HLS format exclusive parameters.
      • Implemented -hls_base_url FFMpeg parameter support.
    • Added support for audio input from external device:
      • Implemented support for audio input from external device.
      • Users can now easily add audio device and decoder by formatting them as python list.
      • Modified -audio parameter to support list data type as value.
      • Modified validate_audio helper function to validate external audio devices.
    • Added -seg_duration to control segment duration.
  • NetGear:
    • New SSH Tunneling Mode for remote connection:
      • New SSH Tunneling Mode for connecting ZMQ sockets across machines via SSH tunneling.
      • Added new ssh_tunnel_mode attribute to enable ssh tunneling at provide address at server end only.
      • Implemented new check_open_port helper method to validate availability of host at given open port.
      • Added new attributes ssh_tunnel_keyfile and ssh_tunnel_pwd to easily validate ssh connection.
      • Extended this feature to be compatible with bi-directional mode and auto-reconnection.
      • Disabled support for exclusive Multi-Server and Multi-Clients modes.
      • Implemented logic to automatically enable paramiko support if installed.
      • Reserved port-47 for testing.
    • Additional colorspace support for input frames with Frame-Compression enabled:
      • Allowed to manually select colorspace on-the-fly with JPEG frame compression.
      • Updated jpeg_compression dict parameter to support colorspace string values.
      • Added all supported colorspace values by underline simplejpeg library.
      • Server enforced frame-compression colorspace on client(s).
      • Enable "BGR" colorspace by default.
      • Added Example for changing incoming frames colorspace with NetGear's Frame Compression.
      • Updated Frame Compression parameters in NetGear docs.
      • Updated existing CI tests to cover new frame compression functionality.
  • NetGear_Async:
    • New exclusive Bidirectional Mode for bidirectional data transfer:
      • NetGear_Async's first-ever exclusive Bidirectional mode with pure asyncio implementation.
      • Bidirectional mode is only available with User-defined Custom Source(i.e. source=None)
      • Added support for PAIR & REQ/REP bidirectional patterns for this mode.
      • Added powerful asyncio.Queues for handling user data and frames in real-time.
      • Implemented new transceive_data method to Transmit (in Recieve mode) and Receive (in Send mode) data in real-time.
      • Implemented terminate_connection internal asyncio method to safely terminate ZMQ connection and queues.
      • Added msgpack automatic compression encoding and decoding of data and frames in bidirectional mode.
      • Added support for np.ndarray video frames.
      • Added new bidirectional_mode attribute for enabling this mode.
      • Added 8-digit random alphanumeric id generator for each device.
      • NetGear_Async will throw RuntimeError if bidirectional mode is disabled at server or client but not both.
    • Added new disable_confirmation used to force disable termination confirmation from client in terminate_connection.
    • Added task_done() method after every get() call to gracefully terminate queues.
    • Added new secrets and string imports.
  • WebGear:
    • Updated JPEG Frame compression with simplejpeg:
      • Implemented JPEG compression algorithm for 4-5% performance boost at cost of minor loss in quality.
      • Utilized encode_jpeg and decode_jpeg methods to implement turbo-JPEG transcoding with simplejpeg.
      • Added new options to control JPEG frames quality, enable fastest dct, fast upsampling to boost performance.
      • Added new jpeg_compression, jpeg_compression_quality, jpeg_compression_fastdct, jpeg_compression_fastupsample attributes.
      • Enabled fast dct by default with JPEG frames at 90%.
      • Incremented default frame reduction to 25%.
      • Implemented automated grayscale colorspace frames handling.
      • Updated old and added new usage examples.
      • Dropped support for depreciated attributes from WebGear and added new attributes.
    • Added new WebGear Theme: (Checkout at https://github.com/abhiTronix/vidgear-vitals)
      • Added responsive image scaling according to screen aspect ratios.
      • Added responsive text scaling.
      • Added rounded border and auto-center to image tag.
      • Added bootstrap css properties to implement auto-scaling.
      • Removed old resize() hack.
      • Improved text spacing and weight.
      • Integrated toggle full-screen to new implementation.
      • Hide Scrollbar both in WebGear_RTC and WebGear Themes.
      • Beautify files syntax and updated files checksum.
      • Refactor files and removed redundant code.
      • Bumped theme version to v0.1.2.
  • WebGear_RTC:
    • Added native support for middlewares:
      • Added new global middleware variable for easily defining Middlewares as list.
      • Added validity check for Middlewares.
      • Added tests for middlewares support.
      • Added example for middlewares support.
      • Extended middlewares support to WebGear API too.
      • Added related imports.
    • Added new WebGear_RTC Theme: (Checkout at https://github.com/abhiTronix/vidgear-vitals)
      • Implemented new responsive video scaling according to screen aspect ratios.
      • Added bootstrap CSS properties to implement auto-scaling.
      • Removed old resize() hack.
      • Beautify files syntax and updated files checksum.
      • Refactored files and removed redundant code.
      • Bumped theme version to v0.1.2
  • Helper:
    • New automated interpolation selection for gears:
      • Implemented retrieve_best_interpolation method to automatically select best available interpolation within OpenCV.
      • Added support for this method in WebGear, WebGear_RTC and Stabilizer Classes/APIs.
      • Added new CI tests for this feature.
    • Implemented get_supported_demuxers method to get list of supported demuxers.
  • CI:
    • Added new no-response work-flow for stale issues.
    • Added new CI tests for SSH Tunneling Mode.
    • Added paramiko to CI dependencies.
    • Added support for "hls" format in existing CI tests.
    • Added new functions check_valid_m3u8 and extract_meta_video for validating HLS files.
    • Added new m3u8 dependency to CI workflows.
    • Added complete CI tests for NetGear_Async's new Bidirectional Mode:
      • Implemented new exclusive Custom_Generator class for testing bidirectional data dynamically on server-end.
      • Implemented new exclusive client_dataframe_iterator method for testing bidirectional data on client-end.
      • Implemented test_netgear_async_options and test_netgear_async_bidirectionalmode two new tests.
      • Added timeout value on server end in CI tests.
  • Setup.py:
    • Added new cython and msgpack dependency.
    • Added msgpack and msgpack_numpy to auto-install latest.
  • BASH:
    • Added new temp_m3u8 folder for generating M3U8 assets in CI tests.
  • Docs:
    • Added docs for new Apple HLS StreamGear format:
      • Added StreamGear HLS transcoding examples for both StreamGear modes.
      • Updated StreamGear parameters to w.r.t new HLS configurations.
      • Added open-sourced "Sintel" - project Durian Teaser Demo with StreamGear's HLS stream using Clappr and raw.githack.com.
      • Added new HLS chunks at https://github.com/abhiTronix/vidgear-docs-additionals for StreamGear
      • Added support for HLS video in Clappr within custom.js using HlsjsPlayback plugin.
      • Added support for Video Thumbnail preview for HLS video in Clappr within custom.js
      • Added hlsjs-playback.min.js JS script and suitable configuration for HlsjsPlayback plugin.
      • Added custom labels for quality levels selector in custom.js.
      • Added new docs content related to new Apple HLS format.
      • Updated DASH chunk folder at https://github.com/abhiTronix/vidgear-docs-additionals.
      • Added example for audio input support from external device in StreamGear.
      • Added steps for using -audio attribute on different OS platforms in StreamGear.
    • Added usage examples for NetGear_Async's Bidirectional Mode:
      • Added new Usage examples and Reference doc for NetGear_Async's Bidirectional Mode.
      • Added new image asset for NetGear_Async's Bidirectional Mode.
      • Added NetGear_Async's option parameter reference.
      • Updated NetGear_Async definition in docs.
      • Changed font size for Helper methods.
      • Renamed Bonus section to References in mkdocs.yml.
    • Added Gitter sidecard embed widget:
      • Imported gitter-sidecar script to main.html.
      • Updated custom.js to set global window option.
      • Updated Sidecard UI in custom.css.
    • Added bonus examples to help section:
      • Implemented a curated list of more advanced examples with unusual configuration for each API.
    • Added several new contents and updated context.
    • Added support for search suggestions, search highlighting and search sharing (i.e. deep linking)
    • Added more content to docs to make it more user-friendly.
    • Added warning that JPEG Frame-Compression is disabled with Custom Source in WebGear.
    • Added steps for identifying and specifying sound card on different OS platforms in WriteGear.
    • Added Zenodo DOI badge and its reference in BibTex citations.
    • Added extra.homepage parameter, which allows for setting a dedicated URL for site_url.
    • Added pymdownx.striphtml plugin for stripping comments.
    • Added complete docs for SSH Tunneling Mode.
    • Added complete docs for NetGear's SSH Tunneling Mode.
    • Added pip upgrade related docs.
    • Added docs for installing vidgear with only selective dependencies
    • Added new advance/experiment admonition with new background color.
    • Added new icons SVGs for advance and warning admonition.
    • Added new usage example and related information.
    • Added new image assets for ssh tunneling example.
    • Added new admonitions
    • Added new FAQs.
Updates/Improvements
  • VidGear Core:
    • New behavior to virtually isolate optional API specific dependencies by silencing ImportError on all VidGear's APIs import.
    • Implemented algorithm to cache all imports on startup but silence any ImportError on missing optional dependency.
    • Now ImportError will be raised only any certain API specific dependency is missing during given API's initialization.
    • New import_dependency_safe to imports specified dependency safely with importlib module.
    • Replaced all APIs imports with import_dependency_safe.
    • Added support for relative imports in import_dependency_safe.
    • Implemented error parameter to by default ImportError with a meaningful message if a dependency is missing, Otherwise if error = log a warning will be logged and on error = silent everything will be quit. But If a dependency is present, but older than specified, an error is raised if specified.
    • Implemented behavior that if a dependency is present, but older than min_version specified, an error is raised always.
    • Implemented custom_message to display custom message on error instead of default one.
    • Implemented separate import_core_dependency function to import and check for specified core dependency.
    • ImportError will be raised immediately if core dependency not found.
  • StreamGear:
    • Replaced depreciated -min_seg_duration flag with -seg_duration.
    • Removed redundant -re flag from RTFM.
    • Improved Live-Streaming performance by disabling SegmentTimline
    • Improved DASH assets detection for removal by using filename prefixes.
  • NetGear:
    • Replaced np.newaxis with np.expand_dims.
    • Replaced random module with secrets while generating system ID.
    • Update array indexing with np.copy.
  • NetGear_Async:
    • Improved custom source handling.
    • Removed deprecated loop parameter from asyncio methods.
    • Re-implemented skip_loop parameter in close() method.
    • run_until_complete will not used if skip_loop is enabled.
    • skip_loop now will create asyncio task instead and will enable disable_confirmation by default.
    • Replaced create_task with ensure_future to ensure backward compatibility with python-3.6 legacies.
    • Simplified code for transceive_data method.
  • WebGear_RTC:
    • Improved handling of failed ICE connection.
    • Made is_running variable globally available for internal use.
  • Helper:
    • Added 4320p resolution support to dimensions_to_resolutions method.
    • Implemented new delete_file_safe to safely delete files at given path.
    • Replaced os.remove calls with delete_file_safe.
    • Added support for filename prefixes in delete_ext_safe method.
    • Improved and simplified create_blank_frame functions frame channels detection.
    • Added logging parameter to capPropId function to forcefully discard any error(if required).
  • Setup.py:
  • Asyncio:
    • Changed asyncio.sleep value to 0.
      • The amount of time sleep is irrelevant; the only purpose await asyncio.sleep() serves is to force asyncio to suspend execution to the event loop, and give other tasks a chance to run. Also, await asyncio.sleep(0) will achieve the same effect. https://stackoverflow.com/a/55782965/10158117
  • License:
    • Dropped publication year range to avoid confusion. (Signed and Approved by @abhiTronix)
    • Updated Vidgear license's year of first publication of the work in accordance with US copyright notices defined by Title 17, Chapter 4(Visually perceptible copies): https://www.copyright.gov/title17/92chap4.html
    • Reflected changes in all copyright notices.
  • CI:
    • Updated macOS VM Image to latest in azure devops.
    • Updated VidGear Docs Deployer Workflow.
    • Updated WebGear_RTC CI tests.
    • Removed redundant code from CI tests.
    • Updated tests to increase coverage.
    • Enabled Helper tests for python 3.8+ legacies.
    • Enabled logging in validate_video method.
    • Added -hls_base_url to streamgear tests.
    • Update mpegdash dependency to 0.3.0-dev2 version in Appveyor.
    • Updated CI tests for new HLS support
    • Updated CI tests from scratch for new native HLS support in StreamGear.
    • Updated test patch for StreamGear.
    • Added exception for RunTimeErrors in NetGear CI tests.
    • Added more directories to Codecov ignore list.
    • Imported relative logger_handler for asyncio tests.
  • Docs:
    • Re-positioned few docs comments at bottom for easier detection during stripping.
    • Updated to new extra analytics parameter in Material Mkdocs.
    • Updated dark theme to dark orange.
    • Changed fonts => text: Muli & code: Fira Code
    • Updated fonts to Source Sans Pro.
    • Updated setup.py update-link for modules.
    • Re-added missing StreamGear Code docs.
    • Several minor tweaks and typos fixed.
    • Updated 404.html page.
    • Updated admonitions colors and beautified custom.css.
    • Replaced VideoGear & CamGear with OpenCV in CPU intensive examples.
    • Updated mkdocs.yml with new changes and URLs.
    • Moved FAQ examples to bonus examples.
    • Moved StreamGear primary modes to separate sections for better readability.
    • Implemented separate overview and usage example pages for StreamGear primary modes.
    • Improved StreamGear docs context and simplified language.
    • Renamed StreamGear overview page to introduction.
    • Re-written Threaded-Queue-Mode from scratch with elaborated functioning.
    • Replace Paypal with Liberpay in FUNDING.yml.
    • Updated FFmpeg Download links.
    • Reverted UI change in CSS.
    • Updated changelog.md and fixed clutter.
    • Updated README.md and mkdocs.yml with new additions
    • Updated context for CamGear example.
    • Restructured and added more content to docs.
    • Updated comments in source code.
    • Removed redundant data table tweaks from custom.css.
    • Re-aligned badges in README.md.
    • Beautify custom.css.
    • Updated mkdocs.yml.
    • Updated context and fixed typos.
    • Added missing helper methods in Reference.
    • Updated Admonitions.
    • Updates images assets.
    • Bumped CodeCov.
  • Logging:
    • Improved logging level-names.
    • Updated logging messages.
  • Minor tweaks to needs-more-info template.
  • Updated issue templates and labels.
  • Removed redundant imports.
Breaking Updates/Changes
  • Virtually isolated all API specific dependencies, Now ImportError for API-specific dependencies will be raised only when any of them is missing at API's initialization.
  • Renamed delete_safe to delete_ext_safe.
  • Dropped support for frame_jpeg_quality, frame_jpeg_optimize, frame_jpeg_progressive attributes from WebGear.
Bug-fixes
  • CamGear:
    • Hot-fix for Live Camera Streams:
      • Added new event flag to keep check on stream read.
      • Implemented event wait for read() to block it when source stream is busy.
      • Added and Linked THREAD_TIMEOUT with event wait timout.
      • Improved backward compatibility of new additions.
    • Enforced logging for YouTube live.
  • NetGear:
    • Fixed Bidirectional Video-Frame Transfer broken with frame-compression:
      • Fixed return_data interfering with return JSON-data in receive mode.
      • Fixed logic.
    • Fixed color-subsampling interfering with colorspace.
    • Patched external simplejpeg bug. Issue: https://gitlab.com/jfolz/simplejpeg/-/issues/11
      • Added np.squeeze to drop grayscale frame's 3rd dimension on Client's end.
    • Fixed bug that cause server end frame dimensions differ from client's end when frame compression enabled.
  • NetGear_Async:
    • Fixed bug related asyncio queue freezing on calling join().
    • Fixed ZMQ connection bugs in bidirectional mode.
    • Fixed several critical bugs in event loop handling.
    • Fixed several bugs in bidirectional mode implementation.
    • Fixed missing socket termination in both server and client end.
    • Fixed timeout parameter logic.
    • Fixed typos in error messages.
  • WebGear_RTC:
    • Fixed stream freezes after web-page reloading:
      • Implemented new algorithm to continue stream even when webpage is reloaded.
      • Inherit and modified next_timestamp VideoStreamTrack method for generating accurate timestamps.
      • Implemented reset_connections callable to reset all peer connections and recreate Video-Server timestamps. (Implemented by @kpetrykin)
      • Added close_connection endpoint in JavaScript to inform server page refreshing.(Thanks to @kpetrykin)
      • Added exclusive reset connection node /close_connection in routes.
      • Added reset() method to Video-Server class for manually resetting timestamp clock.
      • Added reset_enabled flag to keep check on reloads.
      • Fixed premature webpage auto-reloading.
      • Added additional related imports.
    • Fixed web-page reloading bug after stream ended:
      • Disable webpage reload behavior handling for Live broadcasting.
      • Disable reload CI test on Windows machines due to random failures.
      • Improved handling of failed ICE connection.
    • Fixed Assertion error bug:
      • Source must raise MediaStreamError when stream ends instead of returning None-type.
  • WebGear
    • Removed format specific OpenCV decoding and encoding support for WebGear.
  • Helper:
    • Regex bugs fixed:
      • New improved regex for discovering supported encoders in get_supported_vencoders.
      • Re-implemented check for extracting only valid output protocols in is_valid_url.
      • Minor tweaks for better regex compatibility.
    • Bugfix related to OpenCV import:
      • Bug fixed for OpenCV import comparison test failing with Legacy versions and throwing ImportError.
      • Replaced packaging.parse_version with more robust distutils.version.
    • Fixed bug with create_blank_frame that throws error with gray frames:
      • Implemented automatic output channel correction inside create_blank_frame function.
      • Extended automatic output channel correction support to asyncio package.
    • Implemented RTSP protocol validation as demuxer, since it's not a protocol but a demuxer.
    • Removed redundant logger_handler, mkdir_safe, retrieve_best_interpolation, capPropId helper functions from asyncio package. Relatively imported helper functions from non-asyncio package.
    • Removed unused aiohttp dependency.
    • Removed asctime formatting from logging.
  • StreamGear:
    • Fixed Multi-Bitrate HLS VOD streams:
      • Re-implemented complete workflow for Multi-Bitrate HLS VOD streams.
      • Extended support to both Single-Source and Real-time Frames Modes.
    • Fixed bugs with audio-video mapping.
    • Fixed master playlist not generating in output.
    • Fixed improper -seg_duration value resulting in broken pipeline.
    • Fixed expected aspect ratio not calculated correctly for additional streams.
    • Fixed stream not terminating when provided input from external audio device.
    • Fixed bugs related to external audio not mapped correctly in HLS format.
    • Fixed OPUS audio fragments not supported with MP4 video in HLS.
    • Fixed unsupported high audio bit-rate bug.
  • Setup.py:
    • Fixed latest_version returning incorrect version for some PYPI packages.
    • Removed latest_version variable support from simplejpeg.
    • Fixed streamlink only supporting requests==2.25.1 on Windows.
    • Removed all redundant dependencies like colorama, aiofiles, aiohttp.
    • Fixed typos in dependencies.
  • Setup.cfg:
    • Replaced dashes with underscores to remove warnings.
  • CI:
    • Replaced buggy starlette.TestClient with async-asgi-testclient in WebGear_RTC
    • Removed run() method and replaced with pure asyncio implementation.
    • Added new async-asgi-testclient CI dependency.
    • Fixed fake_picamera class logger calling vidgear imports prematurely before importing picamera class in tests.
      • Implemented new fake_picamera class logger inherently with logging module.
      • Moved sys.module logic for faking to init.py.
      • Added __init__.py to ignore in Codecov.
    • Fixed event loop closing prematurely while reloading:
      • Internally disabled suspending event loop while reloading.
    • Event Policy Loop patcher added for WebGear_RTC tests.
    • Fixed return_assets_path path bug.
    • Fixed typo in TimeoutError exception import.
    • Fixed eventloop is already closed bug.
    • Fixed eventloop bugs in Helper CI tests.
    • Fixed several minor bugs related to new CI tests.
    • Fixed bug in PiGear tests.
  • Docs:
    • Fixed 404 page does not work outside the site root with mkdocs.
    • Fixed markdown files comments not stripped when converted to HTML.
    • Fixed missing heading in VideoGear.
    • Typos in links and code comments fixed.
    • Several minor tweaks and typos fixed.
    • Fixed improper URLs/Hyperlinks and related typos.
    • Fixed typos in usage examples.
    • Fixed redundant properties in CSS.
    • Fixed bugs in mkdocs.yml.
    • Fixed docs contexts and typos.
    • Fixed stream.release() missing in docs.
    • Fixed several typos in code comments.
    • Removed dead code from docs.
  • Refactored Code and reduced redundancy.
  • Fixed shutdown in main.py.
  • Fixed logging comments.
Pull Requests
New Contributors

 

 

v0.2.1 (2021-04-25)

New Features
  • WebGear_RTC:
    • A new API that is similar to WeGear API in all aspects but utilizes WebRTC standard instead of Motion JPEG for streaming.
    • Now it is possible to share data and perform teleconferencing peer-to-peer, without requiring that the user install plugins or any other third-party software.
    • Added a flexible backend for aiortc - a python library for Web Real-Time Communication (WebRTC).
    • Integrated all functionality and parameters of WebGear into WebGear_RTC API.
    • Implemented JSON Response with a WebRTC Peer Connection of Video Server.
    • Added a internal RTC_VideoServer server on WebGear_RTC, a inherit-class to aiortc's VideoStreamTrack API.
    • New Standalone UI Default theme v0.1.1 for WebGear_RTC from scratch without using 3rd-party assets. (by @abhiTronix)
    • New custom.js and custom.css for custom responsive behavior.
    • Added WebRTC support to custom.js and ensured compatibility with WebGear_RTC.
    • Added example support for ICE framework and STUN protocol like WebRTC features to custom.js.
    • Added resize() function to custom.js to automatically adjust video & img tags for smaller screens.
    • Added WebGear_RTC support in main.py for easy access through terminal using --mode flag.
    • Integrated all WebGear_RTC enhancements to WebGear Themes.
    • Added CI test for WebGear_RTC.
    • Added complete docs for WebGear_RTC API.
    • Added bare-minimum as well as advanced examples usage code.
    • Added new theme images.
    • Added Reference and FAQs.
  • CamGear API:
    • New Improved Pure-Python Multiple-Threaded Implementation:
      • Optimized Threaded-Queue-Mode Performance. (PR by @bml1g12)
      • Replaced regular queue.full checks followed by sleep with implicit sleep with blocking queue.put.
      • Replaced regular queue.empty checks followed by queue.
      • Replaced nowait_get with a blocking queue.get natural empty check.
      • Up-to 2x performance boost than previous implementations.
    • New THREAD_TIMEOUT attribute to prevent deadlocks:
      • Added support for THREAD_TIMEOUT attribute to its options parameter.
      • Updated CI Tests and docs.
  • WriteGear API:
    • New more robust handling of default video-encoder in compression mode:
      • Implemented auto-switching of default video-encoder automatically based on availability.
      • API now selects Default encoder based on priority: "libx264" > "libx265" > "libxvid" > "mpeg4".
      • Added get_supported_vencoders Helper method to enumerate Supported Video Encoders.
      • Added common handler for -c:v and -vcodec flags.
  • NetGear API:
    • New Turbo-JPEG compression with simplejpeg
      • Implemented JPEG compression algorithm for 4-5% performance boost at cost of minor loss in quality.
      • Utilized encode_jpeg and decode_jpeg methods to implement turbo-JPEG transcoding with simplejpeg.
      • Added options to control JPEG frames quality, enable fastest dct, fast upsampling to boost performance.
      • Added new jpeg_compression, jpeg_compression_quality, jpeg_compression_fastdct, jpeg_compression_fastupsample attributes.
      • Enabled fast dct by default with JPEG frames at 90%.
      • Added Docs for JPEG Frame Compression.
  • WebGear API:
    • New modular and flexible configuration for Custom Sources:
      • Implemented more convenient approach for handling custom source configuration.
      • Added new config global variable for this new behavior.
      • Now None-type source parameter value is allowed for defining own custom sources.
      • Added new Example case and Updates Docs for this feature.
      • Added new CI Tests.
    • New Browser UI Updates:
      • New Standalone UI Default theme v0.1.0 for browser (by @abhiTronix)
      • Completely rewritten theme from scratch with only local resources.
      • New custom.js and custom.css for custom responsive behavior.
      • New sample glow effect with css.
      • New sample click to full-screen behavior with javascript.
      • Removed all third-party theme dependencies.
      • Update links to new github server abhiTronix/vidgear-vitals
      • Updated docs with new theme's screenshots.
    • Added enable_infinite_frames attribute for enabling infinite frames.
    • Added New modular and flexible configuration for Custom Sources.
    • Bumped WebGear Theme Version to v0.1.1.
    • Updated Docs and CI tests.
  • ScreenGear API:
    • Implemented Improved Pure-Python Multiple-Threaded like CamGear.
    • Added support for THREAD_TIMEOUT attribute to its options parameter.
  • StreamGear API:
    • Enabled pseudo live-streaming flag re for live content.
  • Docs:
    • Added new native docs versioning to mkdocs-material.
    • Added new examples and few visual tweaks.
    • Updated Stylesheet for versioning.
    • Added new DASH video chunks at https://github.com/abhiTronix/vidgear-docs-additionals for StreamGear and Stabilizer streams.
    • Added open-sourced "Tears of Steel" * project Mango Teaser video chunks.
    • Added open-sourced "Subspace Video Stabilization" http://web.cecs.pdx.edu/~fliu/project/subspace_stabilization/ video chunks.
    • Added support for DASH Video Thumbnail preview in Clappr within custom.js.
    • Added responsive clappr DASH player with bootstrap's embed-responsive.
    • Added new permalink icon and slugify to toc.
    • Added "back-to-top" button for easy navigation.
  • Helper:
    • New GitHub Mirror with latest Auto-built FFmpeg Static Binaries:
      • Replaced new GitHub Mirror abhiTronix/FFmpeg-Builds in helper.py
      • New CI maintained Auto-built FFmpeg Static Binaries.
      • Removed all 3rd-party and old links for better compatibility and Open-Source reliability.
      • Updated Related CI tests.
    • Added auto-font-scaling for create_blank_frame method.
    • Added c_name parameter to generate_webdata and download_webdata to specify class.
    • A more robust Implementation of Downloading Artifacts:
      • Added a custom HTTP TimeoutHTTPAdapter Adapter with a default timeout for all HTTP calls based on this GitHub comment.
      • Implemented http client and the send() method to ensure that the default timeout is used if a timeout argument isn't provided.
      • Implemented Requests sessionwith block to exit properly even if there are unhandled exceptions.
      • Add a retry strategy to custom TimeoutHTTPAdapter Adapter with max 3 retries and sleep(backoff_factor=1) between failed requests.
    • Added create_blank_frame method to create bland frames with suitable text.
  • [CI] Continuous Integration:
    • Added new fake frame generated for fake picamera class with numpy.
    • Added new create_bug parameter to fake picamera class for emulating various artificial bugs.
    • Added float/int instance check on time_delay for camgear and pigear.
    • Added EXIT_CODE to new timeout implementation for pytests to upload codecov report when no timeout.
    • Added auxiliary classes to fake picamera for facilitating the emulation.
    • Added new CI tests for PiGear Class for testing on all platforms.
    • Added shutdown() function to gracefully terminate WebGear_RTC API.
    • Added new coreutils brew dependency.
    • Added handler for variable check on exit and codecov upload.
    • Added is_running flag to WebGear_RTC to exit safely.
  • Setup:
    • New automated latest version retriever for packages:
      • Implemented new latest_version method to automatically retrieve latest version for packages.
      • Added Some Dependencies.
    • Added simplejpeg package for all platforms.
Updates/Improvements
  • Added exception for RunTimeErrors in NetGear CI tests.
  • WriteGear: Critical file write access checking method:
    • Added new check_WriteAccess Helper method.
    • Implemented a new robust algorithm to check if given directory has write-access.
    • Removed old behavior which gives irregular results.
  • Helper: Maintenance Updates
    • Added workaround for Python bug.
    • Added safe_mkdir to check_WriteAccess to automatically create non-existential parent folder in path.
    • Extended check_WriteAccess Patch to StreamGear.
    • Simplified check_WriteAccess to handle Windows envs easily.
    • Updated FFmpeg Static Download URL for WriteGear.
    • Implemented fallback option for auto-calculating bitrate from extracted audio sample-rate in validate_audio method.
  • Docs: General UI Updates
    • Updated Meta tags for og site and twitter cards.
    • Replaced Custom dark theme toggle with mkdocs-material's official Color palette toggle
    • Added example for external audio input and creating segmented MP4 video in WriteGear FAQ.
    • Added example for YouTube streaming with WriteGear.
    • Removed custom dark-material.js and header.html files from theme.
    • Added blogpost link for detailed information on Stabilizer Working.
    • Updated mkdocs.yml and custom.css configuration.
    • Remove old hack to resize clappr DASH player with css.
    • Updated Admonitions.
    • Improved docs contexts.
    • Updated CSS for version-selector-button.
    • Adjusted files to match new themes.
    • Updated welcome-bot message for typos.
    • Removed redundant FAQs from NetGear Docs.
    • Updated Assets Images.
    • Updated spacing.
  • CI:
    • Removed unused github.ref from yaml.
    • Updated OpenCV Bash Script for Linux envs.
    • Added timeout-minutes flag to github-actions workflow.
    • Added timeout flag to pytest.
    • Replaced Threaded Gears with OpenCV VideoCapture API.
    • Moved files and Removed redundant code.
    • Replaced grayscale frames with color frames for WebGear tests.
    • Updated pytest timeout value to 15mins.
    • Removed aiortc automated install on Windows platform within setup.py.
    • Added new timeout logic to continue to run on external timeout for GitHub Actions Workflows.
    • Removed unreliable old timeout solution from WebGear_RTC.
    • Removed timeout_decorator and asyncio_timeout dependencies for CI.
    • Removed WebGear_RTC API exception from codecov.
    • Implemented new fake picamera class to CI utils for emulating RPi Camera-Module Real-time capabilities.
    • Implemented new get_RTCPeer_payload method to receive WebGear_RTC peer payload.
    • Removed PiGear from Codecov exceptions.
    • Disable Frame Compression in few NetGear tests failing on frame matching.
    • Updated NetGear CI tests to support new attributes
    • Removed warnings and updated yaml
      • Added pytest.ini to address multiple warnings.
      • Updated azure workflow condition syntax.
    • Update mike settings for mkdocs versioning.
    • Updated codecov configurations.
    • Minor logging and docs updates.
    • Implemented pytest timeout for azure pipelines for macOS envs.
    • Added aiortc as external dependency in appveyor.yml.
    • Re-implemented WebGear_RTC improper offer-answer handshake in CI tests.
    • WebGear_RTC CI Updated with VideoTransformTrack to test stream play.
    • Implemented fake AttributeError for fake picamera class.
    • Updated PiGear CI tests to increment codecov.
    • Update Tests docs and other minor tweaks to increase overall coverage.
    • Enabled debugging and disabled exit 1 on error in azure pipeline.
    • Removed redundant benchmark tests.
  • Helper: Added missing RTSP URL scheme to is_valid_url method.
  • NetGear_Async: Added fix for uvloop only supporting python>=3.7 legacies.
  • Extended WebGear's Video-Handler scope to https.
  • CI: Remove all redundant 32-bit Tests from Appveyor:
  • Setup: Removed latest_version behavior from some packages.
  • NetGear_Async: Revised logic for handling uvloop for all platforms and legacies.
  • Setup: Updated logic to install uvloop-"v0.14.0" for python-3.6 legacies.
  • Removed any redundant code from webgear.
  • StreamGear:
    • Replaced Ordinary dict with Ordered Dict to use move_to_end method.
    • Moved external audio input to output parameters dict.
    • Added additional imports.
    • Updated docs to reflect changes.
  • Numerous Updates to Readme and mkdocs.yml.
  • Updated font to FONT_HERSHEY_SCRIPT_COMPLEX and enabled logging in create_blank_frame.
  • Separated channels for downloading and storing theme files for WebGear and WebGear_RTC APIs.
  • Removed logging condition to always inform user in a event of FFmpeg binary download failure.
  • WebGear_RTC:
    • Improved auto internal termination.
    • More Performance updates through setCodecPreferences.
    • Moved default Video RTC video launcher to __offer.
  • NetGear_Async: Added timeout to client in CI tests.
  • Reimplemented and updated changelog.md.
  • Updated code comments.
  • Setup: Updated keywords and classifiers.
  • Bumped codecov.
Breaking Updates/Changes
  • WriteGear will automatically switch video encoder to default if specified encoder not found.
  • WriteGear will throw RuntimeError if no suitable default encoder found!
  • Removed format specific OpenCV decoding and encoding support for NetGear.
  • Dropped support for compression_format, compression_param attributes from NetGear.
  • Non-existent parent folder in output_filename value will no longer be considered as invalid in StreamGear and WriteGear APIs.
  • None-type source parameter value is allowed for WebGear and NetGear_Async for defining custom sources.
Bug-fixes
  • CamGear: Fixed F821 undefined name 'queue' bug.
  • NetGear_Async: Fixed source parameter missing None as default value.
  • Fixed uvloops only supporting python>=3.7 in NetGear_Async.
  • Helper:
  • Docs:
    • Invalid site_url bug patched in mkdocs.yml
    • Remove redundant mike theme support and its files.
    • Fixed video not centered when DASH video in fullscreen mode with clappr.
    • Fixed Incompatible new mkdocs-docs theme.
    • Fixed missing hyperlinks.
  • CI:
    • Fixed NetGear Address bug
    • Fixed bugs related to termination in WebGear_RTC.
    • Fixed random CI test failures and code cleanup.
    • Fixed string formating bug in Helper.py.
    • Fixed F821 undefined name bugs in WebGear_RTC tests.
    • NetGear_Async Tests fixes.
    • Fixed F821 undefined name bugs.
    • Fixed typo bugs in main.py.
    • Fixed Relative import bug in PiGear.
    • Fixed regex bug in warning filter.
    • Fixed WebGear_RTC frozen threads on exit.
    • Fixed bugs in codecov bash uploader setting for azure pipelines.
    • Fixed False-positive picamera import due to improper sys.module settings.
    • Fixed Frozen Threads on exit in WebGear_RTC API.
    • Fixed deploy error in VidGear Docs Deployer workflow
    • Fixed low timeout bug.
    • Fixed bugs in PiGear tests.
    • Patched F821 undefined name bug.
  • StreamGear:
    • Fixed StreamGear throwing Picture size 0x0 is invalid bug with external audio.
    • Fixed default input framerate value getting discarded in Real-time Frame Mode.
    • Fixed internal list-formatting bug.
  • Fixed E999 SyntaxError bug in main.py.
  • Fixed Typo in bash script.
  • Fixed WebGear freeze on reloading bug.
  • Fixed anomalies in install_opencv bash script.
  • Helper: Bug Fixed in download_ffmpeg_binaries method.
  • Helper: Fixed OSError bug in check_WriteAccess method.
  • Helper: Fixed Input Audio stream bitrate test failing to detect audio-bitrate in certain videos with validate_audio method.
  • Fixed bugs in requests module's function arguments.
  • Fixed None-type stream bug in WebGear.
  • Fixed random crashes in WebGear.
  • Fixed numerous CI test bugs.
  • Fixed several typos.
Pull Requests
New Contributors

 

 

v0.2.0 (2021-01-01)

New Features
  • CamGear API:
    • Support for various Live-Video-Streaming services:
      • Added seamless support for live video streaming sites like Twitch, LiveStream, Dailymotion etc.
      • Implemented flexible framework around streamlink python library with easy control over parameters and quality.
      • Stream Mode can now automatically detects whether source belong to YouTube or elsewhere, and handles it with appropriate API.
    • Re-implemented YouTube URLs Handler:
      • Re-implemented CamGear's YouTube URLs Handler completely from scratch.
      • New Robust Logic to flexibly handing video and video-audio streams.
      • Intelligent stream selector for selecting best possible stream compatible with OpenCV.
      • Added support for selecting stream qualities and parameters.
      • Implemented new get_supported_quality helper method for handling specified qualities
      • Fixed Live-Stream URLs not supported by OpenCV's Videocapture and its FFmpeg.
    • Added additional STREAM_QUALITY and STREAM_PARAMS attributes.
  • ScreenGear API:
    • Multiple Backends Support:
      • Added new multiple backend support with new pyscreenshot python library.
      • Made pyscreenshot the default API for ScreenGear, replaces mss.
      • Added new backend parameter for this feature while retaining previous behavior.
      • Added native automated RGB to BGR conversion for default PIL backend.
      • Kept support for old mss for old compatibility and multi-screen support.
      • Added native dimensional support for multi-screen.
      • Added support all input from all multiple screens.
      • Updated ScreenGear Docs.
      • Updated ScreenGear CI tests.
  • StreamGear API:
    • Changed default behaviour to support complete video transcoding.
    • Added -livestream attribute to support live-streaming.
    • Added additional parameters for -livestream attribute functionality.
    • Updated StreamGear Tests.
    • Updated StreamGear docs.
  • Stabilizer Class:
    • New Robust Error Handling with Blank Frames:
      • Elegantly handles all crashes due to Empty/Blank/Dark frames.
      • Stabilizer throws Warning with this new behavior instead of crashing.
      • Updated CI test for this feature.
  • Docs:
    • Automated Docs Versioning:
      • Implemented Docs versioning through mike API.
      • Separate new workflow steps to handle different versions.
      • Updated docs deploy worflow to support release and dev builds.
      • Added automatic version extraction from github events.
      • Added version-select.js and version-select.css files.
    • Toggleable Dark-White Docs Support:
      • Toggle-button to easily switch dark, white and preferred theme.
      • New Updated Assets for dark backgrounds
      • New css, js files/content to implement this behavior.
      • New material icons for button.
      • Updated scheme to slate in mkdocs.yml.
    • New Theme and assets:
      • New purple theme with dark-purple accent color.
      • New images assets with updated transparent background.
      • Support for both dark and white theme.
      • Increased rebufferingGoal for dash videos.
      • New updated custom 404 page for docs.
    • Issue and PR automated-bots changes
      • New need_info.yml YAML Workflow.
      • New needs-more-info.yml Request-Info template.
      • Replaced Request-Info templates.
      • Improved PR and Issue welcome formatting.
    • Added custom HTML pages.
    • Added show_root_heading flag to disable headings in References.
    • Added new inserAfter function to version-select.js.
    • Adjusted hue for dark-theme for better contrast.
    • New usage examples and FAQs.
    • Added gitmoji for commits.
  • Continuous Integration:
    • Maintenance Updates:
      • Added support for new VIDGEAR_LOGFILE environment variable in Travis CI.
      • Added missing CI tests.
      • Added logging for helper functions.
    • Azure-Pipeline workflow for MacOS envs
      • Added Azure-Pipeline Workflow for testing MacOS environment.
      • Added codecov support.
    • GitHub Actions workflow for Linux envs
      • Added GitHub Action work-flow for testing Linux environment.
    • New YAML to implement GitHub Action workflow for python 3.6, 3.7, 3,8 & 3.9 matrices.
    • Added Upload coverage to Codecov GitHub Action workflow.
    • New codecov-bash uploader for Azure Pipelines.
  • Logging:
    • Added file support
      • Added VIDGEAR_LOGFILE environment variable to manually add file/dir path.
      • Reworked logger_handler() Helper methods (in asyncio too).
      • Added new formatter and Filehandler for handling logger files.
    • Added restore_levelnames auxiliary method for restoring logging levelnames.
  • Added auto version extraction from package version.py in setup.py.
Updates/Improvements
  • Added missing Lazy-pirate auto-reconnection support for Multi-Servers and Multi-Clients Mode in NetGear API.
  • Added new FFmpeg test path to Bash-Script and updated README broken links.
  • Asset Cleanup:
    • Removed all third-party javascripts from projects.
    • Linked all third-party javascript directly.
    • Cleaned up necessary code from CSS and JS files.
    • Removed any copyrighted material or links.
  • Rewritten Docs from scratch:
    • Improved complete docs formatting.
    • Simplified language for easier understanding.
    • Fixed mkdocstrings showing root headings.
    • Included all APIs methods to mkdocstrings docs.
    • Removed unnecessary information from docs.
    • Corrected Spelling and typos.
    • Fixed context and grammar.
    • Removed motivation.md.
    • Renamed many terms.
    • Fixed hyper-links.
    • Reformatted missing or improper information.
    • Fixed context and spellings in Docs files.
    • Simplified language for easy understanding.
    • Updated image sizes for better visibility.
  • Bash Script: Updated to Latest OpenCV Binaries version and related changes
  • Docs: Moved version-selector to header and changed default to alias.
  • Docs: Updated deploy_docs.yml for releasing dev, stable, and release versions.
  • Re-implemented overridden material theme.
  • Updated docs with all new additions and examples.
  • CamGear: CI Stream Mode test updated.
  • Updated ReadMe.md badges.
  • Updated CI tests.
  • Updated setup.py with new features.
  • Updated contributing.md and ReadMe.md.
  • Updated OpenCV version to 4.5.1-dev in bash scripts
  • Updated changelog.md.
  • Moved WebGear API to Streaming Gears.
  • Bumped Codecov.
  • UI changes to version-select.js
  • Docs: Retitle the versions and mkdocs.yml formatting updated.
  • Docs: Version Selector UI reworked and other minor changes.
Breaking Updates/Changes
  • y_tube parameter renamed as stream_mode in CamGear API!
  • Removed Travis support and travis.yml deleted.
Bug-fixes
  • Fixed StreamGear API Limited Segments Bug
  • Fixed Missing links in docs and bump up version.
  • CI: Fixed Appveyor need newer VM image to support Python 3.9.x matrix.
  • ScreenGear BugFix: Fixed Error Handling and updated CI Tests.
  • Fixed improper mkdocs.yml variables.
  • Fixed GStreamer plugin support in bash scripts.
  • Fixed typos in YAMLs and docs.
  • Docs: Fixed Docs Deployer YAML bug for CI envs.
  • Fixed wrong import in YAML.
  • Fixed visible hyperlink on hover in dark-toggle button.
  • Docs: Deployer YAML bug fixed.
  • Docs YAML: issue jimporter/mike#33 patched and fixed fetch-depth=0.
  • Docs: version-select.js bug fixed.
  • Docs: UI Bugs Fixed.
  • CI: Codecov bugfixes.
  • Azure-Pipelines Codecov BugFixes.
  • Fixed version.json not detecting properly in version-select.js.
  • Fixed images not centered inside <figure> tag.
  • Fixed Asset Colors.
  • Fixed failing CI tests.
  • Fixed Several logging bugs.
Pull Requests

 

 

v0.1.9 (2020-08-31)

New Features
  • StreamGear API:
    • New API that automates transcoding workflow for generating Ultra-Low Latency, High-Quality, Dynamic & Adaptive Streaming Formats.
    • Implemented multi-platform , standalone, highly extensible and flexible wrapper around FFmpeg for generating chunked-encoded media segments of the media, and easily accessing almost all of its parameters.
    • API automatically transcodes videos/audio files & real-time frames into a sequence of multiple smaller chunks/segments and also creates a Manifest file.
    • Added initial support for MPEG-DASH (Dynamic Adaptive Streaming over HTTP, ISO/IEC 23009-1).
    • Constructed default behavior in StreamGear, for auto-creating a Primary Stream of same resolution and framerate as source.
    • Added TQDM progress bar in non-debugged output for visual representation of internal processes.
    • Implemented several internal methods for preprocessing FFmpeg and internal parameters for producing streams.
    • Several standalone internal checks to ensure robust performance.
    • New terminate() function to terminate StremGear Safely.
    • New StreamGear Dual Modes of Operation:
      • Implemented Single-Source and Real-time Frames like independent Transcoding Modes.
      • Linked -video_source attribute for activating these modes
      • Single-Source Mode, transcodes entire video/audio file (as opposed to frames by frame) into a sequence of multiple smaller segments for streaming
      • Real-time Frames Mode, directly transcodes video-frames (as opposed to a entire file), into a sequence of multiple smaller segments for streaming
      • Added separate functions, stream() for Real-time Frame Mode and transcode_source() for Single-Source Mode for easy transcoding.
      • Included auto-colorspace detection and RGB Mode like features (extracted from WriteGear), into StreamGear.
    • New StreamGear Parameters:
      • Developed several new parameters such as:
        • output: handles assets directory
        • formats: handles adaptive HTTP streaming format.
        • custom_ffmpeg: handles custom FFmpeg location.
        • stream_params: handles internal and FFmpeg parameter seamlessly.
        • logging: turns logging on or off.
      • New stream_params parameter allows us to exploit almost all FFmpeg parameters and flexibly change its internal settings, and seamlessly generating high-quality streams with its attributes:
        • -streams (list of dictionaries) for building additional streams with -resolution, -video_bitrate & -framerate like sub-attributes.
        • -audio for specifying external audio.
        • -video_source for specifying Single-Source Mode source.
        • -input_framerate for handling input framerate in Real-time Frames Mode.
        • -bpp attribute for handling bits-per-pixels used to auto-calculate video-bitrate.
        • -gop to manually specify GOP length.
        • -ffmpeg_download_path to handle custom FFmpeg download path on windows.
        • -clear_prev_assets to remove any previous copies of SteamGear Assets.
    • New StreamGear docs, MPEG-DASH demo, and recommended DASH players list:
      • Added new StreamGear docs, usage examples, parameters, references, new FAQs.
      • Added Several StreamGear usage examples w.r.t Mode of Operation.
      • Implemented Clappr based on Shaka-Player, as Demo Player.
      • Added Adaptive-dimensional behavior for Demo-player, purely in css.
      • Hosted StreamGear generated DASH chunks on GitHub and served with raw.githack.com.
      • Introduced variable quality level-selector plugin for Clapper Player.
      • Provide various required javascripts and implemented additional functionality for player in extra.js.
      • Recommended tested Online, Command-line and GUI Adaptive Stream players.
      • Implemented separate FFmpeg installation doc for StreamGear API.
      • Reduced rebufferingGoal for faster response.
    • New StreamGear CI tests:
      • Added IO and API initialization CI tests for its Modes.
      • Added various mode Streaming check CI tests.
  • NetGear_Async API:
    • Added new send_terminate_signal internal method.
    • Added WindowsSelectorEventLoopPolicy() for windows 3.8+ envs.
    • Moved Client auto-termination to separate method.
    • Implemented graceful termination with signal API on UNIX machines.
    • Added new timeout attribute for controlling Timeout in Connections.
    • Added missing termination optimizer (linger=0) flag.
    • Several ZMQ Optimizer Flags added to boost performance.
  • WriteGear API:
    • Added support for adding duplicate FFmpeg parameters to output_params:
      • Added new -clones attribute in output_params parameter for handing this behavior..
      • Support to pass FFmpeg parameters as list, while maintaining the exact order it was specified.
      • Built support for zmq.REQ/zmq.REP and zmq.PUB/zmq.SUB patterns in this mode.
      • Added new CI tests debugging this behavior.
      • Updated docs accordingly.
    • Added support for Networks URLs in Compression Mode:
      • output_filename parameter supports Networks URLs in compression modes only
      • Added automated handling of non path/file Networks URLs as input.
      • Implemented new is_valid_url helper method to easily validate assigned URLs value.
      • Validates whether the given URL value has scheme/protocol supported by assigned/installed ffmpeg or not.
      • WriteGear will throw ValueError if -output_filename is not supported.
      • Added related CI tests and docs.
    • Added disable_force_termination attribute in WriteGear to disable force-termination.
  • NetGear API:
    • Added option to completely disable Native Frame-Compression:
      • Checks if any Incorrect/Invalid value is assigned on compression_format attribute.
      • Completely disables Native Frame-Compression.
      • Updated docs accordingly.
  • CamGear API:
    • Added new and robust regex for identifying YouTube URLs.
    • Moved youtube_url_validator to Helper.
  • New helper.py methods:
    • Added validate_video function to validate video_source.
    • Added extract_time Extract time from give string value.
    • Added get_video_bitrate to calculate video birate from resolution, framerate, bits-per-pixels values.
    • Added delete_safe to safely delete files of given extension.
    • Added validate_audio to validate audio source.
    • Added new Helper CI tests.
      • Added new check_valid_mpd function to test MPD files validity.
      • Added mpegdash library to CI requirements.
  • Deployed New Docs Upgrades:
    • Added new assets like images, gifs, custom scripts, javascripts fonts etc. for achieving better visual graphics in docs.
    • Added clappr.min.js, dash-shaka-playback.js, clappr-level-selector.min.js third-party javascripts locally.
    • Extended Overview docs Hyperlinks to include all major sub-pages (such as Usage Examples, Reference, FAQs etc.).
    • Replaced GIF with interactive MPEG-DASH Video Example in Stabilizer Docs.
    • Added new pymdownx.keys to replace [Ctrl+C]/[⌘+C] formats.
    • Added new custom.css stylescripts variables for fluid animations in docs.
    • Overridden announce bar and added donation button.
    • Lossless WEBP compressed all PNG assets for faster loading.
    • Enabled lazy-loading for GIFS and Images for performance.
    • Reimplemented Admonitions contexts and added new ones.
    • Added StreamGear and its different modes Docs Assets.
    • Added patch for images & unicodes for PiP flavored markdown in setup.py.
  • Added Request Info and Welcome GitHub Apps to automate PR and issue workflow
    • Added new config.yml for customizations.
    • Added various suitable configurations.
  • Added new -clones attribute to handle FFmpeg parameter clones in StreamGear and WriteGear API.
  • Added new Video-only and Audio-Only sources in bash script.
  • Added new paths in bash script for storing StreamGear & WriteGear assets temporarily.
Updates/Improvements
  • Added patch for NotImplementedError in NetGear_Async API on Windows 3.8+ envs.
  • Check for valid output file extension according to format selected in StreamGear.
  • Completed migration to travis.com.
  • Created new temp_write temp directory for WriteGear Assets in bash script.
  • Deleted old Redundant assets and added new ones.
  • Employed isort library to sort and group imports in Vidgear APIs.
  • Enabled exception for list, tuple, int, float in WriteGear API's output_params dict.
  • Enabled missing support for frame-compression in its primary Receive Mode.
  • Enforced pixel formats for streams.
  • Improved check for valid system path detection in WriteGear API.
  • Overrided pytest-asyncio fixture in NetGear_Async API.
  • Quoted Gear Headline for understanding each gear easily.
  • Re-Positioned Gear's banner images in overview for better readability.
  • Reduced redundant try-except blocks in NetGear Async.
  • Reformatted and Simplified Docs context.
  • Reimplemented return_testvideo_path CI function with variable streams.
  • Reimplemented skip_loop in NetGear_Async to fix asyncio.CancelledError.
  • Reimplemented buggy audio handler in StreamGear.
  • Reimplemented images with <figure> and <figurecaption> like tags.
  • Removed Python < 3.8 condition from all CI tests.
  • Removed or Grouped redundant code for increasing codecov.
  • Removed redundant code and simplified algorithmic complexities in Gears.
  • Replaced ;nbsp with ;thinsp and ;emsp.
  • Replaced IOError with more reliable RuntimeError in StreamGear Pipelines.
  • Replaced del with pop in dicts.
  • Replaced all Netgear CI tests with more reliable try-except-final blocks.
  • Replaced simple lists with pymdownx.tasklist.
  • Replaced subprocess call() with run() for better error handling in execute_ffmpeg_cmd function.
  • Resized over-sized docs images.
  • Simplified delete_safe Helper function.
  • Simplified default audio-bitrate logic in StreamGear
  • Updated CI tests and cleared redundant code from NetGear_Async API.
  • Updated CI with new tests and Bumped Codecov.
  • Updated Issue and PR templates.
  • Updated Licenses for new files and shrink images dimensions.
  • Updated Missing Helpful tips and increased logging.
  • Updated PR guidelines for more clarity.
  • Updated WebGear examples addresses from 0.0.0.0 to localhost.
  • Updated WriteGear and StreamGear CI tests for not supporting temp directory.
  • Updated README.md and changelog.md with new changes.
  • Updated check_output and added force_retrieve_stderr support to **kwargs to extract stderr output even on FFmpeg error.
  • Updated dicts2args to support internal repeated coreX FFmpeg parameters for StreamGear.
  • Updated mkdocs.yml, changelog.md and README.md with latest changes.
  • Updated validate_audio Helper function will now retrieve audio-bitrate for validation.
  • Updated buggy mpegdash dependency with custom dev fork for Windows machines.
  • Updated core parameters for audio handling.
  • Updated logging for debugging selected eventloops in NetGear_Async API.
  • Updated termination linger to zero at Server's end.
Breaking Updates/Changes
  • Changed Webgear API default address to localhost for cross-compatibility between different platforms.
  • In Netgear_Async API, source value can now be NoneType for a custom frame-generator at Server-end only.
  • Temp (such as /tmp in linux) is now not a valid directory for WriteGear & StreamGear API outputs.
  • Moved vidgear docs assets (i.e images, gifs, javascripts and stylescripts) to override directory.
Bug-fixes
  • Added workaround for system path not handle correctly.
  • Fixed Bug: URL Audio format not being handled properly.
  • Fixed Critical Bug in NetGear_Async throwing ValueError with None-type Source.
  • Fixed Critical StreamGear Bug: FFmpeg pipeline terminating prematurely in Single-Source Mode.
  • Fixed Critical external audio handler bug: moved audio-input to input_parameters.
  • Fixed Frozen-threads bug in CI tests.
  • Fixed Mkdocs only accepting Relative paths.
  • Fixed OSError in WriteGear's compression mode.
  • Fixed StreamGear CI bugs for Windows and CI envs.
  • Fixed Typos and Indentation bugs in NetGear API.
  • Fixed ZMQ throwing error on termination if all max-tries exhausted.
  • Fixed NameError bug in NetGear API and CI tests.
  • Fixed TimeoutError bug in NetGear_Async CI tests.
  • Fixed get_valid_ffmpeg_path throwing TypeError with non-string values.
  • Fixed broken links in docs.
  • Fixed critical duplicate logging bug.
  • Fixed default gop value not handle correctly.
  • Fixed handling of incorrect paths detection.
  • Fixed incorrect definitions in NetGear_Async.
  • Fixed left-over attribute bug in WriteGear.
  • Fixed logic and indentation bugs in CI tests.
  • Fixed logic for handling output parameters in WriteGear API.
  • Fixed missing definitions and logic bug in StreamGear.
  • Fixed missing import and incorrect CI definitions.
  • Fixed missing source dimensions from extract_resolutions output in StreamGear API.
  • Fixed missing support for compression parameters in Multi-Clients Mode.
  • Fixed round off error in FPS.
  • Fixed several CI bugs and updated extract_resolutions method.
  • Fixed several bugs from CI Bidirectional Mode tests.
  • Fixed several typos in docs usage examples.
  • Fixed various AttributeError with wrong attribute names and definition in CI Helper functions.
  • Fixed wrong and missing definitions in docs.
  • Fixed wrong logic for extracting OpenCV frames.
  • Fixed wrong type bug in StreamGear API.
  • Fixed wrong type error bug in WriteGear API.
  • Fixed wrong variable assignments bug in WriteGear API.
  • Fixes to CLI tests and missing docs imports.
  • Many minor typos and wrong definitions.
Pull Requests

 

 

v0.1.8 (2020-06-12)

New Features
  • NetGear API:
    • Multiple Clients support:
      • Implemented support for handling any number of Clients simultaneously with a single Server in this mode.
      • Added new multiclient_mode attribute for enabling this mode easily.
      • Built support for zmq.REQ/zmq.REP and zmq.PUB/zmq.SUB patterns in this mode.
      • Implemented ability to receive data from all Client(s) along with frames with zmq.REQ/zmq.REP pattern only.
      • Updated related CI tests
    • Support for robust Lazy Pirate pattern(auto-reconnection) in NetGear API for both server and client ends:
      • Implemented a algorithm where NetGear rather than doing a blocking receive, will now:
        • Poll the socket and receive from it only when it's sure a reply has arrived.
        • Attempt to reconnect, if no reply has arrived within a timeout period.
        • Abandon the connection if there is still no reply after several requests.
      • Implemented its default support for REQ/REP and PAIR messaging patterns internally.
      • Added new max_retries and request_timeout(in seconds) for handling polling.
      • Added DONTWAIT flag for interruption-free data receiving.
      • Both Server and Client can now reconnect even after a premature termination.
    • Performance Updates:
      • Added default Frame Compression support for Bidirectional frame transmission in Bidirectional mode.
      • Added support for Reducer() function in Helper.py to aid reducing frame-size on-the-go for more performance.
      • Added small delay in recv() function at client's end to reduce system load.
      • Reworked and Optimized NetGear termination, and also removed/changed redundant definitions and flags.
  • Docs: Migration to Mkdocs
    • Implemented a beautiful, static documentation site based on MkDocs which will then be hosted on GitHub Pages.
    • Crafted base mkdocs with third-party elegant & simplistic mkdocs-material theme.
    • Implemented new mkdocs.yml for Mkdocs with relevant data.
    • Added new docs folder to handle markdown pages and its assets.
    • Added new Markdown pages(.md) to docs folder, which are carefully crafted documents - [x] based on previous Wiki's docs, and some completely new additions.
    • Added navigation under tabs for easily accessing each document.
    • New Assets:
    • Added Required Plugins and Extensions:
      • Added support for all pymarkdown-extensions.
      • Added support for some important admonition, attr_list, codehilite, def_list, footnotes, meta, and toc like Mkdocs extensions.
      • Enabled search, minify and git-revision-date-localized plugins support.
      • Added various VidGear's social links to yaml.
      • Added support for en (English) language.
    • Auto-Build API Reference with mkdocstrings:
      • Added support for mkdocstrings plugin for auto-building each VidGear's API references.
      • Added python handler for parsing python source-code to mkdocstrings.
    • Auto-Deploy Docs with GitHub Actions:
      • Implemented Automated Docs Deployment on gh-pages through GitHub Actions workflow.
      • Added new workflow yaml with minimal configuration for automated docs deployment.
      • Added all required python dependencies and environment for this workflow.
      • Added master branch on Ubuntu machine to build matrix.
Updates/Improvements
  • Added in-built support for bidirectional frames(NDarray) transfer in Bidirectional mode.
  • Added support for User-Defined compression params in Bidirectional frames transfer.
  • Added workaround for address already in use bug at client's end.
  • Unified Bidirectional and Multi-Clients mode for client's return data transmission.
  • Replaced ValueError with more suitable RuntimeError.
  • Updated logging for better readability.
  • Added CI test for Multi-Clients mode.
  • Reformatted and grouped imports in VidGear.
  • Added Reducer Helper function CI test.
  • Added Reliability tests for both Server and Client end.
  • Disabled reliable reconnection for Multi-Clients mode.
  • Replaced os.devnull with suprocess's inbuilt function.
  • Updated README.md, Issue and PR templates with new information and updates.
  • Moved changelog.md to /docs and updated contribution guidelines.
  • Improved source-code docs for compatibility with mkdocstrings.
  • Added additional dependency mkdocs-exclude, for excluding files from Mkdocs builds.
  • Updated license and compressed images/diagrams.
  • Added new CI tests and Bumped Codecov.
  • Changed YouTube video URL for CI tests to Creative Commons(CC) video.
  • Removed redundant code.
Breaking Updates/Changes
  • VidGear Docs moved to GitHub Pages, Now Available at https://abhitronix.github.io/vidgear.
  • Removed filter attribute from options parameter in NetGear API.
  • Removed force_terminate parameter support from NetGear API.
  • Disabled additional data of datatype numpy.ndarray for Server end in Bidirectional Mode.
Bug-fixes
  • Fixed 'NoneType' object is not subscriptable bug.
  • Fixed bugs related to delayed termination in NetGear API.
  • Reduced default request_timeout value to 4 and also lowered cut-off limit for the same.
  • Removed redundant ZMQ context termination and similar variables.
  • Added missing VidGear installation in workflow.
  • Excluded conflicting assets README.md from Mkdocs builds.
  • Fixed pattern value check bypassed if wrong value is assigned.
  • Fixed incorrect handling of additional data transferred in synchronous mode at both Server and Client end.
  • Replaced Netgear CI test with more reliable try-except-final blocks.
  • Updated termination linger to zero at Server's end.
  • Fixed NameError bug in NetGear API.
  • Fixed missing support for compression parameters in Multi-Clients Mode.
  • Fixed ZMQ throwing error on termination if all max-tries exhausted.
  • Enabled missing support for frame compression in its primary receive mode.
  • Fixed several bugs from CI Bidirectional Mode tests.
  • Removed or Grouped redundant code for increasing codecov.
  • Fixed Mkdocs only accepting Relative paths.
  • Fixed broken links in docs.
  • Fixed round off error in FPS.
  • Many small typos and bugs fixes.
Pull Requests

 

 

v0.1.7 (2020-04-29)

New Features
  • WebGear API:
    • Added a robust Live Video Server API that can transfer live video frames to any web browser on the network in real-time.
    • Implemented a flexible asyncio wrapper around starlette ASGI Application Server.
    • Added seamless access to various starlette's Response classes, Routing tables, Static Files, Template engine(with Jinja2), etc.
    • Added a special internal access to VideoGear API and all its parameters.
    • Implemented a new Auto-Generation Work-flow to generate/download & thereby validate WebGear API data files from its GitHub server automatically.
    • Added on-the-go dictionary parameter in WebGear to tweak performance, Route Tables and other internal properties easily.
    • Added new simple & elegant default Bootstrap Cover Template for WebGear Server.
    • Added __main__.py to directly run WebGear Server through the terminal.
    • Added new gif and related docs for WebGear API.
    • Added and Updated various CI tests for this API.
  • NetGear_Async API:
    • Designed NetGear_Async asynchronous network API built upon ZeroMQ's asyncio API.
    • Implemented support for state-of-the-art asyncio event loop uvloop at its backend.
    • Achieved Unmatchable high-speed and lag-free video streaming over the network with minimal resource constraint.
    • Added exclusive internal wrapper around VideoGear API for this API.
    • Implemented complete server-client handling and options to use variable protocols/patterns for this API.
    • Implemented support for all four ZeroMQ messaging patterns: i.e zmq.PAIR, zmq.REQ/zmq.REP, zmq.PUB/zmq.SUB, and zmq.PUSH/zmq.PULL.
    • Implemented initial support for tcp and ipc protocols.
    • Added new Coverage CI tests for NetGear_Async Network Gear.
    • Added new Benchmark tests for benchmarking NetGear_Async against NetGear.
  • Asynchronous Enhancements:
    • Added asyncio package to for handling asynchronous APIs.
    • Moved WebGear API(webgear.py) to asyncio and created separate asyncio helper.py for it.
    • Various Performance tweaks for Asyncio APIs with concurrency within a single thread.
    • Moved __main__.py to asyncio for easier access to WebGear API through the terminal.
    • Updated setup.py with new dependencies and separated asyncio dependencies.
  • General Enhancements:
    • Added new highly-precise Threaded FPS class for accurate benchmarking with time.perf_counter python module.
    • Added a new Gitter community channel.
    • Added a new Reducer function to reduce the frame size on-the-go.
    • Add Flake8 tests to Travis CI to find undefined names. (PR by @cclauss)
    • Added a new unified logging handler helper function for vidgear.
Updates/Improvements
  • Re-implemented and simplified logic for NetGear Async server-end.
  • Added new dependencies for upcoming asyncio updates to setup.py.
  • Added retry function and replaced wget with curl for Linux test envs.
  • Bumped OpenCV to latest 4.2.0-dev for Linux test envs.
  • Updated YAML files to reflect new changes to different CI envs.
  • Separated each API logger with a common helper method to avoid multiple copies.
  • Limited Importing OpenCV API version check's scope to helper.py only.
  • Implemented case for incorrect color_space value in ScreenGear API.
  • Removed old conflicting logging formatter with a common method and expanded logging.
  • Improved and added shutdown function for safely stopping frame producer threads in WebGear API.
  • Re-implemented and simplified all CI tests with maximum code-coverage in mind.
  • Replaced old mkdir function with new mkdir_safe helper function for creating directories safely.
  • Updated ReadMe.md with updated diagrams, gifs and information.
  • Improve, structured and Simplified the Contribution Guidelines.
  • Bundled CI requirements in a single command.(Suggested by @cclauss)
  • Replaced line endings CRLF with LF endings.
  • Added dos2unix for Travis OSX envs.
  • Bumped Codecov to maximum.
Breaking Updates/Changes
  • Dropped support for Python 3.5 and below legacies. (See issue #99)
  • Dropped and replaced Python 3.5 matrices with new Python 3.8 matrices in all CI environments.
  • Implemented PEP-8 Styled Black formatting throughout the source-code.
  • Limited protocols support to tcp and ipc only, in NetGear API.
Bug-fixes
  • Fixed Major NetGear_Async bug where __address and __port are not set in async mode.(PR by @otter-in-a-suit)
  • Fixed Major PiGear Color-space Conversion logic bug.
  • Workaround for CAP_IMAGES error in YouTube Mode.
  • Replaced incorrect terminate() with join() in PiGear.
  • Removed uvloop for windows as still NOT yet supported.
  • Refactored Asynchronous Package name async to asyncio, since it is used as Keyword in python>=3.7 (raises SyntaxError).
  • Fixed unfinished close of event loops bug in WebGear API.
  • Fixed NameError in helper.py.
  • Added fix for OpenCV installer failure on Linux test envs.
  • Fixed undefined NameError in helper.py context. (@cclauss)
  • Fixed incorrect logic while pulling frames from ScreenGear API.
  • Fixed missing functions in __main__.py.
  • Fixed Typos and definitions in docs.
  • Added missing camera_num parameter to VideoGear.
  • Added OpenSSL's [SSL: CERTIFICATE_VERIFY_FAILED] bug workaround for macOS envs.
  • Removed download_url meta from setup.py.
  • Removed PiGear from CI completely due to hardware emulation limitation.
  • Removed VideoCapture benchmark tests for macOS envs.
  • Removed trivial __main__.py from codecov.
  • Removed several redundant try-catch loops.
  • Renamed youtube_url_validation as youtube_url_validator.
  • Several minor wrong/duplicate variable definitions and various bugs fixed.
  • Fixed, Improved & removed many Redundant CI tests for various APIs.
Pull Requests
New Contributors

 

 

v0.1.6 (2020-01-01)

New Features
  • NetGear API:
    • Added powerful ZMQ Authentication & Data Encryption features for NetGear API:
      • Added exclusive secure_mode param for enabling it.
      • Added support for two most powerful Stonehouse & Ironhouse ZMQ security mechanisms.
      • Added smart auth-certificates/key generation and validation features.
    • Implemented Robust Multi-Servers support for NetGear API:
      • Enables Multiple Servers messaging support with a single client.
      • Added exclusive multiserver_mode param for enabling it.
      • Added support for REQ/REP & PUB/SUB patterns for this mode.
      • Added ability to send additional data of any datatype along with the frame in realtime in this mode.
    • Introducing exclusive Bidirectional Mode for bidirectional data transmission:
      • Added new return_data parameter to recv() function.
      • Added new bidirectional_mode attribute for enabling this mode.
      • Added support for PAIR & REQ/REP patterns for this mode
      • Added support for sending data of any python datatype.
      • Added support for message parameter for non-exclusive primary modes for this mode.
    • Implemented compression support with on-the-fly flexible frame encoding for the Server-end:
      • Added initial support for JPEG, PNG & BMP encoding formats .
      • Added exclusive options attribute compression_format & compression_param to tweak this feature.
      • Client-end will now decode frame automatically based on the encoding as well as support decoding flags.
    • Added force_terminate attribute flag for handling force socket termination at the Server-end if there's latency in the network.
    • Implemented new Publish/Subscribe(zmq.PUB/zmq.SUB) pattern for seamless Live Streaming in NetGear API.
  • PiGear API:
    • Added new threaded internal timing function for PiGear to handle any hardware failures/frozen threads.
    • PiGear will not exit safely with SystemError if Picamera ribbon cable is pulled out to save resources.
    • Added support for new user-defined HWFAILURE_TIMEOUT options attribute to alter timeout.
  • VideoGear API:
    • Added framerate global variable and removed redundant function.
    • Added CROP_N_ZOOM attribute in Videogear API for supporting Crop and Zoom stabilizer feature.
  • WriteGear API:
    • Added new execute_ffmpeg_cmd function to pass a custom command to its FFmpeg pipeline.
  • Stabilizer class:
    • Added new Crop and Zoom feature.
      • Added crop_n_zoom param for enabling this feature.
    • Updated docs.
  • CI & Tests updates:
    • Replaced python 3.5 matrices with latest python 3.8 matrices in Linux environment.
    • Added full support for Codecov in all CI environments.
    • Updated OpenCV to v4.2.0-pre(master branch).
    • Added various Netgear API tests.
    • Added initial Screengear API test.
    • More test RTSP feeds added with better error handling in CamGear network test.
    • Added tests for ZMQ authentication certificate generation.
    • Added badge and Minor doc updates.
  • Added VidGear's official native support for MacOS environments.
Updates/Improvements
  • Replace print logging commands with python's logging module completely.
  • Implemented encapsulation for class functions and variables on all gears.
  • Updated support for screen casting from multiple/all monitors in ScreenGear API.
  • Updated ScreenGear API to use Threaded Queue Mode by default, thereby removed redundant THREADED_QUEUE_MODE param.
  • Updated bash script path to download test dataset in $TMPDIR rather than $HOME directory for downloading testdata.
  • Implemented better error handling of colorspace in various videocapture APIs.
  • Updated bash scripts, Moved FFmpeg static binaries to github.com.
  • Updated bash scripts, Added additional flag to support un-secure apt sources.
  • CamGear API will now throw RuntimeError if source provided is invalid.
  • Updated threaded Queue mode in CamGear API for more robust performance.
  • Added new camera_num to support multiple Picameras.
  • Moved thread exceptions to the main thread and then re-raised.
  • Added alternate github mirror for FFmpeg static binaries auto-installation on windows oses.
  • Added colorlog python module for presentable colored logging.
  • Replaced traceback with sys.exc_info.
  • Overall APIs Code and Docs optimizations.
  • Updated Code Readability and Wiki Docs.
  • Updated ReadMe & Changelog with the latest changes.
  • Updated Travis CI Tests with support for macOS environment.
  • Reformatted & implemented necessary MacOS related changes and dependencies in travis.yml.
Breaking Updates/Changes
  • Python 2.7 legacy support dropped completely.
  • Source-code Relicensed to Apache 2.0 License.
  • Python 3+ are only supported legacies for installing v0.1.6 and above.
  • Python 2.7 and 3.4 legacies support dropped from CI tests.
Bug-fixes
  • Reimplemented Pub/Sub pattern for smoother performance on various networks.
  • Fixed Assertion error in CamGear API during colorspace manipulation.
  • Fixed random freezing in Secure Mode and several related performance updates
  • Fixed multiserver_mode not working properly over some networks.
  • Fixed assigned Port address ignored bug (commit 073bca1).
  • Fixed several wrong definition bugs from NetGear API(commit 8f7153c).
  • Fixed unreliable dataset video URL(rehosted file on github.com).
  • Disabled overwrite_cert for client-end in NetGear API.
  • Disabled Universal Python wheel builds in setup.cfgfile.
  • Removed duplicate code to import MSS(@BoboTiG) from ScreenGear API.
  • Eliminated unused redundant code blocks from library.
  • Fixed Code indentation in setup.py and updated new release information.
  • Fixed code definitions & Typos.
  • Fixed several bugs related to secure_mode & multiserver_mode Modes.
  • Fixed various macOS environment bugs.
Pull Requests
New Contributors

 

 

v0.1.5 (2019-07-24)

New Features
  • Added new ScreenGear API, supports Live ScreenCasting.
  • Added new NetGear API, aids real-time frame transfer through messaging(ZmQ) over network.
  • Added new new Stabilizer Class, for minimum latency Video Stabilization with OpenCV.
  • Added Option to use API's standalone.
  • Added Option to use VideoGear API as internal wrapper around Stabilizer Class.
  • Added new parameter stabilize to API, to enable or disable Video Stabilization.
  • Added support for **option dict attributes to update VidGear's video stabilizer parameters directly.
  • Added brand new logo and functional block diagram (.svg) in readme.md
  • Added new pictures and GIFs for improving readme.md readability
  • Added new contributing.md and changelog.md for reference.
  • Added collections.deque import in Threaded Queue Mode for performance consideration
  • Added new install_opencv.sh bash scripts for Travis cli, to handle OpenCV installation.
  • Added new Project Issue & PR Templates
  • Added new Sponsor Button(FUNDING.yml)
Updates/Improvements
  • Updated New dependencies: mss, pyzmq and rejected redundant ones.
  • Revamped and refreshed look for readme.md and added new badges.
  • Updated Releases Documentation completely.
  • Updated CI tests for new changes
  • Updated Code Documentation.
  • Updated bash scripts and removed redundant information
  • Updated Youtube video URL in tests
  • Completely Reformatted and Updated Wiki Docs with new changes.
Breaking Updates/Changes
  • Implemented experimental Threaded Queue Mode(a.k.a Blocking Mode) for fast, synchronized, error-free multi-threading.
  • Renamed bash script pre-install.sh to prepare_dataset.sh - [x] downloads opensourced test datasets and static FFmpeg binaries for debugging.
  • Changed script folder location to bash/script.
  • Python 3.4 removed from Travis CI tests.
Bug-fixes
  • Temporarily fixed Travis CI bug: Replaced opencv-contrib-python with OpenCV built from scratch as dependency.
  • Fixed CI Timeout Bug: Disable Threaded Queue Mode for CI Tests
  • Fixes** sys.stderr.close() throws ValueError bug: Replaced sys.close() with DEVNULL.close()
  • Fixed Youtube Live Stream bug that return NonType frames in CamGear API.
  • Fixed NoneType frames bug in PiGear class on initialization.
  • Fixed Wrong function definitions
  • Removed /xe2 unicode bug from Stabilizer class.
  • Fixed **output_params KeyError bug in WriteGear API
  • Fixed subprocess not closing properly on exit in WriteGear API.
  • Fixed bugs in ScreenGear: Non-negative monitor values
  • Fixed missing import, typos, wrong variable definitions
  • Removed redundant hack from setup.py
  • Fixed Minor YouTube playback Test CI Bug
  • Fixed new Twitter Intent
  • Fixed bug in bash script that not working properly due to changes at server end.
Pull Requests

 

 

v0.1.4 (2019-05-11)

New Features
  • Added new WriteGear API: for enabling lossless video encoding and compression(built around FFmpeg and OpenCV Video Writer)
  • Added YouTube Mode for direct Video Pipelining from YouTube in CamGear API
  • Added new y_tube to access YouTube Mode in CamGear API.
  • Added flexible Output file Compression control capabilities in compression-mode(WriteGear).
  • Added -output_dimensions special parameter to WriteGear API.
  • Added new helper.py to handle special helper functions.
  • Added feature to auto-download and configure FFmpeg Static binaries(if not found) on Windows platforms.
  • Added -input_framerate special parameter to WriteGear class to change/control output constant framerate in compression mode(WriteGear).
  • Added new Direct Video colorspace Conversion capabilities in CamGear and PiGear API.
  • Added new framerate class variable for CamGear API, to retrieve input framerate.
  • Added new parameter backend - [x] changes the backend of CamGear's API
  • Added automatic required prerequisites installation ability, when installation from source.
  • Added Travis CI Complete Integration for Linux-based Testing for VidGear.
  • Added and configured travis.yml
  • Added Appveyor CI Complete Integration for Windows-based Testing in VidGear.
  • Added and configured new appveyor.yml
  • Added new bash script pre-install.sh to download opensourced test datasets and static FFmpeg binaries for debugging.
  • Added several new Tests(including Benchmarking Tests) for each API for testing with pytest.
  • Added license to code docs.
  • Added Say Thank you! badge to Readme.md.
Updates/Improvements
  • Removed redundant dependencies
  • Updated youtube-dl as a dependency, as required by pafy's backend.
  • Updated common VideoGear API with new parameter.
  • Update robust algorithm to auto-detect FFmpeg executables and test them, if failed, auto fallback to OpenCV's VideoWriter API.
  • Improved system previously installed OpenCV detection in setup.py.
  • Updated setup.py with hack to remove bullets from pypi description.
  • Updated Code Documentation
  • Reformatted & Modernized readme.md with new badges.
  • Reformatted and Updated Wiki Docs.
Breaking Updates/Changes
  • Removed -height and -width parameter from CamGear API.
  • Replaced dependency opencv-python with opencv-contrib-python completely
Bug-fixes
  • Windows Cross-Platform fix: replaced dependency os with platform in setup.py.
  • Fixed Bug: Arises due to spaces in input **options/**output_param dictionary keys.
  • Fixed several wrong/missing variable & function definitions.
  • Fixed code uneven indentation.
  • Fixed several typos in docs.
Pull Requests

 

 

v0.1.3 (2019-04-07)

Bug-fixes
  • Patched Major PiGear Bug: Incorrect import of PiRGBArray function in PiGear Class
  • Several Fixes for backend picamera API handling during frame capture(PiGear)
  • Fixed missing frame variable initialization.
  • Fixed minor typos
Pull Requests

 

 

v0.1.2 (2019-03-27)

New Features
  • Added easy Source manipulation feature in CamGear API, to control features like resolution, brightness, framerate etc.
  • Added new **option parameter to CamGear API, provides the flexibility to manipulate input stream directly.
  • Added new parameters for Camgear API for time delay and logging.
  • Added new Logo to readme.md
  • Added new Wiki Documentation.
Updates/Improvements
  • Reformatted readme.md.
  • Updated Wiki Docs with new changes.
Bug-fixes
  • Improved Error Handling in CamGear & PiGear API.
  • Fixed minor typos in docs.
Pull Requests

 

 

v0.1.1 (2019-03-24)

New Features
  • Release ViGear binaries on the Python Package Index (PyPI)
  • Added new and configured setup.py & setup.cfg
Bug-fixes
  • Fixed PEP bugs: added and configured properly __init__.py in each folder
  • Fixed PEP bugs: improved code Indentation
  • Fixed wrong imports: replaced distutils.core with setuptools
  • Fixed readme.md

 

 

v0.1.0 (2019-03-17)

New Features
  • Initial Release
  • Converted my imutils PR into Python Project.
  • Renamed conventions and reformatted complete source-code from scratch.
  • Added support for both python 2.7 and 3 legacies
  • Added new multi-threaded CamGear, PiGear, and VideoGear APIs
  • Added multi-platform compatibility
  • Added robust & flexible control over the source in PiGear API.
\ No newline at end of file diff --git a/dev/overrides/hooks/__pycache__/js_hook.cpython-39.pyc b/dev/overrides/hooks/__pycache__/js_hook.cpython-39.pyc index ea350d72..389b974f 100644 Binary files a/dev/overrides/hooks/__pycache__/js_hook.cpython-39.pyc and b/dev/overrides/hooks/__pycache__/js_hook.cpython-39.pyc differ diff --git a/dev/search/search_index.json b/dev/search/search_index.json index c4433795..6791c72c 100644 --- a/dev/search/search_index.json +++ b/dev/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Overview","text":""},{"location":"#introduction","title":"Introduction","text":"

VidGear is a cross-platform High-Performance Video-Processing Framework for building complex real-time media applications in python

VidGear provides an easy-to-use, highly extensible, Multi-Threaded + Asyncio API Framework on top of many state-of-the-art specialized libraries like OpenCV, FFmpeg, ZeroMQ, picamera2, starlette, yt_dlp, pyscreenshot, dxcam, aiortc and python-mss at its backend, and enable us to flexibly exploit their internal parameters and methods, while silently delivering robust error-handling and real-time performance \u26a1\ufe0f.

\"Write Less and Accomplish More\" \u2014 VidGear's Motto

VidGear focuses on simplicity, and thereby lets programmers and software developers to easily integrate and perform Complex Video Processing Tasks without going through hefty documentation and in just a few lines of code.

"},{"location":"#getting-started","title":"Getting Started","text":"

In case you're run into any problems, consult the Help section.

If you're just getting started with OpenCV-Python programming, then refer this FAQ \u27b6

"},{"location":"#gears","title":"Gears","text":"

VidGear is built with multiple Gears each with some unique functionality.

Each Gear is designed exclusively to handle/control/process different data-specific & device-specific video streams, network streams, and media encoders/decoders.

These Gears can be classified as follows:

"},{"location":"#videocapture-gears","title":"VideoCapture Gears","text":""},{"location":"#videowriter-gears","title":"VideoWriter Gears","text":""},{"location":"#streaming-gears","title":"Streaming Gears","text":""},{"location":"#network-gears","title":"Network Gears","text":"

"},{"location":"#contributions","title":"Contributions","text":"

Contributions are welcome, and greatly appreciated!

Please see our Contribution Guidelines for more details.

"},{"location":"#community-channel","title":"Community Channel","text":"

If you've come up with some new idea, or looking for the fastest way troubleshoot your problems. Please checkout our Gitter community channel \u27b6

"},{"location":"#become-a-stargazer","title":"Become a Stargazer","text":"

You can be a Stargazer by starring us on Github, it helps us a lot and you're making it easier for others to find & trust this library. Thanks!

"},{"location":"#donations","title":"Donations","text":"

VidGear is free and open source and will always remain so.

It is something I am doing with my own free time. But so much more needs to be done, and I need your help to do this. For just the price of a cup of coffee, you can make a difference

"},{"location":"#citation","title":"Citation","text":"

Here is a Bibtex entry you can use to cite this project in a publication:

@software{vidgear,\n  author       = {Abhishek Thakur and\n                  Zoe Papakipos and\n                  Christian Clauss and\n                  Christian Hollinger and\n                  Ian Max Andolina and\n                  Vincent Boivin and\n                  Kyle Ahn and\n                  freol35241 and\n                  Benjamin Lowe and\n                  Micka\u00ebl Schoentgen and\n                  Renaud Bouckenooghe and\n                  Ibtsam Ahmad},\n  title        = {abhiTronix/vidgear: VidGear Stable v0.3.2},\n  month        = sep,\n  year         = 2023,\n  publisher    = {Zenodo},\n  version      = {vidgear-0.3.2},\n  doi          = {10.5281/zenodo.8332548},\n  url          = {https://doi.org/10.5281/zenodo.8332548}\n}\n

"},{"location":"changelog/","title":"Release Notes","text":""},{"location":"changelog/#release-notes","title":"Release Notes","text":""},{"location":"changelog/#v032-2023-09-10","title":"v0.3.2 (2023-09-10)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v031-2023-07-22","title":"v0.3.1 (2023-07-22)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests "},{"location":"changelog/#v030-2023-01-26","title":"v0.3.0 (2023-01-26)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v026-2022-07-05","title":"v0.2.6 (2022-07-05)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v025-2021-02-11","title":"v0.2.5 (2021-02-11)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests "},{"location":"changelog/#v024-2021-12-05","title":"v0.2.4 (2021-12-05)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v023-2021-10-27","title":"v0.2.3 (2021-10-27)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v022-2021-09-02","title":"v0.2.2 (2021-09-02)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v021-2021-04-25","title":"v0.2.1 (2021-04-25)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v020-2021-01-01","title":"v0.2.0 (2021-01-01)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests "},{"location":"changelog/#v019-2020-08-31","title":"v0.1.9 (2020-08-31)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests "},{"location":"changelog/#v018-2020-06-12","title":"v0.1.8 (2020-06-12)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests "},{"location":"changelog/#v017-2020-04-29","title":"v0.1.7 (2020-04-29)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v016-2020-01-01","title":"v0.1.6 (2020-01-01)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v015-2019-07-24","title":"v0.1.5 (2019-07-24)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests "},{"location":"changelog/#v014-2019-05-11","title":"v0.1.4 (2019-05-11)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests "},{"location":"changelog/#v013-2019-04-07","title":"v0.1.3 (2019-04-07)","text":"Bug-fixes Pull Requests "},{"location":"changelog/#v012-2019-03-27","title":"v0.1.2 (2019-03-27)","text":"New Features Updates/Improvements Bug-fixes Pull Requests "},{"location":"changelog/#v011-2019-03-24","title":"v0.1.1 (2019-03-24)","text":"New Features Bug-fixes "},{"location":"changelog/#v010-2019-03-17","title":"v0.1.0 (2019-03-17)","text":"New Features "},{"location":"contribution/","title":"Contribution Overview","text":""},{"location":"contribution/#contribution-overview","title":"Contribution Overview","text":"

Contributions are welcome, We'd love your contribution to VidGear in order to fix bugs or to implement new features!

Contribution Opportunities

If you're looking for something to work on, check for the PR WELCOMED labeled issues on our GitHub Repository.

"},{"location":"contribution/#submission-guidelines","title":"Submission Guidelines","text":""},{"location":"contribution/#submission-contexts","title":"Submission Contexts","text":""},{"location":"contribution/#got-a-question-or-problem","title":"Got a question or problem?","text":"

For quick questions, please refrain from opening an issue, instead read our FAQ & Troubleshooting section or you can reach us on Gitter community channel.

"},{"location":"contribution/#found-a-typo","title":"Found a typo?","text":"

There's no need to contribute for some typos. Just reach us on Gitter \u27b6 community channel, We will correct them in (less than) no time.

"},{"location":"contribution/#found-a-bug","title":"Found a bug?","text":"

If you encountered a bug, you can help us by submitting an issue in our GitHub repository. Even better, you can submit a Pull Request(PR) with a fix, but make sure to read the guidelines \u27b6.

"},{"location":"contribution/#request-for-a-featureimprovement","title":"Request for a feature/improvement?","text":"Subscribe to Github Repository

You can subscribe our GitHub Repository to receive notifications through email for new pull requests, commits and issues that are created in VidGear. Learn more about it here \u27b6

You can request our GitHub Repository for a new feature/improvement based on the type of request:

Please submit an issue with a proposal template for your request to explain how it benefits everyone in the community.

"},{"location":"gears/","title":"Introduction","text":""},{"location":"gears/#introduction","title":"Introduction","text":"Gears: generalized workflow"},{"location":"gears/#gears-what-are-these","title":"Gears , What are these?","text":"

VidGear is built on Standalone APIs - also known as Gears , each with some unique functionality. Each Gears is designed exclusively to handle/control/process different data-specific & device-specific video streams, network streams, and media encoders/decoders.

Gears allows users to work with an inherently optimized, easy-to-use, extensible, and exposed API Framework on top of many state-of-the-art libraries, while silently delivering robust error handling and unmatched real-time performance.

"},{"location":"gears/#gears-classification","title":"Gears Classification","text":"

These Gears can be classified as follows:

"},{"location":"gears/#a-videocapture-gears","title":"A. VideoCapture Gears","text":"

Basic Function: Retrieves numpy.ndarray frames from various sources.

"},{"location":"gears/#b-videowriter-gears","title":"B. VideoWriter Gears","text":"

Basic Function: Writes numpy.ndarray frames to a video file or network stream.

"},{"location":"gears/#c-streaming-gears","title":"C. Streaming Gears","text":"

Basic Function: Transcodes/Broadcasts files and numpy.ndarray frames for streaming.

You can also use WriteGear for streaming with traditional protocols such as RTMP, RTSP/RTP.

"},{"location":"gears/#d-network-gears","title":"D. Network Gears","text":"

Basic Function: Sends/Receives data and numpy.ndarray frames over connected networks.

"},{"location":"help/","title":"Helping VidGear","text":""},{"location":"help/#helping-vidgear","title":"Helping VidGear","text":"

Liked VidGear? Would you like to help VidGear, other users, and the author?

There are many simple ways to help us:

"},{"location":"help/#star-vidgear-on-github","title":"Star VidGear on GitHub","text":"

You can star VidGear on GitHub:

It helps us a lot by making it easier for others to find & trust this library. Thanks!

"},{"location":"help/#help-others-with-issues-on-github","title":"Help others with issues on GitHub","text":"

You can see through any opened or pinned existing issues on our GitHub repository, and try helping others, wherever possible:

"},{"location":"help/#watch-the-github-repository","title":"Watch the GitHub repository","text":"

You can watch \ud83d\udc40 VidGear Activities on GitHub:

When you watch a repository, you will be notified of all conversations for that repository, including when someone creates a new issue, or pushes a new pull request.

You can try helping solving those issues, or give valuable feedback/review on new Pull Requests.

"},{"location":"help/#helping-author","title":"Helping Author","text":"

Donations help keep VidGear's development alive and motivate me (as author).

It is something I am doing with my own free time. But so much more needs to be done, and I need your help to do this. For just the price of a cup of coffee, you can make a difference

Thanks a million!

"},{"location":"help/#connect-with-author","title":"Connect with Author","text":"

You can connect with me, the author \ud83d\udc4b:

"},{"location":"installation/","title":"Installation Overview","text":""},{"location":"installation/#installation-overview","title":"Installation Overview","text":""},{"location":"installation/#supported-systems","title":"Supported Systems","text":"

VidGear is well-tested and supported on the following systems(but not limited to), with python 3.8+ and pip installed:

"},{"location":"installation/#supported-python-legacies","title":"Supported Python legacies","text":"

Depreciation Notice

Python-3.7 legacies support has been dropped from Vidgear.

Python 3.8+ are only supported legacies for installing Vidgear v0.3.1 and above.

"},{"location":"installation/#installation-methods","title":"Installation methods","text":""},{"location":"license/","title":"License","text":""},{"location":"license/#license","title":"License","text":"

This library is released under the Apache 2.0 License.

"},{"location":"license/#copyright-notice","title":"Copyright Notice","text":"
Copyright (c) 2019 Abhishek Thakur(@abhiTronix) <abhi.una12@gmail.com>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n
"},{"location":"switch_from_cv/","title":"Switching from OpenCV","text":""},{"location":"switch_from_cv/#switching-from-opencv-library","title":"Switching from OpenCV Library","text":"

Switching OpenCV with VidGear APIs is fairly painless process, and will just require changing a few lines in your python script.

This document is intended to software developers who want to migrate their python code from OpenCV Library to VidGear APIs.

Prior knowledge of Python or OpenCV won't be covered in this guide. Proficiency with OpenCV-Python (Python API for OpenCV) is a must in order understand this document.

If you're just getting started with OpenCV-Python programming, then refer this FAQ \u27b6

"},{"location":"switch_from_cv/#why-vidgear-is-better-than-opencv","title":"Why VidGear is better than OpenCV?","text":"

Learn more about OpenCV here \u27b6

VidGear employs OpenCV at its backend and enhances its existing capabilities even further by introducing many new state-of-the-art functionalities such as:

Vidgear offers all this at once while maintaining the same standard OpenCV-Python (Python API for OpenCV) coding syntax for all of its APIs, thereby making it even easier to implement complex real-time OpenCV applications in python code without changing things much.

"},{"location":"switch_from_cv/#switching-the-videocapture-apis","title":"Switching the VideoCapture APIs","text":"

Let's compare a bare-minimum python code for extracting frames out of any Webcam/USB-camera (connected at index 0), between OpenCV's VideoCapture Class and VidGear's CamGear VideoCapture API side-by-side:

CamGear API share the same syntax as other VideoCapture APIs, thereby you can easily switch to any of those APIs in a similar manner.

OpenCV VideoCapture ClassVidGear's CamGear API
# import required libraries\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = cv2.VideoCapture(0) \n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n      break\n\n\n    # {do something with the frame here}\n\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.release()\n
# import required libraries\nfrom vidgear.gears import CamGear\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = CamGear(source=0).start() \n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

and both syntax almost looks the same, easy, isn't it?

"},{"location":"switch_from_cv/#differences","title":"Differences","text":"

Let's breakdown a few noteworthy difference in both syntaxes:

Task OpenCV VideoCapture Class VidGear's CamGear API Initiating stream = cv2.VideoCapture(0) stream = CamGear(source=0).start() Reading frames (grabbed, frame) = stream.read() frame = stream.read() Checking empty frame if not grabbed: if frame is None: Terminating stream.release() stream.stop()

Now checkout other VideoCapture Gears \u27b6

"},{"location":"switch_from_cv/#switching-the-videowriter-api","title":"Switching the VideoWriter API","text":"

Let's extend previous bare-minimum python code and save those extracted frames to disk as a valid file, with OpenCV's VideoWriter Class and VidGear's WriteGear (with FFmpeg backend), compared side-to-side:

WriteGear API also provides backend for OpenCV's VideoWriter Class. More information here \u27b6

OpenCV VideoWriter ClassVidGear's WriteGear API
# import required libraries\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = cv2.VideoCapture(0) \n\n# Define the codec and create VideoWriter object with suitable output \n# filename for e.g. `Output.avi`\nfourcc = cv2.VideoWriter_fourcc(*'XVID') \nwriter = cv2.VideoWriter('output.avi', fourcc, 20.0, (640, 480)) \n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n      break\n\n\n    # {do something with the frame here}\n\n\n    # write frame to writer\n    writer.write(frame)\n\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.release()\n\n# safely close writer\nwriter.release() \n
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = CamGear(source=0).start() \n\n# Define WriteGear Object with suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output = 'Output.mp4') \n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if None-type\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

Noticed WriteGear's coding syntax looks similar but less complex?

"},{"location":"switch_from_cv/#differences_1","title":"Differences","text":"

Let's breakdown a few noteworthy difference in both syntaxes:

Task OpenCV VideoWriter Class VidGear's WriteGear API Initiating writer = cv2.VideoWriter('output.avi', cv2.VideoWriter_fourcc(*'XVID'), 20.0, (640, 480)) writer = WriteGear(output='Output.mp4') Writing frames writer.write(frame) writer.write(frame) Terminating writer.release() writer.close()

Now checkout more about WriteGear API here \u27b6

"},{"location":"bonus/TQM/","title":"Threaded Queue Mode","text":""},{"location":"bonus/TQM/#threaded-queue-mode","title":"Threaded Queue Mode","text":""},{"location":"bonus/TQM/#overview","title":"Overview","text":"Threaded-Queue-Mode: generalized timing diagram

Threaded Queue Mode is designed exclusively for VidGear's Videocapture Gears (namely CamGear, VideoGear) and few Network Gears (such as NetGear(Client's end)) for achieving high-performance, asynchronous, error-free video-frames handling.

Threaded-Queue-Mode is enabled by default, but can be disabled, only if extremely necessary.

Threaded-Queue-Mode is NOT required and thereby automatically disabled for Live feed such as Camera Devices/Modules, since .

"},{"location":"bonus/TQM/#what-does-threaded-queue-mode-exactly-do","title":"What does Threaded-Queue-Mode exactly do?","text":"

Threaded-Queue-Mode helps VidGear do the Threaded Video-Processing tasks in highly optimized, well-organized, and most competent way possible:

"},{"location":"bonus/TQM/#a-enables-multi-threading","title":"A. Enables Multi-Threading","text":"

In case you don't already know, OpenCV's' read() is a Blocking I/O function for reading and decoding the next video-frame, and consumes much of the I/O bound memory depending upon our video source properties & system hardware. This essentially means, the corresponding thread that reads data from it, is continuously blocked from retrieving the next frame. As a result, our python program appears slow and sluggish even without any type of computationally expensive image processing operations. This problem is far more severe on low memory SBCs like Raspberry Pis.

In Threaded-Queue-Mode, VidGear creates several Python Threads within one process to offload the frame-decoding task to a different thread. Thereby, VidGear is able to execute different Video I/O-bounded operations at the same time by overlapping there waiting times. Moreover, threads are managed by operating system itself and is capable of distributing them between available CPU cores efficiently. In this way, Threaded-Queue-Mode keeps on processing frames faster in the background without affecting by sluggishness in our main python program thread.

"},{"location":"bonus/TQM/#b-utilizes-fixed-size-queues","title":"B. Utilizes Fixed-Size Queues","text":"

Although Multi-threading is fast, easy, and efficient, it can lead to some serious undesired effects like frame-skipping, Global Interpreter Lock, race conditions, etc. This is because there is no isolation whatsoever in python threads, and in case there is any crash it will cause the whole process to crash. That's not all, the memory of the process is shared by different threads and that may result in random process crashes due to unwanted race conditions.

These problems are avoided in Threaded-Queue-Mode by utilizing Thread-Safe, Memory-Efficient, and Fixed-Size Queues (with approximately same O(1) performance in both directions), that isolates the frame-decoding thread from other parallel threads and provide synchronized access to incoming frames without any obstruction.

"},{"location":"bonus/TQM/#c-accelerates-frame-processing","title":"C. Accelerates Frame Processing","text":"

With queues, VidGear always maintains a fixed-length frames buffer in the memory and blocks the thread temporarily if the queue is full to avoid possible frame drops or otherwise pops out the frames synchronously without any obstructions. This significantly accelerates frame processing rate (and therefore our overall video processing pipeline) comes from dramatically reducing latency \u2014 since we don\u2019t have to wait for the read() method to finish reading and decoding a frame; instead, there is always a pre-decoded frame ready for us to process.

"},{"location":"bonus/TQM/#what-are-the-advantages-of-threaded-queue-mode","title":"What are the advantages of Threaded-Queue-Mode?","text":"

"},{"location":"bonus/TQM/#manually-disabling-threaded-queue-mode","title":"Manually disabling Threaded-Queue-Mode","text":"

To manually disable Threaded-Queue-Mode, VidGear provides THREADED_QUEUE_MODE boolean attribute for options dictionary parameter in respective VideoCapture APIs:

Important Warnings

Disabling Threaded-Queue-Mode may lead to Random Intermittent Bugs that can be quite difficult to discover. More insight can be found here \u27b6

THREADED_QUEUE_MODE (boolean): This attribute can be used to override Threaded-Queue-Mode mode to manually disable it:

options = {'THREADED_QUEUE_MODE': False} # to disable Threaded Queue Mode. \n

and you can pass it to options dictionary parameter of the respective API.

"},{"location":"bonus/colorspace_manipulation/","title":"Colorspace Manipulation","text":""},{"location":"bonus/colorspace_manipulation/#colorspace-manipulation-for-videocapture-gears","title":"Colorspace Manipulation for VideoCapture Gears","text":""},{"location":"bonus/colorspace_manipulation/#source-colorspace-manipulation","title":"Source ColorSpace manipulation","text":"

All VidGear's Videocapture Gears (namely CamGear, ScreenGear, VideoGear) and some Streaming Gears (namely WebGear, WebGear_RTC) and Network Gears (Client's end) - provides exclusive internal support for Source Color Space manipulation.

There are two ways to alter source colorspace:

"},{"location":"bonus/colorspace_manipulation/#using-colorspace-parameter","title":"Using colorspace parameter","text":"

Primarily, the safest way is by colorspace (string) parameter of the respective VideoCapture API, that can be used to easily alter the colorspace of the input source, during initialization. But on the downside, colorspace parameter value CANNOT be changed/altered at runtime.

All possible values for this parameter are discussed below \u27b6

"},{"location":"bonus/colorspace_manipulation/#using-color_space-global-variable","title":"Using color_space global variable","text":"

Alternatively, a more direct approach is by using color_space (integer) global variable the respective VideoCapture API, can be used for directly changing the source colorspace at runtime. It can be used in conjunction with colorspace parameter easily.

Supported Colorspace Conversions

Any conversion from default Source colorspace (i.e. BGR in case of OpenCV), to any other colorspace and vice-versa (use None to revert), is supported.

Important Information

Tip

It is advised to enable logging(logging = True) on the first run for easily identifying any runtime errors.

"},{"location":"bonus/colorspace_manipulation/#supported-colorspace-parameter-values","title":"Supported colorspace parameter values","text":"

All supported string values for colorspace parameter are as follows:

You can check all OpenCV Colorspace Conversion Codes here \u27b6.

Supported Conversion Values Description COLOR_BGR2BGRA BGR to BGRA COLOR_BGR2RGBA BGR to RGBA COLOR_BGR2RGB BGR to RGB backward conversions to RGB/BGR COLOR_BGR2GRAY BGR to GRAY COLOR_BGR2BGR565 BGR to BGR565 COLOR_BGR2BGR555 BGR to BGR555 COLOR_BGR2XYZ BGR to CIE XYZ COLOR_BGR2YCrCb BGR to luma-chroma (aka YCC) COLOR_BGR2HSV BGR to HSV (hue saturation value) COLOR_BGR2Lab BGR to CIE Lab COLOR_BGR2Luv BGR to CIE Luv COLOR_BGR2HLS BGR to HLS (hue lightness saturation) COLOR_BGR2HSV_FULL BGR to HSV_FULL COLOR_BGR2HLS_FULL BGR to HLS_FULL COLOR_BGR2YUV BGR to YUV COLOR_BGR2YUV_I420 BGR to YUV 4:2:0 family COLOR_BGR2YUV_IYUV BGR to IYUV COLOR_BGR2YUV_YV12 BGR to YUV_YV12 None Back to default colorspace (i.e. BGR)

"},{"location":"bonus/colorspace_manipulation/#usage-examples","title":"Usage examples","text":""},{"location":"bonus/colorspace_manipulation/#using-camgear-with-direct-colorspace-manipulation","title":"Using CamGear with Direct Colorspace Manipulation","text":"

The complete usage example can be found here \u27b6

"},{"location":"bonus/colorspace_manipulation/#using-pigear-with-direct-colorspace-manipulation","title":"Using PiGear with Direct Colorspace Manipulation","text":"

The complete usage example can be found here \u27b6

"},{"location":"bonus/colorspace_manipulation/#using-videogear-with-colorspace-manipulation","title":"Using VideoGear with Colorspace Manipulation","text":"

The complete usage example can be found here \u27b6

"},{"location":"bonus/colorspace_manipulation/#using-screengear-with-direct-colorspace-manipulation","title":"Using ScreenGear with Direct Colorspace Manipulation","text":"

The complete usage example can be found here \u27b6

"},{"location":"bonus/reference/camgear/","title":"CamGear API References","text":"

CamGear API usage examples can be found here \u27b6

CamGear API parameters are explained here \u27b6

CamGear supports a diverse range of video streams which can handle/control video stream almost any IP/USB Cameras, multimedia video file format (upto 4k tested), any network stream URL such as http(s), rtp, rtsp, rtmp, mms, etc. It also supports Gstreamer's RAW pipelines.

CamGear API provides a flexible, high-level multi-threaded wrapper around OpenCV's VideoCapture API with direct access to almost all of its available parameters. It relies on Threaded Queue mode for threaded, error-free and synchronized frame handling.

CamGear internally implements yt_dlp backend class for seamlessly pipelining live video-frames and metadata from various streaming services like YouTube, Dailymotion, Twitch, and many more \u27b6

Source code in vidgear/gears/camgear.py
class CamGear:\n    \"\"\"\n    CamGear supports a diverse range of video streams which can handle/control video stream almost any IP/USB Cameras, multimedia video file format (upto 4k tested),\n    any network stream URL such as http(s), rtp, rtsp, rtmp, mms, etc. It also supports Gstreamer's RAW pipelines.\n\n    CamGear API provides a flexible, high-level multi-threaded wrapper around OpenCV's VideoCapture API with direct access to almost all of its available parameters.\n    It relies on Threaded Queue mode for threaded, error-free and synchronized frame handling.\n\n    CamGear internally implements `yt_dlp` backend class for seamlessly pipelining live video-frames and metadata from various streaming services like YouTube, Dailymotion,\n    Twitch, and [many more \u27b6](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md#supported-sites)\n    \"\"\"\n\n    def __init__(\n        self,\n        source=0,\n        stream_mode=False,\n        backend=0,\n        colorspace=None,\n        logging=False,\n        time_delay=0,\n        **options\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the CamGear class.\n\n        Parameters:\n            source (based on input): defines the source for the input stream.\n            stream_mode (bool): controls the exclusive **Stream Mode** for handling streaming URLs.\n            backend (int): selects the backend for OpenCV's VideoCapture class.\n            colorspace (str): selects the colorspace of the input stream.\n            logging (bool): enables/disables logging.\n            time_delay (int): time delay (in sec) before start reading the frames.\n            options (dict): provides ability to alter Source Tweak Parameters.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # initialize global\n        self.ytv_metadata = {}\n\n        # check if Stream-Mode is ON (True)\n        if stream_mode:\n            # TODO: check GStreamer backend support\n            # gst_support = check_gstreamer_support(logging=self.__logging)\n            # handle special Stream Mode parameters\n            stream_resolution = get_supported_resolution(\n                options.pop(\"STREAM_RESOLUTION\", \"best\"), logging=self.__logging\n            )\n            # handle Stream-Mode\n            if not (yt_dlp is None):\n                # extract user-defined params\n                yt_stream_params = options.pop(\"STREAM_PARAMS\", {})\n                if isinstance(yt_stream_params, dict):\n                    yt_stream_params = {\n                        str(k).strip(): v for k, v in yt_stream_params.items()\n                    }\n                else:\n                    yt_stream_params = {}\n                try:\n                    # Validate source for Yt_dlp backend\n                    logger.info(\n                        \"Verifying Streaming URL using yt-dlp backend. Please wait...\"\n                    )\n                    # initialize YT_backend\n                    ytbackend = YT_backend(\n                        source_url=source, logging=self.__logging, **yt_stream_params\n                    )\n                    if ytbackend:\n                        # save video metadata\n                        self.ytv_metadata = ytbackend.meta_data\n                        # handle live-streams\n                        # Throw warning for livestreams\n                        ytbackend.is_livestream and logger.warning(\n                            \"Livestream URL detected. It is strongly recommended to use the GStreamer backend (`backend=cv2.CAP_GSTREAMER`) with these URLs.\"\n                        )\n                        # check whether stream-resolution was specified and available\n                        if not (stream_resolution in ytbackend.streams.keys()):\n                            logger.warning(\n                                \"Specified stream-resolution `{}` is not available. Reverting to `best`!\".format(\n                                    stream_resolution\n                                )\n                            )\n                            # revert to best\n                            stream_resolution = \"best\"\n                        else:\n                            self.__logging and logger.debug(\n                                \"Using `{}` resolution for streaming.\".format(\n                                    stream_resolution\n                                )\n                            )\n                        # extract stream URL as source using stream-resolution\n                        source = ytbackend.streams[stream_resolution]\n                        # log progress\n                        self.__logging and logger.debug(\n                            \"YouTube source ID: `{}`, Title: `{}`, Quality: `{}`\".format(\n                                self.ytv_metadata[\"id\"],\n                                self.ytv_metadata[\"title\"],\n                                stream_resolution,\n                            )\n                        )\n                except Exception as e:\n                    # raise error if something went wrong\n                    raise ValueError(\n                        \"[CamGear:ERROR] :: Stream Mode is enabled but Input URL is invalid!\"\n                    )\n            else:\n                # raise import errors\n                import_dependency_safe(\"yt_dlp\")\n\n        # assigns special parameter to global variable and clear\n        # Threaded Queue Mode\n        self.__threaded_queue_mode = options.pop(\"THREADED_QUEUE_MODE\", True)\n        if not isinstance(self.__threaded_queue_mode, bool):\n            # reset improper values\n            self.__threaded_queue_mode = True\n        # Thread Timeout\n        self.__thread_timeout = options.pop(\"THREAD_TIMEOUT\", None)\n        if self.__thread_timeout and isinstance(self.__thread_timeout, (int, float)):\n            # set values\n            self.__thread_timeout = float(self.__thread_timeout)\n        else:\n            # defaults to 5mins timeout\n            self.__thread_timeout = None\n\n        self.__queue = None\n        # initialize queue for video files only\n        if self.__threaded_queue_mode and isinstance(source, str):\n            # define queue and assign it to global var\n            self.__queue = queue.Queue(maxsize=96)  # max bufferlen 96 to check overflow\n            # log it\n            self.__logging and logger.debug(\n                \"Enabling Threaded Queue Mode for the current video source!\"\n            )\n        else:\n            # otherwise disable it\n            self.__threaded_queue_mode = False\n            # log it\n            self.__logging and logger.warning(\n                \"Threaded Queue Mode is disabled for the current video source!\"\n            )\n\n        self.__thread_timeout and logger.info(\n            \"Setting Video-Thread Timeout to {}s.\".format(self.__thread_timeout)\n        )\n\n        # stream variable initialization\n        self.stream = None\n\n        if backend and isinstance(backend, int):\n            # add backend if specified and initialize the camera stream\n            if check_CV_version() == 3:\n                # Different OpenCV 3.4.x statement\n                self.stream = cv2.VideoCapture(source + backend)\n            else:\n                # Two parameters are available since OpenCV 4+ (master branch)\n                self.stream = cv2.VideoCapture(source, backend)\n            logger.info(\"Setting backend `{}` for this source.\".format(backend))\n        else:\n            # initialize the camera stream\n            self.stream = cv2.VideoCapture(source)\n\n        # initializing colorspace variable\n        self.color_space = None\n\n        # apply attributes to source if specified\n        options = {str(k).strip(): v for k, v in options.items()}\n        for key, value in options.items():\n            property = capPropId(key)\n            not (property is None) and self.stream.set(property, value)\n\n        # handle colorspace value\n        if not (colorspace is None):\n            self.color_space = capPropId(colorspace.strip())\n            self.__logging and not (self.color_space is None) and logger.debug(\n                \"Enabling `{}` colorspace for this video stream!\".format(\n                    colorspace.strip()\n                )\n            )\n\n        # initialize and assign frame-rate variable\n        self.framerate = 0.0\n        _fps = self.stream.get(cv2.CAP_PROP_FPS)\n        if _fps > 1.0:\n            self.framerate = _fps\n\n        # applying time delay to warm-up webcam only if specified\n        time_delay and isinstance(time_delay, (int, float)) and time.sleep(time_delay)\n\n        # frame variable initialization\n        (grabbed, self.frame) = self.stream.read()\n\n        # check if valid stream\n        if grabbed:\n            # render colorspace if defined\n            if not (self.color_space is None):\n                self.frame = cv2.cvtColor(self.frame, self.color_space)\n\n            # initialize and append to queue\n            self.__threaded_queue_mode and self.__queue.put(self.frame)\n        else:\n            raise RuntimeError(\n                \"[CamGear:ERROR] :: Source is invalid, CamGear failed to initialize stream on this source!\"\n            )\n\n        # thread initialization\n        self.__thread = None\n\n        # initialize termination flag event\n        self.__terminate = Event()\n\n        # initialize stream read flag event\n        self.__stream_read = Event()\n\n    def start(self):\n        \"\"\"\n        Launches the internal *Threaded Frames Extractor* daemon.\n\n        **Returns:** A reference to the CamGear class object.\n        \"\"\"\n\n        self.__thread = Thread(target=self.__update, name=\"CamGear\", args=())\n        self.__thread.daemon = True\n        self.__thread.start()\n        return self\n\n    def __update(self):\n        \"\"\"\n        A **Threaded Frames Extractor**, that keep iterating frames from OpenCV's VideoCapture API to a internal monitored queue,\n        until the thread is terminated, or frames runs out.\n        \"\"\"\n\n        # keep iterating infinitely\n        # until the thread is terminated\n        # or frames runs out\n        # if the thread indicator variable is set, stop the thread\n        while not self.__terminate.is_set():\n            # stream not read yet\n            self.__stream_read.clear()\n\n            # otherwise, read the next frame from the stream\n            (grabbed, frame) = self.stream.read()\n\n            # stream read completed\n            self.__stream_read.set()\n\n            # check for valid frame if received\n            if not grabbed:\n                # no frames received, then safely exit\n                if self.__threaded_queue_mode:\n                    if self.__queue.empty():\n                        break\n                    else:\n                        continue\n                else:\n                    break\n\n            # apply colorspace to frames if valid\n            if not (self.color_space is None):\n                # apply colorspace to frames\n                color_frame = None\n                try:\n                    color_frame = cv2.cvtColor(frame, self.color_space)\n                except Exception as e:\n                    # Catch if any error occurred\n                    color_frame = None\n                    self.color_space = None\n                    self.__logging and logger.exception(str(e))\n                    logger.warning(\"Assigned colorspace value is invalid. Discarding!\")\n                self.frame = color_frame if not (color_frame is None) else frame\n            else:\n                self.frame = frame\n\n            # append to queue\n            self.__threaded_queue_mode and self.__queue.put(self.frame)\n\n        # signal queue we're done\n        self.__threaded_queue_mode and self.__queue.put(None)\n        self.__threaded_queue_mode = False\n\n        # indicate immediate termination\n        self.__terminate.set()\n        self.__stream_read.set()\n\n        # release resources\n        self.stream.release()\n\n    def read(self):\n        \"\"\"\n        Extracts frames synchronously from monitored queue, while maintaining a fixed-length frame buffer in the memory,\n        and blocks the thread if the queue is full.\n\n        **Returns:** A n-dimensional numpy array.\n        \"\"\"\n        while self.__threaded_queue_mode and not self.__terminate.is_set():\n            return self.__queue.get(timeout=self.__thread_timeout)\n        # return current frame\n        # only after stream is read\n        return (\n            self.frame\n            if not self.__terminate.is_set()  # check if already terminated\n            and self.__stream_read.wait(timeout=self.__thread_timeout)  # wait for it\n            else None\n        )\n\n    def stop(self):\n        \"\"\"\n        Safely terminates the thread, and release the multi-threaded resources.\n        \"\"\"\n        self.__logging and logger.debug(\"Terminating processes.\")\n        # terminate Threaded queue mode separately\n        self.__threaded_queue_mode = False\n\n        # indicate that the thread\n        # should be terminated immediately\n        self.__stream_read.set()\n        self.__terminate.set()\n\n        # wait until stream resources are released (producer thread might be still grabbing frame)\n        if self.__thread is not None:\n            if not (self.__queue is None):\n                while not self.__queue.empty():\n                    try:\n                        self.__queue.get_nowait()\n                    except queue.Empty:\n                        continue\n                    self.__queue.task_done()\n            self.__thread.join()\n

"},{"location":"bonus/reference/camgear/#vidgear.gears.camgear.CamGear.__init__","title":"__init__(self, source=0, stream_mode=False, backend=0, colorspace=None, logging=False, time_delay=0, **options) special","text":"

This constructor method initializes the object state and attributes of the CamGear class.

Parameters:

Name Type Description Default source based on input

defines the source for the input stream.

0 stream_mode bool

controls the exclusive Stream Mode for handling streaming URLs.

False backend int

selects the backend for OpenCV's VideoCapture class.

0 colorspace str

selects the colorspace of the input stream.

None logging bool

enables/disables logging.

False time_delay int

time delay (in sec) before start reading the frames.

0 options dict

provides ability to alter Source Tweak Parameters.

{} Source code in vidgear/gears/camgear.py
def __init__(\n    self,\n    source=0,\n    stream_mode=False,\n    backend=0,\n    colorspace=None,\n    logging=False,\n    time_delay=0,\n    **options\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the CamGear class.\n\n    Parameters:\n        source (based on input): defines the source for the input stream.\n        stream_mode (bool): controls the exclusive **Stream Mode** for handling streaming URLs.\n        backend (int): selects the backend for OpenCV's VideoCapture class.\n        colorspace (str): selects the colorspace of the input stream.\n        logging (bool): enables/disables logging.\n        time_delay (int): time delay (in sec) before start reading the frames.\n        options (dict): provides ability to alter Source Tweak Parameters.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # initialize global\n    self.ytv_metadata = {}\n\n    # check if Stream-Mode is ON (True)\n    if stream_mode:\n        # TODO: check GStreamer backend support\n        # gst_support = check_gstreamer_support(logging=self.__logging)\n        # handle special Stream Mode parameters\n        stream_resolution = get_supported_resolution(\n            options.pop(\"STREAM_RESOLUTION\", \"best\"), logging=self.__logging\n        )\n        # handle Stream-Mode\n        if not (yt_dlp is None):\n            # extract user-defined params\n            yt_stream_params = options.pop(\"STREAM_PARAMS\", {})\n            if isinstance(yt_stream_params, dict):\n                yt_stream_params = {\n                    str(k).strip(): v for k, v in yt_stream_params.items()\n                }\n            else:\n                yt_stream_params = {}\n            try:\n                # Validate source for Yt_dlp backend\n                logger.info(\n                    \"Verifying Streaming URL using yt-dlp backend. Please wait...\"\n                )\n                # initialize YT_backend\n                ytbackend = YT_backend(\n                    source_url=source, logging=self.__logging, **yt_stream_params\n                )\n                if ytbackend:\n                    # save video metadata\n                    self.ytv_metadata = ytbackend.meta_data\n                    # handle live-streams\n                    # Throw warning for livestreams\n                    ytbackend.is_livestream and logger.warning(\n                        \"Livestream URL detected. It is strongly recommended to use the GStreamer backend (`backend=cv2.CAP_GSTREAMER`) with these URLs.\"\n                    )\n                    # check whether stream-resolution was specified and available\n                    if not (stream_resolution in ytbackend.streams.keys()):\n                        logger.warning(\n                            \"Specified stream-resolution `{}` is not available. Reverting to `best`!\".format(\n                                stream_resolution\n                            )\n                        )\n                        # revert to best\n                        stream_resolution = \"best\"\n                    else:\n                        self.__logging and logger.debug(\n                            \"Using `{}` resolution for streaming.\".format(\n                                stream_resolution\n                            )\n                        )\n                    # extract stream URL as source using stream-resolution\n                    source = ytbackend.streams[stream_resolution]\n                    # log progress\n                    self.__logging and logger.debug(\n                        \"YouTube source ID: `{}`, Title: `{}`, Quality: `{}`\".format(\n                            self.ytv_metadata[\"id\"],\n                            self.ytv_metadata[\"title\"],\n                            stream_resolution,\n                        )\n                    )\n            except Exception as e:\n                # raise error if something went wrong\n                raise ValueError(\n                    \"[CamGear:ERROR] :: Stream Mode is enabled but Input URL is invalid!\"\n                )\n        else:\n            # raise import errors\n            import_dependency_safe(\"yt_dlp\")\n\n    # assigns special parameter to global variable and clear\n    # Threaded Queue Mode\n    self.__threaded_queue_mode = options.pop(\"THREADED_QUEUE_MODE\", True)\n    if not isinstance(self.__threaded_queue_mode, bool):\n        # reset improper values\n        self.__threaded_queue_mode = True\n    # Thread Timeout\n    self.__thread_timeout = options.pop(\"THREAD_TIMEOUT\", None)\n    if self.__thread_timeout and isinstance(self.__thread_timeout, (int, float)):\n        # set values\n        self.__thread_timeout = float(self.__thread_timeout)\n    else:\n        # defaults to 5mins timeout\n        self.__thread_timeout = None\n\n    self.__queue = None\n    # initialize queue for video files only\n    if self.__threaded_queue_mode and isinstance(source, str):\n        # define queue and assign it to global var\n        self.__queue = queue.Queue(maxsize=96)  # max bufferlen 96 to check overflow\n        # log it\n        self.__logging and logger.debug(\n            \"Enabling Threaded Queue Mode for the current video source!\"\n        )\n    else:\n        # otherwise disable it\n        self.__threaded_queue_mode = False\n        # log it\n        self.__logging and logger.warning(\n            \"Threaded Queue Mode is disabled for the current video source!\"\n        )\n\n    self.__thread_timeout and logger.info(\n        \"Setting Video-Thread Timeout to {}s.\".format(self.__thread_timeout)\n    )\n\n    # stream variable initialization\n    self.stream = None\n\n    if backend and isinstance(backend, int):\n        # add backend if specified and initialize the camera stream\n        if check_CV_version() == 3:\n            # Different OpenCV 3.4.x statement\n            self.stream = cv2.VideoCapture(source + backend)\n        else:\n            # Two parameters are available since OpenCV 4+ (master branch)\n            self.stream = cv2.VideoCapture(source, backend)\n        logger.info(\"Setting backend `{}` for this source.\".format(backend))\n    else:\n        # initialize the camera stream\n        self.stream = cv2.VideoCapture(source)\n\n    # initializing colorspace variable\n    self.color_space = None\n\n    # apply attributes to source if specified\n    options = {str(k).strip(): v for k, v in options.items()}\n    for key, value in options.items():\n        property = capPropId(key)\n        not (property is None) and self.stream.set(property, value)\n\n    # handle colorspace value\n    if not (colorspace is None):\n        self.color_space = capPropId(colorspace.strip())\n        self.__logging and not (self.color_space is None) and logger.debug(\n            \"Enabling `{}` colorspace for this video stream!\".format(\n                colorspace.strip()\n            )\n        )\n\n    # initialize and assign frame-rate variable\n    self.framerate = 0.0\n    _fps = self.stream.get(cv2.CAP_PROP_FPS)\n    if _fps > 1.0:\n        self.framerate = _fps\n\n    # applying time delay to warm-up webcam only if specified\n    time_delay and isinstance(time_delay, (int, float)) and time.sleep(time_delay)\n\n    # frame variable initialization\n    (grabbed, self.frame) = self.stream.read()\n\n    # check if valid stream\n    if grabbed:\n        # render colorspace if defined\n        if not (self.color_space is None):\n            self.frame = cv2.cvtColor(self.frame, self.color_space)\n\n        # initialize and append to queue\n        self.__threaded_queue_mode and self.__queue.put(self.frame)\n    else:\n        raise RuntimeError(\n            \"[CamGear:ERROR] :: Source is invalid, CamGear failed to initialize stream on this source!\"\n        )\n\n    # thread initialization\n    self.__thread = None\n\n    # initialize termination flag event\n    self.__terminate = Event()\n\n    # initialize stream read flag event\n    self.__stream_read = Event()\n
"},{"location":"bonus/reference/camgear/#vidgear.gears.camgear.CamGear.read","title":"read(self)","text":"

Extracts frames synchronously from monitored queue, while maintaining a fixed-length frame buffer in the memory, and blocks the thread if the queue is full.

Returns: A n-dimensional numpy array.

Source code in vidgear/gears/camgear.py
def read(self):\n    \"\"\"\n    Extracts frames synchronously from monitored queue, while maintaining a fixed-length frame buffer in the memory,\n    and blocks the thread if the queue is full.\n\n    **Returns:** A n-dimensional numpy array.\n    \"\"\"\n    while self.__threaded_queue_mode and not self.__terminate.is_set():\n        return self.__queue.get(timeout=self.__thread_timeout)\n    # return current frame\n    # only after stream is read\n    return (\n        self.frame\n        if not self.__terminate.is_set()  # check if already terminated\n        and self.__stream_read.wait(timeout=self.__thread_timeout)  # wait for it\n        else None\n    )\n
"},{"location":"bonus/reference/camgear/#vidgear.gears.camgear.CamGear.start","title":"start(self)","text":"

Launches the internal Threaded Frames Extractor daemon.

Returns: A reference to the CamGear class object.

Source code in vidgear/gears/camgear.py
def start(self):\n    \"\"\"\n    Launches the internal *Threaded Frames Extractor* daemon.\n\n    **Returns:** A reference to the CamGear class object.\n    \"\"\"\n\n    self.__thread = Thread(target=self.__update, name=\"CamGear\", args=())\n    self.__thread.daemon = True\n    self.__thread.start()\n    return self\n
"},{"location":"bonus/reference/camgear/#vidgear.gears.camgear.CamGear.stop","title":"stop(self)","text":"

Safely terminates the thread, and release the multi-threaded resources.

Source code in vidgear/gears/camgear.py
def stop(self):\n    \"\"\"\n    Safely terminates the thread, and release the multi-threaded resources.\n    \"\"\"\n    self.__logging and logger.debug(\"Terminating processes.\")\n    # terminate Threaded queue mode separately\n    self.__threaded_queue_mode = False\n\n    # indicate that the thread\n    # should be terminated immediately\n    self.__stream_read.set()\n    self.__terminate.set()\n\n    # wait until stream resources are released (producer thread might be still grabbing frame)\n    if self.__thread is not None:\n        if not (self.__queue is None):\n            while not self.__queue.empty():\n                try:\n                    self.__queue.get_nowait()\n                except queue.Empty:\n                    continue\n                self.__queue.task_done()\n        self.__thread.join()\n
"},{"location":"bonus/reference/helper/","title":"Helper Methods","text":""},{"location":"bonus/reference/helper/#vidgear.gears.helper.logger_handler--logger_handler","title":"logger_handler","text":"

Returns the logger handler

Returns: A logger handler

Source code in vidgear/gears/helper.py
def logger_handler():\n    \"\"\"\n    ## logger_handler\n\n    Returns the logger handler\n\n    **Returns:** A logger handler\n    \"\"\"\n    # logging formatter\n    formatter = ColoredFormatter(\n        \"{green}{asctime}{reset} :: {bold_purple}{name:^13}{reset} :: {log_color}{levelname:^8}{reset} :: {bold_white}{message}\",\n        datefmt=\"%H:%M:%S\",\n        reset=True,\n        log_colors={\n            \"INFO\": \"bold_cyan\",\n            \"DEBUG\": \"bold_yellow\",\n            \"WARNING\": \"bold_red,fg_thin_yellow\",\n            \"ERROR\": \"bold_red\",\n            \"CRITICAL\": \"bold_red,bg_white\",\n        },\n        style=\"{\",\n    )\n    # check if VIDGEAR_LOGFILE defined\n    file_mode = os.environ.get(\"VIDGEAR_LOGFILE\", False)\n    # define handler\n    handler = log.StreamHandler()\n    if file_mode and isinstance(file_mode, str):\n        file_path = os.path.abspath(file_mode)\n        if (os.name == \"nt\" or os.access in os.supports_effective_ids) and os.access(\n            os.path.dirname(file_path), os.W_OK\n        ):\n            file_path = (\n                os.path.join(file_path, \"vidgear.log\")\n                if os.path.isdir(file_path)\n                else file_path\n            )\n            handler = log.FileHandler(file_path, mode=\"a\")\n            formatter = log.Formatter(\n                \"{asctime} :: {name} :: {levelname} :: {message}\",\n                datefmt=\"%H:%M:%S\",\n                style=\"{\",\n            )\n\n    handler.setFormatter(formatter)\n    return handler\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.check_CV_version--check_cv_version","title":"check_CV_version","text":"

Returns: OpenCV's version first bit

Source code in vidgear/gears/helper.py
def check_CV_version():\n    \"\"\"\n    ## check_CV_version\n\n    **Returns:** OpenCV's version first bit\n    \"\"\"\n    if parse_version(cv2.__version__) >= parse_version(\"4\"):\n        return 4\n    else:\n        return 3\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.check_gstreamer_support--check_gstreamer_support","title":"check_gstreamer_support","text":"

Checks whether OpenCV is compiled with Gstreamer(>=1.0.0) support.

Parameters:

Name Type Description Default logging bool

enables logging for its operations

False

Returns: A Boolean value

Source code in vidgear/gears/helper.py
def check_gstreamer_support(logging=False):\n    \"\"\"\n    ## check_gstreamer_support\n\n    Checks whether OpenCV is compiled with Gstreamer(`>=1.0.0`) support.\n\n    Parameters:\n        logging (bool): enables logging for its operations\n\n    **Returns:** A Boolean value\n    \"\"\"\n    raw = cv2.getBuildInformation()\n    gst = [\n        x.strip()\n        for x in raw.split(\"\\n\")\n        if x and re.search(r\"GStreamer[,-:]+\\s*(?:YES|NO)\", x)\n    ]\n    if gst and \"YES\" in gst[0]:\n        version = re.search(r\"(\\d+\\.)?(\\d+\\.)?(\\*|\\d+)\", gst[0])\n        logging and logger.debug(\"Found GStreamer version:{}\".format(version[0]))\n        return version[0] >= \"1.0.0\"\n    else:\n        logger.warning(\"GStreamer not found!\")\n        return False\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.get_supported_resolution--get_supported_resolution","title":"get_supported_resolution","text":"

Parameters:

Name Type Description Default value string

value to be validated

required logging bool

enables logging for its operations

False

Returns: Valid stream resolution

Source code in vidgear/gears/helper.py
def get_supported_resolution(value, logging=False):\n    \"\"\"\n    ## get_supported_resolution\n\n    Parameters:\n        value (string): value to be validated\n        logging (bool): enables logging for its operations\n\n    **Returns:** Valid stream resolution\n    \"\"\"\n    # default to best\n    stream_resolution = \"best\"\n    supported_stream_qualities = [\n        \"144p\",\n        \"240p\",\n        \"360p\",\n        \"480p\",\n        \"720p\",\n        \"1080p\",\n        \"1440p\",\n        \"2160p\",\n        \"4320p\",\n        \"worst\",\n        \"best\",\n    ]\n    if isinstance(value, str):\n        if value.strip().lower() in supported_stream_qualities:\n            stream_resolution = value.strip().lower()\n            logging and logger.debug(\n                \"Selecting `{}` resolution for streams.\".format(stream_resolution)\n            )\n        else:\n            logger.warning(\n                \"Specified stream-resolution `{}` is not supported. Reverting to `best`!\".format(\n                    value\n                )\n            )\n    else:\n        logger.warning(\n            \"Specified stream-resolution `{}` is Invalid. Reverting to `best`!\".format(\n                value\n            )\n        )\n    return stream_resolution\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.dimensions_to_resolutions--dimensions_to_resolutions","title":"dimensions_to_resolutions","text":"

Parameters:

Name Type Description Default value list

list of dimensions (e.g. 640x360)

required

Returns: list of resolutions (e.g. 360p)

Source code in vidgear/gears/helper.py
def dimensions_to_resolutions(value):\n    \"\"\"\n    ## dimensions_to_resolutions\n\n    Parameters:\n        value (list): list of dimensions (e.g. `640x360`)\n\n    **Returns:** list of resolutions (e.g. `360p`)\n    \"\"\"\n    supported_resolutions = {\n        \"256x144\": \"144p\",\n        \"426x240\": \"240p\",\n        \"640x360\": \"360p\",\n        \"854x480\": \"480p\",\n        \"1280x720\": \"720p\",\n        \"1920x1080\": \"1080p\",\n        \"2560x1440\": \"1440p\",\n        \"3840x2160\": \"2160p\",\n        \"7680x4320\": \"4320p\",\n    }\n    return (\n        list(map(supported_resolutions.get, value, value))\n        if isinstance(value, list)\n        else []\n    )\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.mkdir_safe--mkdir_safe","title":"mkdir_safe","text":"

Safely creates directory at given path.

Parameters:

Name Type Description Default dir_path string

path to the directory

required logging bool

enables logging for its operations

False Source code in vidgear/gears/helper.py
def mkdir_safe(dir_path, logging=False):\n    \"\"\"\n    ## mkdir_safe\n\n    Safely creates directory at given path.\n\n    Parameters:\n        dir_path (string): path to the directory\n        logging (bool): enables logging for its operations\n\n    \"\"\"\n    try:\n        os.makedirs(dir_path)\n        logging and logger.debug(\"Created directory at `{}`\".format(dir_path))\n    except (OSError, IOError) as e:\n        if e.errno != errno.EACCES and e.errno != errno.EEXIST:\n            raise\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.delete_ext_safe--delete_ext_safe","title":"delete_ext_safe","text":"

Safely deletes files with given extensions at given path.

Parameters:

Name Type Description Default dir_path string

path to the directory

required extensions list

list of extensions to be deleted

[] logging bool

enables logging for its operations

False Source code in vidgear/gears/helper.py
def delete_ext_safe(dir_path, extensions=[], logging=False):\n    \"\"\"\n    ## delete_ext_safe\n\n    Safely deletes files with given extensions at given path.\n\n    Parameters:\n        dir_path (string): path to the directory\n        extensions (list): list of extensions to be deleted\n        logging (bool): enables logging for its operations\n\n    \"\"\"\n    if not extensions or not os.path.exists(dir_path):\n        logger.warning(\"Invalid input provided for deleting!\")\n        return\n\n    logger.critical(\"Clearing Assets at `{}`!\".format(dir_path))\n\n    for ext in extensions:\n        if len(ext) == 2:\n            files_ext = [\n                os.path.join(dir_path, f)\n                for f in os.listdir(dir_path)\n                if f.startswith(ext[0]) and f.endswith(ext[1])\n            ]\n        else:\n            files_ext = [\n                os.path.join(dir_path, f)\n                for f in os.listdir(dir_path)\n                if f.endswith(ext)\n            ]\n        for file in files_ext:\n            delete_file_safe(file)\n            logging and logger.debug(\"Deleted file: `{}`\".format(file))\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.capPropId--cappropid","title":"capPropId","text":"

Retrieves the OpenCV property's Integer(Actual) value from string.

Parameters:

Name Type Description Default property string

inputs OpenCV property as string.

required logging bool

enables logging for its operations

True

Returns: Resultant integer value.

Source code in vidgear/gears/helper.py
def capPropId(property, logging=True):\n    \"\"\"\n    ## capPropId\n\n    Retrieves the OpenCV property's Integer(Actual) value from string.\n\n    Parameters:\n        property (string): inputs OpenCV property as string.\n        logging (bool): enables logging for its operations\n\n    **Returns:** Resultant integer value.\n    \"\"\"\n    integer_value = 0\n    try:\n        integer_value = getattr(cv2, property)\n    except Exception as e:\n        logging and logger.exception(str(e))\n        logger.critical(\"`{}` is not a valid OpenCV property!\".format(property))\n        return None\n    return integer_value\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.reducer--reducer","title":"reducer","text":"

Reduces frame size by given percentage

Parameters:

Name Type Description Default frame numpy.ndarray

inputs numpy array(frame).

None percentage int/float

inputs size-reduction percentage.

0 interpolation int

Change resize interpolation.

4

Returns: A reduced numpy ndarray array.

Source code in vidgear/gears/helper.py
def reducer(frame=None, percentage=0, interpolation=cv2.INTER_LANCZOS4):\n    \"\"\"\n    ## reducer\n\n    Reduces frame size by given percentage\n\n    Parameters:\n        frame (numpy.ndarray): inputs numpy array(frame).\n        percentage (int/float): inputs size-reduction percentage.\n        interpolation (int): Change resize interpolation.\n\n    **Returns:**  A reduced numpy ndarray array.\n    \"\"\"\n    # check if frame is valid\n    if frame is None:\n        raise ValueError(\"[Helper:ERROR] :: Input frame cannot be NoneType!\")\n\n    # check if valid reduction percentage is given\n    if not (percentage > 0 and percentage < 90):\n        raise ValueError(\n            \"[Helper:ERROR] :: Given frame-size reduction percentage is invalid, Kindly refer docs.\"\n        )\n\n    if not (isinstance(interpolation, int)):\n        raise ValueError(\n            \"[Helper:ERROR] :: Given interpolation is invalid, Kindly refer docs.\"\n        )\n\n    # grab the frame size\n    (height, width) = frame.shape[:2]\n\n    # calculate the ratio of the width from percentage\n    reduction = ((100 - percentage) / 100) * width\n    ratio = reduction / float(width)\n    # construct the dimensions\n    dimensions = (int(reduction), int(height * ratio))\n\n    # return the resized frame\n    return cv2.resize(frame, dimensions, interpolation=interpolation)\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.create_blank_frame--create_blank_frame","title":"create_blank_frame","text":"

Create blank frames of given frame size with text

Parameters:

Name Type Description Default frame numpy.ndarray

inputs numpy array(frame).

None text str

Text to be written on frame.

''

Returns: A reduced numpy ndarray array.

Source code in vidgear/gears/helper.py
def create_blank_frame(frame=None, text=\"\", logging=False):\n    \"\"\"\n    ## create_blank_frame\n\n    Create blank frames of given frame size with text\n\n    Parameters:\n        frame (numpy.ndarray): inputs numpy array(frame).\n        text (str): Text to be written on frame.\n    **Returns:**  A reduced numpy ndarray array.\n    \"\"\"\n    # check if frame is valid\n    if frame is None or not (isinstance(frame, np.ndarray)):\n        raise ValueError(\"[Helper:ERROR] :: Input frame is invalid!\")\n    # grab the frame size\n    (height, width) = frame.shape[:2]\n    # create blank frame\n    blank_frame = np.zeros(frame.shape, frame.dtype)\n    # setup text\n    if text and isinstance(text, str):\n        logging and logger.debug(\"Adding text: {}\".format(text))\n        # setup font\n        font = cv2.FONT_HERSHEY_SCRIPT_COMPLEX\n        # get boundary of this text\n        fontScale = min(height, width) / (25 / 0.25)\n        textsize = cv2.getTextSize(text, font, fontScale, 5)[0]\n        # get coords based on boundary\n        textX = (width - textsize[0]) // 2\n        textY = (height + textsize[1]) // 2\n        # put text\n        cv2.putText(\n            blank_frame, text, (textX, textY), font, fontScale, (125, 125, 125), 6\n        )\n\n    # return frame\n    return blank_frame\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.dict2Args--dict2args","title":"dict2Args","text":"

Converts dictionary attributes to list(args)

Parameters:

Name Type Description Default param_dict dict

Parameters dictionary

required

Returns: Arguments list

Source code in vidgear/gears/helper.py
def dict2Args(param_dict):\n    \"\"\"\n    ## dict2Args\n\n    Converts dictionary attributes to list(args)\n\n    Parameters:\n        param_dict (dict): Parameters dictionary\n\n    **Returns:** Arguments list\n    \"\"\"\n    args = []\n    for key in param_dict.keys():\n        if key in [\"-clones\"] or key.startswith(\"-core\"):\n            if isinstance(param_dict[key], list):\n                args.extend(param_dict[key])\n            else:\n                logger.warning(\n                    \"{} with invalid datatype:`{}`, Skipped!\".format(\n                        \"Core parameter\" if key.startswith(\"-core\") else \"Clone\",\n                        param_dict[key],\n                    )\n                )\n        else:\n            args.append(key)\n            args.append(str(param_dict[key]))\n    return args\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.get_valid_ffmpeg_path--get_valid_ffmpeg_path","title":"get_valid_ffmpeg_path","text":"

Validate the given FFmpeg path/binaries, and returns a valid FFmpeg executable path.

Parameters:

Name Type Description Default custom_ffmpeg string

path to custom FFmpeg executables

'' is_windows boolean

is running on Windows OS?

False ffmpeg_download_path string

FFmpeg static binaries download location (Windows only)

'' logging bool

enables logging for its operations

False

Returns: A valid FFmpeg executable path string.

Source code in vidgear/gears/helper.py
def get_valid_ffmpeg_path(\n    custom_ffmpeg=\"\", is_windows=False, ffmpeg_download_path=\"\", logging=False\n):\n    \"\"\"\n    ## get_valid_ffmpeg_path\n\n    Validate the given FFmpeg path/binaries, and returns a valid FFmpeg executable path.\n\n    Parameters:\n        custom_ffmpeg (string): path to custom FFmpeg executables\n        is_windows (boolean): is running on Windows OS?\n        ffmpeg_download_path (string): FFmpeg static binaries download location _(Windows only)_\n        logging (bool): enables logging for its operations\n\n    **Returns:** A valid FFmpeg executable path string.\n    \"\"\"\n    final_path = \"\"\n    if is_windows:\n        # checks if current os is windows\n        if custom_ffmpeg:\n            # if custom FFmpeg path is given assign to local variable\n            final_path += custom_ffmpeg\n        else:\n            # otherwise auto-download them\n            try:\n                if not (ffmpeg_download_path):\n                    # otherwise save to Temp Directory\n                    import tempfile\n\n                    ffmpeg_download_path = tempfile.gettempdir()\n\n                logging and logger.debug(\n                    \"FFmpeg Windows Download Path: {}\".format(ffmpeg_download_path)\n                )\n\n                # download Binaries\n                os_bit = (\n                    (\"win64\" if platform.machine().endswith(\"64\") else \"win32\")\n                    if is_windows\n                    else \"\"\n                )\n                _path = download_ffmpeg_binaries(\n                    path=ffmpeg_download_path, os_windows=is_windows, os_bit=os_bit\n                )\n                # assign to local variable\n                final_path += _path\n\n            except Exception as e:\n                # log if any error occurred\n                logger.exception(str(e))\n                logger.error(\n                    \"Error in downloading FFmpeg binaries, Check your network and Try again!\"\n                )\n                return False\n\n        if os.path.isfile(final_path):\n            # check if valid FFmpeg file exist\n            pass\n        elif os.path.isfile(os.path.join(final_path, \"ffmpeg.exe\")):\n            # check if FFmpeg directory exists, if does, then check for valid file\n            final_path = os.path.join(final_path, \"ffmpeg.exe\")\n        else:\n            # else return False\n            logging and logger.debug(\n                \"No valid FFmpeg executables found at Custom FFmpeg path!\"\n            )\n            return False\n    else:\n        # otherwise perform test for Unix\n        if custom_ffmpeg:\n            # if custom FFmpeg path is given assign to local variable\n            if os.path.isfile(custom_ffmpeg):\n                # check if valid FFmpeg file exist\n                final_path += custom_ffmpeg\n            elif os.path.isfile(os.path.join(custom_ffmpeg, \"ffmpeg\")):\n                # check if FFmpeg directory exists, if does, then check for valid file\n                final_path = os.path.join(custom_ffmpeg, \"ffmpeg\")\n            else:\n                # else return False\n                logging and logger.debug(\n                    \"No valid FFmpeg executables found at Custom FFmpeg path!\"\n                )\n                return False\n        else:\n            # otherwise assign ffmpeg binaries from system\n            final_path += \"ffmpeg\"\n\n    logging and logger.debug(\"Final FFmpeg Path: {}\".format(final_path))\n\n    # Final Auto-Validation for FFmeg Binaries. returns final path if test is passed\n    return final_path if validate_ffmpeg(final_path, logging=logging) else False\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.download_ffmpeg_binaries--download_ffmpeg_binaries","title":"download_ffmpeg_binaries","text":"

Generates FFmpeg Static Binaries for windows(if not available)

Parameters:

Name Type Description Default path string

path for downloading custom FFmpeg executables

required os_windows boolean

is running on Windows OS?

False os_bit string

32-bit or 64-bit OS?

''

Returns: A valid FFmpeg executable path string.

Source code in vidgear/gears/helper.py
def download_ffmpeg_binaries(path, os_windows=False, os_bit=\"\"):\n    \"\"\"\n    ## download_ffmpeg_binaries\n\n    Generates FFmpeg Static Binaries for windows(if not available)\n\n    Parameters:\n        path (string): path for downloading custom FFmpeg executables\n        os_windows (boolean): is running on Windows OS?\n        os_bit (string): 32-bit or 64-bit OS?\n\n    **Returns:** A valid FFmpeg executable path string.\n    \"\"\"\n    final_path = \"\"\n    if os_windows and os_bit:\n        # initialize with available FFmpeg Static Binaries GitHub Server\n        file_url = \"https://github.com/abhiTronix/FFmpeg-Builds/releases/latest/download/ffmpeg-static-{}-gpl.zip\".format(\n            os_bit\n        )\n\n        file_name = os.path.join(\n            os.path.abspath(path), \"ffmpeg-static-{}-gpl.zip\".format(os_bit)\n        )\n        file_path = os.path.join(\n            os.path.abspath(path),\n            \"ffmpeg-static-{}-gpl/bin/ffmpeg.exe\".format(os_bit),\n        )\n        base_path, _ = os.path.split(file_name)  # extract file base path\n        # check if file already exists\n        if os.path.isfile(file_path):\n            final_path += file_path  # skip download if does\n        else:\n            # import libs\n            import zipfile\n\n            # check if given path has write access\n            assert os.access(path, os.W_OK), (\n                \"[Helper:ERROR] :: Permission Denied, Cannot write binaries to directory = \"\n                + path\n            )\n            # remove leftovers if exists\n            os.path.isfile(file_name) and delete_file_safe(file_name)\n            # download and write file to the given path\n            with open(file_name, \"wb\") as f:\n                logger.debug(\n                    \"No Custom FFmpeg path provided. Auto-Installing FFmpeg static binaries from GitHub Mirror now. Please wait...\"\n                )\n                # create session\n                with requests.Session() as http:\n                    # setup retry strategy\n                    retries = Retry(\n                        total=3,\n                        backoff_factor=1,\n                        status_forcelist=[429, 500, 502, 503, 504],\n                    )\n                    # Mount it for https usage\n                    adapter = TimeoutHTTPAdapter(timeout=2.0, max_retries=retries)\n                    http.mount(\"https://\", adapter)\n                    response = http.get(file_url, stream=True)\n                    response.raise_for_status()\n                    total_length = (\n                        response.headers.get(\"content-length\")\n                        if \"content-length\" in response.headers\n                        else len(response.content)\n                    )\n                    assert not (\n                        total_length is None\n                    ), \"[Helper:ERROR] :: Failed to retrieve files, check your Internet connectivity!\"\n                    bar = tqdm(total=int(total_length), unit=\"B\", unit_scale=True)\n                    for data in response.iter_content(chunk_size=4096):\n                        f.write(data)\n                        len(data) > 0 and bar.update(len(data))\n                    bar.close()\n            logger.debug(\"Extracting executables.\")\n            with zipfile.ZipFile(file_name, \"r\") as zip_ref:\n                zip_fname, _ = os.path.split(zip_ref.infolist()[0].filename)\n                zip_ref.extractall(base_path)\n            # perform cleaning\n            delete_file_safe(file_name)\n            logger.debug(\"FFmpeg binaries for Windows configured successfully!\")\n            final_path += file_path\n    # return final path\n    return final_path\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.validate_ffmpeg--validate_ffmpeg","title":"validate_ffmpeg","text":"

Validate FFmeg Binaries. returns True if tests are passed.

Parameters:

Name Type Description Default path string

absolute path of FFmpeg binaries

required logging bool

enables logging for its operations

False

Returns: A boolean value, confirming whether tests passed, or not?.

Source code in vidgear/gears/helper.py
def validate_ffmpeg(path, logging=False):\n    \"\"\"\n    ## validate_ffmpeg\n\n    Validate FFmeg Binaries. returns `True` if tests are passed.\n\n    Parameters:\n        path (string): absolute path of FFmpeg binaries\n        logging (bool): enables logging for its operations\n\n    **Returns:** A boolean value, confirming whether tests passed, or not?.\n    \"\"\"\n    try:\n        # get the FFmpeg version\n        version = check_output([path, \"-version\"])\n        firstline = version.split(b\"\\n\")[0]\n        version = firstline.split(b\" \")[2].strip()\n        # log if test are passed\n        logging and logger.info(\"FFmpeg validity Test Passed!\")\n        logging and logger.debug(\n            \"Found valid FFmpeg Version: `{}` installed on this system\".format(version)\n        )\n    except Exception as e:\n        # log if test are failed\n        logging and logger.exception(str(e))\n        logger.error(\"FFmpeg validity Test Failed!\")\n        return False\n    return True\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.check_output--check_output","title":"check_output","text":"

Returns stdin output from subprocess module

Source code in vidgear/gears/helper.py
def check_output(*args, **kwargs):\n    \"\"\"\n    ## check_output\n\n    Returns stdin output from subprocess module\n    \"\"\"\n    # import libs\n    import subprocess as sp\n\n    # workaround for python bug: https://bugs.python.org/issue37380\n    if platform.system() == \"Windows\":\n        # see comment https://bugs.python.org/msg370334\n        sp._cleanup = lambda: None\n\n    # handle additional params\n    retrieve_stderr = kwargs.pop(\"force_retrieve_stderr\", False)\n\n    # execute command in subprocess\n    process = sp.Popen(\n        stdout=sp.PIPE,\n        stderr=sp.DEVNULL if not (retrieve_stderr) else sp.PIPE,\n        *args,\n        **kwargs,\n    )\n    output, stderr = process.communicate()\n    retcode = process.poll()\n\n    # handle return code\n    if retcode and not (retrieve_stderr):\n        cmd = kwargs.get(\"args\")\n        if cmd is None:\n            cmd = args[0]\n        error = sp.CalledProcessError(retcode, cmd)\n        error.output = output\n        raise error\n\n    return output if not (retrieve_stderr) else stderr\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.generate_auth_certificates--generate_auth_certificates","title":"generate_auth_certificates","text":"

Auto-Generates, and Auto-validates CURVE ZMQ key-pairs for NetGear API's Secure Mode.

Parameters:

Name Type Description Default path string

path for generating CURVE key-pairs

required overwrite boolean

overwrite existing key-pairs or not?

False logging bool

enables logging for its operations

False

Returns: A valid CURVE key-pairs path as string.

Source code in vidgear/gears/helper.py
def generate_auth_certificates(path, overwrite=False, logging=False):\n    \"\"\"\n    ## generate_auth_certificates\n\n    Auto-Generates, and Auto-validates CURVE ZMQ key-pairs for NetGear API's Secure Mode.\n\n    Parameters:\n        path (string): path for generating CURVE key-pairs\n        overwrite (boolean): overwrite existing key-pairs or not?\n        logging (bool): enables logging for its operations\n\n    **Returns:** A valid CURVE key-pairs path as string.\n    \"\"\"\n    # import necessary lib\n    import zmq.auth\n\n    # check if path corresponds to vidgear only\n    if os.path.basename(path) != \".vidgear\":\n        path = os.path.join(path, \".vidgear\")\n\n    # generate keys dir\n    keys_dir = os.path.join(path, \"keys\")\n    mkdir_safe(keys_dir, logging=logging)\n\n    # generate separate public and private key dirs\n    public_keys_dir = os.path.join(keys_dir, \"public_keys\")\n    secret_keys_dir = os.path.join(keys_dir, \"private_keys\")\n\n    # check if overwriting is allowed\n    if overwrite:\n        # delete previous certificates\n        for dirs in [public_keys_dir, secret_keys_dir]:\n            if os.path.exists(dirs):\n                shutil.rmtree(dirs)\n            mkdir_safe(dirs, logging=logging)\n\n        # generate new keys\n        server_public_file, server_secret_file = zmq.auth.create_certificates(\n            keys_dir, \"server\"\n        )\n        client_public_file, client_secret_file = zmq.auth.create_certificates(\n            keys_dir, \"client\"\n        )\n\n        # move keys to their appropriate directory respectively\n        for key_file in os.listdir(keys_dir):\n            if key_file.endswith(\".key\"):\n                shutil.move(os.path.join(keys_dir, key_file), public_keys_dir)\n            elif key_file.endswith(\".key_secret\"):\n                shutil.move(os.path.join(keys_dir, key_file), secret_keys_dir)\n            else:\n                # clean redundant keys if present\n                redundant_key = os.path.join(keys_dir, key_file)\n                if os.path.isfile(redundant_key):\n                    delete_file_safe(redundant_key)\n    else:\n        # otherwise validate available keys\n        status_public_keys = validate_auth_keys(public_keys_dir, \".key\")\n        status_private_keys = validate_auth_keys(secret_keys_dir, \".key_secret\")\n\n        # check if all valid keys are found\n        if status_private_keys and status_public_keys:\n            return (keys_dir, secret_keys_dir, public_keys_dir)\n\n        # check if valid public keys are found\n        if not (status_public_keys):\n            mkdir_safe(public_keys_dir, logging=logging)\n\n        # check if valid private keys are found\n        if not (status_private_keys):\n            mkdir_safe(secret_keys_dir, logging=logging)\n\n        # generate new keys\n        server_public_file, server_secret_file = zmq.auth.create_certificates(\n            keys_dir, \"server\"\n        )\n        client_public_file, client_secret_file = zmq.auth.create_certificates(\n            keys_dir, \"client\"\n        )\n\n        # move keys to their appropriate directory respectively\n        for key_file in os.listdir(keys_dir):\n            if key_file.endswith(\".key\") and not (status_public_keys):\n                shutil.move(\n                    os.path.join(keys_dir, key_file), os.path.join(public_keys_dir, \".\")\n                )\n            elif key_file.endswith(\".key_secret\") and not (status_private_keys):\n                shutil.move(\n                    os.path.join(keys_dir, key_file), os.path.join(secret_keys_dir, \".\")\n                )\n            else:\n                # clean redundant keys if present\n                redundant_key = os.path.join(keys_dir, key_file)\n                if os.path.isfile(redundant_key):\n                    delete_file_safe(redundant_key)\n\n    # validate newly generated keys\n    status_public_keys = validate_auth_keys(public_keys_dir, \".key\")\n    status_private_keys = validate_auth_keys(secret_keys_dir, \".key_secret\")\n\n    # raise error is validation test fails\n    if not (status_private_keys) or not (status_public_keys):\n        raise RuntimeError(\n            \"[Helper:ERROR] :: Unable to generate valid ZMQ authentication certificates at `{}`!\".format(\n                keys_dir\n            )\n        )\n\n    # finally return valid key paths\n    return (keys_dir, secret_keys_dir, public_keys_dir)\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.validate_audio--validate_audio","title":"validate_audio","text":"

Validates audio by retrieving audio-bitrate from file.

Parameters:

Name Type Description Default path string

absolute path of FFmpeg binaries

required source string/list

source to be validated.

None

Returns: A string value, confirming whether audio is present, or not?.

Source code in vidgear/gears/helper.py
def validate_audio(path, source=None):\n    \"\"\"\n    ## validate_audio\n\n    Validates audio by retrieving audio-bitrate from file.\n\n    Parameters:\n        path (string): absolute path of FFmpeg binaries\n        source (string/list): source to be validated.\n\n    **Returns:** A string value, confirming whether audio is present, or not?.\n    \"\"\"\n    if source is None or not (source):\n        logger.warning(\"Audio input source is empty!\")\n        return \"\"\n\n    # create ffmpeg command\n    cmd = [path, \"-hide_banner\"] + (\n        source if isinstance(source, list) else [\"-i\", source]\n    )\n    # extract metadata\n    metadata = check_output(cmd, force_retrieve_stderr=True)\n    # extract bitrate\n    audio_bitrate_meta = [\n        line.strip()\n        for line in metadata.decode(\"utf-8\").split(\"\\n\")\n        if \"Audio:\" in line\n    ]\n    audio_bitrate = (\n        re.findall(r\"([0-9]+)\\s(kb|mb|gb)\\/s\", audio_bitrate_meta[0])[-1]\n        if audio_bitrate_meta\n        else \"\"\n    )\n    # extract samplerate\n    audio_samplerate_metadata = [\n        line.strip()\n        for line in metadata.decode(\"utf-8\").split(\"\\n\")\n        if all(x in line for x in [\"Audio:\", \"Hz\"])\n    ]\n    audio_samplerate = (\n        re.findall(r\"[0-9]+\\sHz\", audio_samplerate_metadata[0])[0]\n        if audio_samplerate_metadata\n        else \"\"\n    )\n    # format into actual readable bitrate value\n    if audio_bitrate:\n        # return bitrate directly\n        return \"{}{}\".format(int(audio_bitrate[0].strip()), audio_bitrate[1].strip()[0])\n    elif audio_samplerate:\n        # convert samplerate to bitrate first\n        sample_rate_value = int(audio_samplerate.split(\" \")[0])\n        channels_value = 1 if \"mono\" in audio_samplerate_metadata[0] else 2\n        bit_depth_value = re.findall(\n            r\"(u|s|f)([0-9]+)(le|be)\", audio_samplerate_metadata[0]\n        )[0][1]\n        return (\n            (\n                str(\n                    get_audio_bitrate(\n                        sample_rate_value, channels_value, int(bit_depth_value)\n                    )\n                )\n                + \"k\"\n            )\n            if bit_depth_value\n            else \"\"\n        )\n    else:\n        return \"\"\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.extract_time--extract_time","title":"extract_time","text":"

Extract time from give string value.

Parameters:

Name Type Description Default value string

string value.

required

Returns: Time (in seconds) as integer.

Source code in vidgear/gears/helper.py
def extract_time(value):\n    \"\"\"\n    ## extract_time\n\n    Extract time from give string value.\n\n    Parameters:\n        value (string): string value.\n\n    **Returns:** Time _(in seconds)_ as integer.\n    \"\"\"\n    if not (value):\n        logger.warning(\"Value is empty!\")\n        return 0\n    else:\n        stripped_data = value.strip()\n        t_duration = re.findall(r\"\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{2})?\", stripped_data)\n        return (\n            sum(\n                float(x) * 60**i\n                for i, x in enumerate(reversed(t_duration[0].split(\":\")))\n            )\n            if t_duration\n            else 0\n        )\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.validate_video--validate_video","title":"validate_video","text":"

Validates video by retrieving resolution/size and framerate from file.

Parameters:

Name Type Description Default path string

absolute path of FFmpeg binaries

required video_path string

absolute path to Video.

None

Returns: A dictionary of retieved Video resolution (as tuple(width, height)) and framerate (as float).

Source code in vidgear/gears/helper.py
def validate_video(path, video_path=None, logging=False):\n    \"\"\"\n    ## validate_video\n\n    Validates video by retrieving resolution/size and framerate from file.\n\n    Parameters:\n        path (string): absolute path of FFmpeg binaries\n        video_path (string): absolute path to Video.\n\n    **Returns:** A dictionary of retieved Video resolution _(as tuple(width, height))_ and framerate _(as float)_.\n    \"\"\"\n    if video_path is None or not (video_path):\n        logger.warning(\"Video path is empty!\")\n        return None\n\n    # extract metadata\n    metadata = check_output(\n        [path, \"-hide_banner\", \"-i\", video_path], force_retrieve_stderr=True\n    )\n    # clean and search\n    stripped_data = [x.decode(\"utf-8\").strip() for x in metadata.split(b\"\\n\")]\n    logging and logger.debug(stripped_data)\n    result = {}\n    for data in stripped_data:\n        output_a = re.findall(r\"([1-9]\\d+)x([1-9]\\d+)\", data)\n        output_b = re.findall(r\"\\d+(?:\\.\\d+)?\\sfps\", data)\n        if len(result) == 2:\n            break\n        if output_b and not \"framerate\" in result:\n            result[\"framerate\"] = re.findall(r\"[\\d\\.\\d]+\", output_b[0])[0]\n        if output_a and not \"resolution\" in result:\n            result[\"resolution\"] = output_a[-1]\n\n    # return values\n    return result if (len(result) == 2) else None\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.is_valid_url--is_valid_url","title":"is_valid_url","text":"

Checks URL validity by testing its scheme against FFmpeg's supported protocols

Parameters:

Name Type Description Default path string

absolute path of FFmpeg binaries

required url string

URL to be validated

None logging bool

enables logging for its operations

False

Returns: A boolean value, confirming whether tests passed, or not?.

Source code in vidgear/gears/helper.py
def is_valid_url(path, url=None, logging=False):\n    \"\"\"\n    ## is_valid_url\n\n    Checks URL validity by testing its scheme against\n    FFmpeg's supported protocols\n\n    Parameters:\n        path (string): absolute path of FFmpeg binaries\n        url (string): URL to be validated\n        logging (bool): enables logging for its operations\n\n    **Returns:** A boolean value, confirming whether tests passed, or not?.\n    \"\"\"\n    if url is None or not (url):\n        logger.warning(\"URL is empty!\")\n        return False\n    # extract URL scheme\n    extracted_scheme_url = url.split(\"://\", 1)[0]\n    # extract all FFmpeg supported protocols\n    protocols = check_output([path, \"-hide_banner\", \"-protocols\"])\n    splitted = [x.decode(\"utf-8\").strip() for x in protocols.split(b\"\\n\")]\n    supported_protocols = splitted[splitted.index(\"Output:\") + 1 : len(splitted) - 1]\n    # RTSP is a demuxer somehow\n    # support both RTSP and RTSPS(over SSL)\n    supported_protocols += (\n        [\"rtsp\", \"rtsps\"] if \"rtsp\" in get_supported_demuxers(path) else []\n    )\n    # Test and return result whether scheme is supported\n    if extracted_scheme_url and extracted_scheme_url in supported_protocols:\n        logging and logger.debug(\n            \"URL scheme `{}` is supported by FFmpeg.\".format(extracted_scheme_url)\n        )\n        return True\n    else:\n        logger.warning(\n            \"URL scheme `{}` isn't supported by FFmpeg!\".format(extracted_scheme_url)\n        )\n        return False\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.import_dependency_safe--import_dependency_safe","title":"import_dependency_safe","text":"

Imports specified dependency safely. By default(error = raise), if a dependency is missing, an ImportError with a meaningful message will be raised. Otherwise if error = log a warning will be logged and on error = silent everything will be quit. But If a dependency is present, but older than specified, an error is raised if specified.

Parameters:

Name Type Description Default name string

name of dependency to be imported.

required error string

raise or Log or silence ImportError. Possible values are \"raise\", \"log\" and silent. Default is \"raise\".

'raise' pkg_name string

(Optional) package name of dependency(if different pip name). Otherwise name will be used.

None min_version string

(Optional) required minimum version of the dependency to be imported.

None custom_message string

(Optional) custom Import error message to be raised or logged.

None

Returns: The imported module, when found and the version is correct(if specified). Otherwise None.

Source code in vidgear/gears/helper.py
def import_dependency_safe(\n    name,\n    error=\"raise\",\n    pkg_name=None,\n    min_version=None,\n    custom_message=None,\n):\n    \"\"\"\n    ## import_dependency_safe\n\n    Imports specified dependency safely. By default(`error = raise`), if a dependency is missing,\n    an ImportError with a meaningful message will be raised. Otherwise if `error = log` a warning\n    will be logged and on `error = silent` everything will be quit. But If a dependency is present,\n    but older than specified, an error is raised if specified.\n\n    Parameters:\n        name (string): name of dependency to be imported.\n        error (string): raise or Log or silence ImportError. Possible values are `\"raise\"`, `\"log\"` and `silent`. Default is `\"raise\"`.\n        pkg_name (string): (Optional) package name of dependency(if different `pip` name). Otherwise `name` will be used.\n        min_version (string): (Optional) required minimum version of the dependency to be imported.\n        custom_message (string): (Optional) custom Import error message to be raised or logged.\n\n    **Returns:** The imported module, when found and the version is correct(if specified). Otherwise `None`.\n    \"\"\"\n    # check specified parameters\n    sub_class = \"\"\n    if not name or not isinstance(name, str):\n        return None\n    else:\n        # extract name in case of relative import\n        name = name.strip()\n        if name.startswith(\"from\"):\n            name = name.split(\" \")\n            name, sub_class = (name[1].strip(), name[-1].strip())\n\n    assert error in [\n        \"raise\",\n        \"log\",\n        \"silent\",\n    ], \"[Vidgear:ERROR] :: Invalid value at `error` parameter.\"\n\n    # specify package name of dependency(if defined). Otherwise use name\n    install_name = pkg_name if not (pkg_name is None) else name\n\n    # create message\n    msg = (\n        custom_message\n        if not (custom_message is None)\n        else \"Failed to find required dependency '{}'. Install it with  `pip install {}` command.\".format(\n            name, install_name\n        )\n    )\n    # try importing dependency\n    try:\n        module = importlib.import_module(name)\n        module = getattr(module, sub_class) if sub_class else module\n    except Exception as e:\n        if error == \"raise\":\n            if isinstance(e, ModuleNotFoundError):\n                # raise message\n                raise ModuleNotFoundError(msg) from None\n            else:\n                # raise error+message\n                raise ImportError(msg) from e\n        elif error == \"log\":\n            logger.error(msg, exc_info=sys.exc_info())\n            return None\n        else:\n            return None\n\n    # check if minimum required version\n    if not (min_version) is None:\n        # Handle submodules\n        parent_module = name.split(\".\")[0]\n        if parent_module != name:\n            # grab parent module\n            module_to_get = sys.modules[parent_module]\n        else:\n            module_to_get = module\n        # extract version\n        version = get_module_version(module_to_get)\n        # verify\n        if parse_version(version) < parse_version(min_version):\n            # create message\n            msg = \"\"\"Unsupported version '{}' found. Vidgear requires '{}' dependency installed with version '{}' or greater. \n            Update it with  `pip install -U {}` command.\"\"\".format(\n                parent_module, min_version, version, install_name\n            )\n            # handle errors.\n            if error == \"silent\":\n                return None\n            else:\n                # raise\n                raise ImportError(msg)\n\n    return module\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.get_video_bitrate--get_video_bitrate","title":"get_video_bitrate","text":"

Calculate optimum Bitrate from resolution, framerate, bits-per-pixels values

Parameters:

Name Type Description Default width int

video-width

required height int

video-height

required fps float

video-framerate

required bpp float

bit-per-pixels value

required

Returns: Video bitrate (in Kbps) as integer.

Source code in vidgear/gears/helper.py
def get_video_bitrate(width, height, fps, bpp):\n    \"\"\"\n    ## get_video_bitrate\n\n    Calculate optimum Bitrate from resolution, framerate, bits-per-pixels values\n\n    Parameters:\n        width (int): video-width\n        height (int): video-height\n        fps (float): video-framerate\n        bpp (float): bit-per-pixels value\n\n    **Returns:** Video bitrate _(in Kbps)_ as integer.\n    \"\"\"\n    return round((width * height * bpp * fps) / 1000)\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.check_WriteAccess--check_writeaccess","title":"check_WriteAccess","text":"

Checks whether given path directory has Write-Access.

Parameters:

Name Type Description Default path string

absolute path of directory

required is_windows boolean

is running on Windows OS?

False logging bool

enables logging for its operations

False

Returns: A boolean value, confirming whether Write-Access available, or not?.

Source code in vidgear/gears/helper.py
def check_WriteAccess(path, is_windows=False, logging=False):\n    \"\"\"\n    ## check_WriteAccess\n\n    Checks whether given path directory has Write-Access.\n\n    Parameters:\n        path (string): absolute path of directory\n        is_windows (boolean): is running on Windows OS?\n        logging (bool): enables logging for its operations\n\n    **Returns:** A boolean value, confirming whether Write-Access available, or not?.\n    \"\"\"\n    # check if path exists\n    dirpath = Path(path)\n    try:\n        if not (dirpath.exists() and dirpath.is_dir()):\n            logger.warning(\n                \"Specified directory `{}` doesn't exists or valid.\".format(path)\n            )\n            return False\n        else:\n            path = dirpath.resolve()\n    except:\n        return False\n    # check filepath on *nix systems\n    if not is_windows:\n        uid = os.geteuid()\n        gid = os.getegid()\n        s = os.stat(path)\n        mode = s[stat.ST_MODE]\n        return (\n            ((s[stat.ST_UID] == uid) and (mode & stat.S_IWUSR))\n            or ((s[stat.ST_GID] == gid) and (mode & stat.S_IWGRP))\n            or (mode & stat.S_IWOTH)\n        )\n    # otherwise, check filepath on windows\n    else:\n        write_accessible = False\n        temp_fname = os.path.join(path, \"temp.tmp\")\n        try:\n            fd = os.open(temp_fname, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)\n            os.close(fd)\n            write_accessible = True\n        except Exception as e:\n            if isinstance(e, PermissionError):\n                logger.error(\n                    \"You don't have adequate access rights to use `{}` directory!\".format(\n                        path\n                    )\n                )\n            logging and logger.exception(str(e))\n        finally:\n            delete_file_safe(temp_fname)\n        return write_accessible\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.check_open_port--check_open_port","title":"check_open_port","text":"

Checks whether specified port open at given IP address.

Parameters:

Name Type Description Default address string

given IP address.

required port int

check if port is open at given address.

22

Returns: A boolean value, confirming whether given port is open at given IP address.

Source code in vidgear/gears/helper.py
def check_open_port(address, port=22):\n    \"\"\"\n    ## check_open_port\n\n    Checks whether specified port open at given IP address.\n\n    Parameters:\n        address (string): given IP address.\n        port (int): check if port is open at given address.\n\n    **Returns:** A boolean value, confirming whether given port is open at given IP address.\n    \"\"\"\n    if not address:\n        return False\n    with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:\n        if sock.connect_ex((address, port)) == 0:\n            return True\n        else:\n            return False\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.delete_file_safe--delete_ext_safe","title":"delete_ext_safe","text":"

Safely deletes files at given path.

Parameters:

Name Type Description Default file_path string

path to the file

required Source code in vidgear/gears/helper.py
def delete_file_safe(file_path):\n    \"\"\"\n    ## delete_ext_safe\n\n    Safely deletes files at given path.\n\n    Parameters:\n        file_path (string): path to the file\n    \"\"\"\n    try:\n        dfile = Path(file_path)\n        dfile.unlink(missing_ok=True)\n    except Exception as e:\n        logger.exception(str(e))\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.get_supported_demuxers--get_supported_demuxers","title":"get_supported_demuxers","text":"

Find and returns FFmpeg's supported demuxers

Parameters:

Name Type Description Default path string

absolute path of FFmpeg binaries

required

Returns: List of supported demuxers.

Source code in vidgear/gears/helper.py
def get_supported_demuxers(path):\n    \"\"\"\n    ## get_supported_demuxers\n\n    Find and returns FFmpeg's supported demuxers\n\n    Parameters:\n        path (string): absolute path of FFmpeg binaries\n\n    **Returns:** List of supported demuxers.\n    \"\"\"\n    demuxers = check_output([path, \"-hide_banner\", \"-demuxers\"])\n    splitted = [x.decode(\"utf-8\").strip() for x in demuxers.split(b\"\\n\")]\n    split_index = [idx for idx, s in enumerate(splitted) if \"--\" in s][0]\n    supported_demuxers = splitted[split_index + 1 : len(splitted) - 1]\n    # compile regex\n    finder = re.compile(r\"\\s\\s[a-z0-9_,-]+\\s+\")\n    # find all outputs\n    outputs = finder.findall(\"\\n\".join(supported_demuxers))\n    # return output findings\n    return [o.strip() for o in outputs]\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.get_supported_vencoders--get_supported_vencoders","title":"get_supported_vencoders","text":"

Find and returns FFmpeg's supported video encoders

Parameters:

Name Type Description Default path string

absolute path of FFmpeg binaries

required

Returns: List of supported encoders.

Source code in vidgear/gears/helper.py
def get_supported_vencoders(path):\n    \"\"\"\n    ## get_supported_vencoders\n\n    Find and returns FFmpeg's supported video encoders\n\n    Parameters:\n        path (string): absolute path of FFmpeg binaries\n\n    **Returns:** List of supported encoders.\n    \"\"\"\n    encoders = check_output([path, \"-hide_banner\", \"-encoders\"])\n    splitted = encoders.split(b\"\\n\")\n    # extract video encoders\n    supported_vencoders = [\n        x.decode(\"utf-8\").strip()\n        for x in splitted[2 : len(splitted) - 1]\n        if x.decode(\"utf-8\").strip().startswith(\"V\")\n    ]\n    # compile regex\n    finder = re.compile(r\"[A-Z]*[\\.]+[A-Z]*\\s[a-z0-9_-]*\")\n    # find all outputs\n    outputs = finder.findall(\"\\n\".join(supported_vencoders))\n    # return output findings\n    return [[s for s in o.split(\" \")][-1] for o in outputs]\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.validate_auth_keys--validate_auth_keys","title":"validate_auth_keys","text":"

Validates, and also maintains generated ZMQ CURVE Key-pairs.

Parameters:

Name Type Description Default path string

path of generated CURVE key-pairs

required extension string

type of key-pair to be validated

required

Returns: A boolean value, confirming whether tests passed, or not?.

Source code in vidgear/gears/helper.py
def validate_auth_keys(path, extension):\n    \"\"\"\n    ## validate_auth_keys\n\n    Validates, and also maintains generated ZMQ CURVE Key-pairs.\n\n    Parameters:\n        path (string): path of generated CURVE key-pairs\n        extension (string): type of key-pair to be validated\n\n    **Returns:** A boolean value, confirming whether tests passed, or not?.\n    \"\"\"\n    # check for valid path\n    if not (os.path.exists(path)):\n        return False\n\n    # check if directory empty\n    if not (os.listdir(path)):\n        return False\n\n    keys_buffer = []  # stores auth-keys\n\n    # loop over auth-keys\n    for key_file in os.listdir(path):\n        key = os.path.splitext(key_file)\n        # check if valid key is generated\n        if key and (key[0] in [\"server\", \"client\"]) and (key[1] == extension):\n            keys_buffer.append(key_file)  # store it\n\n    # remove invalid keys if found\n    len(keys_buffer) == 1 and delete_file_safe(os.path.join(path, keys_buffer[0]))\n\n    # return results\n    return True if (len(keys_buffer) == 2) else False\n
"},{"location":"bonus/reference/helper_async/","title":"Helper Methods","text":""},{"location":"bonus/reference/helper_async/#vidgear.gears.asyncio.helper.reducer--reducer","title":"reducer","text":"

Asynchronous method that reduces frame size by given percentage.

Parameters:

Name Type Description Default frame numpy.ndarray

inputs numpy array(frame).

None percentage int/float

inputs size-reduction percentage.

0 interpolation int

Change resize interpolation.

4

Returns: A reduced numpy ndarray array.

Source code in vidgear/gears/asyncio/helper.py
async def reducer(frame=None, percentage=0, interpolation=cv2.INTER_LANCZOS4):\n    \"\"\"\n    ## reducer\n\n    Asynchronous method that reduces frame size by given percentage.\n\n    Parameters:\n        frame (numpy.ndarray): inputs numpy array(frame).\n        percentage (int/float): inputs size-reduction percentage.\n        interpolation (int): Change resize interpolation.\n\n    **Returns:**  A reduced numpy ndarray array.\n    \"\"\"\n    # check if frame is valid\n    if frame is None:\n        raise ValueError(\"[Helper:ERROR] :: Input frame cannot be NoneType!\")\n\n    # check if valid reduction percentage is given\n    if not (percentage > 0 and percentage < 90):\n        raise ValueError(\n            \"[Helper:ERROR] :: Given frame-size reduction percentage is invalid, Kindly refer docs.\"\n        )\n\n    if not (isinstance(interpolation, int)):\n        raise ValueError(\n            \"[Helper:ERROR] :: Given interpolation is invalid, Kindly refer docs.\"\n        )\n\n    # grab the frame size\n    (height, width) = frame.shape[:2]\n\n    # calculate the ratio of the width from percentage\n    reduction = ((100 - percentage) / 100) * width\n    ratio = reduction / float(width)\n    # construct the dimensions\n    dimensions = (int(reduction), int(height * ratio))\n\n    # return the resized frame\n    return cv2.resize(frame, dimensions, interpolation=interpolation)\n
"},{"location":"bonus/reference/helper_async/#vidgear.gears.asyncio.helper.create_blank_frame--create_blank_frame","title":"create_blank_frame","text":"

Create blank frames of given frame size with text

Parameters:

Name Type Description Default frame numpy.ndarray

inputs numpy array(frame).

None text str

Text to be written on frame.

''

Returns: A reduced numpy ndarray array.

Source code in vidgear/gears/asyncio/helper.py
def create_blank_frame(frame=None, text=\"\", logging=False):\n    \"\"\"\n    ## create_blank_frame\n\n    Create blank frames of given frame size with text\n\n    Parameters:\n        frame (numpy.ndarray): inputs numpy array(frame).\n        text (str): Text to be written on frame.\n    **Returns:**  A reduced numpy ndarray array.\n    \"\"\"\n    # check if frame is valid\n    if frame is None or not (isinstance(frame, np.ndarray)):\n        raise ValueError(\"[Helper:ERROR] :: Input frame is invalid!\")\n    # grab the frame size\n    (height, width) = frame.shape[:2]\n    # create blank frame\n    blank_frame = np.zeros(frame.shape, frame.dtype)\n    # setup text\n    if text and isinstance(text, str):\n        if logging:\n            logger.debug(\"Adding text: {}\".format(text))\n        # setup font\n        font = cv2.FONT_HERSHEY_SCRIPT_COMPLEX\n        # get boundary of this text\n        fontScale = min(height, width) / (25 / 0.25)\n        textsize = cv2.getTextSize(text, font, fontScale, 5)[0]\n        # get coords based on boundary\n        textX = (width - textsize[0]) // 2\n        textY = (height + textsize[1]) // 2\n        # put text\n        cv2.putText(\n            blank_frame, text, (textX, textY), font, fontScale, (125, 125, 125), 6\n        )\n\n    # return frame\n    return blank_frame\n
"},{"location":"bonus/reference/helper_async/#vidgear.gears.asyncio.helper.generate_webdata--generate_webdata","title":"generate_webdata","text":"

Auto-Generates, and Auto-validates default data for WebGear and WebGear_RTC APIs.

Parameters:

Name Type Description Default path string

path for generating data

required c_name string

class name that is generating files

'webgear' overwrite_default boolean

overwrite existing data or not?

False logging bool

enables logging for its operations

False

Returns: A valid data path as string.

Source code in vidgear/gears/asyncio/helper.py
def generate_webdata(path, c_name=\"webgear\", overwrite_default=False, logging=False):\n    \"\"\"\n    ## generate_webdata\n\n    Auto-Generates, and Auto-validates default data for WebGear and WebGear_RTC APIs.\n\n    Parameters:\n        path (string): path for generating data\n        c_name (string): class name that is generating files\n        overwrite_default (boolean): overwrite existing data or not?\n        logging (bool): enables logging for its operations\n\n    **Returns:** A valid data path as string.\n    \"\"\"\n    # check if path corresponds to vidgear only\n    if os.path.basename(path) != \".vidgear\":\n        path = os.path.join(path, \".vidgear\")\n\n    # generate parent directory\n    path = os.path.join(path, c_name)\n    mkdir_safe(path, logging=logging)\n\n    # self-generate dirs\n    template_dir = os.path.join(path, \"templates\")  # generates HTML templates dir\n    static_dir = os.path.join(path, \"static\")  # generates static dir\n    # generate js & css static and favicon img subdirs\n    js_static_dir = os.path.join(static_dir, \"js\")\n    css_static_dir = os.path.join(static_dir, \"css\")\n    favicon_dir = os.path.join(static_dir, \"img\")\n\n    mkdir_safe(static_dir, logging=logging)\n    mkdir_safe(template_dir, logging=logging)\n    mkdir_safe(js_static_dir, logging=logging)\n    mkdir_safe(css_static_dir, logging=logging)\n    mkdir_safe(favicon_dir, logging=logging)\n\n    # check if overwriting is enabled\n    if overwrite_default or not validate_webdata(\n        template_dir, [\"index.html\", \"404.html\", \"500.html\"]\n    ):\n        logger.critical(\n            \"Overwriting existing {} data-files with default data-files from the server!\".format(\n                c_name.capitalize()\n            )\n            if overwrite_default\n            else \"Failed to detect critical {} data-files: index.html, 404.html & 500.html!\".format(\n                c_name.capitalize()\n            )\n        )\n        # download default files\n        logging and logger.info(\n            \"Downloading default data-files from the Gitlab Server: {}\".format(\n                \"https://gitlab.com/abhiTronix/vidgear-vitals\"\n            )\n        )\n        download_webdata(\n            template_dir,\n            c_name=c_name,\n            files=[\"index.html\", \"404.html\", \"500.html\", \"base.html\"],\n            logging=logging,\n        )\n        download_webdata(\n            css_static_dir, c_name=c_name, files=[\"custom.css\"], logging=logging\n        )\n        download_webdata(\n            js_static_dir,\n            c_name=c_name,\n            files=[\"custom.js\"],\n            logging=logging,\n        )\n        download_webdata(\n            favicon_dir, c_name=c_name, files=[\"favicon-32x32.png\"], logging=logging\n        )\n    else:\n        # validate important data-files\n        if logging:\n            logger.debug(\"Found valid WebGear data-files successfully.\")\n\n    return path\n
"},{"location":"bonus/reference/helper_async/#vidgear.gears.asyncio.helper.download_webdata--download_webdata","title":"download_webdata","text":"

Downloads given list of files for WebGear and WebGear_RTC APIs(if not available) from GitHub/Gitlab Servers, and also Validates them.

Parameters:

Name Type Description Default path string

path for downloading data

required c_name string

class name that is generating files

'webgear' files list

list of files to be downloaded

[] logging bool

enables logging for its operations

False

Returns: A valid path as string.

Source code in vidgear/gears/asyncio/helper.py
def download_webdata(path, c_name=\"webgear\", files=[], logging=False):\n    \"\"\"\n    ## download_webdata\n\n    Downloads given list of files for WebGear and WebGear_RTC APIs(if not available) from GitHub/Gitlab Servers,\n    and also Validates them.\n\n    Parameters:\n        path (string): path for downloading data\n        c_name (string): class name that is generating files\n        files (list): list of files to be downloaded\n        logging (bool): enables logging for its operations\n\n    **Returns:** A valid path as string.\n    \"\"\"\n    basename = os.path.basename(path)\n    if logging:\n        logger.debug(\"Downloading {} data-files at `{}`\".format(basename, path))\n\n    # list all registered urls\n    reg_urls = [\n        \"https://gitlab.com/abhiTronix/vidgear-vitals/-/raw/main\",\n        \"https://raw.githubusercontent.com/abhiTronix/vidgear-vitals/main\",\n    ]\n\n    # create session\n    with requests.Session() as http:\n        for url in reg_urls:\n            try:\n                for file in files:\n                    # get filename\n                    file_name = os.path.join(path, file)\n                    # get URL\n                    file_url = \"{}/{}{}/{}/{}\".format(\n                        url,\n                        c_name,\n                        \"/static\" if basename != \"templates\" else \"\",\n                        basename,\n                        file,\n                    )\n                    # download and write file to the given path\n                    logging and logger.debug(\n                        \"Downloading {} data-file: {}.\".format(basename, file)\n                    )\n\n                    with open(file_name, \"wb\") as f:\n                        # setup retry strategy\n                        retries = Retry(\n                            total=3,\n                            backoff_factor=1,\n                            status_forcelist=[429, 500, 502, 503, 504],\n                        )\n                        # Mount it for https usage\n                        adapter = TimeoutHTTPAdapter(timeout=2.0, max_retries=retries)\n                        http.mount(\"https://\", adapter)\n                        response = http.get(file_url, stream=True)\n                        response.raise_for_status()\n                        total_length = (\n                            response.headers.get(\"content-length\")\n                            if \"content-length\" in response.headers\n                            else len(response.content)\n                        )\n                        assert not (\n                            total_length is None\n                        ), \"[Helper:ERROR] :: Failed to retrieve files, check your Internet connectivity!\"\n                        bar = tqdm(total=int(total_length), unit=\"B\", unit_scale=True)\n                        for data in response.iter_content(chunk_size=256):\n                            f.write(data)\n                            if len(data) > 0:\n                                bar.update(len(data))\n                        bar.close()\n            except AssertionError as e:\n                # raise if connection error\n                raise e\n            except Exception as e:\n                # log error\n                logger.exception(str(e))\n                # log event if necessary\n                url != reg_urls[1] and logger.error(\n                    \"Download failed for Gitlab Server! Retrying from GitHub Server: {}\".format(\n                        url, \"https://github.com/abhiTronix/vidgear-vitals\"\n                    )\n                )\n            else:\n                # break otherwise\n                break\n\n    if logging:\n        logger.debug(\"Verifying downloaded data:\")\n    if validate_webdata(path, files=files, logging=logging):\n        if logging:\n            logger.info(\"Successful!\")\n        return path\n    else:\n        raise RuntimeError(\n            \"[Helper:ERROR] :: Failed to download required {} data-files at: {}, Check your Internet connectivity!\".format(\n                basename, path\n            )\n        )\n
"},{"location":"bonus/reference/helper_async/#vidgear.gears.asyncio.helper.validate_webdata--validate_auth_keys","title":"validate_auth_keys","text":"

Validates, and also maintains downloaded list of files.

Parameters:

Name Type Description Default path string

path of downloaded files

required files list

list of files to be validated

[] logging bool

enables logging for its operations

False

Returns: A boolean value, confirming whether tests passed, or not?.

Source code in vidgear/gears/asyncio/helper.py
def validate_webdata(path, files=[], logging=False):\n    \"\"\"\n    ## validate_auth_keys\n\n    Validates, and also maintains downloaded list of files.\n\n    Parameters:\n        path (string): path of downloaded files\n        files (list): list of files to be validated\n        logging (bool): enables logging for its operations\n\n    **Returns:** A  boolean value, confirming whether tests passed, or not?.\n    \"\"\"\n    # check if valid path or directory empty\n    if not (os.path.exists(path)) or not (os.listdir(path)):\n        return False\n\n    files_buffer = []\n    # loop over files\n    for file in os.listdir(path):\n        if file in files:\n            files_buffer.append(file)  # store them\n\n    # return results\n    if len(files_buffer) < len(files):\n        if logging:\n            logger.warning(\n                \"`{}` file(s) missing from data-files!\".format(\n                    \" ,\".join(list(set(files_buffer) ^ set(files)))\n                )\n            )\n        return False\n    else:\n        return True\n
"},{"location":"bonus/reference/netgear/","title":"NetGear API References","text":"

NetGear API usage examples can be found here \u27b6

NetGear API parameters are explained here \u27b6

NetGear is exclusively designed to transfer video frames synchronously and asynchronously between interconnecting systems over the network in real-time.

NetGear implements a high-level wrapper around PyZmQ python library that contains python bindings for ZeroMQ - a high-performance asynchronous distributed messaging library that provides a message queue, but unlike message-oriented middleware, its system can run without a dedicated message broker.

NetGear also supports real-time Frame Compression capabilities for optimizing performance while sending the frames directly over the network, by encoding the frame before sending it and decoding it on the client's end automatically in real-time.

Info

NetGear API now internally implements robust Lazy Pirate pattern (auto-reconnection) for its synchronous messaging patterns (i.e. zmq.PAIR & zmq.REQ/zmq.REP) at both Server and Client ends, where its API instead of doing a blocking receive, will:

NetGear as of now seamlessly supports three ZeroMQ messaging patterns:

whereas the supported protocol are: tcp and ipc.

Modes of Operation Source code in vidgear/gears/netgear.py
class NetGear:\n    \"\"\"\n    NetGear is exclusively designed to transfer video frames synchronously and asynchronously between interconnecting systems over the network in real-time.\n\n    NetGear implements a high-level wrapper around PyZmQ python library that contains python bindings for ZeroMQ - a high-performance asynchronous distributed messaging library\n    that provides a message queue, but unlike message-oriented middleware, its system can run without a dedicated message broker.\n\n    NetGear also supports real-time Frame Compression capabilities for optimizing performance while sending the frames directly over the network, by encoding the frame before sending\n    it and decoding it on the client's end automatically in real-time.\n\n    !!! info\n        NetGear API now internally implements robust *Lazy Pirate pattern* (auto-reconnection) for its synchronous messaging patterns _(i.e. `zmq.PAIR` & `zmq.REQ/zmq.REP`)_\n        at both Server and Client ends, where its API instead of doing a blocking receive, will:\n\n        * Poll the socket and receive from it only when it's sure a reply has arrived.\n        * Attempt to reconnect, if no reply has arrived within a timeout period.\n        * Abandon the connection if there is still no reply after several requests.\n\n    NetGear as of now seamlessly supports three ZeroMQ messaging patterns:\n\n    - `zmq.PAIR` _(ZMQ Pair Pattern)_\n    - `zmq.REQ/zmq.REP` _(ZMQ Request/Reply Pattern)_\n    - `zmq.PUB/zmq.SUB` _(ZMQ Publish/Subscribe Pattern)_\n\n    _whereas the supported protocol are: `tcp` and `ipc`_.\n\n    ??? tip \"Modes of Operation\"\n\n        * **Primary Modes**\n\n            NetGear API primarily has two modes of operations:\n\n            * **Send Mode:** _which employs `send()` function to send video frames over the network in real-time._\n\n            * **Receive Mode:** _which employs `recv()` function to receive frames, sent over the network with *Send Mode* in real-time. The mode sends back confirmation when the\n            frame is received successfully in few patterns._\n\n        * **Exclusive Modes**\n\n            In addition to these primary modes, NetGear API offers applications-specific Exclusive Modes:\n\n            * **Multi-Servers Mode:** _In this exclusive mode, NetGear API robustly **handles multiple servers at once**, thereby providing seamless access to frames and unidirectional\n            data transfer from multiple Servers/Publishers across the network in real-time._\n\n            * **Multi-Clients Mode:** _In this exclusive mode, NetGear API robustly **handles multiple clients at once**, thereby providing seamless access to frames and unidirectional\n            data transfer to multiple Client/Consumers across the network in real-time._\n\n            * **Bidirectional Mode:** _This exclusive mode **provides seamless support for bidirectional data transmission between between Server and Client along with video frames**._\n\n            * **Secure Mode:** _In this exclusive mode, NetGear API **provides easy access to powerful, smart & secure ZeroMQ's Security Layers** that enables strong encryption on\n            data, and unbreakable authentication between the Server and Client with the help of custom certificates/keys that brings cheap, standardized privacy and authentication\n            for distributed systems over the network._\n    \"\"\"\n\n    def __init__(\n        self,\n        address=None,\n        port=None,\n        protocol=None,\n        pattern=0,\n        receive_mode=False,\n        logging=False,\n        **options\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the NetGear class.\n\n        Parameters:\n            address (str): sets the valid network address of the Server/Client.\n            port (str): sets the valid Network Port of the Server/Client.\n            protocol (str): sets the valid messaging protocol between Server/Client.\n            pattern (int): sets the supported messaging pattern(flow of communication) between Server/Client\n            receive_mode (bool): select the Netgear's Mode of operation.\n            logging (bool): enables/disables logging.\n            options (dict): provides the flexibility to alter various NetGear internal properties.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # raise error(s) for critical Class imports\n        import_dependency_safe(\n            \"zmq\" if zmq is None else \"\", min_version=\"4.0\", pkg_name=\"pyzmq\"\n        )\n        import_dependency_safe(\n            \"simplejpeg\" if simplejpeg is None else \"\", error=\"log\", min_version=\"1.6.1\"\n        )\n\n        # define valid messaging patterns => `0`: zmq.PAIR, `1`:(zmq.REQ,zmq.REP), and `1`:(zmq.SUB,zmq.PUB)\n        valid_messaging_patterns = {\n            0: (zmq.PAIR, zmq.PAIR),\n            1: (zmq.REQ, zmq.REP),\n            2: (zmq.PUB, zmq.SUB),\n        }\n\n        # Handle messaging pattern\n        msg_pattern = None\n        # check whether user-defined messaging pattern is valid\n        if isinstance(pattern, int) and pattern in valid_messaging_patterns.keys():\n            # assign value\n            msg_pattern = valid_messaging_patterns[pattern]\n        else:\n            # otherwise default to 0:`zmq.PAIR`\n            pattern = 0\n            msg_pattern = valid_messaging_patterns[pattern]\n            self.__logging and logger.warning(\n                \"Wrong pattern value, Defaulting to `zmq.PAIR`! Kindly refer Docs for more Information.\"\n            )\n        # assign pattern to global parameter for further use\n        self.__pattern = pattern\n\n        # Handle messaging protocol\n        if protocol is None or not (protocol in [\"tcp\", \"ipc\"]):\n            # else default to `tcp` protocol\n            protocol = \"tcp\"\n            # log it\n            self.__logging and logger.warning(\n                \"Protocol is not supported or not provided. Defaulting to `tcp` protocol!\"\n            )\n\n        # Handle connection params\n\n        self.__msg_flag = 0  # handles connection flags\n        self.__msg_copy = False  # handles whether to copy data\n        self.__msg_track = False  # handles whether to track packets\n\n        # Handle NetGear's internal exclusive modes and params\n\n        # define Secure Mode\n        self.__z_auth = None\n\n        # define SSH Tunneling Mode\n        self.__ssh_tunnel_mode = None  # handles ssh_tunneling mode state\n        self.__ssh_tunnel_pwd = None\n        self.__ssh_tunnel_keyfile = None\n        self.__paramiko_present = False if paramiko is None else True\n\n        # define Multi-Server mode\n        self.__multiserver_mode = False  # handles multi-server mode state\n\n        # define Multi-Client mode\n        self.__multiclient_mode = False  # handles multi-client mode state\n\n        # define Bidirectional mode\n        self.__bi_mode = False  # handles Bidirectional mode state\n\n        # define Secure mode\n        valid_security_mech = {0: \"Grasslands\", 1: \"StoneHouse\", 2: \"IronHouse\"}\n        self.__secure_mode = 0  # handles ZMQ security layer status\n        auth_cert_dir = \"\"  # handles valid ZMQ certificates dir\n        self.__auth_publickeys_dir = \"\"  # handles valid ZMQ public certificates dir\n        self.__auth_secretkeys_dir = \"\"  # handles valid ZMQ private certificates dir\n        overwrite_cert = False  # checks if certificates overwriting allowed\n        custom_cert_location = \"\"  # handles custom ZMQ certificates path\n\n        # define frame-compression handler\n        self.__jpeg_compression = (\n            True if not (simplejpeg is None) else False\n        )  # enabled by default for all connections if simplejpeg is installed\n        self.__jpeg_compression_quality = 90  # 90% quality\n        self.__jpeg_compression_fastdct = True  # fastest DCT on by default\n        self.__jpeg_compression_fastupsample = False  # fastupsample off by default\n        self.__jpeg_compression_colorspace = \"BGR\"  # use BGR colorspace by default\n\n        # defines frame compression on return data\n        self.__ex_compression_params = None\n\n        # define receiver return data handler\n        self.__return_data = None\n\n        # generate 8-digit random system id\n        self.__id = \"\".join(\n            secrets.choice(string.ascii_uppercase + string.digits) for i in range(8)\n        )\n\n        # define termination flag\n        self.__terminate = False\n\n        # additional settings for reliability\n        if pattern < 2:\n            # define zmq poller for reliable transmission\n            self.__poll = zmq.Poller()\n            # define max retries\n            self.__max_retries = 3\n            # request timeout\n            self.__request_timeout = 4000  # 4 secs\n        else:\n            # subscriber timeout\n            self.__subscriber_timeout = None\n\n        # Handle user-defined options dictionary values\n        # reformat dictionary\n        options = {str(k).strip(): v for k, v in options.items()}\n\n        # loop over dictionary key & values and assign to global variables if valid\n        for key, value in options.items():\n            # handle multi-server mode\n            if key == \"multiserver_mode\" and isinstance(value, bool):\n                # check if valid pattern assigned\n                if pattern > 0:\n                    # activate Multi-server mode\n                    self.__multiserver_mode = value\n                else:\n                    # otherwise disable it and raise error\n                    self.__multiserver_mode = False\n                    logger.critical(\"Multi-Server Mode is disabled!\")\n                    raise ValueError(\n                        \"[NetGear:ERROR] :: `{}` pattern is not valid when Multi-Server Mode is enabled. Kindly refer Docs for more Information.\".format(\n                            pattern\n                        )\n                    )\n\n            # handle multi-client mode\n            elif key == \"multiclient_mode\" and isinstance(value, bool):\n                # check if valid pattern assigned\n                if pattern > 0:\n                    # activate Multi-client mode\n                    self.__multiclient_mode = value\n                else:\n                    # otherwise disable it and raise error\n                    self.__multiclient_mode = False\n                    logger.critical(\"Multi-Client Mode is disabled!\")\n                    raise ValueError(\n                        \"[NetGear:ERROR] :: `{}` pattern is not valid when Multi-Client Mode is enabled. Kindly refer Docs for more Information.\".format(\n                            pattern\n                        )\n                    )\n\n            # handle bidirectional mode\n            elif key == \"bidirectional_mode\" and isinstance(value, bool):\n                # check if pattern is valid\n                if pattern < 2:\n                    # activate Bidirectional mode if specified\n                    self.__bi_mode = value\n                else:\n                    # otherwise disable it and raise error\n                    self.__bi_mode = False\n                    logger.warning(\"Bidirectional data transmission is disabled!\")\n                    raise ValueError(\n                        \"[NetGear:ERROR] :: `{}` pattern is not valid when Bidirectional Mode is enabled. Kindly refer Docs for more Information!\".format(\n                            pattern\n                        )\n                    )\n\n            # handle secure mode\n            elif (\n                key == \"secure_mode\"\n                and isinstance(value, int)\n                and (value in valid_security_mech)\n            ):\n                self.__secure_mode = value\n\n            elif key == \"custom_cert_location\" and isinstance(value, str):\n                # verify custom auth certificates path for secure mode\n                custom_cert_location = os.path.abspath(value)\n                assert os.path.isdir(\n                    custom_cert_location\n                ), \"[NetGear:ERROR] :: `custom_cert_location` value must be the path to a valid directory!\"\n                assert check_WriteAccess(\n                    custom_cert_location,\n                    is_windows=True if os.name == \"nt\" else False,\n                    logging=self.__logging,\n                ), \"[NetGear:ERROR] :: Permission Denied!, cannot write ZMQ authentication certificates to '{}' directory!\".format(\n                    value\n                )\n            elif key == \"overwrite_cert\" and isinstance(value, bool):\n                # enable/disable auth certificate overwriting in secure mode\n                overwrite_cert = value\n\n            # handle ssh-tunneling mode\n            elif key == \"ssh_tunnel_mode\" and isinstance(value, str):\n                # enable SSH Tunneling Mode\n                self.__ssh_tunnel_mode = value.strip()\n            elif key == \"ssh_tunnel_pwd\" and isinstance(value, str):\n                # add valid SSH Tunneling password\n                self.__ssh_tunnel_pwd = value\n            elif key == \"ssh_tunnel_keyfile\" and isinstance(value, str):\n                # add valid SSH Tunneling key-file\n                self.__ssh_tunnel_keyfile = value if os.path.isfile(value) else None\n                if self.__ssh_tunnel_keyfile is None:\n                    logger.warning(\n                        \"Discarded invalid or non-existential SSH Tunnel Key-file at {}!\".format(\n                            value\n                        )\n                    )\n\n            # handle jpeg compression\n            elif (\n                key == \"jpeg_compression\"\n                and not (simplejpeg is None)\n                and isinstance(value, (bool, str))\n            ):\n                if isinstance(value, str) and value.strip().upper() in [\n                    \"RGB\",\n                    \"BGR\",\n                    \"RGBX\",\n                    \"BGRX\",\n                    \"XBGR\",\n                    \"XRGB\",\n                    \"GRAY\",\n                    \"RGBA\",\n                    \"BGRA\",\n                    \"ABGR\",\n                    \"ARGB\",\n                    \"CMYK\",\n                ]:\n                    # set encoding colorspace\n                    self.__jpeg_compression_colorspace = value.strip().upper()\n                    # enable frame-compression encoding value\n                    self.__jpeg_compression = True\n                else:\n                    # enable frame-compression encoding value\n                    self.__jpeg_compression = value\n            elif key == \"jpeg_compression_quality\" and isinstance(value, (int, float)):\n                # set valid jpeg quality\n                if value >= 10 and value <= 100:\n                    self.__jpeg_compression_quality = int(value)\n                else:\n                    logger.warning(\"Skipped invalid `jpeg_compression_quality` value!\")\n            elif key == \"jpeg_compression_fastdct\" and isinstance(value, bool):\n                # enable jpeg fastdct\n                self.__jpeg_compression_fastdct = value\n            elif key == \"jpeg_compression_fastupsample\" and isinstance(value, bool):\n                # enable jpeg  fastupsample\n                self.__jpeg_compression_fastupsample = value\n\n            # assign maximum retries in synchronous patterns\n            elif key == \"max_retries\" and isinstance(value, int) and pattern < 2:\n                if value >= 0:\n                    self.__max_retries = value\n                else:\n                    logger.warning(\"Invalid `max_retries` value skipped!\")\n\n            # assign request timeout in synchronous patterns\n            elif key == \"request_timeout\" and isinstance(value, int) and pattern < 2:\n                if value >= 4:\n                    self.__request_timeout = value * 1000  # covert to milliseconds\n                else:\n                    logger.warning(\"Invalid `request_timeout` value skipped!\")\n\n            # assign subscriber timeout\n            elif (\n                key == \"subscriber_timeout\" and isinstance(value, int) and pattern == 2\n            ):\n                if value > 0:\n                    self.__subscriber_timeout = value * 1000  # covert to milliseconds\n                else:\n                    logger.warning(\"Invalid `request_timeout` value skipped!\")\n\n            # handle ZMQ flags\n            elif key == \"flag\" and isinstance(value, int):\n                self.__msg_flag = value\n                self.__msg_flag and logger.warning(\n                    \"The flag optional value is set to `1` (NOBLOCK) for this run. This might cause NetGear to not terminate gracefully.\"\n                )\n            elif key == \"copy\" and isinstance(value, bool):\n                self.__msg_copy = value\n            elif key == \"track\" and isinstance(value, bool):\n                self.__msg_track = value\n                self.__msg_copy and self.__msg_track and logger.info(\n                    \"The `track` optional value will be ignored for this run because `copy=True` is also defined.\"\n                )\n            else:\n                pass\n\n        # Handle ssh tunneling if enabled\n        if not (self.__ssh_tunnel_mode is None):\n            # SSH Tunnel Mode only available for server mode\n            if receive_mode:\n                logger.error(\"SSH Tunneling cannot be enabled for Client-end!\")\n            else:\n                # check if SSH tunneling possible\n                ssh_address = self.__ssh_tunnel_mode\n                ssh_address, ssh_port = (\n                    ssh_address.split(\":\")\n                    if \":\" in ssh_address\n                    else [ssh_address, \"22\"]\n                )  # default to port 22\n                if \"47\" in ssh_port:\n                    self.__ssh_tunnel_mode = self.__ssh_tunnel_mode.replace(\n                        \":47\", \"\"\n                    )  # port-47 is reserved for testing\n                else:\n                    # extract ip for validation\n                    ssh_user, ssh_ip = (\n                        ssh_address.split(\"@\")\n                        if \"@\" in ssh_address\n                        else [\"\", ssh_address]\n                    )\n                    # validate ip specified port\n                    assert check_open_port(\n                        ssh_ip, port=int(ssh_port)\n                    ), \"[NetGear:ERROR] :: Host `{}` is not available for SSH Tunneling at port-{}!\".format(\n                        ssh_address, ssh_port\n                    )\n\n        # Handle multiple exclusive modes if enabled\n        if self.__multiclient_mode and self.__multiserver_mode:\n            raise ValueError(\n                \"[NetGear:ERROR] :: Multi-Client and Multi-Server Mode cannot be enabled simultaneously!\"\n            )\n        elif self.__multiserver_mode or self.__multiclient_mode:\n            # check if Bidirectional Mode also enabled\n            if self.__bi_mode:\n                # log it\n                self.__logging and logger.debug(\n                    \"Bidirectional Data Transmission is also enabled for this connection!\"\n                )\n            # check if SSH Tunneling Mode also enabled\n            if self.__ssh_tunnel_mode:\n                # raise error\n                raise ValueError(\n                    \"[NetGear:ERROR] :: SSH Tunneling and {} Mode cannot be enabled simultaneously. Kindly refer docs!\".format(\n                        \"Multi-Server\" if self.__multiserver_mode else \"Multi-Client\"\n                    )\n                )\n        elif self.__bi_mode:\n            # log Bidirectional mode activation\n            self.__logging and logger.debug(\n                \"Bidirectional Data Transmission is enabled for this connection!\"\n            )\n        elif self.__ssh_tunnel_mode:\n            # log Bidirectional mode activation\n            self.__logging and logger.debug(\n                \"SSH Tunneling is enabled for host:`{}` with `{}` back-end.\".format(\n                    self.__ssh_tunnel_mode,\n                    \"paramiko\" if self.__paramiko_present else \"pexpect\",\n                )\n            )\n\n        # On Windows, NetGear requires the ``WindowsSelectorEventLoop`` but Python 3.8 and above,\n        # defaults to an ``ProactorEventLoop`` loop that is not compatible with it. Thereby,\n        # we had to set it manually.\n        platform.system() == \"Windows\" and asyncio.set_event_loop_policy(\n            asyncio.WindowsSelectorEventLoopPolicy()\n        )\n\n        # define ZMQ messaging context instance\n        self.__msg_context = zmq.Context.instance()\n\n        # initialize and assign receive mode to global variable\n        self.__receive_mode = receive_mode\n\n        # Handle Secure mode\n        if self.__secure_mode > 0:\n            # activate and log if overwriting is enabled\n            if receive_mode:\n                overwrite_cert = False\n                overwrite_cert and logger.warning(\n                    \"Overwriting ZMQ Authentication certificates is disabled for Client's end!\"\n                )\n            else:\n                overwrite_cert and self.__logging and logger.info(\n                    \"Overwriting ZMQ Authentication certificates over previous ones!\"\n                )\n\n            # Validate certificate generation paths\n            # Start threaded authenticator for this context\n            try:\n                # check if custom certificates path is specified\n                if custom_cert_location:\n                    (\n                        auth_cert_dir,\n                        self.__auth_secretkeys_dir,\n                        self.__auth_publickeys_dir,\n                    ) = generate_auth_certificates(\n                        custom_cert_location, overwrite=overwrite_cert, logging=logging\n                    )\n                else:\n                    # otherwise auto-generate suitable path\n                    (\n                        auth_cert_dir,\n                        self.__auth_secretkeys_dir,\n                        self.__auth_publickeys_dir,\n                    ) = generate_auth_certificates(\n                        os.path.join(expanduser(\"~\"), \".vidgear\"),\n                        overwrite=overwrite_cert,\n                        logging=logging,\n                    )\n                # log it\n                self.__logging and logger.debug(\n                    \"`{}` is the default location for storing ZMQ authentication certificates/keys.\".format(\n                        auth_cert_dir\n                    )\n                )\n\n                # start an authenticator for this context\n                self.__z_auth = ThreadAuthenticator(self.__msg_context)\n                self.__z_auth.start()\n                self.__z_auth.allow(str(address))  # allow current address\n\n                # check if `IronHouse` is activated\n                if self.__secure_mode == 2:\n                    # tell authenticator to use the certificate from given valid dir\n                    self.__z_auth.configure_curve(\n                        domain=\"*\", location=self.__auth_publickeys_dir\n                    )\n                else:\n                    # otherwise tell the authenticator how to handle the CURVE requests, if `StoneHouse` is activated\n                    self.__z_auth.configure_curve(\n                        domain=\"*\", location=auth.CURVE_ALLOW_ANY\n                    )\n            except zmq.ZMQError as e:\n                if \"Address in use\" in str(e):\n                    logger.info(\"ZMQ Authenticator already running.\")\n                else:\n                    # catch if any error occurred and disable Secure mode\n                    logger.exception(str(e))\n                    self.__secure_mode = 0\n                    logger.error(\n                        \"ZMQ Security Mechanism is disabled for this connection due to errors!\"\n                    )\n\n        # check whether `receive_mode` is enabled\n        if self.__receive_mode:\n            # define connection address\n            address = \"*\" if address is None else address\n\n            # check if multiserver_mode is enabled\n            if self.__multiserver_mode:\n                # check if unique server port address list/tuple is assigned or not in multiserver_mode\n                if port is None or not isinstance(port, (tuple, list)):\n                    # raise error if not\n                    raise ValueError(\n                        \"[NetGear:ERROR] :: Incorrect port value! Kindly provide a list/tuple of Server ports while Multi-Server mode is enabled. For more information refer VidGear docs.\"\n                    )\n                else:\n                    # otherwise log it\n                    logger.debug(\n                        \"Enabling Multi-Server Mode at PORTS: {}!\".format(port)\n                    )\n                # create port address buffer for keeping track of connected client's port(s)\n                self.__port_buffer = []\n            # check if multiclient_mode is enabled\n            elif self.__multiclient_mode:\n                # check if unique server port address is assigned or not in multiclient_mode\n                if port is None:\n                    # raise error if not\n                    raise ValueError(\n                        \"[NetGear:ERROR] :: Kindly provide a unique & valid port value at Client-end. For more information refer VidGear docs.\"\n                    )\n                else:\n                    # otherwise log it\n                    logger.debug(\n                        \"Enabling Multi-Client Mode at PORT: {} on this device!\".format(\n                            port\n                        )\n                    )\n                # assign value to global variable\n                self.__port = port\n            else:\n                # otherwise assign local port address if None\n                port = \"5555\" if port is None else port\n\n            try:\n                # define thread-safe messaging socket\n                self.__msg_socket = self.__msg_context.socket(msg_pattern[1])\n\n                # define pub-sub flag\n                self.__pattern == 2 and self.__msg_socket.set_hwm(1)\n\n                # enable specified secure mode for the socket\n                if self.__secure_mode > 0:\n                    # load server key\n                    server_secret_file = os.path.join(\n                        self.__auth_secretkeys_dir, \"server.key_secret\"\n                    )\n                    server_public, server_secret = auth.load_certificate(\n                        server_secret_file\n                    )\n                    # load  all CURVE keys\n                    self.__msg_socket.curve_secretkey = server_secret\n                    self.__msg_socket.curve_publickey = server_public\n                    # enable CURVE connection for this socket\n                    self.__msg_socket.curve_server = True\n\n                # define exclusive socket options for `patterns=2`\n                if self.__pattern == 2:\n                    self.__msg_socket.setsockopt_string(zmq.SUBSCRIBE, \"\")\n                    self.__subscriber_timeout and self.__msg_socket.setsockopt(\n                        zmq.RCVTIMEO, self.__subscriber_timeout\n                    )\n                    self.__subscriber_timeout and self.__msg_socket.setsockopt(\n                        zmq.LINGER, 0\n                    )\n\n                # if multiserver_mode is enabled, then assign port addresses to zmq socket\n                if self.__multiserver_mode:\n                    # bind socket to given server protocol, address and ports\n                    for pt in port:\n                        self.__msg_socket.bind(\n                            protocol + \"://\" + str(address) + \":\" + str(pt)\n                        )\n                else:\n                    # bind socket to given protocol, address and port normally\n                    self.__msg_socket.bind(\n                        protocol + \"://\" + str(address) + \":\" + str(port)\n                    )\n\n                # additional settings\n                if pattern < 2:\n                    if self.__multiserver_mode:\n                        self.__connection_address = []\n                        for pt in port:\n                            self.__connection_address.append(\n                                protocol + \"://\" + str(address) + \":\" + str(pt)\n                            )\n                    else:\n                        self.__connection_address = (\n                            protocol + \"://\" + str(address) + \":\" + str(port)\n                        )\n                    self.__msg_pattern = msg_pattern[1]\n                    self.__poll.register(self.__msg_socket, zmq.POLLIN)\n                    self.__logging and logger.debug(\n                        \"Reliable transmission is enabled for this pattern with max-retries: {} and timeout: {} secs.\".format(\n                            self.__max_retries, self.__request_timeout / 1000\n                        )\n                    )\n                else:\n                    self.__logging and self.__subscriber_timeout and logger.debug(\n                        \"Timeout: {} secs is enabled for this system.\".format(\n                            self.__subscriber_timeout / 1000\n                        )\n                    )\n\n            except Exception as e:\n                # otherwise log and raise error\n                logger.exception(str(e))\n                # Handle Secure Mode\n                self.__secure_mode and logger.critical(\n                    \"Failed to activate Secure Mode: `{}` for this connection!\".format(\n                        valid_security_mech[self.__secure_mode]\n                    )\n                )\n                # raise errors for exclusive modes\n                if self.__multiserver_mode or self.__multiclient_mode:\n                    raise RuntimeError(\n                        \"[NetGear:ERROR] :: Receive Mode failed to activate {} Mode at address: {} with pattern: {}! Kindly recheck all parameters.\".format(\n                            (\n                                \"Multi-Server\"\n                                if self.__multiserver_mode\n                                else \"Multi-Client\"\n                            ),\n                            (protocol + \"://\" + str(address) + \":\" + str(port)),\n                            pattern,\n                        )\n                    )\n                else:\n                    self.__bi_mode and logger.critical(\n                        \"Failed to activate Bidirectional Mode for this connection!\"\n                    )\n                    raise RuntimeError(\n                        \"[NetGear:ERROR] :: Receive Mode failed to bind address: {} and pattern: {}! Kindly recheck all parameters.\".format(\n                            (protocol + \"://\" + str(address) + \":\" + str(port)), pattern\n                        )\n                    )\n\n            # Handle threaded queue mode\n            self.__logging and logger.debug(\n                \"Threaded Queue Mode is enabled by default for this connection.\"\n            )\n\n            # define deque and assign it to global var\n            self.__queue = deque(maxlen=96)  # max len 96 to check overflow\n\n            # initialize and start threaded recv_handler\n            self.__thread = Thread(target=self.__recv_handler, name=\"NetGear\", args=())\n            self.__thread.daemon = True\n            self.__thread.start()\n\n            if self.__logging:\n                # finally log progress\n                logger.debug(\n                    \"Successfully Binded to address: {} with pattern: {}.\".format(\n                        (protocol + \"://\" + str(address) + \":\" + str(port)), pattern\n                    )\n                )\n                self.__jpeg_compression and logger.debug(\n                    \"JPEG Frame-Compression is activated for this connection with Colorspace:`{}`, Quality:`{}`%, Fastdct:`{}`, and Fastupsample:`{}`.\".format(\n                        self.__jpeg_compression_colorspace,\n                        self.__jpeg_compression_quality,\n                        (\"enabled\" if self.__jpeg_compression_fastdct else \"disabled\"),\n                        (\n                            \"enabled\"\n                            if self.__jpeg_compression_fastupsample\n                            else \"disabled\"\n                        ),\n                    )\n                )\n                self.__secure_mode and logger.debug(\n                    \"Successfully enabled ZMQ Security Mechanism: `{}` for this connection.\".format(\n                        valid_security_mech[self.__secure_mode]\n                    )\n                )\n                logger.debug(\"Multi-threaded Receive Mode is successfully enabled.\")\n                logger.debug(\"Unique System ID is {}.\".format(self.__id))\n                logger.debug(\"Receive Mode is now activated.\")\n\n        else:\n            # otherwise default to `Send Mode`\n            # define connection address\n            address = \"localhost\" if address is None else address\n\n            # check if multiserver_mode is enabled\n            if self.__multiserver_mode:\n                # check if unique server port address is assigned or not in multiserver_mode\n                if port is None:\n                    # raise error if not\n                    raise ValueError(\n                        \"[NetGear:ERROR] :: Kindly provide a unique & valid port value at Server-end. For more information refer VidGear docs.\"\n                    )\n                else:\n                    # otherwise log it\n                    logger.debug(\n                        \"Enabling Multi-Server Mode at PORT: {} on this device!\".format(\n                            port\n                        )\n                    )\n                # assign value to global variable\n                self.__port = port\n            # check if multiclient_mode is enabled\n            elif self.__multiclient_mode:\n                # check if unique client port address list/tuple is assigned or not in multiclient_mode\n                if port is None or not isinstance(port, (tuple, list)):\n                    # raise error if not\n                    raise ValueError(\n                        \"[NetGear:ERROR] :: Incorrect port value! Kindly provide a list/tuple of Client ports while Multi-Client mode is enabled. For more information refer VidGear docs.\"\n                    )\n                else:\n                    # otherwise log it\n                    logger.debug(\n                        \"Enabling Multi-Client Mode at PORTS: {}!\".format(port)\n                    )\n                # create port address buffer for keeping track of connected client ports\n                self.__port_buffer = []\n            else:\n                # otherwise assign local port address if None\n                port = \"5555\" if port is None else port\n\n            try:\n                # define thread-safe messaging socket\n                self.__msg_socket = self.__msg_context.socket(msg_pattern[0])\n\n                # if req/rep pattern, define additional flags\n                if self.__pattern == 1:\n                    self.__msg_socket.REQ_RELAXED = True\n                    self.__msg_socket.REQ_CORRELATE = True\n\n                # if pub/sub pattern, define additional optimizer\n                if self.__pattern == 2:\n                    self.__msg_socket.set_hwm(1)\n\n                # enable specified secure mode for the socket\n                if self.__secure_mode > 0:\n                    # load client key\n                    client_secret_file = os.path.join(\n                        self.__auth_secretkeys_dir, \"client.key_secret\"\n                    )\n                    client_public, client_secret = auth.load_certificate(\n                        client_secret_file\n                    )\n                    # load  all CURVE keys\n                    self.__msg_socket.curve_secretkey = client_secret\n                    self.__msg_socket.curve_publickey = client_public\n                    # load server key\n                    server_public_file = os.path.join(\n                        self.__auth_publickeys_dir, \"server.key\"\n                    )\n                    server_public, _ = auth.load_certificate(server_public_file)\n                    # inject public key to make a CURVE connection.\n                    self.__msg_socket.curve_serverkey = server_public\n\n                # check if multi-client_mode is enabled\n                if self.__multiclient_mode:\n                    # bind socket to given server protocol, address and ports\n                    for pt in port:\n                        self.__msg_socket.connect(\n                            protocol + \"://\" + str(address) + \":\" + str(pt)\n                        )\n                else:\n                    # handle SSH tunneling if enabled\n                    if self.__ssh_tunnel_mode:\n                        # establish tunnel connection\n                        ssh.tunnel_connection(\n                            self.__msg_socket,\n                            protocol + \"://\" + str(address) + \":\" + str(port),\n                            self.__ssh_tunnel_mode,\n                            keyfile=self.__ssh_tunnel_keyfile,\n                            password=self.__ssh_tunnel_pwd,\n                            paramiko=self.__paramiko_present,\n                        )\n                    else:\n                        # connect socket to given protocol, address and port\n                        self.__msg_socket.connect(\n                            protocol + \"://\" + str(address) + \":\" + str(port)\n                        )\n\n                # additional settings\n                if pattern < 2:\n                    if self.__multiclient_mode:\n                        self.__connection_address = []\n                        for pt in port:\n                            self.__connection_address.append(\n                                protocol + \"://\" + str(address) + \":\" + str(pt)\n                            )\n                    else:\n                        self.__connection_address = (\n                            protocol + \"://\" + str(address) + \":\" + str(port)\n                        )\n                    self.__msg_pattern = msg_pattern[0]\n                    self.__poll.register(self.__msg_socket, zmq.POLLIN)\n\n                    self.__logging and logger.debug(\n                        \"Reliable transmission is enabled for this pattern with max-retries: {} and timeout: {} secs.\".format(\n                            self.__max_retries, self.__request_timeout / 1000\n                        )\n                    )\n\n            except Exception as e:\n                # otherwise log and raise error\n                logger.exception(str(e))\n                # Handle Secure Mode\n                self.__secure_mode and logger.critical(\n                    \"Failed to activate Secure Mode: `{}` for this connection!\".format(\n                        valid_security_mech[self.__secure_mode]\n                    )\n                )\n                # raise errors for exclusive modes\n                if self.__multiserver_mode or self.__multiclient_mode:\n                    raise RuntimeError(\n                        \"[NetGear:ERROR] :: Send Mode failed to activate {} Mode at address: {} with pattern: {}! Kindly recheck all parameters.\".format(\n                            (\n                                \"Multi-Server\"\n                                if self.__multiserver_mode\n                                else \"Multi-Client\"\n                            ),\n                            (protocol + \"://\" + str(address) + \":\" + str(port)),\n                            pattern,\n                        )\n                    )\n                else:\n                    self.__bi_mode and logger.critical(\n                        \"Failed to activate Bidirectional Mode for this connection!\"\n                    )\n                    self.__ssh_tunnel_mode and logger.critical(\n                        \"Failed to initiate SSH Tunneling Mode for this server with `{}` back-end!\".format(\n                            \"paramiko\" if self.__paramiko_present else \"pexpect\"\n                        )\n                    )\n                    raise RuntimeError(\n                        \"[NetGear:ERROR] :: Send Mode failed to connect address: {} and pattern: {}! Kindly recheck all parameters.\".format(\n                            (protocol + \"://\" + str(address) + \":\" + str(port)), pattern\n                        )\n                    )\n\n            if self.__logging:\n                # finally log progress\n                logger.debug(\n                    \"Successfully connected to address: {} with pattern: {}.\".format(\n                        (protocol + \"://\" + str(address) + \":\" + str(port)), pattern\n                    )\n                )\n                self.__jpeg_compression and logger.debug(\n                    \"JPEG Frame-Compression is activated for this connection with Colorspace:`{}`, Quality:`{}`%, Fastdct:`{}`, and Fastupsample:`{}`.\".format(\n                        self.__jpeg_compression_colorspace,\n                        self.__jpeg_compression_quality,\n                        (\"enabled\" if self.__jpeg_compression_fastdct else \"disabled\"),\n                        (\n                            \"enabled\"\n                            if self.__jpeg_compression_fastupsample\n                            else \"disabled\"\n                        ),\n                    )\n                )\n                self.__secure_mode and logger.debug(\n                    \"Enabled ZMQ Security Mechanism: `{}` for this connection.\".format(\n                        valid_security_mech[self.__secure_mode]\n                    )\n                )\n                logger.debug(\"Unique System ID is {}.\".format(self.__id))\n                logger.debug(\n                    \"Send Mode is successfully activated and ready to send data.\"\n                )\n\n    def __recv_handler(self):\n        \"\"\"\n        A threaded receiver handler, that keep iterating data from ZMQ socket to a internally monitored deque,\n        until the thread is terminated, or socket disconnects.\n        \"\"\"\n        # initialize variables\n        frame = None\n        msg_json = None\n\n        # keep looping infinitely until the thread is terminated\n        while not self.__terminate:\n            # check queue buffer for overflow\n            if len(self.__queue) >= 96:\n                # stop iterating if overflowing occurs\n                time.sleep(0.000001)\n                continue\n\n            if self.__pattern < 2:\n                socks = dict(self.__poll.poll(self.__request_timeout * 3))\n                if socks.get(self.__msg_socket) == zmq.POLLIN:\n                    msg_json = self.__msg_socket.recv_json(\n                        flags=self.__msg_flag | zmq.DONTWAIT\n                    )\n                else:\n                    logger.critical(\"No response from Server(s), Reconnecting again...\")\n                    self.__msg_socket.close(linger=0)\n                    self.__poll.unregister(self.__msg_socket)\n                    self.__max_retries -= 1\n\n                    if not (self.__max_retries):\n                        if self.__multiserver_mode:\n                            logger.error(\"All Servers seems to be offline, Abandoning!\")\n                        else:\n                            logger.error(\"Server seems to be offline, Abandoning!\")\n                        self.__terminate = True\n                        continue\n\n                    # Create new connection\n                    try:\n                        self.__msg_socket = self.__msg_context.socket(\n                            self.__msg_pattern\n                        )\n                        if isinstance(self.__connection_address, list):\n                            for _connection in self.__connection_address:\n                                self.__msg_socket.bind(_connection)\n                        else:\n                            self.__msg_socket.bind(self.__connection_address)\n                    except Exception as e:\n                        logger.exception(str(e))\n                        self.__terminate = True\n                        raise RuntimeError(\"API failed to restart the Client-end!\")\n                    self.__poll.register(self.__msg_socket, zmq.POLLIN)\n\n                    continue\n            else:\n                try:\n                    msg_json = self.__msg_socket.recv_json(flags=self.__msg_flag)\n                except zmq.ZMQError as e:\n                    if e.errno == zmq.EAGAIN:\n                        logger.critical(\"Connection Timeout. Exiting!\")\n                        self.__terminate = True\n                        self.__queue.append(None)\n                        break\n\n            # check if terminate_flag` received\n            if msg_json and msg_json[\"terminate_flag\"]:\n                # if multiserver_mode is enabled\n                if self.__multiserver_mode:\n                    # check and remove from which ports signal is received\n                    if msg_json[\"port\"] in self.__port_buffer:\n                        # if pattern is 1, then send back server the info about termination\n                        if self.__pattern == 1:\n                            self.__msg_socket.send_string(\n                                \"Termination signal successfully received at client!\"\n                            )\n                        self.__port_buffer.remove(msg_json[\"port\"])\n                        self.__logging and logger.warning(\n                            \"Termination signal received from Server at port: {}!\".format(\n                                msg_json[\"port\"]\n                            )\n                        )\n                    # if termination signal received from all servers then exit client.\n                    if not self.__port_buffer:\n                        logger.critical(\n                            \"Termination signal received from all Servers!!!\"\n                        )\n                        self.__terminate = True  # termination\n                else:\n                    # if pattern is 1, then send back server the info about termination\n                    if self.__pattern == 1:\n                        self.__msg_socket.send_string(\n                            \"Termination signal successfully received at Client's end!\"\n                        )\n                    # termination\n                    self.__terminate = True\n                    # notify client\n                    self.__logging and logger.critical(\n                        \"Termination signal received from server!\"\n                    )\n                continue\n\n            try:\n                msg_data = self.__msg_socket.recv(\n                    flags=self.__msg_flag | zmq.DONTWAIT,\n                    copy=self.__msg_copy,\n                    track=self.__msg_track,\n                )\n            except zmq.ZMQError as e:\n                logger.critical(\"Socket Session Expired. Exiting!\")\n                self.__terminate = True\n                self.__queue.append(None)\n                break\n\n            # handle data transfer in synchronous modes.\n            if self.__pattern < 2:\n                if self.__bi_mode or self.__multiclient_mode:\n                    # check if we are returning `ndarray` frames\n                    if not (self.__return_data is None) and isinstance(\n                        self.__return_data, np.ndarray\n                    ):\n                        # handle return data for compression\n                        return_data = np.copy(self.__return_data)\n\n                        # check whether exit_flag is False\n                        if not (return_data.flags[\"C_CONTIGUOUS\"]):\n                            # check whether the incoming frame is contiguous\n                            return_data = np.ascontiguousarray(\n                                return_data, dtype=return_data.dtype\n                            )\n\n                        # handle jpeg-compression encoding\n                        if self.__jpeg_compression:\n                            if self.__jpeg_compression_colorspace == \"GRAY\":\n                                if return_data.ndim == 2:\n                                    # patch for https://gitlab.com/jfolz/simplejpeg/-/issues/11\n                                    return_data = return_data[:, :, np.newaxis]\n                                return_data = simplejpeg.encode_jpeg(\n                                    return_data,\n                                    quality=self.__jpeg_compression_quality,\n                                    colorspace=self.__jpeg_compression_colorspace,\n                                    fastdct=self.__jpeg_compression_fastdct,\n                                )\n                            else:\n                                return_data = simplejpeg.encode_jpeg(\n                                    return_data,\n                                    quality=self.__jpeg_compression_quality,\n                                    colorspace=self.__jpeg_compression_colorspace,\n                                    colorsubsampling=\"422\",\n                                    fastdct=self.__jpeg_compression_fastdct,\n                                )\n\n                        return_dict = (\n                            dict(port=self.__port)\n                            if self.__multiclient_mode\n                            else dict()\n                        )\n\n                        return_dict.update(\n                            dict(\n                                return_type=(type(self.__return_data).__name__),\n                                compression=(\n                                    {\n                                        \"dct\": self.__jpeg_compression_fastdct,\n                                        \"ups\": self.__jpeg_compression_fastupsample,\n                                        \"colorspace\": self.__jpeg_compression_colorspace,\n                                    }\n                                    if self.__jpeg_compression\n                                    else False\n                                ),\n                                array_dtype=(\n                                    str(self.__return_data.dtype)\n                                    if not (self.__jpeg_compression)\n                                    else \"\"\n                                ),\n                                array_shape=(\n                                    self.__return_data.shape\n                                    if not (self.__jpeg_compression)\n                                    else \"\"\n                                ),\n                                data=None,\n                            )\n                        )\n\n                        # send the json dict\n                        self.__msg_socket.send_json(\n                            return_dict, self.__msg_flag | zmq.SNDMORE\n                        )\n                        # send the array with correct flags\n                        self.__msg_socket.send(\n                            return_data,\n                            flags=self.__msg_flag,\n                            copy=self.__msg_copy,\n                            track=self.__msg_track,\n                        )\n                    else:\n                        return_dict = (\n                            dict(port=self.__port)\n                            if self.__multiclient_mode\n                            else dict()\n                        )\n                        return_dict.update(\n                            dict(\n                                return_type=(type(self.__return_data).__name__),\n                                data=self.__return_data,\n                            )\n                        )\n                        self.__msg_socket.send_json(return_dict, self.__msg_flag)\n                else:\n                    # send confirmation message to server\n                    self.__msg_socket.send_string(\n                        \"Data received on device: {} !\".format(self.__id)\n                    )\n            else:\n                # else raise warning\n                if self.__return_data:\n                    logger.warning(\"`return_data` is disabled for this pattern!\")\n\n            # check if encoding was enabled\n            if msg_json[\"compression\"]:\n                # decode JPEG frame\n                frame = simplejpeg.decode_jpeg(\n                    msg_data,\n                    colorspace=msg_json[\"compression\"][\"colorspace\"],\n                    fastdct=self.__jpeg_compression_fastdct\n                    or msg_json[\"compression\"][\"dct\"],\n                    fastupsample=self.__jpeg_compression_fastupsample\n                    or msg_json[\"compression\"][\"ups\"],\n                )\n                # check if valid frame returned\n                if frame is None:\n                    self.__terminate = True\n                    # otherwise raise error and exit\n                    raise RuntimeError(\n                        \"[NetGear:ERROR] :: Received compressed JPEG frame decoding failed\"\n                    )\n                if msg_json[\"compression\"][\"colorspace\"] == \"GRAY\" and frame.ndim == 3:\n                    # patch for https://gitlab.com/jfolz/simplejpeg/-/issues/11\n                    frame = np.squeeze(frame, axis=2)\n            else:\n                # recover and reshape frame from buffer\n                frame_buffer = np.frombuffer(msg_data, dtype=msg_json[\"dtype\"])\n                frame = frame_buffer.reshape(msg_json[\"shape\"])\n\n            # check if multiserver_mode\n            if self.__multiserver_mode:\n                # save the unique port addresses\n                if not msg_json[\"port\"] in self.__port_buffer:\n                    self.__port_buffer.append(msg_json[\"port\"])\n                # extract if any message from server and display it\n                if msg_json[\"message\"]:\n                    self.__queue.append((msg_json[\"port\"], msg_json[\"message\"], frame))\n                else:\n                    # append recovered unique port and frame to queue\n                    self.__queue.append((msg_json[\"port\"], frame))\n            # extract if any message from server if Bidirectional Mode is enabled\n            elif self.__bi_mode:\n                if msg_json[\"message\"]:\n                    # append grouped frame and data to queue\n                    self.__queue.append((msg_json[\"message\"], frame))\n                else:\n                    self.__queue.append((None, frame))\n            else:\n                # otherwise append recovered frame to queue\n                self.__queue.append(frame)\n\n    def recv(self, return_data=None):\n        \"\"\"\n        A Receiver end method, that extracts received frames synchronously from monitored deque, while maintaining a\n        fixed-length frame buffer in the memory, and blocks the thread if the deque is full.\n\n        Parameters:\n            return_data (any): inputs return data _(of any datatype)_, for sending back to Server.\n\n        **Returns:** A n-dimensional numpy array.\n        \"\"\"\n        # check whether `receive mode` is activated\n        if not (self.__receive_mode):\n            # raise value error and exit\n            self.__terminate = True\n            raise ValueError(\n                \"[NetGear:ERROR] :: `recv()` function cannot be used while receive_mode is disabled. Kindly refer vidgear docs!\"\n            )\n\n        # handle Bidirectional return data\n        if (self.__bi_mode or self.__multiclient_mode) and not (return_data is None):\n            self.__return_data = return_data\n\n        # check whether or not termination flag is enabled\n        while not self.__terminate:\n            try:\n                # check if queue is empty\n                if len(self.__queue) > 0:\n                    return self.__queue.popleft()\n                else:\n                    time.sleep(0.00001)\n                    continue\n            except KeyboardInterrupt:\n                self.__terminate = True\n                break\n        # otherwise return NoneType\n        return None\n\n    def send(self, frame, message=None):\n        \"\"\"\n        A Server end method, that sends the data and frames over the network to Client(s).\n\n        Parameters:\n            frame (numpy.ndarray): inputs numpy array(frame).\n            message (any): input for sending additional data _(of any datatype except `numpy.ndarray`)_ to Client(s).\n\n        **Returns:** Data _(of any datatype)_ in selected exclusive modes, otherwise None-type.\n\n        \"\"\"\n        # check whether `receive_mode` is disabled\n        if self.__receive_mode:\n            # raise value error and exit\n            self.__terminate = True\n            raise ValueError(\n                \"[NetGear:ERROR] :: `send()` function cannot be used while receive_mode is enabled. Kindly refer vidgear docs!\"\n            )\n\n        if not (message is None) and isinstance(message, np.ndarray):\n            logger.warning(\n                \"Skipped unsupported `message` of datatype: {}!\".format(\n                    type(message).__name__\n                )\n            )\n            message = None\n\n        # define exit_flag and assign value\n        exit_flag = True if (frame is None or self.__terminate) else False\n\n        # check whether exit_flag is False\n        if not (exit_flag) and not (frame.flags[\"C_CONTIGUOUS\"]):\n            # check whether the incoming frame is contiguous\n            frame = np.ascontiguousarray(frame, dtype=frame.dtype)\n\n        # handle JPEG compression encoding\n        if self.__jpeg_compression:\n            if self.__jpeg_compression_colorspace == \"GRAY\":\n                if frame.ndim == 2:\n                    # patch for https://gitlab.com/jfolz/simplejpeg/-/issues/11\n                    frame = np.expand_dims(frame, axis=2)\n                frame = simplejpeg.encode_jpeg(\n                    frame,\n                    quality=self.__jpeg_compression_quality,\n                    colorspace=self.__jpeg_compression_colorspace,\n                    fastdct=self.__jpeg_compression_fastdct,\n                )\n            else:\n                frame = simplejpeg.encode_jpeg(\n                    frame,\n                    quality=self.__jpeg_compression_quality,\n                    colorspace=self.__jpeg_compression_colorspace,\n                    colorsubsampling=\"422\",\n                    fastdct=self.__jpeg_compression_fastdct,\n                )\n\n        # check if multiserver_mode is activated and assign values with unique port\n        msg_dict = dict(port=self.__port) if self.__multiserver_mode else dict()\n\n        # prepare the exclusive json dict\n        msg_dict.update(\n            dict(\n                terminate_flag=exit_flag,\n                compression=(\n                    {\n                        \"dct\": self.__jpeg_compression_fastdct,\n                        \"ups\": self.__jpeg_compression_fastupsample,\n                        \"colorspace\": self.__jpeg_compression_colorspace,\n                    }\n                    if self.__jpeg_compression\n                    else False\n                ),\n                message=message,\n                pattern=str(self.__pattern),\n                dtype=str(frame.dtype) if not (self.__jpeg_compression) else \"\",\n                shape=frame.shape if not (self.__jpeg_compression) else \"\",\n            )\n        )\n\n        # send the json dict\n        self.__msg_socket.send_json(msg_dict, self.__msg_flag | zmq.SNDMORE)\n        # send the frame array with correct flags\n        self.__msg_socket.send(\n            frame, flags=self.__msg_flag, copy=self.__msg_copy, track=self.__msg_track\n        )\n\n        # check if synchronous patterns, then wait for confirmation\n        if self.__pattern < 2:\n            # check if Bidirectional data transmission is enabled\n            if self.__bi_mode or self.__multiclient_mode:\n                # handles return data\n                recvd_data = None\n\n                socks = dict(self.__poll.poll(self.__request_timeout))\n                if socks.get(self.__msg_socket) == zmq.POLLIN:\n                    # handle return data\n                    recv_json = self.__msg_socket.recv_json(flags=self.__msg_flag)\n                else:\n                    logger.critical(\"No response from Client, Reconnecting again...\")\n                    # Socket is confused. Close and remove it.\n                    self.__msg_socket.setsockopt(zmq.LINGER, 0)\n                    self.__msg_socket.close()\n                    self.__poll.unregister(self.__msg_socket)\n                    self.__max_retries -= 1\n\n                    if not (self.__max_retries):\n                        if self.__multiclient_mode:\n                            logger.error(\n                                \"All Clients failed to respond on multiple attempts.\"\n                            )\n                        else:\n                            logger.error(\n                                \"Client failed to respond on multiple attempts.\"\n                            )\n                        self.__terminate = True\n                        raise RuntimeError(\n                            \"[NetGear:ERROR] :: Client(s) seems to be offline, Abandoning.\"\n                        )\n\n                    # Create new connection\n                    self.__msg_socket = self.__msg_context.socket(self.__msg_pattern)\n                    if isinstance(self.__connection_address, list):\n                        for _connection in self.__connection_address:\n                            self.__msg_socket.connect(_connection)\n                    else:\n                        # handle SSH tunneling if enabled\n                        if self.__ssh_tunnel_mode:\n                            # establish tunnel connection\n                            ssh.tunnel_connection(\n                                self.__msg_socket,\n                                self.__connection_address,\n                                self.__ssh_tunnel_mode,\n                                keyfile=self.__ssh_tunnel_keyfile,\n                                password=self.__ssh_tunnel_pwd,\n                                paramiko=self.__paramiko_present,\n                            )\n                        else:\n                            # connect normally\n                            self.__msg_socket.connect(self.__connection_address)\n                    self.__poll.register(self.__msg_socket, zmq.POLLIN)\n                    # return None for mean-time\n                    return None\n\n                # save the unique port addresses\n                if (\n                    self.__multiclient_mode\n                    and not recv_json[\"port\"] in self.__port_buffer\n                ):\n                    self.__port_buffer.append(recv_json[\"port\"])\n\n                if recv_json[\"return_type\"] == \"ndarray\":\n                    recv_array = self.__msg_socket.recv(\n                        flags=self.__msg_flag,\n                        copy=self.__msg_copy,\n                        track=self.__msg_track,\n                    )\n                    # check if encoding was enabled\n                    if recv_json[\"compression\"]:\n                        # decode JPEG frame\n                        recvd_data = simplejpeg.decode_jpeg(\n                            recv_array,\n                            colorspace=recv_json[\"compression\"][\"colorspace\"],\n                            fastdct=self.__jpeg_compression_fastdct\n                            or recv_json[\"compression\"][\"dct\"],\n                            fastupsample=self.__jpeg_compression_fastupsample\n                            or recv_json[\"compression\"][\"ups\"],\n                        )\n                        # check if valid frame returned\n                        if recvd_data is None:\n                            self.__terminate = True\n                            # otherwise raise error and exit\n                            raise RuntimeError(\n                                \"[NetGear:ERROR] :: Received compressed frame `{}` decoding failed with flag: {}.\".format(\n                                    recv_json[\"compression\"],\n                                    self.__ex_compression_params,\n                                )\n                            )\n\n                        if (\n                            recv_json[\"compression\"][\"colorspace\"] == \"GRAY\"\n                            and recvd_data.ndim == 3\n                        ):\n                            # patch for https://gitlab.com/jfolz/simplejpeg/-/issues/11\n                            recvd_data = np.squeeze(recvd_data, axis=2)\n                    else:\n                        recvd_data = np.frombuffer(\n                            recv_array, dtype=recv_json[\"array_dtype\"]\n                        ).reshape(recv_json[\"array_shape\"])\n                else:\n                    recvd_data = recv_json[\"data\"]\n\n                return (\n                    (recv_json[\"port\"], recvd_data)\n                    if self.__multiclient_mode\n                    else recvd_data\n                )\n            else:\n                # otherwise log normally\n                socks = dict(self.__poll.poll(self.__request_timeout))\n                if socks.get(self.__msg_socket) == zmq.POLLIN:\n                    recv_confirmation = self.__msg_socket.recv()\n                else:\n                    logger.critical(\"No response from Client, Reconnecting again...\")\n                    # Socket is confused. Close and remove it.\n                    self.__msg_socket.setsockopt(zmq.LINGER, 0)\n                    self.__msg_socket.close()\n                    self.__poll.unregister(self.__msg_socket)\n                    self.__max_retries -= 1\n\n                    if not (self.__max_retries):\n                        logger.error(\"Client failed to respond on repeated attempts.\")\n                        self.__terminate = True\n                        raise RuntimeError(\n                            \"[NetGear:ERROR] :: Client seems to be offline, Abandoning!\"\n                        )\n\n                    # Create new connection\n                    self.__msg_socket = self.__msg_context.socket(self.__msg_pattern)\n                    # handle SSH tunneling if enabled\n                    if self.__ssh_tunnel_mode:\n                        # establish tunnel connection\n                        ssh.tunnel_connection(\n                            self.__msg_socket,\n                            self.__connection_address,\n                            self.__ssh_tunnel_mode,\n                            keyfile=self.__ssh_tunnel_keyfile,\n                            password=self.__ssh_tunnel_pwd,\n                            paramiko=self.__paramiko_present,\n                        )\n                    else:\n                        # connect normally\n                        self.__msg_socket.connect(self.__connection_address)\n                    self.__poll.register(self.__msg_socket, zmq.POLLIN)\n                    return None\n\n                # log confirmation\n                self.__logging and logger.debug(recv_confirmation)\n\n    def close(self, kill=False):\n        \"\"\"\n        Safely terminates the threads, and NetGear resources.\n\n        Parameters:\n            kill (bool): Kills ZMQ context instead of graceful exiting in receive mode.\n        \"\"\"\n        # log it\n        self.__logging and logger.debug(\n            \"Terminating various {} Processes.\".format(\n                \"Receive Mode\" if self.__receive_mode else \"Send Mode\"\n            )\n        )\n        #  whether `receive_mode` is enabled or not\n        if self.__receive_mode:\n            # check whether queue mode is empty\n            if not (self.__queue is None) and self.__queue:\n                self.__queue.clear()\n            # call immediate termination\n            self.__terminate = True\n            # properly close the socket\n            self.__logging and logger.debug(\"Terminating. Please wait...\")\n            # Handle Secure Mode Thread\n            if self.__z_auth:\n                self.__logging and logger.debug(\"Terminating Authenticator Thread.\")\n                self.__z_auth.stop()\n                while self.__z_auth.is_alive():\n                    pass\n            # wait until stream resources are released\n            # (producer thread might be still grabbing frame)\n            if self.__thread is not None:\n                self.__logging and logger.debug(\"Terminating Main Thread.\")\n                # properly handle thread exit\n                if self.__thread.is_alive() and kill:\n                    # force close if still alive\n                    logger.warning(\"Thread still running...Killing it forcefully!\")\n                    self.__msg_context.destroy()\n                    self.__thread.join()\n                else:\n                    self.__msg_socket.close(linger=0)\n                    self.__thread.join()\n                self.__thread = None\n            self.__logging and logger.debug(\"Terminated Successfully!\")\n        else:\n            # indicate that process should be terminated\n            self.__terminate = True\n            # log if kill enabled\n            kill and logger.warning(\n                \"`kill` parmeter is only available in the receive mode.\"\n            )\n            # Handle Secure Mode Thread\n            if self.__z_auth:\n                self.__logging and logger.debug(\"Terminating Authenticator Thread.\")\n                self.__z_auth.stop()\n                while self.__z_auth.is_alive():\n                    pass\n            # check if all attempts of reconnecting failed, then skip to closure\n            if (self.__pattern < 2 and not self.__max_retries) or (\n                self.__multiclient_mode and not self.__port_buffer\n            ):\n                try:\n                    # properly close the socket\n                    self.__msg_socket.setsockopt(zmq.LINGER, 0)\n                    self.__msg_socket.close()\n                except ZMQError:\n                    pass\n                finally:\n                    # exit\n                    return\n\n            if self.__multiserver_mode:\n                # check if multiserver_mode\n                # send termination flag to client with its unique port\n                term_dict = dict(terminate_flag=True, port=self.__port)\n            else:\n                # otherwise send termination flag to client\n                term_dict = dict(terminate_flag=True)\n\n            try:\n                if self.__multiclient_mode:\n                    for _ in self.__port_buffer:\n                        self.__msg_socket.send_json(term_dict)\n                else:\n                    self.__msg_socket.send_json(term_dict)\n\n                # check for confirmation if available within 1/5 timeout\n                if self.__pattern < 2:\n                    self.__logging and logger.debug(\"Terminating. Please wait...\")\n                    if self.__msg_socket.poll(self.__request_timeout // 5, zmq.POLLIN):\n                        self.__msg_socket.recv()\n            except Exception as e:\n                if not isinstance(e, ZMQError):\n                    logger.exception(str(e))\n            finally:\n                # properly close the socket\n                self.__msg_socket.setsockopt(zmq.LINGER, 0)\n                self.__msg_socket.close()\n                self.__logging and logger.debug(\"Terminated Successfully!\")\n

"},{"location":"bonus/reference/netgear/#vidgear.gears.netgear.NetGear.__init__","title":"__init__(self, address=None, port=None, protocol=None, pattern=0, receive_mode=False, logging=False, **options) special","text":"

This constructor method initializes the object state and attributes of the NetGear class.

Parameters:

Name Type Description Default address str

sets the valid network address of the Server/Client.

None port str

sets the valid Network Port of the Server/Client.

None protocol str

sets the valid messaging protocol between Server/Client.

None pattern int

sets the supported messaging pattern(flow of communication) between Server/Client

0 receive_mode bool

select the Netgear's Mode of operation.

False logging bool

enables/disables logging.

False options dict

provides the flexibility to alter various NetGear internal properties.

{} Source code in vidgear/gears/netgear.py
def __init__(\n    self,\n    address=None,\n    port=None,\n    protocol=None,\n    pattern=0,\n    receive_mode=False,\n    logging=False,\n    **options\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the NetGear class.\n\n    Parameters:\n        address (str): sets the valid network address of the Server/Client.\n        port (str): sets the valid Network Port of the Server/Client.\n        protocol (str): sets the valid messaging protocol between Server/Client.\n        pattern (int): sets the supported messaging pattern(flow of communication) between Server/Client\n        receive_mode (bool): select the Netgear's Mode of operation.\n        logging (bool): enables/disables logging.\n        options (dict): provides the flexibility to alter various NetGear internal properties.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # raise error(s) for critical Class imports\n    import_dependency_safe(\n        \"zmq\" if zmq is None else \"\", min_version=\"4.0\", pkg_name=\"pyzmq\"\n    )\n    import_dependency_safe(\n        \"simplejpeg\" if simplejpeg is None else \"\", error=\"log\", min_version=\"1.6.1\"\n    )\n\n    # define valid messaging patterns => `0`: zmq.PAIR, `1`:(zmq.REQ,zmq.REP), and `1`:(zmq.SUB,zmq.PUB)\n    valid_messaging_patterns = {\n        0: (zmq.PAIR, zmq.PAIR),\n        1: (zmq.REQ, zmq.REP),\n        2: (zmq.PUB, zmq.SUB),\n    }\n\n    # Handle messaging pattern\n    msg_pattern = None\n    # check whether user-defined messaging pattern is valid\n    if isinstance(pattern, int) and pattern in valid_messaging_patterns.keys():\n        # assign value\n        msg_pattern = valid_messaging_patterns[pattern]\n    else:\n        # otherwise default to 0:`zmq.PAIR`\n        pattern = 0\n        msg_pattern = valid_messaging_patterns[pattern]\n        self.__logging and logger.warning(\n            \"Wrong pattern value, Defaulting to `zmq.PAIR`! Kindly refer Docs for more Information.\"\n        )\n    # assign pattern to global parameter for further use\n    self.__pattern = pattern\n\n    # Handle messaging protocol\n    if protocol is None or not (protocol in [\"tcp\", \"ipc\"]):\n        # else default to `tcp` protocol\n        protocol = \"tcp\"\n        # log it\n        self.__logging and logger.warning(\n            \"Protocol is not supported or not provided. Defaulting to `tcp` protocol!\"\n        )\n\n    # Handle connection params\n\n    self.__msg_flag = 0  # handles connection flags\n    self.__msg_copy = False  # handles whether to copy data\n    self.__msg_track = False  # handles whether to track packets\n\n    # Handle NetGear's internal exclusive modes and params\n\n    # define Secure Mode\n    self.__z_auth = None\n\n    # define SSH Tunneling Mode\n    self.__ssh_tunnel_mode = None  # handles ssh_tunneling mode state\n    self.__ssh_tunnel_pwd = None\n    self.__ssh_tunnel_keyfile = None\n    self.__paramiko_present = False if paramiko is None else True\n\n    # define Multi-Server mode\n    self.__multiserver_mode = False  # handles multi-server mode state\n\n    # define Multi-Client mode\n    self.__multiclient_mode = False  # handles multi-client mode state\n\n    # define Bidirectional mode\n    self.__bi_mode = False  # handles Bidirectional mode state\n\n    # define Secure mode\n    valid_security_mech = {0: \"Grasslands\", 1: \"StoneHouse\", 2: \"IronHouse\"}\n    self.__secure_mode = 0  # handles ZMQ security layer status\n    auth_cert_dir = \"\"  # handles valid ZMQ certificates dir\n    self.__auth_publickeys_dir = \"\"  # handles valid ZMQ public certificates dir\n    self.__auth_secretkeys_dir = \"\"  # handles valid ZMQ private certificates dir\n    overwrite_cert = False  # checks if certificates overwriting allowed\n    custom_cert_location = \"\"  # handles custom ZMQ certificates path\n\n    # define frame-compression handler\n    self.__jpeg_compression = (\n        True if not (simplejpeg is None) else False\n    )  # enabled by default for all connections if simplejpeg is installed\n    self.__jpeg_compression_quality = 90  # 90% quality\n    self.__jpeg_compression_fastdct = True  # fastest DCT on by default\n    self.__jpeg_compression_fastupsample = False  # fastupsample off by default\n    self.__jpeg_compression_colorspace = \"BGR\"  # use BGR colorspace by default\n\n    # defines frame compression on return data\n    self.__ex_compression_params = None\n\n    # define receiver return data handler\n    self.__return_data = None\n\n    # generate 8-digit random system id\n    self.__id = \"\".join(\n        secrets.choice(string.ascii_uppercase + string.digits) for i in range(8)\n    )\n\n    # define termination flag\n    self.__terminate = False\n\n    # additional settings for reliability\n    if pattern < 2:\n        # define zmq poller for reliable transmission\n        self.__poll = zmq.Poller()\n        # define max retries\n        self.__max_retries = 3\n        # request timeout\n        self.__request_timeout = 4000  # 4 secs\n    else:\n        # subscriber timeout\n        self.__subscriber_timeout = None\n\n    # Handle user-defined options dictionary values\n    # reformat dictionary\n    options = {str(k).strip(): v for k, v in options.items()}\n\n    # loop over dictionary key & values and assign to global variables if valid\n    for key, value in options.items():\n        # handle multi-server mode\n        if key == \"multiserver_mode\" and isinstance(value, bool):\n            # check if valid pattern assigned\n            if pattern > 0:\n                # activate Multi-server mode\n                self.__multiserver_mode = value\n            else:\n                # otherwise disable it and raise error\n                self.__multiserver_mode = False\n                logger.critical(\"Multi-Server Mode is disabled!\")\n                raise ValueError(\n                    \"[NetGear:ERROR] :: `{}` pattern is not valid when Multi-Server Mode is enabled. Kindly refer Docs for more Information.\".format(\n                        pattern\n                    )\n                )\n\n        # handle multi-client mode\n        elif key == \"multiclient_mode\" and isinstance(value, bool):\n            # check if valid pattern assigned\n            if pattern > 0:\n                # activate Multi-client mode\n                self.__multiclient_mode = value\n            else:\n                # otherwise disable it and raise error\n                self.__multiclient_mode = False\n                logger.critical(\"Multi-Client Mode is disabled!\")\n                raise ValueError(\n                    \"[NetGear:ERROR] :: `{}` pattern is not valid when Multi-Client Mode is enabled. Kindly refer Docs for more Information.\".format(\n                        pattern\n                    )\n                )\n\n        # handle bidirectional mode\n        elif key == \"bidirectional_mode\" and isinstance(value, bool):\n            # check if pattern is valid\n            if pattern < 2:\n                # activate Bidirectional mode if specified\n                self.__bi_mode = value\n            else:\n                # otherwise disable it and raise error\n                self.__bi_mode = False\n                logger.warning(\"Bidirectional data transmission is disabled!\")\n                raise ValueError(\n                    \"[NetGear:ERROR] :: `{}` pattern is not valid when Bidirectional Mode is enabled. Kindly refer Docs for more Information!\".format(\n                        pattern\n                    )\n                )\n\n        # handle secure mode\n        elif (\n            key == \"secure_mode\"\n            and isinstance(value, int)\n            and (value in valid_security_mech)\n        ):\n            self.__secure_mode = value\n\n        elif key == \"custom_cert_location\" and isinstance(value, str):\n            # verify custom auth certificates path for secure mode\n            custom_cert_location = os.path.abspath(value)\n            assert os.path.isdir(\n                custom_cert_location\n            ), \"[NetGear:ERROR] :: `custom_cert_location` value must be the path to a valid directory!\"\n            assert check_WriteAccess(\n                custom_cert_location,\n                is_windows=True if os.name == \"nt\" else False,\n                logging=self.__logging,\n            ), \"[NetGear:ERROR] :: Permission Denied!, cannot write ZMQ authentication certificates to '{}' directory!\".format(\n                value\n            )\n        elif key == \"overwrite_cert\" and isinstance(value, bool):\n            # enable/disable auth certificate overwriting in secure mode\n            overwrite_cert = value\n\n        # handle ssh-tunneling mode\n        elif key == \"ssh_tunnel_mode\" and isinstance(value, str):\n            # enable SSH Tunneling Mode\n            self.__ssh_tunnel_mode = value.strip()\n        elif key == \"ssh_tunnel_pwd\" and isinstance(value, str):\n            # add valid SSH Tunneling password\n            self.__ssh_tunnel_pwd = value\n        elif key == \"ssh_tunnel_keyfile\" and isinstance(value, str):\n            # add valid SSH Tunneling key-file\n            self.__ssh_tunnel_keyfile = value if os.path.isfile(value) else None\n            if self.__ssh_tunnel_keyfile is None:\n                logger.warning(\n                    \"Discarded invalid or non-existential SSH Tunnel Key-file at {}!\".format(\n                        value\n                    )\n                )\n\n        # handle jpeg compression\n        elif (\n            key == \"jpeg_compression\"\n            and not (simplejpeg is None)\n            and isinstance(value, (bool, str))\n        ):\n            if isinstance(value, str) and value.strip().upper() in [\n                \"RGB\",\n                \"BGR\",\n                \"RGBX\",\n                \"BGRX\",\n                \"XBGR\",\n                \"XRGB\",\n                \"GRAY\",\n                \"RGBA\",\n                \"BGRA\",\n                \"ABGR\",\n                \"ARGB\",\n                \"CMYK\",\n            ]:\n                # set encoding colorspace\n                self.__jpeg_compression_colorspace = value.strip().upper()\n                # enable frame-compression encoding value\n                self.__jpeg_compression = True\n            else:\n                # enable frame-compression encoding value\n                self.__jpeg_compression = value\n        elif key == \"jpeg_compression_quality\" and isinstance(value, (int, float)):\n            # set valid jpeg quality\n            if value >= 10 and value <= 100:\n                self.__jpeg_compression_quality = int(value)\n            else:\n                logger.warning(\"Skipped invalid `jpeg_compression_quality` value!\")\n        elif key == \"jpeg_compression_fastdct\" and isinstance(value, bool):\n            # enable jpeg fastdct\n            self.__jpeg_compression_fastdct = value\n        elif key == \"jpeg_compression_fastupsample\" and isinstance(value, bool):\n            # enable jpeg  fastupsample\n            self.__jpeg_compression_fastupsample = value\n\n        # assign maximum retries in synchronous patterns\n        elif key == \"max_retries\" and isinstance(value, int) and pattern < 2:\n            if value >= 0:\n                self.__max_retries = value\n            else:\n                logger.warning(\"Invalid `max_retries` value skipped!\")\n\n        # assign request timeout in synchronous patterns\n        elif key == \"request_timeout\" and isinstance(value, int) and pattern < 2:\n            if value >= 4:\n                self.__request_timeout = value * 1000  # covert to milliseconds\n            else:\n                logger.warning(\"Invalid `request_timeout` value skipped!\")\n\n        # assign subscriber timeout\n        elif (\n            key == \"subscriber_timeout\" and isinstance(value, int) and pattern == 2\n        ):\n            if value > 0:\n                self.__subscriber_timeout = value * 1000  # covert to milliseconds\n            else:\n                logger.warning(\"Invalid `request_timeout` value skipped!\")\n\n        # handle ZMQ flags\n        elif key == \"flag\" and isinstance(value, int):\n            self.__msg_flag = value\n            self.__msg_flag and logger.warning(\n                \"The flag optional value is set to `1` (NOBLOCK) for this run. This might cause NetGear to not terminate gracefully.\"\n            )\n        elif key == \"copy\" and isinstance(value, bool):\n            self.__msg_copy = value\n        elif key == \"track\" and isinstance(value, bool):\n            self.__msg_track = value\n            self.__msg_copy and self.__msg_track and logger.info(\n                \"The `track` optional value will be ignored for this run because `copy=True` is also defined.\"\n            )\n        else:\n            pass\n\n    # Handle ssh tunneling if enabled\n    if not (self.__ssh_tunnel_mode is None):\n        # SSH Tunnel Mode only available for server mode\n        if receive_mode:\n            logger.error(\"SSH Tunneling cannot be enabled for Client-end!\")\n        else:\n            # check if SSH tunneling possible\n            ssh_address = self.__ssh_tunnel_mode\n            ssh_address, ssh_port = (\n                ssh_address.split(\":\")\n                if \":\" in ssh_address\n                else [ssh_address, \"22\"]\n            )  # default to port 22\n            if \"47\" in ssh_port:\n                self.__ssh_tunnel_mode = self.__ssh_tunnel_mode.replace(\n                    \":47\", \"\"\n                )  # port-47 is reserved for testing\n            else:\n                # extract ip for validation\n                ssh_user, ssh_ip = (\n                    ssh_address.split(\"@\")\n                    if \"@\" in ssh_address\n                    else [\"\", ssh_address]\n                )\n                # validate ip specified port\n                assert check_open_port(\n                    ssh_ip, port=int(ssh_port)\n                ), \"[NetGear:ERROR] :: Host `{}` is not available for SSH Tunneling at port-{}!\".format(\n                    ssh_address, ssh_port\n                )\n\n    # Handle multiple exclusive modes if enabled\n    if self.__multiclient_mode and self.__multiserver_mode:\n        raise ValueError(\n            \"[NetGear:ERROR] :: Multi-Client and Multi-Server Mode cannot be enabled simultaneously!\"\n        )\n    elif self.__multiserver_mode or self.__multiclient_mode:\n        # check if Bidirectional Mode also enabled\n        if self.__bi_mode:\n            # log it\n            self.__logging and logger.debug(\n                \"Bidirectional Data Transmission is also enabled for this connection!\"\n            )\n        # check if SSH Tunneling Mode also enabled\n        if self.__ssh_tunnel_mode:\n            # raise error\n            raise ValueError(\n                \"[NetGear:ERROR] :: SSH Tunneling and {} Mode cannot be enabled simultaneously. Kindly refer docs!\".format(\n                    \"Multi-Server\" if self.__multiserver_mode else \"Multi-Client\"\n                )\n            )\n    elif self.__bi_mode:\n        # log Bidirectional mode activation\n        self.__logging and logger.debug(\n            \"Bidirectional Data Transmission is enabled for this connection!\"\n        )\n    elif self.__ssh_tunnel_mode:\n        # log Bidirectional mode activation\n        self.__logging and logger.debug(\n            \"SSH Tunneling is enabled for host:`{}` with `{}` back-end.\".format(\n                self.__ssh_tunnel_mode,\n                \"paramiko\" if self.__paramiko_present else \"pexpect\",\n            )\n        )\n\n    # On Windows, NetGear requires the ``WindowsSelectorEventLoop`` but Python 3.8 and above,\n    # defaults to an ``ProactorEventLoop`` loop that is not compatible with it. Thereby,\n    # we had to set it manually.\n    platform.system() == \"Windows\" and asyncio.set_event_loop_policy(\n        asyncio.WindowsSelectorEventLoopPolicy()\n    )\n\n    # define ZMQ messaging context instance\n    self.__msg_context = zmq.Context.instance()\n\n    # initialize and assign receive mode to global variable\n    self.__receive_mode = receive_mode\n\n    # Handle Secure mode\n    if self.__secure_mode > 0:\n        # activate and log if overwriting is enabled\n        if receive_mode:\n            overwrite_cert = False\n            overwrite_cert and logger.warning(\n                \"Overwriting ZMQ Authentication certificates is disabled for Client's end!\"\n            )\n        else:\n            overwrite_cert and self.__logging and logger.info(\n                \"Overwriting ZMQ Authentication certificates over previous ones!\"\n            )\n\n        # Validate certificate generation paths\n        # Start threaded authenticator for this context\n        try:\n            # check if custom certificates path is specified\n            if custom_cert_location:\n                (\n                    auth_cert_dir,\n                    self.__auth_secretkeys_dir,\n                    self.__auth_publickeys_dir,\n                ) = generate_auth_certificates(\n                    custom_cert_location, overwrite=overwrite_cert, logging=logging\n                )\n            else:\n                # otherwise auto-generate suitable path\n                (\n                    auth_cert_dir,\n                    self.__auth_secretkeys_dir,\n                    self.__auth_publickeys_dir,\n                ) = generate_auth_certificates(\n                    os.path.join(expanduser(\"~\"), \".vidgear\"),\n                    overwrite=overwrite_cert,\n                    logging=logging,\n                )\n            # log it\n            self.__logging and logger.debug(\n                \"`{}` is the default location for storing ZMQ authentication certificates/keys.\".format(\n                    auth_cert_dir\n                )\n            )\n\n            # start an authenticator for this context\n            self.__z_auth = ThreadAuthenticator(self.__msg_context)\n            self.__z_auth.start()\n            self.__z_auth.allow(str(address))  # allow current address\n\n            # check if `IronHouse` is activated\n            if self.__secure_mode == 2:\n                # tell authenticator to use the certificate from given valid dir\n                self.__z_auth.configure_curve(\n                    domain=\"*\", location=self.__auth_publickeys_dir\n                )\n            else:\n                # otherwise tell the authenticator how to handle the CURVE requests, if `StoneHouse` is activated\n                self.__z_auth.configure_curve(\n                    domain=\"*\", location=auth.CURVE_ALLOW_ANY\n                )\n        except zmq.ZMQError as e:\n            if \"Address in use\" in str(e):\n                logger.info(\"ZMQ Authenticator already running.\")\n            else:\n                # catch if any error occurred and disable Secure mode\n                logger.exception(str(e))\n                self.__secure_mode = 0\n                logger.error(\n                    \"ZMQ Security Mechanism is disabled for this connection due to errors!\"\n                )\n\n    # check whether `receive_mode` is enabled\n    if self.__receive_mode:\n        # define connection address\n        address = \"*\" if address is None else address\n\n        # check if multiserver_mode is enabled\n        if self.__multiserver_mode:\n            # check if unique server port address list/tuple is assigned or not in multiserver_mode\n            if port is None or not isinstance(port, (tuple, list)):\n                # raise error if not\n                raise ValueError(\n                    \"[NetGear:ERROR] :: Incorrect port value! Kindly provide a list/tuple of Server ports while Multi-Server mode is enabled. For more information refer VidGear docs.\"\n                )\n            else:\n                # otherwise log it\n                logger.debug(\n                    \"Enabling Multi-Server Mode at PORTS: {}!\".format(port)\n                )\n            # create port address buffer for keeping track of connected client's port(s)\n            self.__port_buffer = []\n        # check if multiclient_mode is enabled\n        elif self.__multiclient_mode:\n            # check if unique server port address is assigned or not in multiclient_mode\n            if port is None:\n                # raise error if not\n                raise ValueError(\n                    \"[NetGear:ERROR] :: Kindly provide a unique & valid port value at Client-end. For more information refer VidGear docs.\"\n                )\n            else:\n                # otherwise log it\n                logger.debug(\n                    \"Enabling Multi-Client Mode at PORT: {} on this device!\".format(\n                        port\n                    )\n                )\n            # assign value to global variable\n            self.__port = port\n        else:\n            # otherwise assign local port address if None\n            port = \"5555\" if port is None else port\n\n        try:\n            # define thread-safe messaging socket\n            self.__msg_socket = self.__msg_context.socket(msg_pattern[1])\n\n            # define pub-sub flag\n            self.__pattern == 2 and self.__msg_socket.set_hwm(1)\n\n            # enable specified secure mode for the socket\n            if self.__secure_mode > 0:\n                # load server key\n                server_secret_file = os.path.join(\n                    self.__auth_secretkeys_dir, \"server.key_secret\"\n                )\n                server_public, server_secret = auth.load_certificate(\n                    server_secret_file\n                )\n                # load  all CURVE keys\n                self.__msg_socket.curve_secretkey = server_secret\n                self.__msg_socket.curve_publickey = server_public\n                # enable CURVE connection for this socket\n                self.__msg_socket.curve_server = True\n\n            # define exclusive socket options for `patterns=2`\n            if self.__pattern == 2:\n                self.__msg_socket.setsockopt_string(zmq.SUBSCRIBE, \"\")\n                self.__subscriber_timeout and self.__msg_socket.setsockopt(\n                    zmq.RCVTIMEO, self.__subscriber_timeout\n                )\n                self.__subscriber_timeout and self.__msg_socket.setsockopt(\n                    zmq.LINGER, 0\n                )\n\n            # if multiserver_mode is enabled, then assign port addresses to zmq socket\n            if self.__multiserver_mode:\n                # bind socket to given server protocol, address and ports\n                for pt in port:\n                    self.__msg_socket.bind(\n                        protocol + \"://\" + str(address) + \":\" + str(pt)\n                    )\n            else:\n                # bind socket to given protocol, address and port normally\n                self.__msg_socket.bind(\n                    protocol + \"://\" + str(address) + \":\" + str(port)\n                )\n\n            # additional settings\n            if pattern < 2:\n                if self.__multiserver_mode:\n                    self.__connection_address = []\n                    for pt in port:\n                        self.__connection_address.append(\n                            protocol + \"://\" + str(address) + \":\" + str(pt)\n                        )\n                else:\n                    self.__connection_address = (\n                        protocol + \"://\" + str(address) + \":\" + str(port)\n                    )\n                self.__msg_pattern = msg_pattern[1]\n                self.__poll.register(self.__msg_socket, zmq.POLLIN)\n                self.__logging and logger.debug(\n                    \"Reliable transmission is enabled for this pattern with max-retries: {} and timeout: {} secs.\".format(\n                        self.__max_retries, self.__request_timeout / 1000\n                    )\n                )\n            else:\n                self.__logging and self.__subscriber_timeout and logger.debug(\n                    \"Timeout: {} secs is enabled for this system.\".format(\n                        self.__subscriber_timeout / 1000\n                    )\n                )\n\n        except Exception as e:\n            # otherwise log and raise error\n            logger.exception(str(e))\n            # Handle Secure Mode\n            self.__secure_mode and logger.critical(\n                \"Failed to activate Secure Mode: `{}` for this connection!\".format(\n                    valid_security_mech[self.__secure_mode]\n                )\n            )\n            # raise errors for exclusive modes\n            if self.__multiserver_mode or self.__multiclient_mode:\n                raise RuntimeError(\n                    \"[NetGear:ERROR] :: Receive Mode failed to activate {} Mode at address: {} with pattern: {}! Kindly recheck all parameters.\".format(\n                        (\n                            \"Multi-Server\"\n                            if self.__multiserver_mode\n                            else \"Multi-Client\"\n                        ),\n                        (protocol + \"://\" + str(address) + \":\" + str(port)),\n                        pattern,\n                    )\n                )\n            else:\n                self.__bi_mode and logger.critical(\n                    \"Failed to activate Bidirectional Mode for this connection!\"\n                )\n                raise RuntimeError(\n                    \"[NetGear:ERROR] :: Receive Mode failed to bind address: {} and pattern: {}! Kindly recheck all parameters.\".format(\n                        (protocol + \"://\" + str(address) + \":\" + str(port)), pattern\n                    )\n                )\n\n        # Handle threaded queue mode\n        self.__logging and logger.debug(\n            \"Threaded Queue Mode is enabled by default for this connection.\"\n        )\n\n        # define deque and assign it to global var\n        self.__queue = deque(maxlen=96)  # max len 96 to check overflow\n\n        # initialize and start threaded recv_handler\n        self.__thread = Thread(target=self.__recv_handler, name=\"NetGear\", args=())\n        self.__thread.daemon = True\n        self.__thread.start()\n\n        if self.__logging:\n            # finally log progress\n            logger.debug(\n                \"Successfully Binded to address: {} with pattern: {}.\".format(\n                    (protocol + \"://\" + str(address) + \":\" + str(port)), pattern\n                )\n            )\n            self.__jpeg_compression and logger.debug(\n                \"JPEG Frame-Compression is activated for this connection with Colorspace:`{}`, Quality:`{}`%, Fastdct:`{}`, and Fastupsample:`{}`.\".format(\n                    self.__jpeg_compression_colorspace,\n                    self.__jpeg_compression_quality,\n                    (\"enabled\" if self.__jpeg_compression_fastdct else \"disabled\"),\n                    (\n                        \"enabled\"\n                        if self.__jpeg_compression_fastupsample\n                        else \"disabled\"\n                    ),\n                )\n            )\n            self.__secure_mode and logger.debug(\n                \"Successfully enabled ZMQ Security Mechanism: `{}` for this connection.\".format(\n                    valid_security_mech[self.__secure_mode]\n                )\n            )\n            logger.debug(\"Multi-threaded Receive Mode is successfully enabled.\")\n            logger.debug(\"Unique System ID is {}.\".format(self.__id))\n            logger.debug(\"Receive Mode is now activated.\")\n\n    else:\n        # otherwise default to `Send Mode`\n        # define connection address\n        address = \"localhost\" if address is None else address\n\n        # check if multiserver_mode is enabled\n        if self.__multiserver_mode:\n            # check if unique server port address is assigned or not in multiserver_mode\n            if port is None:\n                # raise error if not\n                raise ValueError(\n                    \"[NetGear:ERROR] :: Kindly provide a unique & valid port value at Server-end. For more information refer VidGear docs.\"\n                )\n            else:\n                # otherwise log it\n                logger.debug(\n                    \"Enabling Multi-Server Mode at PORT: {} on this device!\".format(\n                        port\n                    )\n                )\n            # assign value to global variable\n            self.__port = port\n        # check if multiclient_mode is enabled\n        elif self.__multiclient_mode:\n            # check if unique client port address list/tuple is assigned or not in multiclient_mode\n            if port is None or not isinstance(port, (tuple, list)):\n                # raise error if not\n                raise ValueError(\n                    \"[NetGear:ERROR] :: Incorrect port value! Kindly provide a list/tuple of Client ports while Multi-Client mode is enabled. For more information refer VidGear docs.\"\n                )\n            else:\n                # otherwise log it\n                logger.debug(\n                    \"Enabling Multi-Client Mode at PORTS: {}!\".format(port)\n                )\n            # create port address buffer for keeping track of connected client ports\n            self.__port_buffer = []\n        else:\n            # otherwise assign local port address if None\n            port = \"5555\" if port is None else port\n\n        try:\n            # define thread-safe messaging socket\n            self.__msg_socket = self.__msg_context.socket(msg_pattern[0])\n\n            # if req/rep pattern, define additional flags\n            if self.__pattern == 1:\n                self.__msg_socket.REQ_RELAXED = True\n                self.__msg_socket.REQ_CORRELATE = True\n\n            # if pub/sub pattern, define additional optimizer\n            if self.__pattern == 2:\n                self.__msg_socket.set_hwm(1)\n\n            # enable specified secure mode for the socket\n            if self.__secure_mode > 0:\n                # load client key\n                client_secret_file = os.path.join(\n                    self.__auth_secretkeys_dir, \"client.key_secret\"\n                )\n                client_public, client_secret = auth.load_certificate(\n                    client_secret_file\n                )\n                # load  all CURVE keys\n                self.__msg_socket.curve_secretkey = client_secret\n                self.__msg_socket.curve_publickey = client_public\n                # load server key\n                server_public_file = os.path.join(\n                    self.__auth_publickeys_dir, \"server.key\"\n                )\n                server_public, _ = auth.load_certificate(server_public_file)\n                # inject public key to make a CURVE connection.\n                self.__msg_socket.curve_serverkey = server_public\n\n            # check if multi-client_mode is enabled\n            if self.__multiclient_mode:\n                # bind socket to given server protocol, address and ports\n                for pt in port:\n                    self.__msg_socket.connect(\n                        protocol + \"://\" + str(address) + \":\" + str(pt)\n                    )\n            else:\n                # handle SSH tunneling if enabled\n                if self.__ssh_tunnel_mode:\n                    # establish tunnel connection\n                    ssh.tunnel_connection(\n                        self.__msg_socket,\n                        protocol + \"://\" + str(address) + \":\" + str(port),\n                        self.__ssh_tunnel_mode,\n                        keyfile=self.__ssh_tunnel_keyfile,\n                        password=self.__ssh_tunnel_pwd,\n                        paramiko=self.__paramiko_present,\n                    )\n                else:\n                    # connect socket to given protocol, address and port\n                    self.__msg_socket.connect(\n                        protocol + \"://\" + str(address) + \":\" + str(port)\n                    )\n\n            # additional settings\n            if pattern < 2:\n                if self.__multiclient_mode:\n                    self.__connection_address = []\n                    for pt in port:\n                        self.__connection_address.append(\n                            protocol + \"://\" + str(address) + \":\" + str(pt)\n                        )\n                else:\n                    self.__connection_address = (\n                        protocol + \"://\" + str(address) + \":\" + str(port)\n                    )\n                self.__msg_pattern = msg_pattern[0]\n                self.__poll.register(self.__msg_socket, zmq.POLLIN)\n\n                self.__logging and logger.debug(\n                    \"Reliable transmission is enabled for this pattern with max-retries: {} and timeout: {} secs.\".format(\n                        self.__max_retries, self.__request_timeout / 1000\n                    )\n                )\n\n        except Exception as e:\n            # otherwise log and raise error\n            logger.exception(str(e))\n            # Handle Secure Mode\n            self.__secure_mode and logger.critical(\n                \"Failed to activate Secure Mode: `{}` for this connection!\".format(\n                    valid_security_mech[self.__secure_mode]\n                )\n            )\n            # raise errors for exclusive modes\n            if self.__multiserver_mode or self.__multiclient_mode:\n                raise RuntimeError(\n                    \"[NetGear:ERROR] :: Send Mode failed to activate {} Mode at address: {} with pattern: {}! Kindly recheck all parameters.\".format(\n                        (\n                            \"Multi-Server\"\n                            if self.__multiserver_mode\n                            else \"Multi-Client\"\n                        ),\n                        (protocol + \"://\" + str(address) + \":\" + str(port)),\n                        pattern,\n                    )\n                )\n            else:\n                self.__bi_mode and logger.critical(\n                    \"Failed to activate Bidirectional Mode for this connection!\"\n                )\n                self.__ssh_tunnel_mode and logger.critical(\n                    \"Failed to initiate SSH Tunneling Mode for this server with `{}` back-end!\".format(\n                        \"paramiko\" if self.__paramiko_present else \"pexpect\"\n                    )\n                )\n                raise RuntimeError(\n                    \"[NetGear:ERROR] :: Send Mode failed to connect address: {} and pattern: {}! Kindly recheck all parameters.\".format(\n                        (protocol + \"://\" + str(address) + \":\" + str(port)), pattern\n                    )\n                )\n\n        if self.__logging:\n            # finally log progress\n            logger.debug(\n                \"Successfully connected to address: {} with pattern: {}.\".format(\n                    (protocol + \"://\" + str(address) + \":\" + str(port)), pattern\n                )\n            )\n            self.__jpeg_compression and logger.debug(\n                \"JPEG Frame-Compression is activated for this connection with Colorspace:`{}`, Quality:`{}`%, Fastdct:`{}`, and Fastupsample:`{}`.\".format(\n                    self.__jpeg_compression_colorspace,\n                    self.__jpeg_compression_quality,\n                    (\"enabled\" if self.__jpeg_compression_fastdct else \"disabled\"),\n                    (\n                        \"enabled\"\n                        if self.__jpeg_compression_fastupsample\n                        else \"disabled\"\n                    ),\n                )\n            )\n            self.__secure_mode and logger.debug(\n                \"Enabled ZMQ Security Mechanism: `{}` for this connection.\".format(\n                    valid_security_mech[self.__secure_mode]\n                )\n            )\n            logger.debug(\"Unique System ID is {}.\".format(self.__id))\n            logger.debug(\n                \"Send Mode is successfully activated and ready to send data.\"\n            )\n
"},{"location":"bonus/reference/netgear/#vidgear.gears.netgear.NetGear.close","title":"close(self, kill=False)","text":"

Safely terminates the threads, and NetGear resources.

Parameters:

Name Type Description Default kill bool

Kills ZMQ context instead of graceful exiting in receive mode.

False Source code in vidgear/gears/netgear.py
def close(self, kill=False):\n    \"\"\"\n    Safely terminates the threads, and NetGear resources.\n\n    Parameters:\n        kill (bool): Kills ZMQ context instead of graceful exiting in receive mode.\n    \"\"\"\n    # log it\n    self.__logging and logger.debug(\n        \"Terminating various {} Processes.\".format(\n            \"Receive Mode\" if self.__receive_mode else \"Send Mode\"\n        )\n    )\n    #  whether `receive_mode` is enabled or not\n    if self.__receive_mode:\n        # check whether queue mode is empty\n        if not (self.__queue is None) and self.__queue:\n            self.__queue.clear()\n        # call immediate termination\n        self.__terminate = True\n        # properly close the socket\n        self.__logging and logger.debug(\"Terminating. Please wait...\")\n        # Handle Secure Mode Thread\n        if self.__z_auth:\n            self.__logging and logger.debug(\"Terminating Authenticator Thread.\")\n            self.__z_auth.stop()\n            while self.__z_auth.is_alive():\n                pass\n        # wait until stream resources are released\n        # (producer thread might be still grabbing frame)\n        if self.__thread is not None:\n            self.__logging and logger.debug(\"Terminating Main Thread.\")\n            # properly handle thread exit\n            if self.__thread.is_alive() and kill:\n                # force close if still alive\n                logger.warning(\"Thread still running...Killing it forcefully!\")\n                self.__msg_context.destroy()\n                self.__thread.join()\n            else:\n                self.__msg_socket.close(linger=0)\n                self.__thread.join()\n            self.__thread = None\n        self.__logging and logger.debug(\"Terminated Successfully!\")\n    else:\n        # indicate that process should be terminated\n        self.__terminate = True\n        # log if kill enabled\n        kill and logger.warning(\n            \"`kill` parmeter is only available in the receive mode.\"\n        )\n        # Handle Secure Mode Thread\n        if self.__z_auth:\n            self.__logging and logger.debug(\"Terminating Authenticator Thread.\")\n            self.__z_auth.stop()\n            while self.__z_auth.is_alive():\n                pass\n        # check if all attempts of reconnecting failed, then skip to closure\n        if (self.__pattern < 2 and not self.__max_retries) or (\n            self.__multiclient_mode and not self.__port_buffer\n        ):\n            try:\n                # properly close the socket\n                self.__msg_socket.setsockopt(zmq.LINGER, 0)\n                self.__msg_socket.close()\n            except ZMQError:\n                pass\n            finally:\n                # exit\n                return\n\n        if self.__multiserver_mode:\n            # check if multiserver_mode\n            # send termination flag to client with its unique port\n            term_dict = dict(terminate_flag=True, port=self.__port)\n        else:\n            # otherwise send termination flag to client\n            term_dict = dict(terminate_flag=True)\n\n        try:\n            if self.__multiclient_mode:\n                for _ in self.__port_buffer:\n                    self.__msg_socket.send_json(term_dict)\n            else:\n                self.__msg_socket.send_json(term_dict)\n\n            # check for confirmation if available within 1/5 timeout\n            if self.__pattern < 2:\n                self.__logging and logger.debug(\"Terminating. Please wait...\")\n                if self.__msg_socket.poll(self.__request_timeout // 5, zmq.POLLIN):\n                    self.__msg_socket.recv()\n        except Exception as e:\n            if not isinstance(e, ZMQError):\n                logger.exception(str(e))\n        finally:\n            # properly close the socket\n            self.__msg_socket.setsockopt(zmq.LINGER, 0)\n            self.__msg_socket.close()\n            self.__logging and logger.debug(\"Terminated Successfully!\")\n
"},{"location":"bonus/reference/netgear/#vidgear.gears.netgear.NetGear.recv","title":"recv(self, return_data=None)","text":"

A Receiver end method, that extracts received frames synchronously from monitored deque, while maintaining a fixed-length frame buffer in the memory, and blocks the thread if the deque is full.

Parameters:

Name Type Description Default return_data any

inputs return data (of any datatype), for sending back to Server.

None

Returns: A n-dimensional numpy array.

Source code in vidgear/gears/netgear.py
def recv(self, return_data=None):\n    \"\"\"\n    A Receiver end method, that extracts received frames synchronously from monitored deque, while maintaining a\n    fixed-length frame buffer in the memory, and blocks the thread if the deque is full.\n\n    Parameters:\n        return_data (any): inputs return data _(of any datatype)_, for sending back to Server.\n\n    **Returns:** A n-dimensional numpy array.\n    \"\"\"\n    # check whether `receive mode` is activated\n    if not (self.__receive_mode):\n        # raise value error and exit\n        self.__terminate = True\n        raise ValueError(\n            \"[NetGear:ERROR] :: `recv()` function cannot be used while receive_mode is disabled. Kindly refer vidgear docs!\"\n        )\n\n    # handle Bidirectional return data\n    if (self.__bi_mode or self.__multiclient_mode) and not (return_data is None):\n        self.__return_data = return_data\n\n    # check whether or not termination flag is enabled\n    while not self.__terminate:\n        try:\n            # check if queue is empty\n            if len(self.__queue) > 0:\n                return self.__queue.popleft()\n            else:\n                time.sleep(0.00001)\n                continue\n        except KeyboardInterrupt:\n            self.__terminate = True\n            break\n    # otherwise return NoneType\n    return None\n
"},{"location":"bonus/reference/netgear/#vidgear.gears.netgear.NetGear.send","title":"send(self, frame, message=None)","text":"

A Server end method, that sends the data and frames over the network to Client(s).

Parameters:

Name Type Description Default frame numpy.ndarray

inputs numpy array(frame).

required message any

input for sending additional data (of any datatype except numpy.ndarray) to Client(s).

None

Returns: Data (of any datatype) in selected exclusive modes, otherwise None-type.

Source code in vidgear/gears/netgear.py
def send(self, frame, message=None):\n    \"\"\"\n    A Server end method, that sends the data and frames over the network to Client(s).\n\n    Parameters:\n        frame (numpy.ndarray): inputs numpy array(frame).\n        message (any): input for sending additional data _(of any datatype except `numpy.ndarray`)_ to Client(s).\n\n    **Returns:** Data _(of any datatype)_ in selected exclusive modes, otherwise None-type.\n\n    \"\"\"\n    # check whether `receive_mode` is disabled\n    if self.__receive_mode:\n        # raise value error and exit\n        self.__terminate = True\n        raise ValueError(\n            \"[NetGear:ERROR] :: `send()` function cannot be used while receive_mode is enabled. Kindly refer vidgear docs!\"\n        )\n\n    if not (message is None) and isinstance(message, np.ndarray):\n        logger.warning(\n            \"Skipped unsupported `message` of datatype: {}!\".format(\n                type(message).__name__\n            )\n        )\n        message = None\n\n    # define exit_flag and assign value\n    exit_flag = True if (frame is None or self.__terminate) else False\n\n    # check whether exit_flag is False\n    if not (exit_flag) and not (frame.flags[\"C_CONTIGUOUS\"]):\n        # check whether the incoming frame is contiguous\n        frame = np.ascontiguousarray(frame, dtype=frame.dtype)\n\n    # handle JPEG compression encoding\n    if self.__jpeg_compression:\n        if self.__jpeg_compression_colorspace == \"GRAY\":\n            if frame.ndim == 2:\n                # patch for https://gitlab.com/jfolz/simplejpeg/-/issues/11\n                frame = np.expand_dims(frame, axis=2)\n            frame = simplejpeg.encode_jpeg(\n                frame,\n                quality=self.__jpeg_compression_quality,\n                colorspace=self.__jpeg_compression_colorspace,\n                fastdct=self.__jpeg_compression_fastdct,\n            )\n        else:\n            frame = simplejpeg.encode_jpeg(\n                frame,\n                quality=self.__jpeg_compression_quality,\n                colorspace=self.__jpeg_compression_colorspace,\n                colorsubsampling=\"422\",\n                fastdct=self.__jpeg_compression_fastdct,\n            )\n\n    # check if multiserver_mode is activated and assign values with unique port\n    msg_dict = dict(port=self.__port) if self.__multiserver_mode else dict()\n\n    # prepare the exclusive json dict\n    msg_dict.update(\n        dict(\n            terminate_flag=exit_flag,\n            compression=(\n                {\n                    \"dct\": self.__jpeg_compression_fastdct,\n                    \"ups\": self.__jpeg_compression_fastupsample,\n                    \"colorspace\": self.__jpeg_compression_colorspace,\n                }\n                if self.__jpeg_compression\n                else False\n            ),\n            message=message,\n            pattern=str(self.__pattern),\n            dtype=str(frame.dtype) if not (self.__jpeg_compression) else \"\",\n            shape=frame.shape if not (self.__jpeg_compression) else \"\",\n        )\n    )\n\n    # send the json dict\n    self.__msg_socket.send_json(msg_dict, self.__msg_flag | zmq.SNDMORE)\n    # send the frame array with correct flags\n    self.__msg_socket.send(\n        frame, flags=self.__msg_flag, copy=self.__msg_copy, track=self.__msg_track\n    )\n\n    # check if synchronous patterns, then wait for confirmation\n    if self.__pattern < 2:\n        # check if Bidirectional data transmission is enabled\n        if self.__bi_mode or self.__multiclient_mode:\n            # handles return data\n            recvd_data = None\n\n            socks = dict(self.__poll.poll(self.__request_timeout))\n            if socks.get(self.__msg_socket) == zmq.POLLIN:\n                # handle return data\n                recv_json = self.__msg_socket.recv_json(flags=self.__msg_flag)\n            else:\n                logger.critical(\"No response from Client, Reconnecting again...\")\n                # Socket is confused. Close and remove it.\n                self.__msg_socket.setsockopt(zmq.LINGER, 0)\n                self.__msg_socket.close()\n                self.__poll.unregister(self.__msg_socket)\n                self.__max_retries -= 1\n\n                if not (self.__max_retries):\n                    if self.__multiclient_mode:\n                        logger.error(\n                            \"All Clients failed to respond on multiple attempts.\"\n                        )\n                    else:\n                        logger.error(\n                            \"Client failed to respond on multiple attempts.\"\n                        )\n                    self.__terminate = True\n                    raise RuntimeError(\n                        \"[NetGear:ERROR] :: Client(s) seems to be offline, Abandoning.\"\n                    )\n\n                # Create new connection\n                self.__msg_socket = self.__msg_context.socket(self.__msg_pattern)\n                if isinstance(self.__connection_address, list):\n                    for _connection in self.__connection_address:\n                        self.__msg_socket.connect(_connection)\n                else:\n                    # handle SSH tunneling if enabled\n                    if self.__ssh_tunnel_mode:\n                        # establish tunnel connection\n                        ssh.tunnel_connection(\n                            self.__msg_socket,\n                            self.__connection_address,\n                            self.__ssh_tunnel_mode,\n                            keyfile=self.__ssh_tunnel_keyfile,\n                            password=self.__ssh_tunnel_pwd,\n                            paramiko=self.__paramiko_present,\n                        )\n                    else:\n                        # connect normally\n                        self.__msg_socket.connect(self.__connection_address)\n                self.__poll.register(self.__msg_socket, zmq.POLLIN)\n                # return None for mean-time\n                return None\n\n            # save the unique port addresses\n            if (\n                self.__multiclient_mode\n                and not recv_json[\"port\"] in self.__port_buffer\n            ):\n                self.__port_buffer.append(recv_json[\"port\"])\n\n            if recv_json[\"return_type\"] == \"ndarray\":\n                recv_array = self.__msg_socket.recv(\n                    flags=self.__msg_flag,\n                    copy=self.__msg_copy,\n                    track=self.__msg_track,\n                )\n                # check if encoding was enabled\n                if recv_json[\"compression\"]:\n                    # decode JPEG frame\n                    recvd_data = simplejpeg.decode_jpeg(\n                        recv_array,\n                        colorspace=recv_json[\"compression\"][\"colorspace\"],\n                        fastdct=self.__jpeg_compression_fastdct\n                        or recv_json[\"compression\"][\"dct\"],\n                        fastupsample=self.__jpeg_compression_fastupsample\n                        or recv_json[\"compression\"][\"ups\"],\n                    )\n                    # check if valid frame returned\n                    if recvd_data is None:\n                        self.__terminate = True\n                        # otherwise raise error and exit\n                        raise RuntimeError(\n                            \"[NetGear:ERROR] :: Received compressed frame `{}` decoding failed with flag: {}.\".format(\n                                recv_json[\"compression\"],\n                                self.__ex_compression_params,\n                            )\n                        )\n\n                    if (\n                        recv_json[\"compression\"][\"colorspace\"] == \"GRAY\"\n                        and recvd_data.ndim == 3\n                    ):\n                        # patch for https://gitlab.com/jfolz/simplejpeg/-/issues/11\n                        recvd_data = np.squeeze(recvd_data, axis=2)\n                else:\n                    recvd_data = np.frombuffer(\n                        recv_array, dtype=recv_json[\"array_dtype\"]\n                    ).reshape(recv_json[\"array_shape\"])\n            else:\n                recvd_data = recv_json[\"data\"]\n\n            return (\n                (recv_json[\"port\"], recvd_data)\n                if self.__multiclient_mode\n                else recvd_data\n            )\n        else:\n            # otherwise log normally\n            socks = dict(self.__poll.poll(self.__request_timeout))\n            if socks.get(self.__msg_socket) == zmq.POLLIN:\n                recv_confirmation = self.__msg_socket.recv()\n            else:\n                logger.critical(\"No response from Client, Reconnecting again...\")\n                # Socket is confused. Close and remove it.\n                self.__msg_socket.setsockopt(zmq.LINGER, 0)\n                self.__msg_socket.close()\n                self.__poll.unregister(self.__msg_socket)\n                self.__max_retries -= 1\n\n                if not (self.__max_retries):\n                    logger.error(\"Client failed to respond on repeated attempts.\")\n                    self.__terminate = True\n                    raise RuntimeError(\n                        \"[NetGear:ERROR] :: Client seems to be offline, Abandoning!\"\n                    )\n\n                # Create new connection\n                self.__msg_socket = self.__msg_context.socket(self.__msg_pattern)\n                # handle SSH tunneling if enabled\n                if self.__ssh_tunnel_mode:\n                    # establish tunnel connection\n                    ssh.tunnel_connection(\n                        self.__msg_socket,\n                        self.__connection_address,\n                        self.__ssh_tunnel_mode,\n                        keyfile=self.__ssh_tunnel_keyfile,\n                        password=self.__ssh_tunnel_pwd,\n                        paramiko=self.__paramiko_present,\n                    )\n                else:\n                    # connect normally\n                    self.__msg_socket.connect(self.__connection_address)\n                self.__poll.register(self.__msg_socket, zmq.POLLIN)\n                return None\n\n            # log confirmation\n            self.__logging and logger.debug(recv_confirmation)\n
"},{"location":"bonus/reference/netgear_async/","title":"NetGear_Async API References","text":"

NetGear_Async API usage examples can be found here \u27b6

NetGear_Async API parameters are explained here \u27b6

NetGear_Async can generate the same performance as NetGear API at about one-third the memory consumption, and also provide complete server-client handling with various options to use variable protocols/patterns similar to NetGear, but lacks in term of flexibility as it supports only a few NetGear's Exclusive Modes.

NetGear_Async is built on zmq.asyncio, and powered by a high-performance asyncio event loop called uvloop to achieve unwatchable high-speed and lag-free video streaming over the network with minimal resource constraints. NetGear_Async can transfer thousands of frames in just a few seconds without causing any significant load on your system.

NetGear_Async provides complete server-client handling and options to use variable protocols/patterns similar to NetGear API. Furthermore, NetGear_Async allows us to define our custom Server as source to transform frames easily before sending them across the network.

NetGear_Async now supports additional bidirectional data transmission between receiver(client) and sender(server) while transferring frames. Users can easily build complex applications such as like Real-Time Video Chat in just few lines of code.

In addition to all this, NetGear_Async API also provides internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs, thereby granting it exclusive power for transferring frames incoming from any source to the network.

NetGear_Async as of now supports four ZeroMQ messaging patterns:

Whereas supported protocol are: tcp and ipc.

Source code in vidgear/gears/asyncio/netgear_async.py
class NetGear_Async:\n    \"\"\"\n    NetGear_Async can generate the same performance as NetGear API at about one-third the memory consumption, and also provide complete server-client handling with various\n    options to use variable protocols/patterns similar to NetGear, but lacks in term of flexibility as it supports only a few NetGear's Exclusive Modes.\n\n    NetGear_Async is built on `zmq.asyncio`, and powered by a high-performance asyncio event loop called uvloop to achieve unwatchable high-speed and lag-free video streaming\n    over the network with minimal resource constraints. NetGear_Async can transfer thousands of frames in just a few seconds without causing any significant load on your\n    system.\n\n    NetGear_Async provides complete server-client handling and options to use variable protocols/patterns similar to NetGear API. Furthermore, NetGear_Async allows us to define\n     our custom Server as source to transform frames easily before sending them across the network.\n\n    NetGear_Async now supports additional **bidirectional data transmission** between receiver(client) and sender(server) while transferring frames.\n    Users can easily build complex applications such as like _Real-Time Video Chat_ in just few lines of code.\n\n    In addition to all this, NetGear_Async API also provides internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs, thereby\n    granting it exclusive power for transferring frames incoming from any source to the network.\n\n    NetGear_Async as of now supports four ZeroMQ messaging patterns:\n\n    - `zmq.PAIR` _(ZMQ Pair Pattern)_\n    - `zmq.REQ/zmq.REP` _(ZMQ Request/Reply Pattern)_\n    - `zmq.PUB/zmq.SUB` _(ZMQ Publish/Subscribe Pattern)_\n    - `zmq.PUSH/zmq.PULL` _(ZMQ Push/Pull Pattern)_\n\n    Whereas supported protocol are: `tcp` and `ipc`.\n    \"\"\"\n\n    def __init__(\n        self,\n        # NetGear_Async parameters\n        address=None,\n        port=None,\n        protocol=\"tcp\",\n        pattern=0,\n        receive_mode=False,\n        timeout=0.0,\n        # Videogear parameters\n        enablePiCamera=False,\n        stabilize=False,\n        source=None,\n        camera_num=0,\n        stream_mode=False,\n        backend=0,\n        colorspace=None,\n        resolution=(640, 480),\n        framerate=25,\n        time_delay=0,\n        # common parameters\n        logging=False,\n        **options\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the NetGear_Async class.\n\n        Parameters:\n            address (str): sets the valid network address of the Server/Client.\n            port (str): sets the valid Network Port of the Server/Client.\n            protocol (str): sets the valid messaging protocol between Server/Client.\n            pattern (int): sets the supported messaging pattern(flow of communication) between Server/Client\n            receive_mode (bool): select the NetGear_Async's Mode of operation.\n            timeout (int/float): controls the maximum waiting time(in sec) after which Client throws `TimeoutError`.\n            enablePiCamera (bool): provide access to PiGear(if True) or CamGear(if False) APIs respectively.\n            stabilize (bool): enable access to Stabilizer Class for stabilizing frames.\n            camera_num (int): selects the camera module index which will be used as Rpi source.\n            resolution (tuple): sets the resolution (i.e. `(width,height)`) of the Rpi source.\n            framerate (int/float): sets the framerate of the Rpi source.\n            source (based on input): defines the source for the input stream.\n            stream_mode (bool): controls the exclusive YouTube Mode.\n            backend (int): selects the backend for OpenCV's VideoCapture class.\n            colorspace (str): selects the colorspace of the input stream.\n            logging (bool): enables/disables logging.\n            time_delay (int): time delay (in sec) before start reading the frames.\n            options (dict): provides ability to alter Tweak Parameters of NetGear_Async, CamGear, PiGear & Stabilizer.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # raise error(s) for critical Class imports\n        import_dependency_safe(\n            \"zmq\" if zmq is None else \"\", min_version=\"4.0\", pkg_name=\"pyzmq\"\n        )\n        import_dependency_safe(\"msgpack\" if msgpack is None else \"\")\n        import_dependency_safe(\"msgpack_numpy\" if m is None else \"\")\n\n        # define valid messaging patterns => `0`: PAIR, `1`:(REQ, REP), `2`:(SUB, PUB), `3`:(PUSH, PULL)\n        valid_messaging_patterns = {\n            0: (zmq.PAIR, zmq.PAIR),\n            1: (zmq.REQ, zmq.REP),\n            2: (zmq.PUB, zmq.SUB),\n            3: (zmq.PUSH, zmq.PULL),\n        }\n\n        # check whether user-defined messaging pattern is valid\n        if isinstance(pattern, int) and pattern in valid_messaging_patterns:\n            # assign value\n            self.__msg_pattern = pattern\n            self.__pattern = valid_messaging_patterns[pattern]\n        else:\n            # otherwise default to 0:`zmq.PAIR`\n            self.__msg_pattern = 0\n            self.__pattern = valid_messaging_patterns[self.__msg_pattern]\n            self.__logging and logger.warning(\n                \"Invalid pattern {pattern}. Defaulting to `zmq.PAIR`!\".format(\n                    pattern=pattern\n                )\n            )\n\n        # check  whether user-defined messaging protocol is valid\n        if isinstance(protocol, str) and protocol in [\"tcp\", \"ipc\"]:\n            # assign value\n            self.__protocol = protocol\n        else:\n            # else default to `tcp` protocol\n            self.__protocol = \"tcp\"\n            self.__logging and logger.warning(\"Invalid protocol. Defaulting to `tcp`!\")\n\n        # initialize Termination flag\n        self.__terminate = False\n        # initialize and assign `Receive Mode`\n        self.__receive_mode = receive_mode\n        # initialize stream handler\n        self.__stream = None\n        # initialize Messaging Socket\n        self.__msg_socket = None\n        # initialize NetGear_Async's configuration dictionary\n        self.config = {}\n        # asyncio queue handler\n        self.__queue = None\n        # define Bidirectional mode\n        self.__bi_mode = False  # handles Bidirectional mode state\n\n        # assign timeout for Receiver end\n        if timeout and isinstance(timeout, (int, float)):\n            self.__timeout = float(timeout)\n        else:\n            self.__timeout = 15.0\n\n        # generate 8-digit random system id\n        self.__id = \"\".join(\n            secrets.choice(string.ascii_uppercase + string.digits) for i in range(8)\n        )\n\n        # Handle user-defined options dictionary values\n        # reformat dictionary\n        options = {str(k).strip(): v for k, v in options.items()}\n        # handle bidirectional mode\n        if \"bidirectional_mode\" in options:\n            value = options[\"bidirectional_mode\"]\n            # also check if pattern and source is valid\n            if isinstance(value, bool) and pattern < 2 and source is None:\n                # activate Bidirectional mode if specified\n                self.__bi_mode = value\n            else:\n                # otherwise disable it\n                self.__bi_mode = False\n                logger.warning(\"Bidirectional data transmission is disabled!\")\n            # handle errors and logging\n            if pattern >= 2:\n                # raise error\n                raise ValueError(\n                    \"[NetGear_Async:ERROR] :: `{}` pattern is not valid when Bidirectional Mode is enabled. Kindly refer Docs for more Information!\".format(\n                        pattern\n                    )\n                )\n            elif not (source is None):\n                raise ValueError(\n                    \"[NetGear_Async:ERROR] :: Custom source must be used when Bidirectional Mode is enabled. Kindly refer Docs for more Information!\".format(\n                        pattern\n                    )\n                )\n            elif isinstance(value, bool) and self.__logging:\n                # log Bidirectional mode activation\n                logger.debug(\n                    \"Bidirectional Data Transmission is {} for this connection!\".format(\n                        \"enabled\" if value else \"disabled\"\n                    )\n                )\n            else:\n                logger.error(\"`bidirectional_mode` value is invalid!\")\n            # clean\n            del options[\"bidirectional_mode\"]\n\n        # Setup and assign event loop policy\n        if platform.system() == \"Windows\":\n            # On Windows, VidGear requires the ``WindowsSelectorEventLoop``, but Python 3.8 and above,\n            # defaults to an ``ProactorEventLoop`` loop that is not compatible with it. Thereby,\n            # we had to set it manually.\n            asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())\n        else:\n            if not (uvloop is None):\n                # Latest uvloop eventloop is only available for UNIX machines.\n                asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())\n            else:\n                # log if not present\n                import_dependency_safe(\"uvloop\", error=\"log\")\n\n        # Retrieve event loop and assign it\n        try:\n            self.loop = asyncio.get_running_loop()\n        except RuntimeError:\n            # otherwise create one\n            logger.critical(\"No running event loop found. Creating a new one.\")\n            self.loop = asyncio.new_event_loop()\n\n        # log eventloop for debugging\n        self.__logging and logger.info(\n            \"Using ``{}`` event loop for this process.\".format(\n                self.loop.__class__.__name__\n            )\n        )\n\n        # define messaging asynchronous Context\n        self.__msg_context = zmq.asyncio.Context()\n\n        # check whether `Receive Mode` is enabled\n        if receive_mode:\n            # assign local IP address if None\n            if address is None:\n                self.__address = \"*\"  # define address\n            else:\n                self.__address = address\n            # assign default port address if None\n            if port is None:\n                self.__port = \"5555\"\n            else:\n                self.__port = port\n        else:\n            # Handle video source\n            if source is None:\n                self.config = {\"generator\": None}\n                self.__logging and logger.warning(\"Given source is of NoneType!\")\n            else:\n                # define stream with necessary params\n                self.__stream = VideoGear(\n                    enablePiCamera=enablePiCamera,\n                    stabilize=stabilize,\n                    source=source,\n                    camera_num=camera_num,\n                    stream_mode=stream_mode,\n                    backend=backend,\n                    colorspace=colorspace,\n                    resolution=resolution,\n                    framerate=framerate,\n                    logging=logging,\n                    time_delay=time_delay,\n                    **options\n                )\n                # define default frame generator in configuration\n                self.config = {\"generator\": self.__frame_generator()}\n            # assign local ip address if None\n            if address is None:\n                self.__address = \"localhost\"\n            else:\n                self.__address = address\n            # assign default port address if None\n            if port is None:\n                self.__port = \"5555\"\n            else:\n                self.__port = port\n            # add server task handler\n            self.task = None\n\n        # create asyncio queue if bidirectional mode activated\n        self.__queue = asyncio.Queue() if self.__bi_mode else None\n\n    def launch(self):\n        \"\"\"\n        Launches an asynchronous generators and loop executors for respective task.\n        \"\"\"\n        # check if receive mode enabled\n        if self.__receive_mode:\n            self.__logging and logger.debug(\n                \"Launching NetGear_Async asynchronous generator!\"\n            )\n            # run loop executor for Receiver asynchronous generator\n            self.loop.run_in_executor(None, self.recv_generator)\n        else:\n            # Otherwise launch Server handler\n            self.__logging and logger.debug(\n                \"Creating NetGear_Async asynchronous server handler!\"\n            )\n            # create task for Server Handler\n            self.task = self.loop.create_task(self.__server_handler())\n        # return instance\n        return self\n\n    async def __server_handler(self):\n        \"\"\"\n        Handles various Server-end processes/tasks.\n        \"\"\"\n        # validate assigned frame generator in NetGear_Async configuration\n        if isinstance(self.config, dict) and \"generator\" in self.config:\n            # check if its  assigned value is a asynchronous generator\n            if self.config[\"generator\"] is None or not inspect.isasyncgen(\n                self.config[\"generator\"]\n            ):\n                # otherwise raise error\n                raise ValueError(\n                    \"[NetGear_Async:ERROR] :: Invalid configuration. Assigned generator must be a asynchronous generator function/method only!\"\n                )\n        else:\n            # raise error if validation fails\n            raise RuntimeError(\n                \"[NetGear_Async:ERROR] :: Assigned NetGear_Async configuration is invalid!\"\n            )\n\n        # define our messaging socket\n        self.__msg_socket = self.__msg_context.socket(self.__pattern[0])\n\n        # if req/rep pattern, define additional flags\n        if self.__msg_pattern == 1:\n            self.__msg_socket.REQ_RELAXED = True\n            self.__msg_socket.REQ_CORRELATE = True\n\n        # if pub/sub pattern, define additional optimizer\n        if self.__msg_pattern == 2:\n            self.__msg_socket.set_hwm(1)\n\n        # try connecting socket to assigned protocol, address and port\n        try:\n            self.__msg_socket.connect(\n                self.__protocol + \"://\" + str(self.__address) + \":\" + str(self.__port)\n            )\n            # finally log if successful\n            self.__logging and logger.debug(\n                \"Successfully connected to address: {} with pattern: {}.\".format(\n                    (\n                        self.__protocol\n                        + \"://\"\n                        + str(self.__address)\n                        + \":\"\n                        + str(self.__port)\n                    ),\n                    self.__msg_pattern,\n                )\n            )\n            logger.critical(\n                \"Send Mode is successfully activated and ready to send data!\"\n            )\n        except Exception as e:\n            # log ad raise error if failed\n            logger.exception(str(e))\n            if self.__bi_mode:\n                logger.error(\n                    \"Failed to activate Bidirectional Mode for this connection!\"\n                )\n            raise ValueError(\n                \"[NetGear_Async:ERROR] :: Failed to connect address: {} and pattern: {}!\".format(\n                    (\n                        self.__protocol\n                        + \"://\"\n                        + str(self.__address)\n                        + \":\"\n                        + str(self.__port)\n                    ),\n                    self.__msg_pattern,\n                )\n            )\n\n        # loop over our Asynchronous frame generator\n        async for dataframe in self.config[\"generator\"]:\n            # extract data if bidirectional mode\n            if self.__bi_mode and len(dataframe) == 2:\n                (data, frame) = dataframe\n                if not (data is None) and isinstance(data, np.ndarray):\n                    logger.warning(\n                        \"Skipped unsupported `data` of datatype: {}!\".format(\n                            type(data).__name__\n                        )\n                    )\n                    data = None\n                assert isinstance(\n                    frame, np.ndarray\n                ), \"[NetGear_Async:ERROR] :: Invalid data received from server end!\"\n            elif self.__bi_mode:\n                # raise error for invalid data\n                raise ValueError(\n                    \"[NetGear_Async:ERROR] :: Send Mode only accepts tuple(data, frame) as input in Bidirectional Mode. \\\n                    Kindly refer vidgear docs!\"\n                )\n            else:\n                # otherwise just make a copy of frame\n                frame = np.copy(dataframe)\n                data = None\n\n            # check if retrieved frame is `CONTIGUOUS`\n            if not (frame.flags[\"C_CONTIGUOUS\"]):\n                # otherwise make it\n                frame = np.ascontiguousarray(frame, dtype=frame.dtype)\n\n            # create data dict\n            data_dict = dict(\n                terminate=False,\n                bi_mode=self.__bi_mode,\n                data=data if not (data is None) else \"\",\n            )\n            # encode it\n            data_enc = msgpack.packb(data_dict)\n            # send the encoded data with correct flags\n            await self.__msg_socket.send(data_enc, flags=zmq.SNDMORE)\n\n            # encode frame\n            frame_enc = msgpack.packb(frame, default=m.encode)\n            # send the encoded frame\n            await self.__msg_socket.send_multipart([frame_enc])\n\n            # check if bidirectional patterns used\n            if self.__msg_pattern < 2:\n                # handle bidirectional data transfer if enabled\n                if self.__bi_mode:\n                    # get receiver encoded message withing timeout limit\n                    recvdmsg_encoded = await asyncio.wait_for(\n                        self.__msg_socket.recv(), timeout=self.__timeout\n                    )\n                    # retrieve receiver data from encoded message\n                    recvd_data = msgpack.unpackb(recvdmsg_encoded, use_list=False)\n                    # check message type\n                    if recvd_data[\"return_type\"] == \"ndarray\":  # numpy.ndarray\n                        # get encoded frame from receiver\n                        recvdframe_encoded = await asyncio.wait_for(\n                            self.__msg_socket.recv_multipart(), timeout=self.__timeout\n                        )\n                        # retrieve frame and put in queue\n                        await self.__queue.put(\n                            msgpack.unpackb(\n                                recvdframe_encoded[0],\n                                use_list=False,\n                                object_hook=m.decode,\n                            )\n                        )\n                    else:\n                        # otherwise put data directly in queue\n                        await self.__queue.put(\n                            recvd_data[\"return_data\"]\n                            if recvd_data[\"return_data\"]\n                            else None\n                        )\n                else:\n                    # otherwise log received confirmation\n                    recv_confirmation = await asyncio.wait_for(\n                        self.__msg_socket.recv(), timeout=self.__timeout\n                    )\n                    self.__logging and logger.debug(recv_confirmation)\n\n    async def recv_generator(self):\n        \"\"\"\n        A default Asynchronous Frame Generator for NetGear_Async's Receiver-end.\n        \"\"\"\n        # check whether `receive mode` is activated\n        if not (self.__receive_mode):\n            # raise Value error and exit\n            self.__terminate = True\n            raise ValueError(\n                \"[NetGear_Async:ERROR] :: `recv_generator()` function cannot be accessed while `receive_mode` is disabled. Kindly refer vidgear docs!\"\n            )\n\n        # initialize and define messaging socket\n        self.__msg_socket = self.__msg_context.socket(self.__pattern[1])\n\n        # define exclusive socket options for patterns\n        if self.__msg_pattern == 2:\n            self.__msg_socket.set_hwm(1)\n            self.__msg_socket.setsockopt(zmq.SUBSCRIBE, b\"\")\n\n        try:\n            # bind socket to the assigned protocol, address and port\n            self.__msg_socket.bind(\n                self.__protocol + \"://\" + str(self.__address) + \":\" + str(self.__port)\n            )\n            # finally log progress\n            self.__logging and logger.debug(\n                \"Successfully binded to address: {} with pattern: {}.\".format(\n                    (\n                        self.__protocol\n                        + \"://\"\n                        + str(self.__address)\n                        + \":\"\n                        + str(self.__port)\n                    ),\n                    self.__msg_pattern,\n                )\n            )\n            logger.critical(\"Receive Mode is activated successfully!\")\n        except Exception as e:\n            logger.exception(str(e))\n            raise RuntimeError(\n                \"[NetGear_Async:ERROR] :: Failed to bind address: {} and pattern: {}{}!\".format(\n                    (\n                        self.__protocol\n                        + \"://\"\n                        + str(self.__address)\n                        + \":\"\n                        + str(self.__port)\n                    ),\n                    self.__msg_pattern,\n                    \" and Bidirectional Mode enabled\" if self.__bi_mode else \"\",\n                )\n            )\n\n        # loop until terminated\n        while not self.__terminate:\n            # get encoded data message from server withing timeout limit\n            datamsg_encoded = await asyncio.wait_for(\n                self.__msg_socket.recv(), timeout=self.__timeout\n            )\n            # retrieve data from message\n            data = msgpack.unpackb(datamsg_encoded, use_list=False)\n            # terminate if exit` flag received from server\n            if data[\"terminate\"]:\n                # send confirmation message to server if bidirectional patterns\n                if self.__msg_pattern < 2:\n                    # create termination confirmation message\n                    return_dict = dict(\n                        terminated=\"Client-`{}` successfully terminated!\".format(\n                            self.__id\n                        ),\n                    )\n                    # encode message\n                    retdata_enc = msgpack.packb(return_dict)\n                    # send message back to server\n                    await self.__msg_socket.send(retdata_enc)\n                self.__logging and logger.info(\n                    \"Termination signal received from server!\"\n                )\n                # break loop and terminate\n                self.__terminate = True\n                break\n            # get encoded frame message from server withing timeout limit\n            framemsg_encoded = await asyncio.wait_for(\n                self.__msg_socket.recv_multipart(), timeout=self.__timeout\n            )\n            # retrieve frame from message\n            frame = msgpack.unpackb(\n                framemsg_encoded[0], use_list=False, object_hook=m.decode\n            )\n\n            # check if bidirectional patterns\n            if self.__msg_pattern < 2:\n                # handle bidirectional data transfer if enabled\n                if self.__bi_mode and data[\"bi_mode\"]:\n                    # handle empty queue\n                    if not self.__queue.empty():\n                        return_data = await self.__queue.get()\n                        self.__queue.task_done()\n                    else:\n                        return_data = None\n                    # check if we are returning `ndarray` frames\n                    if not (return_data is None) and isinstance(\n                        return_data, np.ndarray\n                    ):\n                        # check whether the incoming frame is contiguous\n                        if not (return_data.flags[\"C_CONTIGUOUS\"]):\n                            return_data = np.ascontiguousarray(\n                                return_data, dtype=return_data.dtype\n                            )\n\n                        # create return type dict without data\n                        rettype_dict = dict(\n                            return_type=(type(return_data).__name__),\n                            return_data=None,\n                        )\n                        # encode it\n                        rettype_enc = msgpack.packb(rettype_dict)\n                        # send it to server with correct flags\n                        await self.__msg_socket.send(rettype_enc, flags=zmq.SNDMORE)\n\n                        # encode return ndarray data\n                        retframe_enc = msgpack.packb(return_data, default=m.encode)\n                        # send it over network to server\n                        await self.__msg_socket.send_multipart([retframe_enc])\n                    else:\n                        # otherwise create type and data dict\n                        return_dict = dict(\n                            return_type=(type(return_data).__name__),\n                            return_data=(\n                                return_data if not (return_data is None) else \"\"\n                            ),\n                        )\n                        # encode it\n                        retdata_enc = msgpack.packb(return_dict)\n                        # send it over network to server\n                        await self.__msg_socket.send(retdata_enc)\n                elif self.__bi_mode or data[\"bi_mode\"]:\n                    # raise error if bidirectional mode is disabled at server or client but not both\n                    raise RuntimeError(\n                        \"[NetGear_Async:ERROR] :: Invalid configuration! Bidirectional Mode is not activate on {} end.\".format(\n                            \"client\" if self.__bi_mode else \"server\"\n                        )\n                    )\n                else:\n                    # otherwise just send confirmation message to server\n                    await self.__msg_socket.send(\n                        bytes(\n                            \"Data received on client: {} !\".format(self.__id), \"utf-8\"\n                        )\n                    )\n            # yield received tuple(data-frame) if bidirectional mode or else just frame\n            if self.__bi_mode:\n                yield (data[\"data\"], frame) if data[\"data\"] else (None, frame)\n            else:\n                yield frame\n            # sleep for sometime\n            await asyncio.sleep(0)\n\n    async def __frame_generator(self):\n        \"\"\"\n        Returns a default frame-generator for NetGear_Async's Server Handler.\n        \"\"\"\n        # start stream\n        self.__stream.start()\n        # loop over stream until its terminated\n        while not self.__terminate:\n            # read frames\n            frame = self.__stream.read()\n            # break if NoneType\n            if frame is None:\n                break\n            # yield frame\n            yield frame\n            # sleep for sometime\n            await asyncio.sleep(0)\n\n    async def transceive_data(self, data=None):\n        \"\"\"\n        Bidirectional Mode exclusive method to Transmit data _(in Receive mode)_ and Receive data _(in Send mode)_.\n\n        Parameters:\n            data (any): inputs data _(of any datatype)_ for sending back to Server.\n        \"\"\"\n        recvd_data = None\n        if not self.__terminate:\n            if self.__bi_mode:\n                if self.__receive_mode:\n                    await self.__queue.put(data)\n                else:\n                    if not self.__queue.empty():\n                        recvd_data = await self.__queue.get()\n                        self.__queue.task_done()\n            else:\n                logger.error(\n                    \"`transceive_data()` function cannot be used when Bidirectional Mode is disabled.\"\n                )\n        return recvd_data\n\n    async def __terminate_connection(self, disable_confirmation=False):\n        \"\"\"\n        Internal asyncio method to safely terminate ZMQ connection and queues\n\n        Parameters:\n            disable_confirmation (boolean): Force disable termination confirmation from client in bidirectional patterns.\n        \"\"\"\n        # log termination\n        self.__logging and logger.debug(\n            \"Terminating various {} Processes. Please wait.\".format(\n                \"Receive Mode\" if self.__receive_mode else \"Send Mode\"\n            )\n        )\n\n        # check whether `receive_mode` is enabled or not\n        if self.__receive_mode:\n            # indicate that process should be terminated\n            self.__terminate = True\n        else:\n            # indicate that process should be terminated\n            self.__terminate = True\n            # terminate stream\n            if not (self.__stream is None):\n                self.__stream.stop()\n            # signal `exit` flag for termination!\n            data_dict = dict(terminate=True)\n            data_enc = msgpack.packb(data_dict)\n            await self.__msg_socket.send(data_enc)\n            # check if bidirectional patterns\n            if self.__msg_pattern < 2 and not disable_confirmation:\n                # then receive and log confirmation\n                recv_confirmation = await self.__msg_socket.recv()\n                recvd_conf = msgpack.unpackb(recv_confirmation, use_list=False)\n                self.__logging and \"terminated\" in recvd_conf and logger.debug(\n                    recvd_conf[\"terminated\"]\n                )\n        # close socket\n        self.__msg_socket.setsockopt(zmq.LINGER, 0)\n        self.__msg_socket.close()\n        # handle asyncio queues in bidirectional mode\n        if self.__bi_mode:\n            # empty queue if not\n            while not self.__queue.empty():\n                try:\n                    self.__queue.get_nowait()\n                except asyncio.QueueEmpty:\n                    continue\n                self.__queue.task_done()\n            # join queues\n            await self.__queue.join()\n\n        logger.critical(\n            \"{} successfully terminated!\".format(\n                \"Receive Mode\" if self.__receive_mode else \"Send Mode\"\n            )\n        )\n\n    def close(self, skip_loop=False):\n        \"\"\"\n        Terminates all NetGear_Async Asynchronous processes gracefully.\n\n        Parameters:\n            skip_loop (Boolean): (optional)used only if don't want to close eventloop(required in pytest).\n        \"\"\"\n        # close event loop if specified\n        if not (skip_loop):\n            # close connection gracefully\n            self.loop.run_until_complete(self.__terminate_connection())\n            self.loop.close()\n        else:\n            # otherwise create a task\n            asyncio.ensure_future(\n                self.__terminate_connection(disable_confirmation=True)\n            )\n

"},{"location":"bonus/reference/netgear_async/#vidgear.gears.asyncio.netgear_async.NetGear_Async.__init__","title":"__init__(self, address=None, port=None, protocol='tcp', pattern=0, receive_mode=False, timeout=0.0, enablePiCamera=False, stabilize=False, source=None, camera_num=0, stream_mode=False, backend=0, colorspace=None, resolution=(640, 480), framerate=25, time_delay=0, logging=False, **options) special","text":"

This constructor method initializes the object state and attributes of the NetGear_Async class.

Parameters:

Name Type Description Default address str

sets the valid network address of the Server/Client.

None port str

sets the valid Network Port of the Server/Client.

None protocol str

sets the valid messaging protocol between Server/Client.

'tcp' pattern int

sets the supported messaging pattern(flow of communication) between Server/Client

0 receive_mode bool

select the NetGear_Async's Mode of operation.

False timeout int/float

controls the maximum waiting time(in sec) after which Client throws TimeoutError.

0.0 enablePiCamera bool

provide access to PiGear(if True) or CamGear(if False) APIs respectively.

False stabilize bool

enable access to Stabilizer Class for stabilizing frames.

False camera_num int

selects the camera module index which will be used as Rpi source.

0 resolution tuple

sets the resolution (i.e. (width,height)) of the Rpi source.

(640, 480) framerate int/float

sets the framerate of the Rpi source.

25 source based on input

defines the source for the input stream.

None stream_mode bool

controls the exclusive YouTube Mode.

False backend int

selects the backend for OpenCV's VideoCapture class.

0 colorspace str

selects the colorspace of the input stream.

None logging bool

enables/disables logging.

False time_delay int

time delay (in sec) before start reading the frames.

0 options dict

provides ability to alter Tweak Parameters of NetGear_Async, CamGear, PiGear & Stabilizer.

{} Source code in vidgear/gears/asyncio/netgear_async.py
def __init__(\n    self,\n    # NetGear_Async parameters\n    address=None,\n    port=None,\n    protocol=\"tcp\",\n    pattern=0,\n    receive_mode=False,\n    timeout=0.0,\n    # Videogear parameters\n    enablePiCamera=False,\n    stabilize=False,\n    source=None,\n    camera_num=0,\n    stream_mode=False,\n    backend=0,\n    colorspace=None,\n    resolution=(640, 480),\n    framerate=25,\n    time_delay=0,\n    # common parameters\n    logging=False,\n    **options\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the NetGear_Async class.\n\n    Parameters:\n        address (str): sets the valid network address of the Server/Client.\n        port (str): sets the valid Network Port of the Server/Client.\n        protocol (str): sets the valid messaging protocol between Server/Client.\n        pattern (int): sets the supported messaging pattern(flow of communication) between Server/Client\n        receive_mode (bool): select the NetGear_Async's Mode of operation.\n        timeout (int/float): controls the maximum waiting time(in sec) after which Client throws `TimeoutError`.\n        enablePiCamera (bool): provide access to PiGear(if True) or CamGear(if False) APIs respectively.\n        stabilize (bool): enable access to Stabilizer Class for stabilizing frames.\n        camera_num (int): selects the camera module index which will be used as Rpi source.\n        resolution (tuple): sets the resolution (i.e. `(width,height)`) of the Rpi source.\n        framerate (int/float): sets the framerate of the Rpi source.\n        source (based on input): defines the source for the input stream.\n        stream_mode (bool): controls the exclusive YouTube Mode.\n        backend (int): selects the backend for OpenCV's VideoCapture class.\n        colorspace (str): selects the colorspace of the input stream.\n        logging (bool): enables/disables logging.\n        time_delay (int): time delay (in sec) before start reading the frames.\n        options (dict): provides ability to alter Tweak Parameters of NetGear_Async, CamGear, PiGear & Stabilizer.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # raise error(s) for critical Class imports\n    import_dependency_safe(\n        \"zmq\" if zmq is None else \"\", min_version=\"4.0\", pkg_name=\"pyzmq\"\n    )\n    import_dependency_safe(\"msgpack\" if msgpack is None else \"\")\n    import_dependency_safe(\"msgpack_numpy\" if m is None else \"\")\n\n    # define valid messaging patterns => `0`: PAIR, `1`:(REQ, REP), `2`:(SUB, PUB), `3`:(PUSH, PULL)\n    valid_messaging_patterns = {\n        0: (zmq.PAIR, zmq.PAIR),\n        1: (zmq.REQ, zmq.REP),\n        2: (zmq.PUB, zmq.SUB),\n        3: (zmq.PUSH, zmq.PULL),\n    }\n\n    # check whether user-defined messaging pattern is valid\n    if isinstance(pattern, int) and pattern in valid_messaging_patterns:\n        # assign value\n        self.__msg_pattern = pattern\n        self.__pattern = valid_messaging_patterns[pattern]\n    else:\n        # otherwise default to 0:`zmq.PAIR`\n        self.__msg_pattern = 0\n        self.__pattern = valid_messaging_patterns[self.__msg_pattern]\n        self.__logging and logger.warning(\n            \"Invalid pattern {pattern}. Defaulting to `zmq.PAIR`!\".format(\n                pattern=pattern\n            )\n        )\n\n    # check  whether user-defined messaging protocol is valid\n    if isinstance(protocol, str) and protocol in [\"tcp\", \"ipc\"]:\n        # assign value\n        self.__protocol = protocol\n    else:\n        # else default to `tcp` protocol\n        self.__protocol = \"tcp\"\n        self.__logging and logger.warning(\"Invalid protocol. Defaulting to `tcp`!\")\n\n    # initialize Termination flag\n    self.__terminate = False\n    # initialize and assign `Receive Mode`\n    self.__receive_mode = receive_mode\n    # initialize stream handler\n    self.__stream = None\n    # initialize Messaging Socket\n    self.__msg_socket = None\n    # initialize NetGear_Async's configuration dictionary\n    self.config = {}\n    # asyncio queue handler\n    self.__queue = None\n    # define Bidirectional mode\n    self.__bi_mode = False  # handles Bidirectional mode state\n\n    # assign timeout for Receiver end\n    if timeout and isinstance(timeout, (int, float)):\n        self.__timeout = float(timeout)\n    else:\n        self.__timeout = 15.0\n\n    # generate 8-digit random system id\n    self.__id = \"\".join(\n        secrets.choice(string.ascii_uppercase + string.digits) for i in range(8)\n    )\n\n    # Handle user-defined options dictionary values\n    # reformat dictionary\n    options = {str(k).strip(): v for k, v in options.items()}\n    # handle bidirectional mode\n    if \"bidirectional_mode\" in options:\n        value = options[\"bidirectional_mode\"]\n        # also check if pattern and source is valid\n        if isinstance(value, bool) and pattern < 2 and source is None:\n            # activate Bidirectional mode if specified\n            self.__bi_mode = value\n        else:\n            # otherwise disable it\n            self.__bi_mode = False\n            logger.warning(\"Bidirectional data transmission is disabled!\")\n        # handle errors and logging\n        if pattern >= 2:\n            # raise error\n            raise ValueError(\n                \"[NetGear_Async:ERROR] :: `{}` pattern is not valid when Bidirectional Mode is enabled. Kindly refer Docs for more Information!\".format(\n                    pattern\n                )\n            )\n        elif not (source is None):\n            raise ValueError(\n                \"[NetGear_Async:ERROR] :: Custom source must be used when Bidirectional Mode is enabled. Kindly refer Docs for more Information!\".format(\n                    pattern\n                )\n            )\n        elif isinstance(value, bool) and self.__logging:\n            # log Bidirectional mode activation\n            logger.debug(\n                \"Bidirectional Data Transmission is {} for this connection!\".format(\n                    \"enabled\" if value else \"disabled\"\n                )\n            )\n        else:\n            logger.error(\"`bidirectional_mode` value is invalid!\")\n        # clean\n        del options[\"bidirectional_mode\"]\n\n    # Setup and assign event loop policy\n    if platform.system() == \"Windows\":\n        # On Windows, VidGear requires the ``WindowsSelectorEventLoop``, but Python 3.8 and above,\n        # defaults to an ``ProactorEventLoop`` loop that is not compatible with it. Thereby,\n        # we had to set it manually.\n        asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())\n    else:\n        if not (uvloop is None):\n            # Latest uvloop eventloop is only available for UNIX machines.\n            asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())\n        else:\n            # log if not present\n            import_dependency_safe(\"uvloop\", error=\"log\")\n\n    # Retrieve event loop and assign it\n    try:\n        self.loop = asyncio.get_running_loop()\n    except RuntimeError:\n        # otherwise create one\n        logger.critical(\"No running event loop found. Creating a new one.\")\n        self.loop = asyncio.new_event_loop()\n\n    # log eventloop for debugging\n    self.__logging and logger.info(\n        \"Using ``{}`` event loop for this process.\".format(\n            self.loop.__class__.__name__\n        )\n    )\n\n    # define messaging asynchronous Context\n    self.__msg_context = zmq.asyncio.Context()\n\n    # check whether `Receive Mode` is enabled\n    if receive_mode:\n        # assign local IP address if None\n        if address is None:\n            self.__address = \"*\"  # define address\n        else:\n            self.__address = address\n        # assign default port address if None\n        if port is None:\n            self.__port = \"5555\"\n        else:\n            self.__port = port\n    else:\n        # Handle video source\n        if source is None:\n            self.config = {\"generator\": None}\n            self.__logging and logger.warning(\"Given source is of NoneType!\")\n        else:\n            # define stream with necessary params\n            self.__stream = VideoGear(\n                enablePiCamera=enablePiCamera,\n                stabilize=stabilize,\n                source=source,\n                camera_num=camera_num,\n                stream_mode=stream_mode,\n                backend=backend,\n                colorspace=colorspace,\n                resolution=resolution,\n                framerate=framerate,\n                logging=logging,\n                time_delay=time_delay,\n                **options\n            )\n            # define default frame generator in configuration\n            self.config = {\"generator\": self.__frame_generator()}\n        # assign local ip address if None\n        if address is None:\n            self.__address = \"localhost\"\n        else:\n            self.__address = address\n        # assign default port address if None\n        if port is None:\n            self.__port = \"5555\"\n        else:\n            self.__port = port\n        # add server task handler\n        self.task = None\n\n    # create asyncio queue if bidirectional mode activated\n    self.__queue = asyncio.Queue() if self.__bi_mode else None\n
"},{"location":"bonus/reference/netgear_async/#vidgear.gears.asyncio.netgear_async.NetGear_Async.close","title":"close(self, skip_loop=False)","text":"

Terminates all NetGear_Async Asynchronous processes gracefully.

Parameters:

Name Type Description Default skip_loop Boolean

(optional)used only if don't want to close eventloop(required in pytest).

False Source code in vidgear/gears/asyncio/netgear_async.py
def close(self, skip_loop=False):\n    \"\"\"\n    Terminates all NetGear_Async Asynchronous processes gracefully.\n\n    Parameters:\n        skip_loop (Boolean): (optional)used only if don't want to close eventloop(required in pytest).\n    \"\"\"\n    # close event loop if specified\n    if not (skip_loop):\n        # close connection gracefully\n        self.loop.run_until_complete(self.__terminate_connection())\n        self.loop.close()\n    else:\n        # otherwise create a task\n        asyncio.ensure_future(\n            self.__terminate_connection(disable_confirmation=True)\n        )\n
"},{"location":"bonus/reference/netgear_async/#vidgear.gears.asyncio.netgear_async.NetGear_Async.launch","title":"launch(self)","text":"

Launches an asynchronous generators and loop executors for respective task.

Source code in vidgear/gears/asyncio/netgear_async.py
def launch(self):\n    \"\"\"\n    Launches an asynchronous generators and loop executors for respective task.\n    \"\"\"\n    # check if receive mode enabled\n    if self.__receive_mode:\n        self.__logging and logger.debug(\n            \"Launching NetGear_Async asynchronous generator!\"\n        )\n        # run loop executor for Receiver asynchronous generator\n        self.loop.run_in_executor(None, self.recv_generator)\n    else:\n        # Otherwise launch Server handler\n        self.__logging and logger.debug(\n            \"Creating NetGear_Async asynchronous server handler!\"\n        )\n        # create task for Server Handler\n        self.task = self.loop.create_task(self.__server_handler())\n    # return instance\n    return self\n
"},{"location":"bonus/reference/netgear_async/#vidgear.gears.asyncio.netgear_async.NetGear_Async.recv_generator","title":"recv_generator(self)","text":"

A default Asynchronous Frame Generator for NetGear_Async's Receiver-end.

Source code in vidgear/gears/asyncio/netgear_async.py
async def recv_generator(self):\n    \"\"\"\n    A default Asynchronous Frame Generator for NetGear_Async's Receiver-end.\n    \"\"\"\n    # check whether `receive mode` is activated\n    if not (self.__receive_mode):\n        # raise Value error and exit\n        self.__terminate = True\n        raise ValueError(\n            \"[NetGear_Async:ERROR] :: `recv_generator()` function cannot be accessed while `receive_mode` is disabled. Kindly refer vidgear docs!\"\n        )\n\n    # initialize and define messaging socket\n    self.__msg_socket = self.__msg_context.socket(self.__pattern[1])\n\n    # define exclusive socket options for patterns\n    if self.__msg_pattern == 2:\n        self.__msg_socket.set_hwm(1)\n        self.__msg_socket.setsockopt(zmq.SUBSCRIBE, b\"\")\n\n    try:\n        # bind socket to the assigned protocol, address and port\n        self.__msg_socket.bind(\n            self.__protocol + \"://\" + str(self.__address) + \":\" + str(self.__port)\n        )\n        # finally log progress\n        self.__logging and logger.debug(\n            \"Successfully binded to address: {} with pattern: {}.\".format(\n                (\n                    self.__protocol\n                    + \"://\"\n                    + str(self.__address)\n                    + \":\"\n                    + str(self.__port)\n                ),\n                self.__msg_pattern,\n            )\n        )\n        logger.critical(\"Receive Mode is activated successfully!\")\n    except Exception as e:\n        logger.exception(str(e))\n        raise RuntimeError(\n            \"[NetGear_Async:ERROR] :: Failed to bind address: {} and pattern: {}{}!\".format(\n                (\n                    self.__protocol\n                    + \"://\"\n                    + str(self.__address)\n                    + \":\"\n                    + str(self.__port)\n                ),\n                self.__msg_pattern,\n                \" and Bidirectional Mode enabled\" if self.__bi_mode else \"\",\n            )\n        )\n\n    # loop until terminated\n    while not self.__terminate:\n        # get encoded data message from server withing timeout limit\n        datamsg_encoded = await asyncio.wait_for(\n            self.__msg_socket.recv(), timeout=self.__timeout\n        )\n        # retrieve data from message\n        data = msgpack.unpackb(datamsg_encoded, use_list=False)\n        # terminate if exit` flag received from server\n        if data[\"terminate\"]:\n            # send confirmation message to server if bidirectional patterns\n            if self.__msg_pattern < 2:\n                # create termination confirmation message\n                return_dict = dict(\n                    terminated=\"Client-`{}` successfully terminated!\".format(\n                        self.__id\n                    ),\n                )\n                # encode message\n                retdata_enc = msgpack.packb(return_dict)\n                # send message back to server\n                await self.__msg_socket.send(retdata_enc)\n            self.__logging and logger.info(\n                \"Termination signal received from server!\"\n            )\n            # break loop and terminate\n            self.__terminate = True\n            break\n        # get encoded frame message from server withing timeout limit\n        framemsg_encoded = await asyncio.wait_for(\n            self.__msg_socket.recv_multipart(), timeout=self.__timeout\n        )\n        # retrieve frame from message\n        frame = msgpack.unpackb(\n            framemsg_encoded[0], use_list=False, object_hook=m.decode\n        )\n\n        # check if bidirectional patterns\n        if self.__msg_pattern < 2:\n            # handle bidirectional data transfer if enabled\n            if self.__bi_mode and data[\"bi_mode\"]:\n                # handle empty queue\n                if not self.__queue.empty():\n                    return_data = await self.__queue.get()\n                    self.__queue.task_done()\n                else:\n                    return_data = None\n                # check if we are returning `ndarray` frames\n                if not (return_data is None) and isinstance(\n                    return_data, np.ndarray\n                ):\n                    # check whether the incoming frame is contiguous\n                    if not (return_data.flags[\"C_CONTIGUOUS\"]):\n                        return_data = np.ascontiguousarray(\n                            return_data, dtype=return_data.dtype\n                        )\n\n                    # create return type dict without data\n                    rettype_dict = dict(\n                        return_type=(type(return_data).__name__),\n                        return_data=None,\n                    )\n                    # encode it\n                    rettype_enc = msgpack.packb(rettype_dict)\n                    # send it to server with correct flags\n                    await self.__msg_socket.send(rettype_enc, flags=zmq.SNDMORE)\n\n                    # encode return ndarray data\n                    retframe_enc = msgpack.packb(return_data, default=m.encode)\n                    # send it over network to server\n                    await self.__msg_socket.send_multipart([retframe_enc])\n                else:\n                    # otherwise create type and data dict\n                    return_dict = dict(\n                        return_type=(type(return_data).__name__),\n                        return_data=(\n                            return_data if not (return_data is None) else \"\"\n                        ),\n                    )\n                    # encode it\n                    retdata_enc = msgpack.packb(return_dict)\n                    # send it over network to server\n                    await self.__msg_socket.send(retdata_enc)\n            elif self.__bi_mode or data[\"bi_mode\"]:\n                # raise error if bidirectional mode is disabled at server or client but not both\n                raise RuntimeError(\n                    \"[NetGear_Async:ERROR] :: Invalid configuration! Bidirectional Mode is not activate on {} end.\".format(\n                        \"client\" if self.__bi_mode else \"server\"\n                    )\n                )\n            else:\n                # otherwise just send confirmation message to server\n                await self.__msg_socket.send(\n                    bytes(\n                        \"Data received on client: {} !\".format(self.__id), \"utf-8\"\n                    )\n                )\n        # yield received tuple(data-frame) if bidirectional mode or else just frame\n        if self.__bi_mode:\n            yield (data[\"data\"], frame) if data[\"data\"] else (None, frame)\n        else:\n            yield frame\n        # sleep for sometime\n        await asyncio.sleep(0)\n
"},{"location":"bonus/reference/netgear_async/#vidgear.gears.asyncio.netgear_async.NetGear_Async.transceive_data","title":"transceive_data(self, data=None) async","text":"

Bidirectional Mode exclusive method to Transmit data (in Receive mode) and Receive data (in Send mode).

Parameters:

Name Type Description Default data any

inputs data (of any datatype) for sending back to Server.

None Source code in vidgear/gears/asyncio/netgear_async.py
async def transceive_data(self, data=None):\n    \"\"\"\n    Bidirectional Mode exclusive method to Transmit data _(in Receive mode)_ and Receive data _(in Send mode)_.\n\n    Parameters:\n        data (any): inputs data _(of any datatype)_ for sending back to Server.\n    \"\"\"\n    recvd_data = None\n    if not self.__terminate:\n        if self.__bi_mode:\n            if self.__receive_mode:\n                await self.__queue.put(data)\n            else:\n                if not self.__queue.empty():\n                    recvd_data = await self.__queue.get()\n                    self.__queue.task_done()\n        else:\n            logger.error(\n                \"`transceive_data()` function cannot be used when Bidirectional Mode is disabled.\"\n            )\n    return recvd_data\n
"},{"location":"bonus/reference/pigear/","title":"PiGear API References","text":"

PiGear API usage examples can be found here \u27b6

PiGear API parameters are explained here \u27b6

PiGear implements a seamless and robust wrapper around the picamera2 python library, simplifying integration with minimal code changes and ensuring a smooth transition for developers already familiar with the Picamera2 API. PiGear leverages the libcamera API under the hood with multi-threading, providing high-performance , enhanced control and functionality for Raspberry Pi camera modules.

PiGear handles common configuration parameters and non-standard settings for various camera types, simplifying the integration process. PiGear currently supports picamera2 API parameters such as sensor, controls, transform, and stride, with internal type and sanity checks for robust performance.

While primarily focused on Raspberry Pi camera modules, PiGear also provides basic functionality for USB webcams only with Picamera2 API, along with the ability to accurately differentiate between USB and Raspberry Pi cameras using metadata.

Backward compatibility with picamera library

PiGear seamlessly switches to the legacy picamera library if the picamera2 library is unavailable, ensuring seamless backward compatibility. For this, PiGear also provides a flexible multi-threaded framework around complete picamera API, allowing developers to effortlessly exploit a wide range of parameters, such as brightness, saturation, sensor_mode, iso, exposure, and more.

Furthermore, PiGear supports the use of multiple camera modules, including those found on Raspberry Pi Compute Module IO boards and USB cameras (only with Picamera2 API).

Threaded Internal Timer

PiGear ensures proper resource release during the termination of the API, preventing potential issues or resource leaks. PiGear API internally implements a Threaded Internal Timer that silently keeps active track of any frozen-threads or hardware-failures and exits safely if any do occur. This means that if you're running the PiGear API in your script and someone accidentally pulls the Camera-Module cable out, instead of going into a possible kernel panic, the API will exit safely to save resources.

Make sure to enable Raspberry Pi hardware-specific settings prior using this API, otherwise nothing will work.

Source code in vidgear/gears/pigear.py
class PiGear:\n    \"\"\"\n    PiGear implements a seamless and robust wrapper around the [picamera2](https://github.com/raspberrypi/picamera2) python library, simplifying integration with minimal code changes and ensuring a\n    smooth transition for developers already familiar with the Picamera2 API. PiGear leverages the `libcamera` API under the hood with multi-threading, providing high-performance :fire:, enhanced\n    control and functionality for Raspberry Pi camera modules.\n\n    PiGear handles common configuration parameters and non-standard settings for various camera types, simplifying the integration process. PiGear currently supports picamera2 API parameters such as\n    `sensor`, `controls`, `transform`, and `stride`, with internal type and sanity checks for robust performance.\n\n    While primarily focused on Raspberry Pi camera modules, PiGear also provides basic functionality for USB webcams only with Picamera2 API, along with the ability to accurately differentiate between\n    USB and Raspberry Pi cameras using metadata.\n\n    ???+ info \"Backward compatibility with `picamera` library\"\n        PiGear seamlessly switches to the legacy [picamera](https://picamera.readthedocs.io/en/release-1.13/index.html) library if the `picamera2` library is unavailable, ensuring seamless backward\n        compatibility. For this, PiGear also provides a flexible multi-threaded framework around complete `picamera` API, allowing developers to effortlessly exploit a wide range of parameters, such\n        as `brightness`, `saturation`, `sensor_mode`, `iso`, `exposure`, and more.\n\n    Furthermore, PiGear supports the use of multiple camera modules, including those found on Raspberry Pi Compute Module IO boards and USB cameras _(only with Picamera2 API)_.\n\n    ??? new \"Threaded Internal Timer :material-camera-timer:\"\n        PiGear ensures proper resource release during the termination of the API, preventing potential issues or resource leaks. PiGear API internally implements a\n        ==Threaded Internal Timer== that silently keeps active track of any frozen-threads or hardware-failures and exits safely if any do occur. This means that if\n        you're running the PiGear API in your script and someone accidentally pulls the Camera-Module cable out, instead of going into a possible kernel panic,\n        the API will exit safely to save resources.\n\n    !!! failure \"Make sure to [enable Raspberry Pi hardware-specific settings](https://picamera.readthedocs.io/en/release-1.13/quickstart.html) prior using this API, otherwise nothing will work.\"\n    \"\"\"\n\n    def __init__(\n        self,\n        camera_num=0,\n        resolution=(640, 480),\n        framerate=30,\n        colorspace=None,\n        logging=False,\n        time_delay=0,\n        **options\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the PiGear class.\n\n        Parameters:\n            camera_num (int): selects the camera module index which will be used as source.\n            resolution (tuple): sets the resolution (i.e. `(width,height)`) of the source..\n            framerate (int/float): sets the framerate of the source.\n            colorspace (str): selects the colorspace of the input stream.\n            logging (bool): enables/disables logging.\n            time_delay (int): time delay (in sec) before start reading the frames.\n            options (dict): provides ability to alter Source Tweak Parameters.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # raise error(s) for critical Class imports\n        global picamera, picamera2\n        if picamera2:\n            # log if picamera2\n            self.__logging and logger.info(\"picamera2 API is currently being accessed.\")\n        elif picamera:\n            # switch to picamera otherwise\n            logger.critical(\n                \"picamera2 library not installed on this system. Defaulting to legacy picamera API.\"\n            )\n        else:\n            # raise error if none\n            import_dependency_safe(\"picamera\")\n\n        assert (\n            isinstance(framerate, (int, float)) and framerate > 0.0\n        ), \"[PiGear:ERROR] :: Input framerate value `{}` is a Invalid! Kindly read docs.\".format(\n            framerate\n        )\n        assert (\n            isinstance(resolution, (tuple, list)) and len(resolution) == 2\n        ), \"[PiGear:ERROR] :: Input resolution value `{}` is a Invalid! Kindly read docs.\".format(\n            resolution\n        )\n        if not (isinstance(camera_num, int) and camera_num >= 0):\n            camera_num = 0\n            logger.warning(\n                \"Input camera_num value `{}` is invalid, Defaulting to index 0!\"\n            )\n\n        # reformat dict\n        options = {str(k).strip(): v for k, v in options.items()}\n\n        # check if legacy picamera backend is enforced\n        enforce_legacy_picamera = options.pop(\"enforce_legacy_picamera\", False)\n        if isinstance(enforce_legacy_picamera, bool) and enforce_legacy_picamera:\n            # check if picamera library is available.\n            if picamera:\n                logger.critical(\n                    \"Enforcing legacy picamera API for this run. picamera2 API access will be disabled!\"\n                )\n                # disable picamera2\n                picamera2 = None\n            else:\n                # raise error otherwise\n                logger.error(\n                    \"`picamera` is unavailable or unsupported on this system, `enforce_legacy_picamera` will be discarded!\"\n                )\n                import_dependency_safe(\"picamera\")\n\n        if picamera2:\n            # handle logging\n            not (self.__logging) and not os.getenv(\n                \"LIBCAMERA_LOG_LEVELS\", False\n            ) and logger.info(\n                \"Kindly set `LIBCAMERA_LOG_LEVELS=2` environment variable to disable common libcamera API messages.\"\n            )\n            # collect metadata\n            cameras_metadata = Picamera2.global_camera_info()\n            # initialize the picamera stream at given index\n            self.__camera = Picamera2(camera_num=camera_num)\n            # extract metadata for current camera\n            camera_metadata = [x for x in cameras_metadata if x[\"Num\"] == camera_num][0]\n            # check connected camera is USB or I2C\n            self.__camera_is_usb = True if \"usb\" in camera_metadata[\"Id\"] else False\n            # handle framerate control\n            if not self.__camera_is_usb:\n                self.__camera.set_controls({\"FrameRate\": framerate})\n            else:\n                logger.warning(\n                    \"USB camera detected. Setting input framerate is NOT supported with Picamera2 API!\"\n                )\n            # log\n            self.__logging and logger.debug(\n                \"Activating Picamera2 API for `{}` camera at index: {} with resolution: {} & framerate: {}\".format(\n                    camera_metadata[\"Model\"],\n                    camera_num,\n                    resolution if not self.__camera_is_usb else \"default\",\n                    framerate,\n                )\n            )\n        else:\n            # initialize the picamera stream at given index\n            self.__camera = PiCamera(camera_num=camera_num)\n            self.__camera.resolution = tuple(resolution)\n            self.__camera.framerate = framerate\n            self.__logging and logger.debug(\n                \"Activating Picamera API at index: {} with resolution: {} & framerate: {}\".format(\n                    camera_num, resolution, framerate\n                )\n            )\n\n        # initialize framerate (Read-only) variable\n        self.framerate = framerate\n\n        # initializing colorspace variable\n        self.color_space = None\n\n        # define timeout variable default value(handles hardware failures)\n        self.__failure_timeout = options.pop(\"HWFAILURE_TIMEOUT\", 2.0)\n        if isinstance(self.__failure_timeout, (int, float)):\n            if not (10.0 > self.__failure_timeout > 1.0):\n                raise ValueError(\n                    \"[PiGear:ERROR] :: `HWFAILURE_TIMEOUT` value can only be between 1.0 ~ 10.0\"\n                )\n            self.__logging and logger.debug(\n                \"Setting HW Failure Timeout: {} seconds\".format(self.__failure_timeout)\n            )\n        else:\n            # reset improper values\n            self.__failure_timeout = 2.0\n\n        try:\n            if picamera2:\n                # define common supported picamera2 config parameters\n                valid_config_options = [\n                    \"auto_align_output_size\",  # internal\n                    \"enable_verbose_logs\",  # internal\n                    \"format\",\n                    \"sensor\",\n                ]\n\n                # define non-USB supported picamera2 config parameters\n                non_usb_options = [\n                    \"controls\",  # not-supported on USB\n                    \"transform\",  # not-working on USB\n                    \"buffer_count\",  # not-supported on USB\n                    \"queue\",  # not-supported on USB\n                ]  # Less are supported (will be changed in future)\n\n                # filter parameter supported with non-USB cameras only\n                if self.__camera_is_usb:\n                    unsupported_config_keys = set(list(options.keys())).intersection(\n                        set(non_usb_options)\n                    )\n                    unsupported_config_keys and logger.warning(\n                        \"Setting parameters: `{}` for USB camera is NOT supported with Picamera2 API!\".format(\n                            \"`, `\".join(unsupported_config_keys)\n                        )\n                    )\n                else:\n                    valid_config_options += non_usb_options\n\n                # log all invalid keys\n                invalid_config_keys = set(list(options.keys())) - set(\n                    valid_config_options\n                )\n                invalid_config_keys and logger.warning(\n                    \"Discarding invalid options NOT supported by Picamera2 API for current Camera Sensor: `{}`\".format(\n                        \"`, `\".join(invalid_config_keys)\n                    )\n                )\n                # delete all unsupported options\n                options = {\n                    x: y for x, y in options.items() if x in valid_config_options\n                }\n\n                # setting size, already defined\n                options.update({\"size\": tuple(resolution)})\n\n                # set 24-bit, BGR format by default\n                if not \"format\" in options:\n                    # auto defaults for USB cameras\n                    not self.__camera_is_usb and options.update({\"format\": \"RGB888\"})\n                elif self.__camera_is_usb:\n                    # check the supported formats, if USB camera\n                    avail_formats = [\n                        mode[\"format\"] for mode in self.__camera.sensor_modes\n                    ]\n                    # handle unsupported formats\n                    if not options[\"format\"] in avail_formats:\n                        logger.warning(\n                            \"Discarding `format={}`. `{}` are the only available formats for USB camera in use!\".format(\n                                options[\"format\"], \"`, `\".join(avail_formats)\n                            )\n                        )\n                        del options[\"format\"]\n                    else:\n                        # `colorspace` parameter must define with  `format` optional parameter\n                        # unless format is MPEG (tested)\n                        (\n                            not (colorspace is None) or options[\"format\"] == \"MPEG\"\n                        ) and logger.warning(\n                            \"Custom Output frames `format={}` detected. It is advised to define `colorspace` parameter or handle this format manually in your code!\".format(\n                                options[\"format\"]\n                            )\n                        )\n                else:\n                    # `colorspace` parameter must define with  `format` optional parameter\n                    # unless format is either BGR or BGRA\n                    (\n                        not (colorspace is None)\n                        or options[\"format\"]\n                        in [\n                            \"RGB888\",\n                            \"XRGB8888\",\n                        ]\n                    ) and logger.warning(\n                        \"Custom Output frames `format={}` detected. It is advised to define `colorspace` parameter or handle this format manually in your code!\".format(\n                            options[\"format\"]\n                        )\n                    )\n\n                # enable verbose logging mode (handled by Picamera2 API)\n                verbose = options.pop(\"enable_verbose_logs\", False)\n                if self.__logging and isinstance(verbose, bool) and verbose:\n                    self.__camera.set_logging(Picamera2.DEBUG)\n                else:\n                    # setup logging\n                    self.__camera.set_logging(Picamera2.WARNING)\n\n                # handle transformations, if specified\n                transform = options.pop(\"transform\", Transform())\n                if not isinstance(transform, Transform):\n                    logger.warning(\"`transform` value is of invalid type, Discarding!\")\n                    transform = Transform()\n\n                # handle sensor configurations, if specified\n                sensor = options.pop(\"sensor\", {})\n                if isinstance(sensor, dict):\n                    # extract all valid sensor keys\n                    valid_sensor = [\"output_size\", \"bit_depth\"]\n                    # log all invalid keys\n                    invalid_sensor_keys = set(list(sensor)) - set(valid_sensor)\n                    invalid_sensor_keys and logger.warning(\n                        \"Discarding sensor properties NOT supported by current Camera Sensor: `{}`. Only supported are: (`{}`)\".format(\n                            \"`, `\".join(invalid_sensor_keys),\n                            \"`, `\".join(valid_sensor),\n                        )\n                    )\n                    # delete all unsupported control keys\n                    sensor = {x: y for x, y in sensor.items() if x in valid_sensor}\n                    # remove size if output size is defined\n                    if \"output_size\" in sensor:\n                        del options[\"size\"]\n                        logger.critical(\n                            \"Overriding output frame size with `output_size={}!\".format(\n                                sensor[\"output_size\"]\n                            )\n                        )\n                else:\n                    logger.warning(\"`sensor` value is of invalid type, Discarding!\")\n                    sensor = {}\n\n                # handle controls, if specified\n                controls = options.pop(\"controls\", {})\n                if isinstance(controls, dict):\n                    # extract all valid control keys\n                    valid_controls = self.__camera.camera_controls\n                    # remove any fps controls, assigned already\n                    valid_controls.pop(\"FrameDuration\", None)\n                    valid_controls.pop(\"FrameDurationLimits\", None)\n                    # log all invalid keys\n                    invalid_control_keys = set(list(controls.keys())) - set(\n                        list(valid_controls.keys())\n                    )\n                    invalid_control_keys and logger.warning(\n                        \"Discarding control properties NOT supported by current Camera Sensor: `{}`. Only supported are: (`{}`)\".format(\n                            \"`, `\".join(invalid_control_keys),\n                            \"`, `\".join(list(valid_controls.keys())),\n                        )\n                    )\n                    # delete all unsupported control keys\n                    controls = {\n                        x: y for x, y in controls.items() if x in valid_controls.keys()\n                    }\n                else:\n                    logger.warning(\"`controls` value is of invalid type, Discarding!\")\n                    controls = {}\n\n                # handle buffer_count, if specified\n                buffer_count = options.pop(\"buffer_count\", 4)\n                if (\n                    not isinstance(buffer_count, int) or buffer_count < 1\n                ):  # must be greater than 1\n                    logger.warning(\n                        \"`buffer_count` value is of invalid type, Discarding!\"\n                    )\n                    # `create_preview_configuration` requests 4 sets of buffers\n                    buffer_count = 4\n\n                # handle queue, if specified\n                queue = options.pop(\"queue\", True)\n                if not isinstance(queue, bool):\n                    logger.warning(\"`queue` value is of invalid type, Discarding!\")\n                    queue = True\n\n                # check if auto-align camera configuration is specified\n                auto_align_output_size = options.pop(\"auto_align_output_size\", False)\n\n                # create default configuration for camera\n                config = self.__camera.create_preview_configuration(\n                    main=options,\n                    transform=transform,\n                    sensor=sensor,\n                    controls=controls,\n                    buffer_count=buffer_count,\n                    queue=queue,\n                )\n\n                # auto-align camera configuration, if specified\n                if isinstance(auto_align_output_size, bool) and auto_align_output_size:\n                    self.__logging and logger.debug(\n                        \"Re-aligning Output frames to optimal size supported by current Camera Sensor.\"\n                    )\n                    self.__camera.align_configuration(config)\n\n                # configure camera\n                self.__camera.configure(config)\n                self.__logging and logger.debug(\n                    \"Setting Picamera2 API Parameters: `{}`, controls: `{}`, sensor: `{}`, buffer_count: `{}`, and queue: `{}`.\".format(\n                        self.__camera.camera_configuration()[\"main\"],\n                        controls,\n                        sensor,\n                        buffer_count,\n                        queue,\n                    )\n                )\n            else:\n                # apply attributes to source if specified\n                for key, value in options.items():\n                    self.__logging and logger.debug(\n                        \"Setting {} API Parameter for Picamera: `{}`\".format(key, value)\n                    )\n                    setattr(self.__camera, key, value)\n        except Exception as e:\n            # Catch if any error occurred\n            logger.exception(str(e))\n\n        # separately handle colorspace value to int conversion\n        if not (colorspace is None):\n            self.color_space = capPropId(colorspace.strip())\n            if self.__logging and not (self.color_space is None):\n                logger.debug(\n                    \"Enabling `{}` colorspace for this video stream!\".format(\n                        colorspace.strip()\n                    )\n                )\n\n        # enable rgb capture array thread and capture stream\n        if not picamera2:\n            self.__rawCapture = PiRGBArray(self.__camera, size=resolution)\n            self.stream = self.__camera.capture_continuous(\n                self.__rawCapture, format=\"bgr\", use_video_port=True\n            )\n\n        # initialize frame variable\n        # with captured frame\n        try:\n            if picamera2:\n                # start camera thread\n                self.__camera.start()\n                # capture frame array\n                self.frame = self.__camera.capture_array(\"main\")\n                # assign camera as stream for setting\n                # parameters after starting the camera\n                self.stream = self.__camera\n            else:\n                # capture frame array from stream\n                stream = next(self.stream)\n                self.frame = stream.array\n                self.__rawCapture.seek(0)\n                self.__rawCapture.truncate()\n            # render colorspace if defined\n            if not (self.frame is None) and not (self.color_space is None):\n                self.frame = cv2.cvtColor(self.frame, self.color_space)\n        except Exception as e:\n            logger.exception(str(e))\n            raise RuntimeError(\"[PiGear:ERROR] :: Camera Module failed to initialize!\")\n\n        # applying time delay to warm-up picamera only if specified\n        if time_delay and isinstance(time_delay, (int, float)):\n            time.sleep(time_delay)\n\n        # thread initialization\n        self.__thread = None\n\n        # timer thread initialization(Keeps check on frozen thread)\n        self.__timer = None\n        self.__t_elapsed = 0.0  # records time taken by thread\n\n        # catching thread exceptions\n        self.__exceptions = None\n\n        # initialize termination flag\n        self.__terminate = False\n\n    def start(self):\n        \"\"\"\n        Launches the internal *Threaded Frames Extractor* daemon\n\n        **Returns:** A reference to the PiGear class object.\n        \"\"\"\n        # Start frame producer thread\n        self.__thread = Thread(target=self.__update, name=\"PiGear\", args=())\n        self.__thread.daemon = True\n        self.__thread.start()\n\n        # Start internal timer thread\n        self.__timer = Thread(target=self.__timeit, name=\"PiTimer\", args=())\n        self.__timer.daemon = True\n        self.__timer.start()\n\n        return self\n\n    def __timeit(self):\n        \"\"\"\n        Threaded Internal Timer that keep checks on thread execution timing\n        \"\"\"\n        # assign current time\n        self.__t_elapsed = time.time()\n\n        # loop until terminated\n        while not (self.__terminate):\n            # check for frozen thread\n            if time.time() - self.__t_elapsed > self.__failure_timeout:\n                # log failure\n                self.__logging and logger.critical(\"Camera Module Disconnected!\")\n                # prepare for clean exit\n                self.__exceptions = True\n                self.__terminate = True  # self-terminate\n\n    def __update(self):\n        \"\"\"\n        A **Threaded Frames Extractor**, that keep iterating frames from PiCamera API to a internal monitored deque,\n        until the thread is terminated, or frames runs out.\n        \"\"\"\n        # keep looping infinitely until the thread is terminated\n        while not (self.__terminate):\n            if not picamera2:\n                try:\n                    # Try to iterate next frame from generator\n                    stream = next(self.stream)\n                except Exception:\n                    # catch and save any exceptions\n                    self.__exceptions = sys.exc_info()\n                    break  # exit\n\n            # __update timer\n            self.__t_elapsed = time.time()\n\n            # grab the frame from the stream\n            if picamera2:\n                frame = self.__camera.capture_array(\"main\")\n            else:\n                frame = stream.array\n                # clear the stream in preparation\n                # for the next frame\n                self.__rawCapture.seek(0)\n                self.__rawCapture.truncate()\n\n            # apply colorspace if specified\n            if not (self.color_space is None):\n                # apply colorspace to frames\n                color_frame = None\n                try:\n                    color_frame = cv2.cvtColor(frame, self.color_space)\n                except Exception as e:\n                    # Catch if any error occurred\n                    color_frame = None\n                    self.color_space = None\n                    self.__logging and logger.exception(str(e))\n                    logger.warning(\"Assigned colorspace value is invalid. Discarding!\")\n                self.frame = color_frame if not (color_frame is None) else frame\n            else:\n                self.frame = frame\n\n        # terminate processes\n        if not (self.__terminate):\n            self.__terminate = True\n\n        # release resources\n        if picamera2:\n            self.__camera.stop()\n        else:\n            self.__rawCapture.close()\n            self.__camera.close()\n\n    def read(self):\n        \"\"\"\n        Extracts frames synchronously from monitored deque, while maintaining a fixed-length frame buffer in the memory,\n        and blocks the thread if the deque is full.\n\n        **Returns:** A n-dimensional numpy array.\n        \"\"\"\n        # check if there are any thread exceptions\n        if not (self.__exceptions is None):\n            if isinstance(self.__exceptions, bool):\n                # clear frame\n                self.frame = None\n                # notify user about hardware failure\n                raise SystemError(\n                    \"[PiGear:ERROR] :: Hardware failure occurred, Kindly reconnect Camera Module and restart your Pi!\"\n                )\n            else:\n                # clear frame\n                self.frame = None\n                # re-raise error for debugging\n                error_msg = (\n                    \"[PiGear:ERROR] :: Camera Module API failure occurred: {}\".format(\n                        self.__exceptions[1]\n                    )\n                )\n                raise RuntimeError(error_msg).with_traceback(self.__exceptions[2])\n        # return the frame\n        return self.frame\n\n    def stop(self):\n        \"\"\"\n        Safely terminates the thread, and release the multi-threaded resources.\n        \"\"\"\n        # log termination\n        self.__logging and logger.debug(\"Terminating PiGear Processes.\")\n\n        # make sure that the threads should be terminated\n        self.__terminate = True\n\n        # stop timer thread\n        if not (self.__timer is None):\n            self.__timer.join()\n            self.__timer = None\n\n        # handle camera thread\n        if not (self.__thread is None):\n            # check if hardware failure occurred\n            if not (self.__exceptions is None) and isinstance(self.__exceptions, bool):\n                if picamera2:\n                    # release picamera2 resources\n                    self.__camera.stop()\n                else:\n                    # force release picamera resources\n                    self.__rawCapture.close()\n                    self.__camera.close()\n            # properly handle thread exit\n            # wait if still process is still\n            # processing some information\n            self.__thread.join()\n            # remove any threads\n            self.__thread = None\n

"},{"location":"bonus/reference/pigear/#vidgear.gears.pigear.PiGear.__init__","title":"__init__(self, camera_num=0, resolution=(640, 480), framerate=30, colorspace=None, logging=False, time_delay=0, **options) special","text":"

This constructor method initializes the object state and attributes of the PiGear class.

Parameters:

Name Type Description Default camera_num int

selects the camera module index which will be used as source.

0 resolution tuple

sets the resolution (i.e. (width,height)) of the source..

(640, 480) framerate int/float

sets the framerate of the source.

30 colorspace str

selects the colorspace of the input stream.

None logging bool

enables/disables logging.

False time_delay int

time delay (in sec) before start reading the frames.

0 options dict

provides ability to alter Source Tweak Parameters.

{} Source code in vidgear/gears/pigear.py
def __init__(\n    self,\n    camera_num=0,\n    resolution=(640, 480),\n    framerate=30,\n    colorspace=None,\n    logging=False,\n    time_delay=0,\n    **options\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the PiGear class.\n\n    Parameters:\n        camera_num (int): selects the camera module index which will be used as source.\n        resolution (tuple): sets the resolution (i.e. `(width,height)`) of the source..\n        framerate (int/float): sets the framerate of the source.\n        colorspace (str): selects the colorspace of the input stream.\n        logging (bool): enables/disables logging.\n        time_delay (int): time delay (in sec) before start reading the frames.\n        options (dict): provides ability to alter Source Tweak Parameters.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # raise error(s) for critical Class imports\n    global picamera, picamera2\n    if picamera2:\n        # log if picamera2\n        self.__logging and logger.info(\"picamera2 API is currently being accessed.\")\n    elif picamera:\n        # switch to picamera otherwise\n        logger.critical(\n            \"picamera2 library not installed on this system. Defaulting to legacy picamera API.\"\n        )\n    else:\n        # raise error if none\n        import_dependency_safe(\"picamera\")\n\n    assert (\n        isinstance(framerate, (int, float)) and framerate > 0.0\n    ), \"[PiGear:ERROR] :: Input framerate value `{}` is a Invalid! Kindly read docs.\".format(\n        framerate\n    )\n    assert (\n        isinstance(resolution, (tuple, list)) and len(resolution) == 2\n    ), \"[PiGear:ERROR] :: Input resolution value `{}` is a Invalid! Kindly read docs.\".format(\n        resolution\n    )\n    if not (isinstance(camera_num, int) and camera_num >= 0):\n        camera_num = 0\n        logger.warning(\n            \"Input camera_num value `{}` is invalid, Defaulting to index 0!\"\n        )\n\n    # reformat dict\n    options = {str(k).strip(): v for k, v in options.items()}\n\n    # check if legacy picamera backend is enforced\n    enforce_legacy_picamera = options.pop(\"enforce_legacy_picamera\", False)\n    if isinstance(enforce_legacy_picamera, bool) and enforce_legacy_picamera:\n        # check if picamera library is available.\n        if picamera:\n            logger.critical(\n                \"Enforcing legacy picamera API for this run. picamera2 API access will be disabled!\"\n            )\n            # disable picamera2\n            picamera2 = None\n        else:\n            # raise error otherwise\n            logger.error(\n                \"`picamera` is unavailable or unsupported on this system, `enforce_legacy_picamera` will be discarded!\"\n            )\n            import_dependency_safe(\"picamera\")\n\n    if picamera2:\n        # handle logging\n        not (self.__logging) and not os.getenv(\n            \"LIBCAMERA_LOG_LEVELS\", False\n        ) and logger.info(\n            \"Kindly set `LIBCAMERA_LOG_LEVELS=2` environment variable to disable common libcamera API messages.\"\n        )\n        # collect metadata\n        cameras_metadata = Picamera2.global_camera_info()\n        # initialize the picamera stream at given index\n        self.__camera = Picamera2(camera_num=camera_num)\n        # extract metadata for current camera\n        camera_metadata = [x for x in cameras_metadata if x[\"Num\"] == camera_num][0]\n        # check connected camera is USB or I2C\n        self.__camera_is_usb = True if \"usb\" in camera_metadata[\"Id\"] else False\n        # handle framerate control\n        if not self.__camera_is_usb:\n            self.__camera.set_controls({\"FrameRate\": framerate})\n        else:\n            logger.warning(\n                \"USB camera detected. Setting input framerate is NOT supported with Picamera2 API!\"\n            )\n        # log\n        self.__logging and logger.debug(\n            \"Activating Picamera2 API for `{}` camera at index: {} with resolution: {} & framerate: {}\".format(\n                camera_metadata[\"Model\"],\n                camera_num,\n                resolution if not self.__camera_is_usb else \"default\",\n                framerate,\n            )\n        )\n    else:\n        # initialize the picamera stream at given index\n        self.__camera = PiCamera(camera_num=camera_num)\n        self.__camera.resolution = tuple(resolution)\n        self.__camera.framerate = framerate\n        self.__logging and logger.debug(\n            \"Activating Picamera API at index: {} with resolution: {} & framerate: {}\".format(\n                camera_num, resolution, framerate\n            )\n        )\n\n    # initialize framerate (Read-only) variable\n    self.framerate = framerate\n\n    # initializing colorspace variable\n    self.color_space = None\n\n    # define timeout variable default value(handles hardware failures)\n    self.__failure_timeout = options.pop(\"HWFAILURE_TIMEOUT\", 2.0)\n    if isinstance(self.__failure_timeout, (int, float)):\n        if not (10.0 > self.__failure_timeout > 1.0):\n            raise ValueError(\n                \"[PiGear:ERROR] :: `HWFAILURE_TIMEOUT` value can only be between 1.0 ~ 10.0\"\n            )\n        self.__logging and logger.debug(\n            \"Setting HW Failure Timeout: {} seconds\".format(self.__failure_timeout)\n        )\n    else:\n        # reset improper values\n        self.__failure_timeout = 2.0\n\n    try:\n        if picamera2:\n            # define common supported picamera2 config parameters\n            valid_config_options = [\n                \"auto_align_output_size\",  # internal\n                \"enable_verbose_logs\",  # internal\n                \"format\",\n                \"sensor\",\n            ]\n\n            # define non-USB supported picamera2 config parameters\n            non_usb_options = [\n                \"controls\",  # not-supported on USB\n                \"transform\",  # not-working on USB\n                \"buffer_count\",  # not-supported on USB\n                \"queue\",  # not-supported on USB\n            ]  # Less are supported (will be changed in future)\n\n            # filter parameter supported with non-USB cameras only\n            if self.__camera_is_usb:\n                unsupported_config_keys = set(list(options.keys())).intersection(\n                    set(non_usb_options)\n                )\n                unsupported_config_keys and logger.warning(\n                    \"Setting parameters: `{}` for USB camera is NOT supported with Picamera2 API!\".format(\n                        \"`, `\".join(unsupported_config_keys)\n                    )\n                )\n            else:\n                valid_config_options += non_usb_options\n\n            # log all invalid keys\n            invalid_config_keys = set(list(options.keys())) - set(\n                valid_config_options\n            )\n            invalid_config_keys and logger.warning(\n                \"Discarding invalid options NOT supported by Picamera2 API for current Camera Sensor: `{}`\".format(\n                    \"`, `\".join(invalid_config_keys)\n                )\n            )\n            # delete all unsupported options\n            options = {\n                x: y for x, y in options.items() if x in valid_config_options\n            }\n\n            # setting size, already defined\n            options.update({\"size\": tuple(resolution)})\n\n            # set 24-bit, BGR format by default\n            if not \"format\" in options:\n                # auto defaults for USB cameras\n                not self.__camera_is_usb and options.update({\"format\": \"RGB888\"})\n            elif self.__camera_is_usb:\n                # check the supported formats, if USB camera\n                avail_formats = [\n                    mode[\"format\"] for mode in self.__camera.sensor_modes\n                ]\n                # handle unsupported formats\n                if not options[\"format\"] in avail_formats:\n                    logger.warning(\n                        \"Discarding `format={}`. `{}` are the only available formats for USB camera in use!\".format(\n                            options[\"format\"], \"`, `\".join(avail_formats)\n                        )\n                    )\n                    del options[\"format\"]\n                else:\n                    # `colorspace` parameter must define with  `format` optional parameter\n                    # unless format is MPEG (tested)\n                    (\n                        not (colorspace is None) or options[\"format\"] == \"MPEG\"\n                    ) and logger.warning(\n                        \"Custom Output frames `format={}` detected. It is advised to define `colorspace` parameter or handle this format manually in your code!\".format(\n                            options[\"format\"]\n                        )\n                    )\n            else:\n                # `colorspace` parameter must define with  `format` optional parameter\n                # unless format is either BGR or BGRA\n                (\n                    not (colorspace is None)\n                    or options[\"format\"]\n                    in [\n                        \"RGB888\",\n                        \"XRGB8888\",\n                    ]\n                ) and logger.warning(\n                    \"Custom Output frames `format={}` detected. It is advised to define `colorspace` parameter or handle this format manually in your code!\".format(\n                        options[\"format\"]\n                    )\n                )\n\n            # enable verbose logging mode (handled by Picamera2 API)\n            verbose = options.pop(\"enable_verbose_logs\", False)\n            if self.__logging and isinstance(verbose, bool) and verbose:\n                self.__camera.set_logging(Picamera2.DEBUG)\n            else:\n                # setup logging\n                self.__camera.set_logging(Picamera2.WARNING)\n\n            # handle transformations, if specified\n            transform = options.pop(\"transform\", Transform())\n            if not isinstance(transform, Transform):\n                logger.warning(\"`transform` value is of invalid type, Discarding!\")\n                transform = Transform()\n\n            # handle sensor configurations, if specified\n            sensor = options.pop(\"sensor\", {})\n            if isinstance(sensor, dict):\n                # extract all valid sensor keys\n                valid_sensor = [\"output_size\", \"bit_depth\"]\n                # log all invalid keys\n                invalid_sensor_keys = set(list(sensor)) - set(valid_sensor)\n                invalid_sensor_keys and logger.warning(\n                    \"Discarding sensor properties NOT supported by current Camera Sensor: `{}`. Only supported are: (`{}`)\".format(\n                        \"`, `\".join(invalid_sensor_keys),\n                        \"`, `\".join(valid_sensor),\n                    )\n                )\n                # delete all unsupported control keys\n                sensor = {x: y for x, y in sensor.items() if x in valid_sensor}\n                # remove size if output size is defined\n                if \"output_size\" in sensor:\n                    del options[\"size\"]\n                    logger.critical(\n                        \"Overriding output frame size with `output_size={}!\".format(\n                            sensor[\"output_size\"]\n                        )\n                    )\n            else:\n                logger.warning(\"`sensor` value is of invalid type, Discarding!\")\n                sensor = {}\n\n            # handle controls, if specified\n            controls = options.pop(\"controls\", {})\n            if isinstance(controls, dict):\n                # extract all valid control keys\n                valid_controls = self.__camera.camera_controls\n                # remove any fps controls, assigned already\n                valid_controls.pop(\"FrameDuration\", None)\n                valid_controls.pop(\"FrameDurationLimits\", None)\n                # log all invalid keys\n                invalid_control_keys = set(list(controls.keys())) - set(\n                    list(valid_controls.keys())\n                )\n                invalid_control_keys and logger.warning(\n                    \"Discarding control properties NOT supported by current Camera Sensor: `{}`. Only supported are: (`{}`)\".format(\n                        \"`, `\".join(invalid_control_keys),\n                        \"`, `\".join(list(valid_controls.keys())),\n                    )\n                )\n                # delete all unsupported control keys\n                controls = {\n                    x: y for x, y in controls.items() if x in valid_controls.keys()\n                }\n            else:\n                logger.warning(\"`controls` value is of invalid type, Discarding!\")\n                controls = {}\n\n            # handle buffer_count, if specified\n            buffer_count = options.pop(\"buffer_count\", 4)\n            if (\n                not isinstance(buffer_count, int) or buffer_count < 1\n            ):  # must be greater than 1\n                logger.warning(\n                    \"`buffer_count` value is of invalid type, Discarding!\"\n                )\n                # `create_preview_configuration` requests 4 sets of buffers\n                buffer_count = 4\n\n            # handle queue, if specified\n            queue = options.pop(\"queue\", True)\n            if not isinstance(queue, bool):\n                logger.warning(\"`queue` value is of invalid type, Discarding!\")\n                queue = True\n\n            # check if auto-align camera configuration is specified\n            auto_align_output_size = options.pop(\"auto_align_output_size\", False)\n\n            # create default configuration for camera\n            config = self.__camera.create_preview_configuration(\n                main=options,\n                transform=transform,\n                sensor=sensor,\n                controls=controls,\n                buffer_count=buffer_count,\n                queue=queue,\n            )\n\n            # auto-align camera configuration, if specified\n            if isinstance(auto_align_output_size, bool) and auto_align_output_size:\n                self.__logging and logger.debug(\n                    \"Re-aligning Output frames to optimal size supported by current Camera Sensor.\"\n                )\n                self.__camera.align_configuration(config)\n\n            # configure camera\n            self.__camera.configure(config)\n            self.__logging and logger.debug(\n                \"Setting Picamera2 API Parameters: `{}`, controls: `{}`, sensor: `{}`, buffer_count: `{}`, and queue: `{}`.\".format(\n                    self.__camera.camera_configuration()[\"main\"],\n                    controls,\n                    sensor,\n                    buffer_count,\n                    queue,\n                )\n            )\n        else:\n            # apply attributes to source if specified\n            for key, value in options.items():\n                self.__logging and logger.debug(\n                    \"Setting {} API Parameter for Picamera: `{}`\".format(key, value)\n                )\n                setattr(self.__camera, key, value)\n    except Exception as e:\n        # Catch if any error occurred\n        logger.exception(str(e))\n\n    # separately handle colorspace value to int conversion\n    if not (colorspace is None):\n        self.color_space = capPropId(colorspace.strip())\n        if self.__logging and not (self.color_space is None):\n            logger.debug(\n                \"Enabling `{}` colorspace for this video stream!\".format(\n                    colorspace.strip()\n                )\n            )\n\n    # enable rgb capture array thread and capture stream\n    if not picamera2:\n        self.__rawCapture = PiRGBArray(self.__camera, size=resolution)\n        self.stream = self.__camera.capture_continuous(\n            self.__rawCapture, format=\"bgr\", use_video_port=True\n        )\n\n    # initialize frame variable\n    # with captured frame\n    try:\n        if picamera2:\n            # start camera thread\n            self.__camera.start()\n            # capture frame array\n            self.frame = self.__camera.capture_array(\"main\")\n            # assign camera as stream for setting\n            # parameters after starting the camera\n            self.stream = self.__camera\n        else:\n            # capture frame array from stream\n            stream = next(self.stream)\n            self.frame = stream.array\n            self.__rawCapture.seek(0)\n            self.__rawCapture.truncate()\n        # render colorspace if defined\n        if not (self.frame is None) and not (self.color_space is None):\n            self.frame = cv2.cvtColor(self.frame, self.color_space)\n    except Exception as e:\n        logger.exception(str(e))\n        raise RuntimeError(\"[PiGear:ERROR] :: Camera Module failed to initialize!\")\n\n    # applying time delay to warm-up picamera only if specified\n    if time_delay and isinstance(time_delay, (int, float)):\n        time.sleep(time_delay)\n\n    # thread initialization\n    self.__thread = None\n\n    # timer thread initialization(Keeps check on frozen thread)\n    self.__timer = None\n    self.__t_elapsed = 0.0  # records time taken by thread\n\n    # catching thread exceptions\n    self.__exceptions = None\n\n    # initialize termination flag\n    self.__terminate = False\n
"},{"location":"bonus/reference/pigear/#vidgear.gears.pigear.PiGear.read","title":"read(self)","text":"

Extracts frames synchronously from monitored deque, while maintaining a fixed-length frame buffer in the memory, and blocks the thread if the deque is full.

Returns: A n-dimensional numpy array.

Source code in vidgear/gears/pigear.py
def read(self):\n    \"\"\"\n    Extracts frames synchronously from monitored deque, while maintaining a fixed-length frame buffer in the memory,\n    and blocks the thread if the deque is full.\n\n    **Returns:** A n-dimensional numpy array.\n    \"\"\"\n    # check if there are any thread exceptions\n    if not (self.__exceptions is None):\n        if isinstance(self.__exceptions, bool):\n            # clear frame\n            self.frame = None\n            # notify user about hardware failure\n            raise SystemError(\n                \"[PiGear:ERROR] :: Hardware failure occurred, Kindly reconnect Camera Module and restart your Pi!\"\n            )\n        else:\n            # clear frame\n            self.frame = None\n            # re-raise error for debugging\n            error_msg = (\n                \"[PiGear:ERROR] :: Camera Module API failure occurred: {}\".format(\n                    self.__exceptions[1]\n                )\n            )\n            raise RuntimeError(error_msg).with_traceback(self.__exceptions[2])\n    # return the frame\n    return self.frame\n
"},{"location":"bonus/reference/pigear/#vidgear.gears.pigear.PiGear.start","title":"start(self)","text":"

Launches the internal Threaded Frames Extractor daemon

Returns: A reference to the PiGear class object.

Source code in vidgear/gears/pigear.py
def start(self):\n    \"\"\"\n    Launches the internal *Threaded Frames Extractor* daemon\n\n    **Returns:** A reference to the PiGear class object.\n    \"\"\"\n    # Start frame producer thread\n    self.__thread = Thread(target=self.__update, name=\"PiGear\", args=())\n    self.__thread.daemon = True\n    self.__thread.start()\n\n    # Start internal timer thread\n    self.__timer = Thread(target=self.__timeit, name=\"PiTimer\", args=())\n    self.__timer.daemon = True\n    self.__timer.start()\n\n    return self\n
"},{"location":"bonus/reference/pigear/#vidgear.gears.pigear.PiGear.stop","title":"stop(self)","text":"

Safely terminates the thread, and release the multi-threaded resources.

Source code in vidgear/gears/pigear.py
def stop(self):\n    \"\"\"\n    Safely terminates the thread, and release the multi-threaded resources.\n    \"\"\"\n    # log termination\n    self.__logging and logger.debug(\"Terminating PiGear Processes.\")\n\n    # make sure that the threads should be terminated\n    self.__terminate = True\n\n    # stop timer thread\n    if not (self.__timer is None):\n        self.__timer.join()\n        self.__timer = None\n\n    # handle camera thread\n    if not (self.__thread is None):\n        # check if hardware failure occurred\n        if not (self.__exceptions is None) and isinstance(self.__exceptions, bool):\n            if picamera2:\n                # release picamera2 resources\n                self.__camera.stop()\n            else:\n                # force release picamera resources\n                self.__rawCapture.close()\n                self.__camera.close()\n        # properly handle thread exit\n        # wait if still process is still\n        # processing some information\n        self.__thread.join()\n        # remove any threads\n        self.__thread = None\n
"},{"location":"bonus/reference/screengear/","title":"ScreenGear API References","text":"

ScreenGear API usage examples can be found here \u27b6

ScreenGear API parameters are explained here \u27b6

ScreenGear is designed exclusively for targeting rapid Screencasting Capabilities, which means it can grab frames from your monitor in real-time, either by defining an area on the computer screen or full-screen, at the expense of inconsiderable latency. ScreenGear also seamlessly support frame capturing from multiple monitors as well as supports multiple backends.

ScreenGear API implements a multi-threaded wrapper around dxcam, pyscreenshot, python-mss python library, and also flexibly supports its internal parameter.

Source code in vidgear/gears/screengear.py
class ScreenGear:\n    \"\"\"\n    ScreenGear is designed exclusively for targeting rapid Screencasting Capabilities, which means it can\n    grab frames from your monitor in real-time, either by defining an area on the computer screen or full-screen,\n    at the expense of inconsiderable latency. ScreenGear also seamlessly support frame capturing from multiple\n    monitors as well as supports multiple backends.\n\n    ScreenGear API implements a multi-threaded wrapper around dxcam, pyscreenshot, python-mss python library,\n    and also flexibly supports its internal parameter.\n    \"\"\"\n\n    def __init__(\n        self, monitor=None, backend=None, colorspace=None, logging=False, **options\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the ScreenGear class.\n\n        Parameters:\n            monitor (int): enables `mss` backend and sets the index of the monitor screen.\n            backend (str): select suitable backend for extracting frames.\n            colorspace (str): selects the colorspace of the input stream.\n            logging (bool): enables/disables logging.\n            options (dict): provides the flexibility to easily alter backend library parameters. Such as, manually set the dimensions of capture screen area etc.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # create instances for the user-defined monitor\n        self.__monitor_instance = None\n        self.__backend = None\n\n        # validate monitor instance\n        assert (\n            monitor is None or monitor and isinstance(monitor, (int, tuple))\n        ), \"[ScreenGear:ERROR] :: Invalid `monitor` value detected!\"\n\n        # initialize backend\n        if backend and monitor is None:\n            self.__backend = backend.lower().strip()\n        else:\n            # enforce `dxcam` for Windows machines if undefined (or monitor is defined)\n            self.__backend = (\n                \"dxcam\" if platform.system() == \"Windows\" and dxcam else None\n            )\n\n        # initiate screen dimension handler\n        screen_dims = {}\n        # reformat proper mss dict and assign to screen dimension handler\n        screen_dims = {\n            k.strip(): v\n            for k, v in options.items()\n            if k.strip() in [\"top\", \"left\", \"width\", \"height\"]\n        }\n        # check whether user-defined dimensions are provided\n        if screen_dims and len(screen_dims) == 4:\n            key_order = (\n                (\"top\", \"left\", \"width\", \"height\")\n                if self.__backend != \"dxcam\"\n                else (\"left\", \"top\", \"width\", \"height\")\n            )\n            screen_dims = OrderedDict((k, screen_dims[k]) for k in key_order)\n            self.__logging and logger.debug(\n                \"Setting Capture-Area dimensions: {}\".format(json.dumps(screen_dims))\n            )\n        else:\n            screen_dims.clear()\n\n        # handle backends\n        if self.__backend == \"dxcam\":\n            # get target fps in case of DXcam\n            self.__target_fps = options.pop(\"dxcam_target_fps\", 0)\n            if self.__target_fps and isinstance(self.__target_fps, (int, float)):\n                # set values\n                self.__target_fps = int(self.__target_fps)\n                self.__logging and logger.debug(\n                    \"Setting Target FPS: {}\".format(self.__target_fps)\n                )\n            else:\n                # defaults to 0fps\n                self.__target_fps = 0\n            # check if platform is windows\n            assert (\n                platform.system() == \"Windows\"\n            ), \"`dxcam` backend is only available for Windows Machines.\"\n            # verify monitor values if tuple\n            assert (\n                monitor is None\n                or isinstance(monitor, int)\n                or (\n                    isinstance(monitor, tuple)\n                    and len(monitor) == 2\n                    and all(isinstance(x, int) for x in monitor)\n                )\n            ), \"For dxcam` backend, monitor` tuple value must be format `int` or `(int, int)` only.\"\n            # raise error(s) for critical Class imports\n            import_dependency_safe(\"dxcam\" if dxcam is None else \"\")\n            if monitor is None:\n                self.__capture_object = dxcam.create(\n                    region=tuple(screen_dims.values()) if screen_dims else None\n                )\n            else:\n                self.__capture_object = (\n                    dxcam.create(\n                        device_idx=monitor[0],\n                        output_idx=monitor[1],\n                        region=tuple(screen_dims.values()) if screen_dims else None,\n                    )\n                    if isinstance(monitor, tuple)\n                    else dxcam.create(\n                        device_idx=monitor,\n                        region=tuple(screen_dims.values()) if screen_dims else None,\n                    )\n                )\n        else:\n            if monitor is None:\n                # raise error(s) for critical Class imports\n                import_dependency_safe(\"pyscreenshot\" if pysct is None else \"\")\n                # reset backend if not provided\n                self.__backend = \"pil\" if self.__backend is None else self.__backend\n                # check if valid backend\n                assert (\n                    self.__backend in pysct.backends()\n                ), \"Unsupported backend {} provided!\".format(backend)\n                # create capture object\n                self.__capture_object = pysct\n            else:\n                # monitor value must be integer\n                assert monitor and isinstance(\n                    monitor, int\n                ), \"[ScreenGear:ERROR] :: Invalid `monitor` value must be integer with mss backend.\"\n                # raise error(s) for critical Class imports\n                import_dependency_safe(\n                    \"from mss import mss\" if mss is None else \"\", pkg_name=\"mss\"\n                )\n                # create capture object\n                self.__capture_object = mss()\n                self.__backend and logger.warning(\n                    \"Backends are disabled for Monitor Indexing(monitor>=0)!\"\n                )\n                self.__monitor_instance = self.__capture_object.monitors[monitor]\n\n        # log backend\n        self.__backend and self.__logging and logger.debug(\n            \"Setting Backend: {}\".format(self.__backend.upper())\n        )\n\n        # assigns special parameter to global variable and clear\n        # separately handle colorspace value to int conversion\n        if colorspace:\n            self.color_space = capPropId(colorspace.strip())\n            self.__logging and not (self.color_space is None) and logger.debug(\n                \"Enabling `{}` colorspace for this video stream!\".format(\n                    colorspace.strip()\n                )\n            )\n        else:\n            self.color_space = None\n\n        # initialize mss capture instance\n        self.__mss_capture_instance = None\n        try:\n            if self.__backend == \"dxcam\":\n                # extract global frame from instance\n                self.frame = self.__capture_object.grab()\n            else:\n                if self.__monitor_instance is None:\n                    if screen_dims:\n                        self.__mss_capture_instance = tuple(screen_dims.values())\n                    # extract global frame from instance\n                    self.frame = np.asanyarray(\n                        self.__capture_object.grab(\n                            bbox=self.__mss_capture_instance,\n                            childprocess=False,\n                            backend=self.__backend,\n                        )\n                    )\n                else:\n                    if screen_dims:\n                        self.__mss_capture_instance = {\n                            \"top\": self.__monitor_instance[\"top\"] + screen_dims[\"top\"],\n                            \"left\": self.__monitor_instance[\"left\"]\n                            + screen_dims[\"left\"],\n                            \"width\": screen_dims[\"width\"],\n                            \"height\": screen_dims[\"height\"],\n                            \"mon\": monitor,\n                        }\n                    else:\n                        self.__mss_capture_instance = (\n                            self.__monitor_instance  # otherwise create instance from monitor\n                        )\n                    # extract global frame from instance\n                    self.frame = np.asanyarray(\n                        self.__capture_object.grab(self.__mss_capture_instance)\n                    )\n            # convert to bgr frame if applicable\n            self.frame = (\n                self.frame[:, :, ::-1]\n                if self.__backend == \"dxcam\" or not (pysct is None)\n                else self.frame\n            )\n            # render colorspace if defined\n            if not (self.frame is None) and not (self.color_space is None):\n                self.frame = cv2.cvtColor(self.frame, self.color_space)\n        except Exception as e:\n            if isinstance(e, ScreenShotError):\n                # otherwise catch and log errors\n                self.__logging and logger.exception(\n                    self.__capture_object.get_error_details()\n                )\n                raise ValueError(\n                    \"[ScreenGear:ERROR] :: ScreenShotError caught, Wrong dimensions passed to python-mss, Kindly Refer Docs!\"\n                )\n            else:\n                raise SystemError(\n                    \"[ScreenGear:ERROR] :: Unable to grab any instance on this system, Are you running headless?\"\n                )\n        # thread initialization\n        self.__thread = None\n        # initialize termination flag\n        self.__terminate = Event()\n\n    def start(self):\n        \"\"\"\n        Launches the internal *Threaded Frames Extractor* daemon\n\n        **Returns:** A reference to the ScreenGear class object.\n        \"\"\"\n        self.__thread = Thread(target=self.__update, name=\"ScreenGear\", args=())\n        self.__thread.daemon = True\n        self.__thread.start()\n        if self.__backend == \"dxcam\":\n            self.__capture_object.start(\n                target_fps=self.__target_fps,\n                video_mode=True,\n            )\n            self.__logging and self.__target_fps and logger.debug(\n                \"Targeting FPS: {}\".format(self.__target_fps)\n            )\n        return self\n\n    def __update(self):\n        \"\"\"\n        A **Threaded Frames Extractor**, that keep iterating frames from `mss` API to a internal monitored deque,\n        until the thread is terminated, or frames runs out.\n        \"\"\"\n        # initialize frame variable\n        frame = None\n        # keep looping infinitely until the thread is terminated\n        while not self.__terminate.is_set():\n            try:\n                if self.__backend == \"dxcam\":\n                    # extract global frame from instance\n                    frame = self.__capture_object.get_latest_frame()\n                else:\n                    if self.__monitor_instance:\n                        frame = np.asanyarray(\n                            self.__capture_object.grab(self.__mss_capture_instance)\n                        )\n                    else:\n                        frame = np.asanyarray(\n                            self.__capture_object.grab(\n                                bbox=self.__mss_capture_instance,\n                                childprocess=False,\n                                backend=self.__backend,\n                            )\n                        )\n                # check if valid frame\n                assert not (\n                    frame is None or np.shape(frame) == ()\n                ), \"[ScreenGear:ERROR] :: Failed to retrieve valid frame!\"\n                # convert to bgr frame if applicable\n                frame = (\n                    frame[:, :, ::-1]\n                    if self.__backend == \"dxcam\" or not (pysct is None)\n                    else frame\n                )\n            except Exception as e:\n                if isinstance(e, ScreenShotError):\n                    raise RuntimeError(self.__capture_object.get_error_details())\n                else:\n                    logger.exception(str(e))\n                self.__terminate.set()\n                continue\n\n            if not (self.color_space is None):\n                # apply colorspace to frames\n                color_frame = None\n                try:\n                    color_frame = cv2.cvtColor(frame, self.color_space)\n                except Exception as e:\n                    # Catch if any error occurred\n                    color_frame = None\n                    self.color_space = None\n                    self.__logging and logger.exception(str(e))\n                    logger.warning(\"Assigned colorspace value is invalid. Discarding!\")\n                self.frame = color_frame if not (color_frame is None) else frame\n            else:\n                self.frame = frame\n\n        # indicate immediate termination\n        self.__terminate.set()\n\n        # finally release mss resources\n        if self.__monitor_instance:\n            self.__capture_object.close()\n        if self.__backend == \"dxcam\":\n            self.__capture_object.stop()\n            del self.__capture_object\n\n    def read(self):\n        \"\"\"\n        Extracts frames synchronously from monitored deque, while maintaining a fixed-length frame buffer in the memory,\n        and blocks the thread if the deque is full.\n\n        **Returns:** A n-dimensional numpy array.\n        \"\"\"\n        # return the frame\n        return self.frame\n\n    def stop(self):\n        \"\"\"\n        Safely terminates the thread, and release the resources.\n        \"\"\"\n        self.__logging and logger.debug(\"Terminating ScreenGear Processes.\")\n\n        # indicate that the thread should be terminate\n        self.__terminate.set()\n\n        # wait until stream resources are released (producer thread might be still grabbing frame)\n        not (self.__thread is None) and self.__thread.join()\n

"},{"location":"bonus/reference/screengear/#vidgear.gears.screengear.ScreenGear.__init__","title":"__init__(self, monitor=None, backend=None, colorspace=None, logging=False, **options) special","text":"

This constructor method initializes the object state and attributes of the ScreenGear class.

Parameters:

Name Type Description Default monitor int

enables mss backend and sets the index of the monitor screen.

None backend str

select suitable backend for extracting frames.

None colorspace str

selects the colorspace of the input stream.

None logging bool

enables/disables logging.

False options dict

provides the flexibility to easily alter backend library parameters. Such as, manually set the dimensions of capture screen area etc.

{} Source code in vidgear/gears/screengear.py
def __init__(\n    self, monitor=None, backend=None, colorspace=None, logging=False, **options\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the ScreenGear class.\n\n    Parameters:\n        monitor (int): enables `mss` backend and sets the index of the monitor screen.\n        backend (str): select suitable backend for extracting frames.\n        colorspace (str): selects the colorspace of the input stream.\n        logging (bool): enables/disables logging.\n        options (dict): provides the flexibility to easily alter backend library parameters. Such as, manually set the dimensions of capture screen area etc.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # create instances for the user-defined monitor\n    self.__monitor_instance = None\n    self.__backend = None\n\n    # validate monitor instance\n    assert (\n        monitor is None or monitor and isinstance(monitor, (int, tuple))\n    ), \"[ScreenGear:ERROR] :: Invalid `monitor` value detected!\"\n\n    # initialize backend\n    if backend and monitor is None:\n        self.__backend = backend.lower().strip()\n    else:\n        # enforce `dxcam` for Windows machines if undefined (or monitor is defined)\n        self.__backend = (\n            \"dxcam\" if platform.system() == \"Windows\" and dxcam else None\n        )\n\n    # initiate screen dimension handler\n    screen_dims = {}\n    # reformat proper mss dict and assign to screen dimension handler\n    screen_dims = {\n        k.strip(): v\n        for k, v in options.items()\n        if k.strip() in [\"top\", \"left\", \"width\", \"height\"]\n    }\n    # check whether user-defined dimensions are provided\n    if screen_dims and len(screen_dims) == 4:\n        key_order = (\n            (\"top\", \"left\", \"width\", \"height\")\n            if self.__backend != \"dxcam\"\n            else (\"left\", \"top\", \"width\", \"height\")\n        )\n        screen_dims = OrderedDict((k, screen_dims[k]) for k in key_order)\n        self.__logging and logger.debug(\n            \"Setting Capture-Area dimensions: {}\".format(json.dumps(screen_dims))\n        )\n    else:\n        screen_dims.clear()\n\n    # handle backends\n    if self.__backend == \"dxcam\":\n        # get target fps in case of DXcam\n        self.__target_fps = options.pop(\"dxcam_target_fps\", 0)\n        if self.__target_fps and isinstance(self.__target_fps, (int, float)):\n            # set values\n            self.__target_fps = int(self.__target_fps)\n            self.__logging and logger.debug(\n                \"Setting Target FPS: {}\".format(self.__target_fps)\n            )\n        else:\n            # defaults to 0fps\n            self.__target_fps = 0\n        # check if platform is windows\n        assert (\n            platform.system() == \"Windows\"\n        ), \"`dxcam` backend is only available for Windows Machines.\"\n        # verify monitor values if tuple\n        assert (\n            monitor is None\n            or isinstance(monitor, int)\n            or (\n                isinstance(monitor, tuple)\n                and len(monitor) == 2\n                and all(isinstance(x, int) for x in monitor)\n            )\n        ), \"For dxcam` backend, monitor` tuple value must be format `int` or `(int, int)` only.\"\n        # raise error(s) for critical Class imports\n        import_dependency_safe(\"dxcam\" if dxcam is None else \"\")\n        if monitor is None:\n            self.__capture_object = dxcam.create(\n                region=tuple(screen_dims.values()) if screen_dims else None\n            )\n        else:\n            self.__capture_object = (\n                dxcam.create(\n                    device_idx=monitor[0],\n                    output_idx=monitor[1],\n                    region=tuple(screen_dims.values()) if screen_dims else None,\n                )\n                if isinstance(monitor, tuple)\n                else dxcam.create(\n                    device_idx=monitor,\n                    region=tuple(screen_dims.values()) if screen_dims else None,\n                )\n            )\n    else:\n        if monitor is None:\n            # raise error(s) for critical Class imports\n            import_dependency_safe(\"pyscreenshot\" if pysct is None else \"\")\n            # reset backend if not provided\n            self.__backend = \"pil\" if self.__backend is None else self.__backend\n            # check if valid backend\n            assert (\n                self.__backend in pysct.backends()\n            ), \"Unsupported backend {} provided!\".format(backend)\n            # create capture object\n            self.__capture_object = pysct\n        else:\n            # monitor value must be integer\n            assert monitor and isinstance(\n                monitor, int\n            ), \"[ScreenGear:ERROR] :: Invalid `monitor` value must be integer with mss backend.\"\n            # raise error(s) for critical Class imports\n            import_dependency_safe(\n                \"from mss import mss\" if mss is None else \"\", pkg_name=\"mss\"\n            )\n            # create capture object\n            self.__capture_object = mss()\n            self.__backend and logger.warning(\n                \"Backends are disabled for Monitor Indexing(monitor>=0)!\"\n            )\n            self.__monitor_instance = self.__capture_object.monitors[monitor]\n\n    # log backend\n    self.__backend and self.__logging and logger.debug(\n        \"Setting Backend: {}\".format(self.__backend.upper())\n    )\n\n    # assigns special parameter to global variable and clear\n    # separately handle colorspace value to int conversion\n    if colorspace:\n        self.color_space = capPropId(colorspace.strip())\n        self.__logging and not (self.color_space is None) and logger.debug(\n            \"Enabling `{}` colorspace for this video stream!\".format(\n                colorspace.strip()\n            )\n        )\n    else:\n        self.color_space = None\n\n    # initialize mss capture instance\n    self.__mss_capture_instance = None\n    try:\n        if self.__backend == \"dxcam\":\n            # extract global frame from instance\n            self.frame = self.__capture_object.grab()\n        else:\n            if self.__monitor_instance is None:\n                if screen_dims:\n                    self.__mss_capture_instance = tuple(screen_dims.values())\n                # extract global frame from instance\n                self.frame = np.asanyarray(\n                    self.__capture_object.grab(\n                        bbox=self.__mss_capture_instance,\n                        childprocess=False,\n                        backend=self.__backend,\n                    )\n                )\n            else:\n                if screen_dims:\n                    self.__mss_capture_instance = {\n                        \"top\": self.__monitor_instance[\"top\"] + screen_dims[\"top\"],\n                        \"left\": self.__monitor_instance[\"left\"]\n                        + screen_dims[\"left\"],\n                        \"width\": screen_dims[\"width\"],\n                        \"height\": screen_dims[\"height\"],\n                        \"mon\": monitor,\n                    }\n                else:\n                    self.__mss_capture_instance = (\n                        self.__monitor_instance  # otherwise create instance from monitor\n                    )\n                # extract global frame from instance\n                self.frame = np.asanyarray(\n                    self.__capture_object.grab(self.__mss_capture_instance)\n                )\n        # convert to bgr frame if applicable\n        self.frame = (\n            self.frame[:, :, ::-1]\n            if self.__backend == \"dxcam\" or not (pysct is None)\n            else self.frame\n        )\n        # render colorspace if defined\n        if not (self.frame is None) and not (self.color_space is None):\n            self.frame = cv2.cvtColor(self.frame, self.color_space)\n    except Exception as e:\n        if isinstance(e, ScreenShotError):\n            # otherwise catch and log errors\n            self.__logging and logger.exception(\n                self.__capture_object.get_error_details()\n            )\n            raise ValueError(\n                \"[ScreenGear:ERROR] :: ScreenShotError caught, Wrong dimensions passed to python-mss, Kindly Refer Docs!\"\n            )\n        else:\n            raise SystemError(\n                \"[ScreenGear:ERROR] :: Unable to grab any instance on this system, Are you running headless?\"\n            )\n    # thread initialization\n    self.__thread = None\n    # initialize termination flag\n    self.__terminate = Event()\n
"},{"location":"bonus/reference/screengear/#vidgear.gears.screengear.ScreenGear.read","title":"read(self)","text":"

Extracts frames synchronously from monitored deque, while maintaining a fixed-length frame buffer in the memory, and blocks the thread if the deque is full.

Returns: A n-dimensional numpy array.

Source code in vidgear/gears/screengear.py
def read(self):\n    \"\"\"\n    Extracts frames synchronously from monitored deque, while maintaining a fixed-length frame buffer in the memory,\n    and blocks the thread if the deque is full.\n\n    **Returns:** A n-dimensional numpy array.\n    \"\"\"\n    # return the frame\n    return self.frame\n
"},{"location":"bonus/reference/screengear/#vidgear.gears.screengear.ScreenGear.start","title":"start(self)","text":"

Launches the internal Threaded Frames Extractor daemon

Returns: A reference to the ScreenGear class object.

Source code in vidgear/gears/screengear.py
def start(self):\n    \"\"\"\n    Launches the internal *Threaded Frames Extractor* daemon\n\n    **Returns:** A reference to the ScreenGear class object.\n    \"\"\"\n    self.__thread = Thread(target=self.__update, name=\"ScreenGear\", args=())\n    self.__thread.daemon = True\n    self.__thread.start()\n    if self.__backend == \"dxcam\":\n        self.__capture_object.start(\n            target_fps=self.__target_fps,\n            video_mode=True,\n        )\n        self.__logging and self.__target_fps and logger.debug(\n            \"Targeting FPS: {}\".format(self.__target_fps)\n        )\n    return self\n
"},{"location":"bonus/reference/screengear/#vidgear.gears.screengear.ScreenGear.stop","title":"stop(self)","text":"

Safely terminates the thread, and release the resources.

Source code in vidgear/gears/screengear.py
def stop(self):\n    \"\"\"\n    Safely terminates the thread, and release the resources.\n    \"\"\"\n    self.__logging and logger.debug(\"Terminating ScreenGear Processes.\")\n\n    # indicate that the thread should be terminate\n    self.__terminate.set()\n\n    # wait until stream resources are released (producer thread might be still grabbing frame)\n    not (self.__thread is None) and self.__thread.join()\n
"},{"location":"bonus/reference/stabilizer/","title":"API References","text":"

Stabilizer API usage examples can be found here \u27b6

Stabilizer API parameters are explained here \u27b6

This is an auxiliary class that enables Video Stabilization for vidgear with minimalistic latency, and at the expense of little to no additional computational requirements.

The basic idea behind it is to tracks and save the salient feature array for the given number of frames and then uses these anchor point to cancel out all perturbations relative to it for the incoming frames in the queue. This class relies heavily on Threaded Queue mode for error-free & ultra-fast frame handling.

Source code in vidgear/gears/stabilizer.py
class Stabilizer:\n    \"\"\"\n    This is an auxiliary class that enables Video Stabilization for vidgear with minimalistic latency, and at the expense\n    of little to no additional computational requirements.\n\n    The basic idea behind it is to tracks and save the salient feature array for the given number of frames and then uses\n    these anchor point to cancel out all perturbations relative to it for the incoming frames in the queue. This class relies\n    heavily on **Threaded Queue mode** for error-free & ultra-fast frame handling.\n    \"\"\"\n\n    def __init__(\n        self,\n        smoothing_radius=25,\n        border_type=\"black\",\n        border_size=0,\n        crop_n_zoom=False,\n        logging=False,\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the Stabilizer class.\n\n        Parameters:\n            smoothing_radius (int): alter averaging window size.\n            border_type (str): changes the extended border type.\n            border_size (int): enables and set the value for extended border size to reduce the black borders.\n            crop_n_zoom (bool): enables cropping and zooming of frames(to original size) to reduce the black borders.\n            logging (bool): enables/disables logging.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # initialize deques for handling input frames and its indexes\n        self.__frame_queue = deque(maxlen=smoothing_radius)\n        self.__frame_queue_indexes = deque(maxlen=smoothing_radius)\n\n        # define and create Adaptive histogram equalization (AHE) object for optimizations\n        self.__clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))\n\n        # initialize global vars\n        self.__smoothing_radius = smoothing_radius  # averaging window, handles the quality of stabilization at expense of latency and sudden panning\n        self.__smoothed_path = None  # handles the smoothed path with box filter\n        self.__path = None  # handles path i.e cumulative sum of previous_2_current transformations along a axis\n        self.__transforms = []  # handles previous_2_current transformations [dx,dy,da]\n        self.__frame_transforms_smoothed = None  # handles smoothed array of previous_2_current transformations w.r.t to frames\n        self.__previous_gray = None  # handles previous gray frame\n        self.__previous_keypoints = (\n            None  # handles previous detect_GFTTed keypoints w.r.t previous gray frame\n        )\n        self.__frame_height, self.frame_width = (\n            0,\n            0,\n        )  # handles width and height of input frames\n        self.__crop_n_zoom = 0  # handles cropping and zooms frames to reduce the black borders from stabilization being too noticeable.\n\n        # if check if crop_n_zoom defined\n        if crop_n_zoom and border_size:\n            self.__crop_n_zoom = border_size  # crops and zoom frame to original size\n            self.__border_size = 0  # zero out border size\n            self.__frame_size = None  # handles frame size for zooming\n            self.__logging and logger.debug(\n                \"Setting Cropping margin {} pixels\".format(border_size)\n            )\n        else:\n            # Add output borders to frame\n            self.__border_size = border_size\n            self.__logging and border_size and logger.debug(\n                \"Setting Border size {} pixels\".format(border_size)\n            )\n\n        # define valid border modes\n        border_modes = {\n            \"black\": cv2.BORDER_CONSTANT,\n            \"reflect\": cv2.BORDER_REFLECT,\n            \"reflect_101\": cv2.BORDER_REFLECT_101,\n            \"replicate\": cv2.BORDER_REPLICATE,\n            \"wrap\": cv2.BORDER_WRAP,\n        }\n        # choose valid border_mode from border_type\n        if border_type in [\"black\", \"reflect\", \"reflect_101\", \"replicate\", \"wrap\"]:\n            if not crop_n_zoom:\n                # initialize global border mode variable\n                self.__border_mode = border_modes[border_type]\n                self.__logging and border_type != \"black\" and logger.info(\n                    \"Setting Border type: {}\".format(border_type)\n                )\n            else:\n                # log and reset to default\n                self.__logging and border_type != \"black\" and logger.debug(\n                    \"Setting border type is disabled if cropping is enabled!\"\n                )\n                self.__border_mode = border_modes[\"black\"]\n        else:\n            # otherwise log if not\n            self.__logging and logger.debug(\"Invalid input border type!\")\n            self.__border_mode = border_modes[\"black\"]  # reset to default mode\n\n        # define OpenCV version\n        self.__cv2_version = check_CV_version()\n\n        # retrieve best interpolation\n        self.__interpolation = retrieve_best_interpolation(\n            [\"INTER_LINEAR_EXACT\", \"INTER_LINEAR\", \"INTER_AREA\"]\n        )\n\n        # define normalized box filter\n        self.__box_filter = np.ones(smoothing_radius) / smoothing_radius\n\n    def stabilize(self, frame):\n        \"\"\"\n        This method takes an unstabilized video frame, and returns a stabilized one.\n\n        Parameters:\n            frame (numpy.ndarray): inputs unstabilized video frames.\n        \"\"\"\n        # check if frame is None\n        if frame is None:\n            # return if it does\n            return\n\n        # save frame size for zooming\n        if self.__crop_n_zoom and self.__frame_size == None:\n            self.__frame_size = frame.shape[:2]\n\n        # initiate transformations capturing\n        if not self.__frame_queue:\n            # for first frame\n            previous_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)  # convert to gray\n            previous_gray = self.__clahe.apply(previous_gray)  # optimize gray frame\n            self.__previous_keypoints = cv2.goodFeaturesToTrack(\n                previous_gray,\n                maxCorners=200,\n                qualityLevel=0.05,\n                minDistance=30.0,\n                blockSize=3,\n                mask=None,\n                useHarrisDetector=False,\n                k=0.04,\n            )  # track features using GFTT\n            self.__frame_height, self.frame_width = frame.shape[\n                :2\n            ]  # save input frame height and width\n            self.__frame_queue.append(frame)  # save frame to deque\n            self.__frame_queue_indexes.append(0)  # save frame index to deque\n            self.__previous_gray = previous_gray[\n                :\n            ]  # save gray frame clone for further processing\n\n        elif self.__frame_queue_indexes[-1] < self.__smoothing_radius - 1:\n            # for rest of frames\n            self.__frame_queue.append(frame)  # save frame to deque\n            self.__frame_queue_indexes.append(\n                self.__frame_queue_indexes[-1] + 1\n            )  # save frame index\n            self.__generate_transformations()  # generate transformations\n        else:\n            # start applying transformations\n            self.__frame_queue.append(frame)  # save frame to deque\n            self.__frame_queue_indexes.append(\n                self.__frame_queue_indexes[-1] + 1\n            )  # save frame index\n            self.__generate_transformations()  # generate transformations\n            # calculate smooth path once transformation capturing is completed\n            for i in range(3):\n                # apply normalized box filter to the path\n                self.__smoothed_path[:, i] = self.__box_filter_convolve(\n                    (self.__path[:, i]), window_size=self.__smoothing_radius\n                )\n            # calculate deviation of path from smoothed path\n            deviation = self.__smoothed_path - self.__path\n            # save smoothed transformation\n            self.__frame_transforms_smoothed = self.frame_transform + deviation\n            # return transformation applied stabilized frame\n            return self.__apply_transformations()\n\n    def __generate_transformations(self):\n        \"\"\"\n        An internal method that generate previous-to-current transformations [dx,dy,da].\n        \"\"\"\n        frame_gray = cv2.cvtColor(\n            self.__frame_queue[-1], cv2.COLOR_BGR2GRAY\n        )  # retrieve current frame and convert to gray\n        frame_gray = self.__clahe.apply(frame_gray)  # optimize it\n\n        transformation = None\n        try:\n            # calculate optical flow using Lucas-Kanade differential method\n            curr_kps, status, error = cv2.calcOpticalFlowPyrLK(\n                self.__previous_gray, frame_gray, self.__previous_keypoints, None\n            )\n\n            # select only valid key-points\n            valid_curr_kps = curr_kps[status == 1]  # current\n            valid_previous_keypoints = self.__previous_keypoints[\n                status == 1\n            ]  # previous\n\n            # calculate optimal affine transformation between previous_2_current key-points\n            if self.__cv2_version == 3:\n                # backward compatibility with OpenCV3\n                transformation = cv2.estimateRigidTransform(\n                    valid_previous_keypoints, valid_curr_kps, False\n                )\n            else:\n                transformation = cv2.estimateAffinePartial2D(\n                    valid_previous_keypoints, valid_curr_kps\n                )[0]\n        except cv2.error as e:\n            # catch any OpenCV assertion errors and warn user\n            logger.warning(\"Video-Frame is too dark to generate any transformations!\")\n            transformation = None\n\n        # check if transformation is not None\n        if not (transformation is None):\n            # previous_2_current translation in x direction\n            dx = transformation[0, 2]\n            # previous_2_current translation in y direction\n            dy = transformation[1, 2]\n            # previous_2_current rotation in angle\n            da = np.arctan2(transformation[1, 0], transformation[0, 0])\n        else:\n            # otherwise zero it\n            dx = dy = da = 0\n\n        # save this transformation\n        self.__transforms.append([dx, dy, da])\n\n        # calculate path from cumulative transformations sum\n        self.frame_transform = np.array(self.__transforms, dtype=\"float32\")\n        self.__path = np.cumsum(self.frame_transform, axis=0)\n        # create smoothed path from a copy of path\n        self.__smoothed_path = np.copy(self.__path)\n\n        # re-calculate and save GFTT key-points for current gray frame\n        self.__previous_keypoints = cv2.goodFeaturesToTrack(\n            frame_gray,\n            maxCorners=200,\n            qualityLevel=0.05,\n            minDistance=30.0,\n            blockSize=3,\n            mask=None,\n            useHarrisDetector=False,\n            k=0.04,\n        )\n        # save this gray frame for further processing\n        self.__previous_gray = frame_gray[:]\n\n    def __box_filter_convolve(self, path, window_size):\n        \"\"\"\n        An internal method that applies *normalized linear box filter* to path w.r.t averaging window\n\n        Parameters:\n\n        * path (numpy.ndarray): a cumulative sum of transformations\n        * window_size (int): averaging window size\n        \"\"\"\n        # pad path to size of averaging window\n        path_padded = np.pad(path, (window_size, window_size), \"median\")\n        # apply linear box filter to path\n        path_smoothed = np.convolve(path_padded, self.__box_filter, mode=\"same\")\n        # crop the smoothed path to original path\n        path_smoothed = path_smoothed[window_size:-window_size]\n        # assert if cropping is completed\n        assert path.shape == path_smoothed.shape\n        # return smoothed path\n        return path_smoothed\n\n    def __apply_transformations(self):\n        \"\"\"\n        An internal method that applies affine transformation to the given frame\n        from previously calculated transformations\n        \"\"\"\n        # extract frame and its index from deque\n        queue_frame = self.__frame_queue.popleft()\n        queue_frame_index = self.__frame_queue_indexes.popleft()\n\n        # create border around extracted frame w.r.t border_size\n        bordered_frame = cv2.copyMakeBorder(\n            queue_frame,\n            top=self.__border_size,\n            bottom=self.__border_size,\n            left=self.__border_size,\n            right=self.__border_size,\n            borderType=self.__border_mode,\n            value=[0, 0, 0],\n        )\n        alpha_bordered_frame = cv2.cvtColor(\n            bordered_frame, cv2.COLOR_BGR2BGRA\n        )  # create alpha channel\n        # extract alpha channel\n        alpha_bordered_frame[:, :, 3] = 0\n        alpha_bordered_frame[\n            self.__border_size : self.__border_size + self.__frame_height,\n            self.__border_size : self.__border_size + self.frame_width,\n            3,\n        ] = 255\n\n        # extracting Transformations w.r.t frame index\n        dx = self.__frame_transforms_smoothed[queue_frame_index, 0]  # x-axis\n        dy = self.__frame_transforms_smoothed[queue_frame_index, 1]  # y-axis\n        da = self.__frame_transforms_smoothed[queue_frame_index, 2]  # angle\n\n        # building 2x3 transformation matrix from extracted transformations\n        queue_frame_transform = np.zeros((2, 3), np.float32)\n        queue_frame_transform[0, 0] = np.cos(da)\n        queue_frame_transform[0, 1] = -np.sin(da)\n        queue_frame_transform[1, 0] = np.sin(da)\n        queue_frame_transform[1, 1] = np.cos(da)\n        queue_frame_transform[0, 2] = dx\n        queue_frame_transform[1, 2] = dy\n\n        # Applying an affine transformation to the frame\n        frame_wrapped = cv2.warpAffine(\n            alpha_bordered_frame,\n            queue_frame_transform,\n            alpha_bordered_frame.shape[:2][::-1],\n            borderMode=self.__border_mode,\n        )\n\n        # drop alpha channel\n        frame_stabilized = frame_wrapped[:, :, :3]\n\n        # crop and zoom\n        if self.__crop_n_zoom:\n            # crop stabilized frame\n            frame_cropped = frame_stabilized[\n                self.__crop_n_zoom : -self.__crop_n_zoom,\n                self.__crop_n_zoom : -self.__crop_n_zoom,\n            ]\n            # zoom stabilized frame\n            frame_stabilized = cv2.resize(\n                frame_cropped,\n                self.__frame_size[::-1],\n                interpolation=self.__interpolation,\n            )\n\n        # finally return stabilized frame\n        return frame_stabilized\n\n    def clean(self):\n        \"\"\"\n        Cleans Stabilizer resources\n        \"\"\"\n        # check if deque present\n        if self.__frame_queue:\n            # clear frame deque\n            self.__frame_queue.clear()\n            # clear frame indexes deque\n            self.__frame_queue_indexes.clear()\n

"},{"location":"bonus/reference/stabilizer/#vidgear.gears.stabilizer.Stabilizer.__init__","title":"__init__(self, smoothing_radius=25, border_type='black', border_size=0, crop_n_zoom=False, logging=False) special","text":"

This constructor method initializes the object state and attributes of the Stabilizer class.

Parameters:

Name Type Description Default smoothing_radius int

alter averaging window size.

25 border_type str

changes the extended border type.

'black' border_size int

enables and set the value for extended border size to reduce the black borders.

0 crop_n_zoom bool

enables cropping and zooming of frames(to original size) to reduce the black borders.

False logging bool

enables/disables logging.

False Source code in vidgear/gears/stabilizer.py
def __init__(\n    self,\n    smoothing_radius=25,\n    border_type=\"black\",\n    border_size=0,\n    crop_n_zoom=False,\n    logging=False,\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the Stabilizer class.\n\n    Parameters:\n        smoothing_radius (int): alter averaging window size.\n        border_type (str): changes the extended border type.\n        border_size (int): enables and set the value for extended border size to reduce the black borders.\n        crop_n_zoom (bool): enables cropping and zooming of frames(to original size) to reduce the black borders.\n        logging (bool): enables/disables logging.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # initialize deques for handling input frames and its indexes\n    self.__frame_queue = deque(maxlen=smoothing_radius)\n    self.__frame_queue_indexes = deque(maxlen=smoothing_radius)\n\n    # define and create Adaptive histogram equalization (AHE) object for optimizations\n    self.__clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))\n\n    # initialize global vars\n    self.__smoothing_radius = smoothing_radius  # averaging window, handles the quality of stabilization at expense of latency and sudden panning\n    self.__smoothed_path = None  # handles the smoothed path with box filter\n    self.__path = None  # handles path i.e cumulative sum of previous_2_current transformations along a axis\n    self.__transforms = []  # handles previous_2_current transformations [dx,dy,da]\n    self.__frame_transforms_smoothed = None  # handles smoothed array of previous_2_current transformations w.r.t to frames\n    self.__previous_gray = None  # handles previous gray frame\n    self.__previous_keypoints = (\n        None  # handles previous detect_GFTTed keypoints w.r.t previous gray frame\n    )\n    self.__frame_height, self.frame_width = (\n        0,\n        0,\n    )  # handles width and height of input frames\n    self.__crop_n_zoom = 0  # handles cropping and zooms frames to reduce the black borders from stabilization being too noticeable.\n\n    # if check if crop_n_zoom defined\n    if crop_n_zoom and border_size:\n        self.__crop_n_zoom = border_size  # crops and zoom frame to original size\n        self.__border_size = 0  # zero out border size\n        self.__frame_size = None  # handles frame size for zooming\n        self.__logging and logger.debug(\n            \"Setting Cropping margin {} pixels\".format(border_size)\n        )\n    else:\n        # Add output borders to frame\n        self.__border_size = border_size\n        self.__logging and border_size and logger.debug(\n            \"Setting Border size {} pixels\".format(border_size)\n        )\n\n    # define valid border modes\n    border_modes = {\n        \"black\": cv2.BORDER_CONSTANT,\n        \"reflect\": cv2.BORDER_REFLECT,\n        \"reflect_101\": cv2.BORDER_REFLECT_101,\n        \"replicate\": cv2.BORDER_REPLICATE,\n        \"wrap\": cv2.BORDER_WRAP,\n    }\n    # choose valid border_mode from border_type\n    if border_type in [\"black\", \"reflect\", \"reflect_101\", \"replicate\", \"wrap\"]:\n        if not crop_n_zoom:\n            # initialize global border mode variable\n            self.__border_mode = border_modes[border_type]\n            self.__logging and border_type != \"black\" and logger.info(\n                \"Setting Border type: {}\".format(border_type)\n            )\n        else:\n            # log and reset to default\n            self.__logging and border_type != \"black\" and logger.debug(\n                \"Setting border type is disabled if cropping is enabled!\"\n            )\n            self.__border_mode = border_modes[\"black\"]\n    else:\n        # otherwise log if not\n        self.__logging and logger.debug(\"Invalid input border type!\")\n        self.__border_mode = border_modes[\"black\"]  # reset to default mode\n\n    # define OpenCV version\n    self.__cv2_version = check_CV_version()\n\n    # retrieve best interpolation\n    self.__interpolation = retrieve_best_interpolation(\n        [\"INTER_LINEAR_EXACT\", \"INTER_LINEAR\", \"INTER_AREA\"]\n    )\n\n    # define normalized box filter\n    self.__box_filter = np.ones(smoothing_radius) / smoothing_radius\n
"},{"location":"bonus/reference/stabilizer/#vidgear.gears.stabilizer.Stabilizer.clean","title":"clean(self)","text":"

Cleans Stabilizer resources

Source code in vidgear/gears/stabilizer.py
def clean(self):\n    \"\"\"\n    Cleans Stabilizer resources\n    \"\"\"\n    # check if deque present\n    if self.__frame_queue:\n        # clear frame deque\n        self.__frame_queue.clear()\n        # clear frame indexes deque\n        self.__frame_queue_indexes.clear()\n
"},{"location":"bonus/reference/stabilizer/#vidgear.gears.stabilizer.Stabilizer.stabilize","title":"stabilize(self, frame)","text":"

This method takes an unstabilized video frame, and returns a stabilized one.

Parameters:

Name Type Description Default frame numpy.ndarray

inputs unstabilized video frames.

required Source code in vidgear/gears/stabilizer.py
def stabilize(self, frame):\n    \"\"\"\n    This method takes an unstabilized video frame, and returns a stabilized one.\n\n    Parameters:\n        frame (numpy.ndarray): inputs unstabilized video frames.\n    \"\"\"\n    # check if frame is None\n    if frame is None:\n        # return if it does\n        return\n\n    # save frame size for zooming\n    if self.__crop_n_zoom and self.__frame_size == None:\n        self.__frame_size = frame.shape[:2]\n\n    # initiate transformations capturing\n    if not self.__frame_queue:\n        # for first frame\n        previous_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)  # convert to gray\n        previous_gray = self.__clahe.apply(previous_gray)  # optimize gray frame\n        self.__previous_keypoints = cv2.goodFeaturesToTrack(\n            previous_gray,\n            maxCorners=200,\n            qualityLevel=0.05,\n            minDistance=30.0,\n            blockSize=3,\n            mask=None,\n            useHarrisDetector=False,\n            k=0.04,\n        )  # track features using GFTT\n        self.__frame_height, self.frame_width = frame.shape[\n            :2\n        ]  # save input frame height and width\n        self.__frame_queue.append(frame)  # save frame to deque\n        self.__frame_queue_indexes.append(0)  # save frame index to deque\n        self.__previous_gray = previous_gray[\n            :\n        ]  # save gray frame clone for further processing\n\n    elif self.__frame_queue_indexes[-1] < self.__smoothing_radius - 1:\n        # for rest of frames\n        self.__frame_queue.append(frame)  # save frame to deque\n        self.__frame_queue_indexes.append(\n            self.__frame_queue_indexes[-1] + 1\n        )  # save frame index\n        self.__generate_transformations()  # generate transformations\n    else:\n        # start applying transformations\n        self.__frame_queue.append(frame)  # save frame to deque\n        self.__frame_queue_indexes.append(\n            self.__frame_queue_indexes[-1] + 1\n        )  # save frame index\n        self.__generate_transformations()  # generate transformations\n        # calculate smooth path once transformation capturing is completed\n        for i in range(3):\n            # apply normalized box filter to the path\n            self.__smoothed_path[:, i] = self.__box_filter_convolve(\n                (self.__path[:, i]), window_size=self.__smoothing_radius\n            )\n        # calculate deviation of path from smoothed path\n        deviation = self.__smoothed_path - self.__path\n        # save smoothed transformation\n        self.__frame_transforms_smoothed = self.frame_transform + deviation\n        # return transformation applied stabilized frame\n        return self.__apply_transformations()\n
"},{"location":"bonus/reference/streamgear/","title":"StreamGear API References","text":"

StreamGear API usage examples for: Single-Source Mode \u27b6 and Real-time Frames Mode \u27b6

StreamGear API parameters are explained here \u27b6

StreamGear automates transcoding workflow for generating Ultra-Low Latency, High-Quality, Dynamic & Adaptive Streaming Formats (such as MPEG-DASH and HLS) in just few lines of python code. StreamGear provides a standalone, highly extensible, and flexible wrapper around FFmpeg multimedia framework for generating chunked-encoded media segments of the content.

SteamGear easily transcodes source videos/audio files & real-time video-frames and breaks them into a sequence of multiple smaller chunks/segments of suitable length. These segments make it possible to stream videos at different quality levels (different bitrate or spatial resolutions) and can be switched in the middle of a video from one quality level to another - if bandwidth permits - on a per-segment basis. A user can serve these segments on a web server that makes it easier to download them through HTTP standard-compliant GET requests.

SteamGear also creates a Manifest/Playlist file (such as MPD in-case of DASH and M3U8 in-case of HLS) besides segments that describe these segment information (timing, URL, media characteristics like video resolution and bit rates) and is provided to the client before the streaming session.

SteamGear currently supports MPEG-DASH (Dynamic Adaptive Streaming over HTTP, ISO/IEC 23009-1) and Apple HLS (HTTP live streaming).

Source code in vidgear/gears/streamgear.py
class StreamGear:\n    \"\"\"\n    StreamGear automates transcoding workflow for generating Ultra-Low Latency, High-Quality, Dynamic & Adaptive Streaming Formats (such as MPEG-DASH and HLS) in just few lines of python code.\n    StreamGear provides a standalone, highly extensible, and flexible wrapper around FFmpeg multimedia framework for generating chunked-encoded media segments of the content.\n\n    SteamGear easily transcodes source videos/audio files & real-time video-frames and breaks them into a sequence of multiple smaller chunks/segments of suitable length. These segments make it\n    possible to stream videos at different quality levels _(different bitrate or spatial resolutions)_ and can be switched in the middle of a video from one quality level to another - if bandwidth\n    permits - on a per-segment basis. A user can serve these segments on a web server that makes it easier to download them through HTTP standard-compliant GET requests.\n\n    SteamGear also creates a Manifest/Playlist file (such as MPD in-case of DASH and M3U8 in-case of HLS) besides segments that describe these segment information\n    (timing, URL, media characteristics like video resolution and bit rates) and is provided to the client before the streaming session.\n\n    SteamGear currently supports MPEG-DASH (Dynamic Adaptive Streaming over HTTP, ISO/IEC 23009-1) and Apple HLS (HTTP live streaming).\n    \"\"\"\n\n    def __init__(\n        self, output=\"\", format=\"dash\", custom_ffmpeg=\"\", logging=False, **stream_params\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the StreamGear class.\n\n        Parameters:\n            output (str): sets the valid filename/path for generating the StreamGear assets.\n            format (str): select the adaptive HTTP streaming format(DASH and HLS).\n            custom_ffmpeg (str): assigns the location of custom path/directory for custom FFmpeg executables.\n            logging (bool): enables/disables logging.\n            stream_params (dict): provides the flexibility to control supported internal parameters and FFmpeg properties.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # checks if machine in-use is running windows os or not\n        self.__os_windows = True if os.name == \"nt\" else False\n\n        # initialize various class variables\n        # handles user-defined parameters\n        self.__params = {}\n        # handle input video/frame resolution and channels\n        self.__inputheight = None\n        self.__inputwidth = None\n        self.__inputchannels = None\n        self.__sourceframerate = None\n        # handle process to be frames written\n        self.__process = None\n        # handle valid FFmpeg assets location\n        self.__ffmpeg = \"\"\n        # handle one time process for valid process initialization\n        self.__initiate_stream = True\n\n        # cleans and reformat user-defined parameters\n        self.__params = {\n            str(k).strip(): (v.strip() if isinstance(v, str) else v)\n            for k, v in stream_params.items()\n        }\n\n        # handle where to save the downloaded FFmpeg Static assets on Windows(if specified)\n        __ffmpeg_download_path = self.__params.pop(\"-ffmpeg_download_path\", \"\")\n        if not isinstance(__ffmpeg_download_path, (str)):\n            # reset improper values\n            __ffmpeg_download_path = \"\"\n\n        # validate the FFmpeg assets and return location (also downloads static assets on windows)\n        self.__ffmpeg = get_valid_ffmpeg_path(\n            str(custom_ffmpeg),\n            self.__os_windows,\n            ffmpeg_download_path=__ffmpeg_download_path,\n            logging=self.__logging,\n        )\n\n        # check if valid FFmpeg path returned\n        if self.__ffmpeg:\n            self.__logging and logger.debug(\n                \"Found valid FFmpeg executables: `{}`.\".format(self.__ffmpeg)\n            )\n        else:\n            # else raise error\n            raise RuntimeError(\n                \"[StreamGear:ERROR] :: Failed to find FFmpeg assets on this system. Kindly compile/install FFmpeg or provide a valid custom FFmpeg binary path!\"\n            )\n\n        # handle streaming format\n        supported_formats = [\"dash\", \"hls\"]  # TODO will be extended in future\n        if format and isinstance(format, str):\n            _format = format.strip().lower()\n            if _format in supported_formats:\n                self.__format = _format\n                logger.info(\n                    \"StreamGear will generate asset files for {} streaming format.\".format(\n                        self.__format.upper()\n                    )\n                )\n            elif difflib.get_close_matches(_format, supported_formats):\n                raise ValueError(\n                    \"[StreamGear:ERROR] :: Incorrect `format` parameter value! Did you mean `{}`?\".format(\n                        difflib.get_close_matches(_format, supported_formats)[0]\n                    )\n                )\n            else:\n                raise ValueError(\n                    \"[StreamGear:ERROR] :: The `format` parameter value `{}` not valid/supported!\".format(\n                        format\n                    )\n                )\n        else:\n            raise ValueError(\n                \"[StreamGear:ERROR] :: The `format` parameter value is Missing or Invalid!\"\n            )\n\n        # handle Audio-Input\n        audio = self.__params.pop(\"-audio\", False)\n        if audio and isinstance(audio, str):\n            if os.path.isfile(audio):\n                self.__audio = os.path.abspath(audio)\n            elif is_valid_url(self.__ffmpeg, url=audio, logging=self.__logging):\n                self.__audio = audio\n            else:\n                self.__audio = False\n        elif audio and isinstance(audio, list):\n            self.__audio = audio\n        else:\n            self.__audio = False\n        # log external audio source\n        self.__audio and self.__logging and logger.debug(\n            \"External audio source `{}` detected.\".format(self.__audio)\n        )\n\n        # handle Video-Source input\n        source = self.__params.pop(\"-video_source\", False)\n        # Check if input is valid string\n        if source and isinstance(source, str) and len(source) > 1:\n            # Differentiate input\n            if os.path.isfile(source):\n                self.__video_source = os.path.abspath(source)\n            elif is_valid_url(self.__ffmpeg, url=source, logging=self.__logging):\n                self.__video_source = source\n            else:\n                # discard the value otherwise\n                self.__video_source = False\n\n            # Validate input\n            if self.__video_source:\n                validation_results = validate_video(\n                    self.__ffmpeg, video_path=self.__video_source\n                )\n                assert not (\n                    validation_results is None\n                ), \"[StreamGear:ERROR] :: Given `{}` video_source is Invalid, Check Again!\".format(\n                    self.__video_source\n                )\n                self.__aspect_source = validation_results[\"resolution\"]\n                self.__fps_source = validation_results[\"framerate\"]\n                # log it\n                self.__logging and logger.debug(\n                    \"Given video_source is valid and has {}x{} resolution, and a framerate of {} fps.\".format(\n                        self.__aspect_source[0],\n                        self.__aspect_source[1],\n                        self.__fps_source,\n                    )\n                )\n            else:\n                # log warning\n                logger.warning(\"Discarded invalid `-video_source` value provided.\")\n        else:\n            if source:\n                # log warning if source provided\n                logger.warning(\"Invalid `-video_source` value provided.\")\n            else:\n                # log normally\n                logger.info(\"No `-video_source` value provided.\")\n            # discard the value otherwise\n            self.__video_source = False\n\n        # handle user-defined framerate\n        self.__inputframerate = self.__params.pop(\"-input_framerate\", 0.0)\n        if isinstance(self.__inputframerate, (float, int)):\n            # must be float\n            self.__inputframerate = float(self.__inputframerate)\n        else:\n            # reset improper values\n            self.__inputframerate = 0.0\n\n        # handle old assets\n        clear_assets = self.__params.pop(\"-clear_prev_assets\", False)\n        if isinstance(clear_assets, bool):\n            self.__clear_assets = clear_assets\n            # log if clearing assets is enabled\n            clear_assets and logger.info(\n                \"The `-clear_prev_assets` parameter is enabled successfully. All previous StreamGear API assets for `{}` format will be removed for this run.\".format(\n                    self.__format.upper()\n                )\n            )\n        else:\n            # reset improper values\n            self.__clear_assets = False\n\n        # handle whether to livestream?\n        livestreaming = self.__params.pop(\"-livestream\", False)\n        if isinstance(livestreaming, bool) and livestreaming:\n            # NOTE:  `livestream` is only available with real-time mode.\n            self.__livestreaming = livestreaming if not (self.__video_source) else False\n            if self.__video_source:\n                logger.error(\n                    \"Live-Streaming is only available with Real-time Mode. Refer docs for more information.\"\n                )\n            else:\n                # log if live streaming is enabled\n                livestreaming and logger.info(\n                    \"Live-Streaming is successfully enabled for this run.\"\n                )\n        else:\n            # reset improper values\n            self.__livestreaming = False\n\n        # handle the special-case of forced-termination\n        enable_force_termination = self.__params.pop(\"-enable_force_termination\", False)\n        # check if value is valid\n        if isinstance(enable_force_termination, bool):\n            self.__forced_termination = enable_force_termination\n            # log if forced termination is enabled\n            self.__forced_termination and logger.warning(\n                \"Forced termination is enabled for this run. This may result in corrupted output in certain scenarios!\"\n            )\n        else:\n            # handle improper values\n            self.__forced_termination = False\n\n        # handle streaming format\n        supported_formats = [\"dash\", \"hls\"]  # TODO will be extended in future\n        if format and isinstance(format, str):\n            _format = format.strip().lower()\n            if _format in supported_formats:\n                self.__format = _format\n                logger.info(\n                    \"StreamGear will generate asset files for {} streaming format.\".format(\n                        self.__format.upper()\n                    )\n                )\n            elif difflib.get_close_matches(_format, supported_formats):\n                raise ValueError(\n                    \"[StreamGear:ERROR] :: Incorrect `format` parameter value! Did you mean `{}`?\".format(\n                        difflib.get_close_matches(_format, supported_formats)[0]\n                    )\n                )\n            else:\n                raise ValueError(\n                    \"[StreamGear:ERROR] :: The `format` parameter value `{}` not valid/supported!\".format(\n                        format\n                    )\n                )\n        else:\n            raise ValueError(\n                \"[StreamGear:ERROR] :: The `format` parameter value is Missing or Invalid!\"\n            )\n\n        # handles output asset filenames\n        if output:\n            # validate this class has the access rights to specified directory or not\n            abs_path = os.path.abspath(output)\n            # check if given output is a valid system path\n            if check_WriteAccess(\n                os.path.dirname(abs_path),\n                is_windows=self.__os_windows,\n                logging=self.__logging,\n            ):\n                # get all assets extensions\n                valid_extension = \"mpd\" if self.__format == \"dash\" else \"m3u8\"\n                assets_exts = [\n                    (\"chunk-stream\", \".m4s\"),  # filename prefix, extension\n                    (\"chunk-stream\", \".ts\"),  # filename prefix, extension\n                    \".{}\".format(valid_extension),\n                ]\n                # add source file extension too\n                self.__video_source and assets_exts.append(\n                    (\n                        \"chunk-stream\",\n                        os.path.splitext(self.__video_source)[1],\n                    )  # filename prefix, extension\n                )\n                # handle output\n                # check if path is a directory\n                if os.path.isdir(abs_path):\n                    # clear previous assets if specified\n                    self.__clear_assets and delete_ext_safe(\n                        abs_path, assets_exts, logging=self.__logging\n                    )\n                    # auto-assign valid name and adds it to path\n                    abs_path = os.path.join(\n                        abs_path,\n                        \"{}-{}.{}\".format(\n                            self.__format,\n                            time.strftime(\"%Y%m%d-%H%M%S\"),\n                            valid_extension,\n                        ),\n                    )\n                # or check if path is a file\n                elif os.path.isfile(abs_path) and self.__clear_assets:\n                    # clear previous assets if specified\n                    delete_ext_safe(\n                        os.path.dirname(abs_path),\n                        assets_exts,\n                        logging=self.__logging,\n                    )\n                # check if path has valid file extension\n                assert abs_path.endswith(\n                    valid_extension\n                ), \"Given `{}` path has invalid file-extension w.r.t selected format: `{}`!\".format(\n                    output, self.__format.upper()\n                )\n                self.__logging and logger.debug(\n                    \"Output Path:`{}` is successfully configured for generating streaming assets.\".format(\n                        abs_path\n                    )\n                )\n                # workaround patch for Windows only,\n                # others platforms will not be affected\n                self.__out_file = abs_path.replace(\"\\\\\", \"/\")\n            # check if given output is a valid URL\n            elif is_valid_url(self.__ffmpeg, url=output, logging=self.__logging):\n                self.__logging and logger.debug(\n                    \"URL:`{}` is valid and successfully configured for generating streaming assets.\".format(\n                        output\n                    )\n                )\n                self.__out_file = output\n            # raise ValueError otherwise\n            else:\n                raise ValueError(\n                    \"[StreamGear:ERROR] :: The output parameter value:`{}` is not valid/supported!\".format(\n                        output\n                    )\n                )\n        else:\n            # raise ValueError otherwise\n            raise ValueError(\n                \"[StreamGear:ERROR] :: Kindly provide a valid `output` parameter value. Refer Docs for more information.\"\n            )\n\n        # log Mode of operation\n        self.__video_source and logger.info(\n            \"StreamGear has been successfully configured for {} Mode.\".format(\n                \"Single-Source\" if self.__video_source else \"Real-time Frames\"\n            )\n        )\n\n    @deprecated(\n        parameter=\"rgb_mode\",\n        message=\"The `rgb_mode` parameter is deprecated and will be removed in a future version. Only BGR format frames will be supported going forward.\",\n    )\n    def stream(self, frame, rgb_mode=False):\n        \"\"\"\n        Pipes `ndarray` frames to FFmpeg Pipeline for transcoding them into chunked-encoded media segments of\n        streaming formats such as MPEG-DASH and HLS.\n\n        !!! warning \"[DEPRECATION NOTICE]: The `rgb_mode` parameter is deprecated and will be removed in a future version.\"\n\n        Parameters:\n            frame (ndarray): a valid numpy frame\n            rgb_mode (boolean): enable this flag to activate RGB mode _(i.e. specifies that incoming frames are of RGB format instead of default BGR)_.\n        \"\"\"\n        # check if function is called in correct context\n        if self.__video_source:\n            raise RuntimeError(\n                \"[StreamGear:ERROR] :: The `stream()` method cannot be used when streaming from a `-video_source` input file. Kindly refer vidgear docs!\"\n            )\n        # None-Type frames will be skipped\n        if frame is None:\n            return\n        # extract height, width and number of channels of frame\n        height, width = frame.shape[:2]\n        channels = frame.shape[-1] if frame.ndim == 3 else 1\n        # assign values to class variables on first run\n        if self.__initiate_stream:\n            self.__inputheight = height\n            self.__inputwidth = width\n            self.__inputchannels = channels\n            self.__sourceframerate = (\n                25.0 if not (self.__inputframerate) else self.__inputframerate\n            )\n            self.__logging and logger.debug(\n                \"InputFrame => Height:{} Width:{} Channels:{}\".format(\n                    self.__inputheight, self.__inputwidth, self.__inputchannels\n                )\n            )\n        # validate size of frame\n        if height != self.__inputheight or width != self.__inputwidth:\n            raise ValueError(\"[StreamGear:ERROR] :: All frames must have same size!\")\n        # validate number of channels\n        if channels != self.__inputchannels:\n            raise ValueError(\n                \"[StreamGear:ERROR] :: All frames must have same number of channels!\"\n            )\n        # initiate FFmpeg process on first run\n        if self.__initiate_stream:\n            # launch pre-processing\n            self.__PreProcess(channels=channels, rgb=rgb_mode)\n            # Check status of the process\n            assert self.__process is not None\n\n        # write the frame to pipeline\n        try:\n            self.__process.stdin.write(frame.tobytes())\n        except (OSError, IOError):\n            # log something is wrong!\n            logger.error(\n                \"BrokenPipeError caught, Wrong values passed to FFmpeg Pipe, Kindly Refer Docs!\"\n            )\n            raise ValueError  # for testing purpose only\n\n    def transcode_source(self):\n        \"\"\"\n        Transcodes an entire video file _(with or without audio)_ into chunked-encoded media segments of\n        streaming formats such as MPEG-DASH and HLS.\n        \"\"\"\n        # check if function is called in correct context\n        if not (self.__video_source):\n            raise RuntimeError(\n                \"[StreamGear:ERROR] :: The `transcode_source()` method cannot be used without a valid `-video_source` input. Kindly refer vidgear docs!\"\n            )\n        # assign height, width and framerate\n        self.__inputheight = int(self.__aspect_source[1])\n        self.__inputwidth = int(self.__aspect_source[0])\n        self.__sourceframerate = float(self.__fps_source)\n        # launch pre-processing\n        self.__PreProcess()\n\n    def __PreProcess(self, channels=0, rgb=False):\n        \"\"\"\n        Internal method that pre-processes default FFmpeg parameters before starting pipelining.\n\n        Parameters:\n            channels (int): Number of channels\n            rgb (boolean): activates RGB mode _(if enabled)_.\n        \"\"\"\n        # turn off initiate flag\n        self.__initiate_stream = False\n        # initialize I/O parameters\n        input_parameters = OrderedDict()\n        output_parameters = OrderedDict()\n        # pre-assign default codec parameters (if not assigned by user).\n        default_codec = \"libx264rgb\" if rgb else \"libx264\"\n        output_vcodec = self.__params.pop(\"-vcodec\", default_codec)\n        # enforce default encoder if stream copy specified\n        # in Real-time Frames Mode\n        output_parameters[\"-vcodec\"] = (\n            default_codec\n            if output_vcodec == \"copy\"\n            and (not (self.__video_source) or \"-streams\" in self.__params)\n            else output_vcodec\n        )\n        # enforce compatibility with stream copy\n        if output_parameters[\"-vcodec\"] != \"copy\":\n            # NOTE: these parameters only supported when stream copy not defined\n            output_parameters[\"-vf\"] = self.__params.pop(\"-vf\", \"format=yuv420p\")\n            # Non-essential `-aspect` parameter is removed from the default pipeline.\n        else:\n            # log warnings if stream copy specified in Real-time Frames Mode\n            not (self.__video_source) and logger.error(\n                \"Stream copy is not compatible with Real-time Frames Mode as it require re-encoding of incoming frames. Discarding the `-vcodec copy` parameter!\"\n            )\n            (\"-streams\" in self.__params) and logger.error(\n                \"Stream copying is incompatible with Custom Streams as it require re-encoding for each additional stream. Discarding the `-vcodec copy` parameter!\"\n            )\n            # log warnings for these parameters\n            self.__params.pop(\"-vf\", False) and logger.warning(\n                \"Filtering and stream copy cannot be used together. Discarding specified `-vf` parameter!\"\n            )\n            self.__params.pop(\"-aspect\", False) and logger.warning(\n                \"Overriding aspect ratio with stream copy may produce invalid files. Discarding specified `-aspect` parameter!\"\n            )\n\n        # enable optimizations w.r.t selected codec\n        ### OPTIMIZATION-1 ###\n        if output_parameters[\"-vcodec\"] in [\n            \"libx264\",\n            \"libx264rgb\",\n            \"libx265\",\n            \"libvpx-vp9\",\n        ]:\n            output_parameters[\"-crf\"] = self.__params.pop(\"-crf\", \"20\")\n        ### OPTIMIZATION-2 ###\n        if output_parameters[\"-vcodec\"] == \"libx264\":\n            if not (self.__video_source):\n                output_parameters[\"-profile:v\"] = self.__params.pop(\n                    \"-profile:v\", \"high\"\n                )\n        ### OPTIMIZATION-3 ###\n        if output_parameters[\"-vcodec\"] in [\"libx264\", \"libx264rgb\"]:\n            output_parameters[\"-tune\"] = self.__params.pop(\"-tune\", \"zerolatency\")\n            output_parameters[\"-preset\"] = self.__params.pop(\"-preset\", \"veryfast\")\n        ### OPTIMIZATION-4 ###\n        if output_parameters[\"-vcodec\"] == \"libx265\":\n            output_parameters[\"-x265-params\"] = self.__params.pop(\n                \"-x265-params\", \"lossless=1\"\n            )\n\n        # enable audio (if present)\n        if self.__audio:\n            # validate audio source\n            bitrate = validate_audio(self.__ffmpeg, source=self.__audio)\n            if bitrate:\n                logger.info(\n                    \"Detected External Audio Source is valid, and will be used for generating streams.\"\n                )\n                # assign audio source\n                output_parameters[\n                    \"{}\".format(\n                        \"-core_asource\" if isinstance(self.__audio, list) else \"-i\"\n                    )\n                ] = self.__audio\n                # assign audio codec\n                output_parameters[\"-acodec\"] = self.__params.pop(\"-acodec\", \"aac\")\n                output_parameters[\"a_bitrate\"] = bitrate  # temporary handler\n                output_parameters[\"-core_audio\"] = (\n                    [\"-map\", \"1:a:0\"] if self.__format == \"dash\" else []\n                )\n            else:\n                # discard invalid audio\n                logger.warning(\n                    \"Audio source `{}` is not valid, Skipped!\".format(self.__audio)\n                )\n                self.__audio = False\n        # validate input video's audio source if available\n        elif self.__video_source:\n            bitrate = validate_audio(self.__ffmpeg, source=self.__video_source)\n            if bitrate:\n                logger.info(\"Input video's audio source will be used for this run.\")\n                # assign audio codec\n                output_parameters[\"-acodec\"] = self.__params.pop(\n                    \"-acodec\",\n                    \"aac\" if (\"-streams\" in self.__params) else \"copy\",\n                )\n                if output_parameters[\"-acodec\"] != \"copy\":\n                    output_parameters[\"a_bitrate\"] = bitrate  # temporary handler\n            else:\n                logger.info(\n                    \"No valid audio source available in the input video. Disabling audio while generating streams.\"\n                )\n        else:\n            logger.info(\n                \"No valid audio source provided. Disabling audio while generating streams.\"\n            )\n        # enable audio optimizations based on audio codec\n        if \"-acodec\" in output_parameters and output_parameters[\"-acodec\"] == \"aac\":\n            output_parameters[\"-movflags\"] = \"+faststart\"\n\n        # set input framerate\n        if self.__sourceframerate > 0.0 and not (self.__video_source):\n            # set input framerate\n            self.__logging and logger.debug(\n                \"Setting Input framerate: {}\".format(self.__sourceframerate)\n            )\n            input_parameters[\"-framerate\"] = str(self.__sourceframerate)\n\n        # handle input resolution and pixel format\n        if not (self.__video_source):\n            dimensions = \"{}x{}\".format(self.__inputwidth, self.__inputheight)\n            input_parameters[\"-video_size\"] = str(dimensions)\n            # handles pix_fmt based on channels(HACK)\n            if channels == 1:\n                input_parameters[\"-pix_fmt\"] = \"gray\"\n            elif channels == 2:\n                input_parameters[\"-pix_fmt\"] = \"ya8\"\n            elif channels == 3:\n                input_parameters[\"-pix_fmt\"] = \"rgb24\" if rgb else \"bgr24\"\n            elif channels == 4:\n                input_parameters[\"-pix_fmt\"] = \"rgba\" if rgb else \"bgra\"\n            else:\n                raise ValueError(\n                    \"[StreamGear:ERROR] :: Frames with channels outside range 1-to-4 are not supported!\"\n                )\n        # process assigned format parameters\n        process_params = self.__handle_streams(\n            input_params=input_parameters, output_params=output_parameters\n        )\n        # check if processing completed successfully\n        assert not (\n            process_params is None\n        ), \"[StreamGear:ERROR] :: `{}` stream cannot be initiated properly!\".format(\n            self.__format.upper()\n        )\n        # Finally start FFmpeg pipeline and process everything\n        self.__Build_n_Execute(process_params[0], process_params[1])\n\n    def __handle_streams(self, input_params, output_params):\n        \"\"\"\n        An internal function that parses various streams and its parameters.\n\n        Parameters:\n            input_params (dict): Input FFmpeg parameters\n            output_params (dict): Output FFmpeg parameters\n        \"\"\"\n        # handle bit-per-pixels\n        bpp = self.__params.pop(\"-bpp\", 0.1000)\n        if isinstance(bpp, float) and bpp >= 0.001:\n            bpp = float(bpp)\n        else:\n            # reset to default if invalid\n            bpp = 0.1000\n        # log it\n        bpp and self.__logging and logger.debug(\n            \"Setting bit-per-pixels: {} for this stream.\".format(bpp)\n        )\n\n        # handle gop\n        gop = self.__params.pop(\"-gop\", 2 * int(self.__sourceframerate))\n        if isinstance(gop, (int, float)) and gop >= 0:\n            gop = int(gop)\n        else:\n            # reset to some recommended value\n            gop = 2 * int(self.__sourceframerate)\n        # log it\n        gop and self.__logging and logger.debug(\n            \"Setting GOP: {} for this stream.\".format(gop)\n        )\n\n        # define default stream and its mapping\n        if self.__format == \"hls\":\n            output_params[\"-corev0\"] = [\"-map\", \"0:v\"]\n            if \"-acodec\" in output_params:\n                output_params[\"-corea0\"] = [\n                    \"-map\",\n                    \"{}:a\".format(1 if \"-core_audio\" in output_params else 0),\n                ]\n        else:\n            output_params[\"-map\"] = 0\n\n        # assign default output resolution\n        if \"-s:v:0\" in self.__params:\n            # prevent duplicates\n            del self.__params[\"-s:v:0\"]\n        if output_params[\"-vcodec\"] != \"copy\":\n            output_params[\"-s:v:0\"] = \"{}x{}\".format(\n                self.__inputwidth, self.__inputheight\n            )\n        # assign default output video-bitrate\n        if \"-b:v:0\" in self.__params:\n            # prevent duplicates\n            del self.__params[\"-b:v:0\"]\n        if output_params[\"-vcodec\"] != \"copy\":\n            output_params[\"-b:v:0\"] = (\n                str(\n                    get_video_bitrate(\n                        int(self.__inputwidth),\n                        int(self.__inputheight),\n                        self.__sourceframerate,\n                        bpp,\n                    )\n                )\n                + \"k\"\n            )\n\n        # assign default output audio-bitrate\n        if \"-b:a:0\" in self.__params:\n            # prevent duplicates\n            del self.__params[\"-b:a:0\"]\n        # extract and assign audio-bitrate from temporary handler\n        a_bitrate = output_params.pop(\"a_bitrate\", False)\n        if \"-acodec\" in output_params and a_bitrate:\n            output_params[\"-b:a:0\"] = a_bitrate\n\n        # handle user-defined streams\n        streams = self.__params.pop(\"-streams\", {})\n        output_params = self.__evaluate_streams(streams, output_params, bpp)\n\n        # define additional streams optimization parameters\n        if output_params[\"-vcodec\"] in [\"libx264\", \"libx264rgb\"]:\n            if not \"-bf\" in self.__params:\n                output_params[\"-bf\"] = 1\n            if not \"-sc_threshold\" in self.__params:\n                output_params[\"-sc_threshold\"] = 0\n            if not \"-keyint_min\" in self.__params:\n                output_params[\"-keyint_min\"] = gop\n        if (\n            output_params[\"-vcodec\"] in [\"libx264\", \"libx264rgb\", \"libvpx-vp9\"]\n            and not \"-g\" in self.__params\n        ):\n            output_params[\"-g\"] = gop\n        if output_params[\"-vcodec\"] == \"libx265\":\n            output_params[\"-core_x265\"] = [\n                \"-x265-params\",\n                \"keyint={}:min-keyint={}\".format(gop, gop),\n            ]\n\n        # process given dash/hls stream and return it\n        if self.__format == \"dash\":\n            processed_params = self.__generate_dash_stream(\n                input_params=input_params,\n                output_params=output_params,\n            )\n        else:\n            processed_params = self.__generate_hls_stream(\n                input_params=input_params,\n                output_params=output_params,\n            )\n        return processed_params\n\n    def __evaluate_streams(self, streams, output_params, bpp):\n        \"\"\"\n        Internal function that Extracts, Evaluates & Validates user-defined streams\n\n        Parameters:\n            streams (dict): Individual streams formatted as list of dict.\n            output_params (dict): Output FFmpeg parameters\n        \"\"\"\n        # temporary streams count variable\n        output_params[\"stream_count\"] = 1  # default is 1\n\n        # check if streams are empty\n        if not streams:\n            logger.info(\"No additional `-streams` are provided.\")\n            return output_params\n\n        # check if streams are valid\n        if isinstance(streams, list) and all(isinstance(x, dict) for x in streams):\n            # keep track of streams\n            stream_count = 1\n            # calculate source aspect-ratio\n            source_aspect_ratio = self.__inputwidth / self.__inputheight\n            # log the process\n            self.__logging and logger.debug(\n                \"Processing {} streams.\".format(len(streams))\n            )\n            # iterate over given streams\n            for idx, stream in enumerate(streams):\n                # log stream processing\n                self.__logging and logger.debug(\"Processing Stream: #{}\".format(idx))\n                # make copy\n                stream_copy = stream.copy()\n                # handle intermediate stream data as dictionary\n                intermediate_dict = {}\n                # define and map stream to intermediate dict\n                if self.__format == \"hls\":\n                    intermediate_dict[\"-corev{}\".format(stream_count)] = [\"-map\", \"0:v\"]\n                    if \"-acodec\" in output_params:\n                        intermediate_dict[\"-corea{}\".format(stream_count)] = [\n                            \"-map\",\n                            \"{}:a\".format(1 if \"-core_audio\" in output_params else 0),\n                        ]\n                else:\n                    intermediate_dict[\"-core{}\".format(stream_count)] = [\"-map\", \"0\"]\n\n                # extract resolution & individual dimension of stream\n                resolution = stream.pop(\"-resolution\", \"\")\n                dimensions = (\n                    resolution.lower().split(\"x\")\n                    if (resolution and isinstance(resolution, str))\n                    else []\n                )\n                # validate resolution\n                if (\n                    len(dimensions) == 2\n                    and dimensions[0].isnumeric()\n                    and dimensions[1].isnumeric()\n                ):\n                    # verify resolution is w.r.t source aspect-ratio\n                    expected_width = math.floor(\n                        int(dimensions[1]) * source_aspect_ratio\n                    )\n                    if int(dimensions[0]) != expected_width:\n                        logger.warning(\n                            \"The provided stream resolution '{}' does not align with the source aspect ratio. Output stream may appear distorted!\".format(\n                                resolution\n                            )\n                        )\n                    # assign stream resolution to intermediate dict\n                    intermediate_dict[\"-s:v:{}\".format(stream_count)] = resolution\n                else:\n                    # otherwise log error and skip stream\n                    logger.error(\n                        \"Missing `-resolution` value. Invalid stream `{}` Skipped!\".format(\n                            stream_copy\n                        )\n                    )\n                    continue\n\n                # verify given stream video-bitrate\n                video_bitrate = stream.pop(\"-video_bitrate\", \"\")\n                if (\n                    video_bitrate\n                    and isinstance(video_bitrate, str)\n                    and video_bitrate.endswith((\"k\", \"M\"))\n                ):\n                    # assign it\n                    intermediate_dict[\"-b:v:{}\".format(stream_count)] = video_bitrate\n                else:\n                    # otherwise calculate video-bitrate\n                    fps = stream.pop(\"-framerate\", 0.0)\n                    if dimensions and isinstance(fps, (float, int)) and fps > 0:\n                        intermediate_dict[\"-b:v:{}\".format(stream_count)] = (\n                            \"{}k\".format(\n                                get_video_bitrate(\n                                    int(dimensions[0]), int(dimensions[1]), fps, bpp\n                                )\n                            )\n                        )\n                    else:\n                        # If everything fails, log and skip the stream!\n                        logger.error(\n                            \"Unable to determine Video-Bitrate for the stream `{}`. Skipped!\".format(\n                                stream_copy\n                            )\n                        )\n                        continue\n                # verify given stream audio-bitrate\n                audio_bitrate = stream.pop(\"-audio_bitrate\", \"\")\n                if \"-acodec\" in output_params:\n                    if audio_bitrate and audio_bitrate.endswith((\"k\", \"M\")):\n                        intermediate_dict[\"-b:a:{}\".format(stream_count)] = (\n                            audio_bitrate\n                        )\n                    else:\n                        # otherwise calculate audio-bitrate\n                        if dimensions:\n                            aspect_width = int(dimensions[0])\n                            intermediate_dict[\"-b:a:{}\".format(stream_count)] = (\n                                \"{}k\".format(128 if (aspect_width > 800) else 96)\n                            )\n                # update output parameters\n                output_params.update(intermediate_dict)\n                # clear intermediate dict\n                intermediate_dict.clear()\n                # clear stream copy\n                stream_copy.clear()\n                # increment to next stream\n                stream_count += 1\n                # log stream processing\n                self.__logging and logger.debug(\n                    \"Processed #{} stream successfully.\".format(idx)\n                )\n            # store stream count\n            output_params[\"stream_count\"] = stream_count\n            # log streams processing\n            self.__logging and logger.debug(\"All streams processed successfully!\")\n        else:\n            # skip and log\n            logger.warning(\"Invalid type `-streams` skipped!\")\n\n        return output_params\n\n    def __generate_hls_stream(self, input_params, output_params):\n        \"\"\"\n        An internal function that parses user-defined parameters and generates\n        suitable FFmpeg Terminal Command for transcoding input into HLS Stream.\n\n        Parameters:\n            input_params (dict): Input FFmpeg parameters\n            output_params (dict): Output FFmpeg parameters\n        \"\"\"\n        # validate `hls_segment_type`\n        default_hls_segment_type = self.__params.pop(\"-hls_segment_type\", \"mpegts\")\n        if isinstance(\n            default_hls_segment_type, str\n        ) and default_hls_segment_type.strip() in [\"fmp4\", \"mpegts\"]:\n            output_params[\"-hls_segment_type\"] = default_hls_segment_type.strip()\n        else:\n            # otherwise reset to default\n            logger.warning(\"Invalid `-hls_segment_type` value skipped!\")\n            output_params[\"-hls_segment_type\"] = \"mpegts\"\n        # gather required parameters\n        if self.__livestreaming:\n            # `hls_list_size` must be greater than or equal to 0\n            default_hls_list_size = self.__params.pop(\"-hls_list_size\", 6)\n            if isinstance(default_hls_list_size, int) and default_hls_list_size >= 0:\n                output_params[\"-hls_list_size\"] = default_hls_list_size\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-hls_list_size` value skipped!\")\n                output_params[\"-hls_list_size\"] = 6\n            # `hls_init_time` must be greater than or equal to 0\n            default_hls_init_time = self.__params.pop(\"-hls_init_time\", 4)\n            if isinstance(default_hls_init_time, int) and default_hls_init_time >= 0:\n                output_params[\"-hls_init_time\"] = default_hls_init_time\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-hls_init_time` value skipped!\")\n                output_params[\"-hls_init_time\"] = 4\n            # `hls_time` must be greater than or equal to 0\n            default_hls_time = self.__params.pop(\"-hls_time\", 4)\n            if isinstance(default_hls_time, int) and default_hls_time >= 0:\n                output_params[\"-hls_time\"] = default_hls_time\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-hls_time` value skipped!\")\n                output_params[\"-hls_time\"] = 6\n            # `hls_flags` must be string\n            default_hls_flags = self.__params.pop(\n                \"-hls_flags\", \"delete_segments+discont_start+split_by_time\"\n            )\n            if isinstance(default_hls_flags, str):\n                output_params[\"-hls_flags\"] = default_hls_flags\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-hls_flags` value skipped!\")\n                output_params[\"-hls_flags\"] = (\n                    \"delete_segments+discont_start+split_by_time\"\n                )\n            # clean everything at exit?\n            remove_at_exit = self.__params.pop(\"-remove_at_exit\", 0)\n            if isinstance(remove_at_exit, int) and remove_at_exit in [\n                0,\n                1,\n            ]:\n                output_params[\"-remove_at_exit\"] = remove_at_exit\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-remove_at_exit` value skipped!\")\n                output_params[\"-remove_at_exit\"] = 0\n        else:\n            # enforce \"contain all the segments\"\n            output_params[\"-hls_list_size\"] = 0\n            output_params[\"-hls_playlist_type\"] = \"vod\"\n\n        # handle base URL for absolute paths\n        hls_base_url = self.__params.pop(\"-hls_base_url\", \"\")\n        if isinstance(hls_base_url, str):\n            output_params[\"-hls_base_url\"] = hls_base_url\n        else:\n            # otherwise reset to default\n            logger.warning(\"Invalid `-hls_base_url` value skipped!\")\n            output_params[\"-hls_base_url\"] = \"\"\n\n        # Hardcoded HLS parameters (Refer FFmpeg docs for more info.)\n        output_params[\"-allowed_extensions\"] = \"ALL\"\n        # Handling <hls_segment_filename>\n        # Here filename will be based on `stream_count` dict parameter that\n        # would be used to check whether stream is multi-variant(>1) or single(0-1)\n        segment_template = (\n            \"{}-stream%v-%03d.{}\"\n            if output_params[\"stream_count\"] > 1\n            else \"{}-stream-%03d.{}\"\n        )\n        output_params[\"-hls_segment_filename\"] = segment_template.format(\n            os.path.join(os.path.dirname(self.__out_file), \"chunk\"),\n            \"m4s\" if output_params[\"-hls_segment_type\"] == \"fmp4\" else \"ts\",\n        )\n        # Hardcoded HLS parameters (Refer FFmpeg docs for more info.)\n        output_params[\"-hls_allow_cache\"] = 0\n        # enable hls formatting\n        output_params[\"-f\"] = \"hls\"\n        # return HLS params\n        return (input_params, output_params)\n\n    def __generate_dash_stream(self, input_params, output_params):\n        \"\"\"\n        An internal function that parses user-defined parameters and generates\n        suitable FFmpeg Terminal Command for transcoding input into MPEG-dash Stream.\n\n        Parameters:\n            input_params (dict): Input FFmpeg parameters\n            output_params (dict): Output FFmpeg parameters\n        \"\"\"\n\n        # Check if live-streaming or not?\n        if self.__livestreaming:\n            # `extra_window_size` must be greater than or equal to 0\n            window_size = self.__params.pop(\"-window_size\", 5)\n            if isinstance(window_size, int) and window_size >= 0:\n                output_params[\"-window_size\"] = window_size\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-window_size` value skipped!\")\n                output_params[\"-window_size\"] = 5\n            # `extra_window_size` must be greater than or equal to 0\n            extra_window_size = self.__params.pop(\"-extra_window_size\", 5)\n            if isinstance(extra_window_size, int) and extra_window_size >= 0:\n                output_params[\"-extra_window_size\"] = window_size\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-extra_window_size` value skipped!\")\n                output_params[\"-extra_window_size\"] = 5\n            # clean everything at exit?\n            remove_at_exit = self.__params.pop(\"-remove_at_exit\", 0)\n            if isinstance(remove_at_exit, int) and remove_at_exit in [\n                0,\n                1,\n            ]:\n                output_params[\"-remove_at_exit\"] = remove_at_exit\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-remove_at_exit` value skipped!\")\n                output_params[\"-remove_at_exit\"] = 0\n            # `seg_duration` must be greater than or equal to 0\n            seg_duration = self.__params.pop(\"-seg_duration\", 20)\n            if isinstance(seg_duration, int) and seg_duration >= 0:\n                output_params[\"-seg_duration\"] = seg_duration\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-seg_duration` value skipped!\")\n                output_params[\"-seg_duration\"] = 20\n            # Disable (0) the use of a SegmentTimeline inside a SegmentTemplate.\n            output_params[\"-use_timeline\"] = 0\n        else:\n            # `seg_duration` must be greater than or equal to 0\n            seg_duration = self.__params.pop(\"-seg_duration\", 5)\n            if isinstance(seg_duration, int) and seg_duration >= 0:\n                output_params[\"-seg_duration\"] = seg_duration\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-seg_duration` value skipped!\")\n                output_params[\"-seg_duration\"] = 5\n            # Enable (1) the use of a SegmentTimeline inside a SegmentTemplate.\n            output_params[\"-use_timeline\"] = 1\n\n        # Finally, some hardcoded DASH parameters (Refer FFmpeg docs for more info.)\n        output_params[\"-use_template\"] = 1\n        output_params[\"-adaptation_sets\"] = \"id=0,streams=v {}\".format(\n            \"id=1,streams=a\" if (\"-acodec\" in output_params) else \"\"\n        )\n        # enable dash formatting\n        output_params[\"-f\"] = \"dash\"\n        # return DASH params\n        return (input_params, output_params)\n\n    def __Build_n_Execute(self, input_params, output_params):\n        \"\"\"\n        An Internal function that launches FFmpeg subprocess and pipelines commands.\n\n        Parameters:\n            input_params (dict): Input FFmpeg parameters\n            output_params (dict): Output FFmpeg parameters\n        \"\"\"\n        # handle audio source if present\n        \"-core_asource\" in output_params and output_params.move_to_end(\n            \"-core_asource\", last=False\n        )\n        # handle `-i` parameter\n        \"-i\" in output_params and output_params.move_to_end(\"-i\", last=False)\n        # copy streams count\n        stream_count = output_params.pop(\"stream_count\", 1)\n\n        # convert input parameters to list\n        input_commands = dict2Args(input_params)\n        # convert output parameters to list\n        output_commands = dict2Args(output_params)\n        # convert any additional parameters to list\n        stream_commands = dict2Args(self.__params)\n\n        # create exclusive HLS params\n        hls_commands = []\n        # handle HLS multi-variant streams\n        if self.__format == \"hls\" and stream_count > 1:\n            stream_map = \"\"\n            for count in range(0, stream_count):\n                stream_map += \"v:{}{} \".format(\n                    count, \",a:{}\".format(count) if \"-acodec\" in output_params else \",\"\n                )\n            hls_commands += [\n                \"-master_pl_name\",\n                os.path.basename(self.__out_file),\n                \"-var_stream_map\",\n                stream_map.strip(),\n                os.path.join(os.path.dirname(self.__out_file), \"stream_%v.m3u8\"),\n            ]\n\n        # log it if enabled\n        self.__logging and logger.debug(\n            \"User-Defined Output parameters: `{}`\".format(\n                \" \".join(output_commands) if output_commands else None\n            )\n        )\n        self.__logging and logger.debug(\n            \"Additional parameters: `{}`\".format(\n                \" \".join(stream_commands) if stream_commands else None\n            )\n        )\n        # build FFmpeg command from parameters\n        ffmpeg_cmd = None\n        # ensuring less cluttering if silent mode\n        hide_banner = [] if self.__logging else [\"-hide_banner\"]\n        # format commands\n        if self.__video_source:\n            ffmpeg_cmd = (\n                [self.__ffmpeg, \"-y\"]\n                + ([\"-re\"] if self.__livestreaming else [])  # pseudo live-streaming\n                + hide_banner\n                + [\"-i\", self.__video_source]\n                + input_commands\n                + output_commands\n                + stream_commands\n            )\n        else:\n            ffmpeg_cmd = (\n                [self.__ffmpeg, \"-y\"]\n                + hide_banner\n                + [\"-f\", \"rawvideo\", \"-vcodec\", \"rawvideo\"]\n                + input_commands\n                + [\"-i\", \"-\"]\n                + output_commands\n                + stream_commands\n            )\n        # format outputs\n        ffmpeg_cmd.extend([self.__out_file] if not (hls_commands) else hls_commands)\n        # Launch the FFmpeg pipeline with built command\n        logger.critical(\"Transcoding streaming chunks. Please wait...\")  # log it\n        self.__process = sp.Popen(\n            ffmpeg_cmd,\n            stdin=sp.PIPE,\n            stdout=(\n                sp.DEVNULL\n                if (not self.__video_source and not self.__logging)\n                else sp.PIPE\n            ),\n            stderr=None if self.__logging else sp.STDOUT,\n        )\n        # post handle progress bar and runtime errors in case of video_source\n        if self.__video_source:\n            return_code = 0\n            pbar = None\n            sec_prev = 0\n            if self.__logging:\n                self.__process.communicate()\n                return_code = self.__process.returncode\n            else:\n                # iterate until stdout runs out\n                while True:\n                    # read and process data\n                    data = self.__process.stdout.readline()\n                    if data:\n                        data = data.decode(\"utf-8\")\n                        # extract duration and time-left\n                        if pbar is None and \"Duration:\" in data:\n                            # extract time in seconds\n                            sec_duration = extract_time(data)\n                            # initiate progress bar\n                            pbar = tqdm(\n                                total=sec_duration,\n                                desc=\"Processing Frames\",\n                                unit=\"frame\",\n                            )\n                        elif \"time=\" in data:\n                            # extract time in seconds\n                            sec_current = extract_time(data)\n                            # update progress bar\n                            if sec_current:\n                                pbar.update(sec_current - sec_prev)\n                                sec_prev = sec_current\n                    else:\n                        # poll if no data\n                        if self.__process.poll() is not None:\n                            break\n                return_code = self.__process.poll()\n            # close progress bar\n            not (pbar is None) and pbar.close()\n            # handle return_code\n            if return_code != 0:\n                # log and raise error if return_code is `1`\n                logger.error(\n                    \"StreamGear failed to initiate stream for this video source!\"\n                )\n                raise sp.CalledProcessError(return_code, ffmpeg_cmd)\n            else:\n                # log if successful\n                logger.critical(\n                    \"Transcoding Ended. {} Streaming assets are successfully generated at specified path.\".format(\n                        self.__format.upper()\n                    )\n                )\n\n    def __enter__(self):\n        \"\"\"\n        Handles entry with the `with` statement. See [PEP343 -- The 'with' statement'](https://peps.python.org/pep-0343/).\n\n        **Returns:** Returns a reference to the StreamGear Class\n        \"\"\"\n        return self\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        \"\"\"\n        Handles exit with the `with` statement. See [PEP343 -- The 'with' statement'](https://peps.python.org/pep-0343/).\n        \"\"\"\n        self.close()\n\n    @deprecated(\n        message=\"The `terminate()` method will be removed in the next release. Kindly use `close()` method instead.\"\n    )\n    def terminate(self):\n        \"\"\"\n        !!! warning \"[DEPRECATION NOTICE]: This method is now deprecated and will be removed in a future release.\"\n\n        This function ensures backward compatibility for the `terminate()` method to maintain the API on existing systems.\n        It achieves this by calling the new `close()` method to terminate various\n        StreamGear processes.\n        \"\"\"\n\n        self.close()\n\n    def close(self):\n        \"\"\"\n        Safely terminates various StreamGear process.\n        \"\"\"\n        # log termination\n        self.__logging and logger.debug(\"Terminating StreamGear Processes.\")\n\n        # return if no process was initiated at first place\n        if self.__process is None or not (self.__process.poll() is None):\n            return\n        # close `stdin` output\n        self.__process.stdin and self.__process.stdin.close()\n        # close `stdout` output\n        self.__process.stdout and self.__process.stdout.close()\n        # forced termination if specified.\n        if self.__forced_termination:\n            self.__process.terminate()\n        # handle device audio streams\n        elif self.__audio and isinstance(self.__audio, list):\n            # send `CTRL_BREAK_EVENT` signal if Windows else `SIGINT`\n            self.__process.send_signal(\n                signal.CTRL_BREAK_EVENT if self.__os_windows else signal.SIGINT\n            )\n        # wait if process is still processing\n        self.__process.wait()\n        # discard process\n        self.__process = None\n

"},{"location":"bonus/reference/streamgear/#vidgear.gears.streamgear.StreamGear.__enter__","title":"__enter__(self) special","text":"

Handles entry with the with statement. See PEP343 -- The 'with' statement'.

Returns: Returns a reference to the StreamGear Class

Source code in vidgear/gears/streamgear.py
def __enter__(self):\n    \"\"\"\n    Handles entry with the `with` statement. See [PEP343 -- The 'with' statement'](https://peps.python.org/pep-0343/).\n\n    **Returns:** Returns a reference to the StreamGear Class\n    \"\"\"\n    return self\n
"},{"location":"bonus/reference/streamgear/#vidgear.gears.streamgear.StreamGear.__exit__","title":"__exit__(self, exc_type, exc_val, exc_tb) special","text":"

Handles exit with the with statement. See PEP343 -- The 'with' statement'.

Source code in vidgear/gears/streamgear.py
def __exit__(self, exc_type, exc_val, exc_tb):\n    \"\"\"\n    Handles exit with the `with` statement. See [PEP343 -- The 'with' statement'](https://peps.python.org/pep-0343/).\n    \"\"\"\n    self.close()\n
"},{"location":"bonus/reference/streamgear/#vidgear.gears.streamgear.StreamGear.__init__","title":"__init__(self, output='', format='dash', custom_ffmpeg='', logging=False, **stream_params) special","text":"

This constructor method initializes the object state and attributes of the StreamGear class.

Parameters:

Name Type Description Default output str

sets the valid filename/path for generating the StreamGear assets.

'' format str

select the adaptive HTTP streaming format(DASH and HLS).

'dash' custom_ffmpeg str

assigns the location of custom path/directory for custom FFmpeg executables.

'' logging bool

enables/disables logging.

False stream_params dict

provides the flexibility to control supported internal parameters and FFmpeg properties.

{} Source code in vidgear/gears/streamgear.py
def __init__(\n    self, output=\"\", format=\"dash\", custom_ffmpeg=\"\", logging=False, **stream_params\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the StreamGear class.\n\n    Parameters:\n        output (str): sets the valid filename/path for generating the StreamGear assets.\n        format (str): select the adaptive HTTP streaming format(DASH and HLS).\n        custom_ffmpeg (str): assigns the location of custom path/directory for custom FFmpeg executables.\n        logging (bool): enables/disables logging.\n        stream_params (dict): provides the flexibility to control supported internal parameters and FFmpeg properties.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # checks if machine in-use is running windows os or not\n    self.__os_windows = True if os.name == \"nt\" else False\n\n    # initialize various class variables\n    # handles user-defined parameters\n    self.__params = {}\n    # handle input video/frame resolution and channels\n    self.__inputheight = None\n    self.__inputwidth = None\n    self.__inputchannels = None\n    self.__sourceframerate = None\n    # handle process to be frames written\n    self.__process = None\n    # handle valid FFmpeg assets location\n    self.__ffmpeg = \"\"\n    # handle one time process for valid process initialization\n    self.__initiate_stream = True\n\n    # cleans and reformat user-defined parameters\n    self.__params = {\n        str(k).strip(): (v.strip() if isinstance(v, str) else v)\n        for k, v in stream_params.items()\n    }\n\n    # handle where to save the downloaded FFmpeg Static assets on Windows(if specified)\n    __ffmpeg_download_path = self.__params.pop(\"-ffmpeg_download_path\", \"\")\n    if not isinstance(__ffmpeg_download_path, (str)):\n        # reset improper values\n        __ffmpeg_download_path = \"\"\n\n    # validate the FFmpeg assets and return location (also downloads static assets on windows)\n    self.__ffmpeg = get_valid_ffmpeg_path(\n        str(custom_ffmpeg),\n        self.__os_windows,\n        ffmpeg_download_path=__ffmpeg_download_path,\n        logging=self.__logging,\n    )\n\n    # check if valid FFmpeg path returned\n    if self.__ffmpeg:\n        self.__logging and logger.debug(\n            \"Found valid FFmpeg executables: `{}`.\".format(self.__ffmpeg)\n        )\n    else:\n        # else raise error\n        raise RuntimeError(\n            \"[StreamGear:ERROR] :: Failed to find FFmpeg assets on this system. Kindly compile/install FFmpeg or provide a valid custom FFmpeg binary path!\"\n        )\n\n    # handle streaming format\n    supported_formats = [\"dash\", \"hls\"]  # TODO will be extended in future\n    if format and isinstance(format, str):\n        _format = format.strip().lower()\n        if _format in supported_formats:\n            self.__format = _format\n            logger.info(\n                \"StreamGear will generate asset files for {} streaming format.\".format(\n                    self.__format.upper()\n                )\n            )\n        elif difflib.get_close_matches(_format, supported_formats):\n            raise ValueError(\n                \"[StreamGear:ERROR] :: Incorrect `format` parameter value! Did you mean `{}`?\".format(\n                    difflib.get_close_matches(_format, supported_formats)[0]\n                )\n            )\n        else:\n            raise ValueError(\n                \"[StreamGear:ERROR] :: The `format` parameter value `{}` not valid/supported!\".format(\n                    format\n                )\n            )\n    else:\n        raise ValueError(\n            \"[StreamGear:ERROR] :: The `format` parameter value is Missing or Invalid!\"\n        )\n\n    # handle Audio-Input\n    audio = self.__params.pop(\"-audio\", False)\n    if audio and isinstance(audio, str):\n        if os.path.isfile(audio):\n            self.__audio = os.path.abspath(audio)\n        elif is_valid_url(self.__ffmpeg, url=audio, logging=self.__logging):\n            self.__audio = audio\n        else:\n            self.__audio = False\n    elif audio and isinstance(audio, list):\n        self.__audio = audio\n    else:\n        self.__audio = False\n    # log external audio source\n    self.__audio and self.__logging and logger.debug(\n        \"External audio source `{}` detected.\".format(self.__audio)\n    )\n\n    # handle Video-Source input\n    source = self.__params.pop(\"-video_source\", False)\n    # Check if input is valid string\n    if source and isinstance(source, str) and len(source) > 1:\n        # Differentiate input\n        if os.path.isfile(source):\n            self.__video_source = os.path.abspath(source)\n        elif is_valid_url(self.__ffmpeg, url=source, logging=self.__logging):\n            self.__video_source = source\n        else:\n            # discard the value otherwise\n            self.__video_source = False\n\n        # Validate input\n        if self.__video_source:\n            validation_results = validate_video(\n                self.__ffmpeg, video_path=self.__video_source\n            )\n            assert not (\n                validation_results is None\n            ), \"[StreamGear:ERROR] :: Given `{}` video_source is Invalid, Check Again!\".format(\n                self.__video_source\n            )\n            self.__aspect_source = validation_results[\"resolution\"]\n            self.__fps_source = validation_results[\"framerate\"]\n            # log it\n            self.__logging and logger.debug(\n                \"Given video_source is valid and has {}x{} resolution, and a framerate of {} fps.\".format(\n                    self.__aspect_source[0],\n                    self.__aspect_source[1],\n                    self.__fps_source,\n                )\n            )\n        else:\n            # log warning\n            logger.warning(\"Discarded invalid `-video_source` value provided.\")\n    else:\n        if source:\n            # log warning if source provided\n            logger.warning(\"Invalid `-video_source` value provided.\")\n        else:\n            # log normally\n            logger.info(\"No `-video_source` value provided.\")\n        # discard the value otherwise\n        self.__video_source = False\n\n    # handle user-defined framerate\n    self.__inputframerate = self.__params.pop(\"-input_framerate\", 0.0)\n    if isinstance(self.__inputframerate, (float, int)):\n        # must be float\n        self.__inputframerate = float(self.__inputframerate)\n    else:\n        # reset improper values\n        self.__inputframerate = 0.0\n\n    # handle old assets\n    clear_assets = self.__params.pop(\"-clear_prev_assets\", False)\n    if isinstance(clear_assets, bool):\n        self.__clear_assets = clear_assets\n        # log if clearing assets is enabled\n        clear_assets and logger.info(\n            \"The `-clear_prev_assets` parameter is enabled successfully. All previous StreamGear API assets for `{}` format will be removed for this run.\".format(\n                self.__format.upper()\n            )\n        )\n    else:\n        # reset improper values\n        self.__clear_assets = False\n\n    # handle whether to livestream?\n    livestreaming = self.__params.pop(\"-livestream\", False)\n    if isinstance(livestreaming, bool) and livestreaming:\n        # NOTE:  `livestream` is only available with real-time mode.\n        self.__livestreaming = livestreaming if not (self.__video_source) else False\n        if self.__video_source:\n            logger.error(\n                \"Live-Streaming is only available with Real-time Mode. Refer docs for more information.\"\n            )\n        else:\n            # log if live streaming is enabled\n            livestreaming and logger.info(\n                \"Live-Streaming is successfully enabled for this run.\"\n            )\n    else:\n        # reset improper values\n        self.__livestreaming = False\n\n    # handle the special-case of forced-termination\n    enable_force_termination = self.__params.pop(\"-enable_force_termination\", False)\n    # check if value is valid\n    if isinstance(enable_force_termination, bool):\n        self.__forced_termination = enable_force_termination\n        # log if forced termination is enabled\n        self.__forced_termination and logger.warning(\n            \"Forced termination is enabled for this run. This may result in corrupted output in certain scenarios!\"\n        )\n    else:\n        # handle improper values\n        self.__forced_termination = False\n\n    # handle streaming format\n    supported_formats = [\"dash\", \"hls\"]  # TODO will be extended in future\n    if format and isinstance(format, str):\n        _format = format.strip().lower()\n        if _format in supported_formats:\n            self.__format = _format\n            logger.info(\n                \"StreamGear will generate asset files for {} streaming format.\".format(\n                    self.__format.upper()\n                )\n            )\n        elif difflib.get_close_matches(_format, supported_formats):\n            raise ValueError(\n                \"[StreamGear:ERROR] :: Incorrect `format` parameter value! Did you mean `{}`?\".format(\n                    difflib.get_close_matches(_format, supported_formats)[0]\n                )\n            )\n        else:\n            raise ValueError(\n                \"[StreamGear:ERROR] :: The `format` parameter value `{}` not valid/supported!\".format(\n                    format\n                )\n            )\n    else:\n        raise ValueError(\n            \"[StreamGear:ERROR] :: The `format` parameter value is Missing or Invalid!\"\n        )\n\n    # handles output asset filenames\n    if output:\n        # validate this class has the access rights to specified directory or not\n        abs_path = os.path.abspath(output)\n        # check if given output is a valid system path\n        if check_WriteAccess(\n            os.path.dirname(abs_path),\n            is_windows=self.__os_windows,\n            logging=self.__logging,\n        ):\n            # get all assets extensions\n            valid_extension = \"mpd\" if self.__format == \"dash\" else \"m3u8\"\n            assets_exts = [\n                (\"chunk-stream\", \".m4s\"),  # filename prefix, extension\n                (\"chunk-stream\", \".ts\"),  # filename prefix, extension\n                \".{}\".format(valid_extension),\n            ]\n            # add source file extension too\n            self.__video_source and assets_exts.append(\n                (\n                    \"chunk-stream\",\n                    os.path.splitext(self.__video_source)[1],\n                )  # filename prefix, extension\n            )\n            # handle output\n            # check if path is a directory\n            if os.path.isdir(abs_path):\n                # clear previous assets if specified\n                self.__clear_assets and delete_ext_safe(\n                    abs_path, assets_exts, logging=self.__logging\n                )\n                # auto-assign valid name and adds it to path\n                abs_path = os.path.join(\n                    abs_path,\n                    \"{}-{}.{}\".format(\n                        self.__format,\n                        time.strftime(\"%Y%m%d-%H%M%S\"),\n                        valid_extension,\n                    ),\n                )\n            # or check if path is a file\n            elif os.path.isfile(abs_path) and self.__clear_assets:\n                # clear previous assets if specified\n                delete_ext_safe(\n                    os.path.dirname(abs_path),\n                    assets_exts,\n                    logging=self.__logging,\n                )\n            # check if path has valid file extension\n            assert abs_path.endswith(\n                valid_extension\n            ), \"Given `{}` path has invalid file-extension w.r.t selected format: `{}`!\".format(\n                output, self.__format.upper()\n            )\n            self.__logging and logger.debug(\n                \"Output Path:`{}` is successfully configured for generating streaming assets.\".format(\n                    abs_path\n                )\n            )\n            # workaround patch for Windows only,\n            # others platforms will not be affected\n            self.__out_file = abs_path.replace(\"\\\\\", \"/\")\n        # check if given output is a valid URL\n        elif is_valid_url(self.__ffmpeg, url=output, logging=self.__logging):\n            self.__logging and logger.debug(\n                \"URL:`{}` is valid and successfully configured for generating streaming assets.\".format(\n                    output\n                )\n            )\n            self.__out_file = output\n        # raise ValueError otherwise\n        else:\n            raise ValueError(\n                \"[StreamGear:ERROR] :: The output parameter value:`{}` is not valid/supported!\".format(\n                    output\n                )\n            )\n    else:\n        # raise ValueError otherwise\n        raise ValueError(\n            \"[StreamGear:ERROR] :: Kindly provide a valid `output` parameter value. Refer Docs for more information.\"\n        )\n\n    # log Mode of operation\n    self.__video_source and logger.info(\n        \"StreamGear has been successfully configured for {} Mode.\".format(\n            \"Single-Source\" if self.__video_source else \"Real-time Frames\"\n        )\n    )\n
"},{"location":"bonus/reference/streamgear/#vidgear.gears.streamgear.StreamGear.close","title":"close(self)","text":"

Safely terminates various StreamGear process.

Source code in vidgear/gears/streamgear.py
def close(self):\n    \"\"\"\n    Safely terminates various StreamGear process.\n    \"\"\"\n    # log termination\n    self.__logging and logger.debug(\"Terminating StreamGear Processes.\")\n\n    # return if no process was initiated at first place\n    if self.__process is None or not (self.__process.poll() is None):\n        return\n    # close `stdin` output\n    self.__process.stdin and self.__process.stdin.close()\n    # close `stdout` output\n    self.__process.stdout and self.__process.stdout.close()\n    # forced termination if specified.\n    if self.__forced_termination:\n        self.__process.terminate()\n    # handle device audio streams\n    elif self.__audio and isinstance(self.__audio, list):\n        # send `CTRL_BREAK_EVENT` signal if Windows else `SIGINT`\n        self.__process.send_signal(\n            signal.CTRL_BREAK_EVENT if self.__os_windows else signal.SIGINT\n        )\n    # wait if process is still processing\n    self.__process.wait()\n    # discard process\n    self.__process = None\n
"},{"location":"bonus/reference/streamgear/#vidgear.gears.streamgear.StreamGear.stream","title":"stream(self, frame, rgb_mode=False)","text":"

Pipes ndarray frames to FFmpeg Pipeline for transcoding them into chunked-encoded media segments of streaming formats such as MPEG-DASH and HLS.

[DEPRECATION NOTICE]: The rgb_mode parameter is deprecated and will be removed in a future version.

Parameters:

Name Type Description Default frame ndarray

a valid numpy frame

required rgb_mode boolean

enable this flag to activate RGB mode (i.e. specifies that incoming frames are of RGB format instead of default BGR).

False Source code in vidgear/gears/streamgear.py
@deprecated(\n    parameter=\"rgb_mode\",\n    message=\"The `rgb_mode` parameter is deprecated and will be removed in a future version. Only BGR format frames will be supported going forward.\",\n)\ndef stream(self, frame, rgb_mode=False):\n    \"\"\"\n    Pipes `ndarray` frames to FFmpeg Pipeline for transcoding them into chunked-encoded media segments of\n    streaming formats such as MPEG-DASH and HLS.\n\n    !!! warning \"[DEPRECATION NOTICE]: The `rgb_mode` parameter is deprecated and will be removed in a future version.\"\n\n    Parameters:\n        frame (ndarray): a valid numpy frame\n        rgb_mode (boolean): enable this flag to activate RGB mode _(i.e. specifies that incoming frames are of RGB format instead of default BGR)_.\n    \"\"\"\n    # check if function is called in correct context\n    if self.__video_source:\n        raise RuntimeError(\n            \"[StreamGear:ERROR] :: The `stream()` method cannot be used when streaming from a `-video_source` input file. Kindly refer vidgear docs!\"\n        )\n    # None-Type frames will be skipped\n    if frame is None:\n        return\n    # extract height, width and number of channels of frame\n    height, width = frame.shape[:2]\n    channels = frame.shape[-1] if frame.ndim == 3 else 1\n    # assign values to class variables on first run\n    if self.__initiate_stream:\n        self.__inputheight = height\n        self.__inputwidth = width\n        self.__inputchannels = channels\n        self.__sourceframerate = (\n            25.0 if not (self.__inputframerate) else self.__inputframerate\n        )\n        self.__logging and logger.debug(\n            \"InputFrame => Height:{} Width:{} Channels:{}\".format(\n                self.__inputheight, self.__inputwidth, self.__inputchannels\n            )\n        )\n    # validate size of frame\n    if height != self.__inputheight or width != self.__inputwidth:\n        raise ValueError(\"[StreamGear:ERROR] :: All frames must have same size!\")\n    # validate number of channels\n    if channels != self.__inputchannels:\n        raise ValueError(\n            \"[StreamGear:ERROR] :: All frames must have same number of channels!\"\n        )\n    # initiate FFmpeg process on first run\n    if self.__initiate_stream:\n        # launch pre-processing\n        self.__PreProcess(channels=channels, rgb=rgb_mode)\n        # Check status of the process\n        assert self.__process is not None\n\n    # write the frame to pipeline\n    try:\n        self.__process.stdin.write(frame.tobytes())\n    except (OSError, IOError):\n        # log something is wrong!\n        logger.error(\n            \"BrokenPipeError caught, Wrong values passed to FFmpeg Pipe, Kindly Refer Docs!\"\n        )\n        raise ValueError  # for testing purpose only\n
"},{"location":"bonus/reference/streamgear/#vidgear.gears.streamgear.StreamGear.terminate","title":"terminate(self)","text":"

[DEPRECATION NOTICE]: This method is now deprecated and will be removed in a future release.

This function ensures backward compatibility for the terminate() method to maintain the API on existing systems. It achieves this by calling the new close() method to terminate various StreamGear processes.

Source code in vidgear/gears/streamgear.py
@deprecated(\n    message=\"The `terminate()` method will be removed in the next release. Kindly use `close()` method instead.\"\n)\ndef terminate(self):\n    \"\"\"\n    !!! warning \"[DEPRECATION NOTICE]: This method is now deprecated and will be removed in a future release.\"\n\n    This function ensures backward compatibility for the `terminate()` method to maintain the API on existing systems.\n    It achieves this by calling the new `close()` method to terminate various\n    StreamGear processes.\n    \"\"\"\n\n    self.close()\n
"},{"location":"bonus/reference/streamgear/#vidgear.gears.streamgear.StreamGear.transcode_source","title":"transcode_source(self)","text":"

Transcodes an entire video file (with or without audio) into chunked-encoded media segments of streaming formats such as MPEG-DASH and HLS.

Source code in vidgear/gears/streamgear.py
def transcode_source(self):\n    \"\"\"\n    Transcodes an entire video file _(with or without audio)_ into chunked-encoded media segments of\n    streaming formats such as MPEG-DASH and HLS.\n    \"\"\"\n    # check if function is called in correct context\n    if not (self.__video_source):\n        raise RuntimeError(\n            \"[StreamGear:ERROR] :: The `transcode_source()` method cannot be used without a valid `-video_source` input. Kindly refer vidgear docs!\"\n        )\n    # assign height, width and framerate\n    self.__inputheight = int(self.__aspect_source[1])\n    self.__inputwidth = int(self.__aspect_source[0])\n    self.__sourceframerate = float(self.__fps_source)\n    # launch pre-processing\n    self.__PreProcess()\n
"},{"location":"bonus/reference/videogear/","title":"VideoGear API References","text":"

VideoGear API usage examples can be found here \u27b6

VideoGear API parameters are explained here \u27b6

VideoGear API provides a special internal wrapper around VidGear's exclusive Video Stabilizer class. VideoGear also acts as a Common Video-Capture API that provides internal access for both CamGear and PiGear APIs and their parameters with an exclusive enablePiCamera boolean flag.

VideoGear is ideal when you need to switch to different video sources without changing your code much. Also, it enables easy stabilization for various video-streams (real-time or not) with minimum effort and writing way fewer lines of code.

Source code in vidgear/gears/videogear.py
class VideoGear:\n    \"\"\"\n    VideoGear API provides a special internal wrapper around VidGear's exclusive Video Stabilizer class.\n    VideoGear also acts as a Common Video-Capture API that provides internal access for both CamGear and PiGear APIs and\n    their parameters with an exclusive enablePiCamera boolean flag.\n\n    VideoGear is ideal when you need to switch to different video sources without changing your code much. Also, it enables\n    easy stabilization for various video-streams (real-time or not)\n    with minimum effort and writing way fewer lines of code.\n    \"\"\"\n\n    def __init__(\n        self,\n        # VideoGear parameters\n        enablePiCamera=False,\n        stabilize=False,\n        # PiGear parameters\n        camera_num=0,\n        resolution=(640, 480),\n        framerate=30,\n        # CamGear parameters\n        source=0,\n        stream_mode=False,\n        backend=0,\n        # common parameters\n        time_delay=0,\n        colorspace=None,\n        logging=False,\n        **options\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the VideoGear class.\n\n        Parameters:\n            enablePiCamera (bool): provide access to PiGear(if True) or CamGear(if False) APIs respectively.\n            stabilize (bool): enable access to Stabilizer Class for stabilizing frames.\n            camera_num (int): selects the camera module index which will be used as Rpi source.\n            resolution (tuple): sets the resolution (i.e. `(width,height)`) of the Rpi source.\n            framerate (int/float): sets the framerate of the Rpi source.\n            source (based on input): defines the source for the input stream.\n            stream_mode (bool): controls the exclusive YouTube Mode.\n            backend (int): selects the backend for OpenCV's VideoCapture class.\n            colorspace (str): selects the colorspace of the input stream.\n            logging (bool): enables/disables logging.\n            time_delay (int): time delay (in sec) before start reading the frames.\n            options (dict): provides ability to alter Tweak Parameters of CamGear, PiGear & Stabilizer.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # initialize stabilizer\n        self.__stabilization_mode = stabilize\n\n        # reformat dictionary\n        options = {str(k).strip(): v for k, v in options.items()}\n\n        if self.__stabilization_mode:\n            from .stabilizer import Stabilizer\n\n            s_radius = options.pop(\"SMOOTHING_RADIUS\", 25)\n            if not isinstance(s_radius, int):\n                s_radius = 25\n\n            border_size = options.pop(\"BORDER_SIZE\", 0)\n            if not isinstance(border_size, int):\n                border_size = 0\n\n            border_type = options.pop(\"BORDER_TYPE\", \"black\")\n            if not isinstance(border_type, str):\n                border_type = \"black\"\n\n            crop_n_zoom = options.pop(\"CROP_N_ZOOM\", False)\n            if not isinstance(crop_n_zoom, bool):\n                crop_n_zoom = False\n\n            self.__stabilizer_obj = Stabilizer(\n                smoothing_radius=s_radius,\n                border_type=border_type,\n                border_size=border_size,\n                crop_n_zoom=crop_n_zoom,\n                logging=logging,\n            )\n            self.__logging and logger.debug(\n                \"Enabling Stabilization Mode for the current video source!\"\n            )  # log info\n\n        if enablePiCamera:\n            # only import the pigear module only if required\n            from .pigear import PiGear\n\n            # initialize the picamera stream by enabling PiGear API\n            self.stream = PiGear(\n                camera_num=camera_num,\n                resolution=resolution,\n                framerate=framerate,\n                colorspace=colorspace,\n                logging=logging,\n                time_delay=time_delay,\n                **options\n            )\n        else:\n            # otherwise, we are using OpenCV so initialize the webcam\n            # stream by activating CamGear API\n            self.stream = CamGear(\n                source=source,\n                stream_mode=stream_mode,\n                backend=backend,\n                colorspace=colorspace,\n                logging=logging,\n                time_delay=time_delay,\n                **options\n            )\n\n        # initialize framerate variable\n        self.framerate = self.stream.framerate\n\n    def start(self):\n        \"\"\"\n        Launches the internal *Threaded Frames Extractor* daemon of API in use.\n\n        **Returns:** A reference to the selected class object.\n        \"\"\"\n        self.stream.start()\n        return self\n\n    def read(self):\n        \"\"\"\n        Extracts frames synchronously from selected API's monitored deque, while maintaining a fixed-length frame\n        buffer in the memory, and blocks the thread if the deque is full.\n\n        **Returns:** A n-dimensional numpy array.\n        \"\"\"\n        while self.__stabilization_mode:\n            frame = self.stream.read()\n            if frame is None:\n                break\n            frame_stab = self.__stabilizer_obj.stabilize(frame)\n            if not (frame_stab is None):\n                return frame_stab\n        return self.stream.read()\n\n    def stop(self):\n        \"\"\"\n        Safely terminates the thread, and release the respective multi-threaded resources.\n        \"\"\"\n        self.stream.stop()\n        # logged\n        self.__logging and logger.debug(\"Terminating VideoGear.\")\n        # clean queue\n        self.__stabilization_mode and self.__stabilizer_obj.clean()\n

"},{"location":"bonus/reference/videogear/#vidgear.gears.videogear.VideoGear.__init__","title":"__init__(self, enablePiCamera=False, stabilize=False, camera_num=0, resolution=(640, 480), framerate=30, source=0, stream_mode=False, backend=0, time_delay=0, colorspace=None, logging=False, **options) special","text":"

This constructor method initializes the object state and attributes of the VideoGear class.

Parameters:

Name Type Description Default enablePiCamera bool

provide access to PiGear(if True) or CamGear(if False) APIs respectively.

False stabilize bool

enable access to Stabilizer Class for stabilizing frames.

False camera_num int

selects the camera module index which will be used as Rpi source.

0 resolution tuple

sets the resolution (i.e. (width,height)) of the Rpi source.

(640, 480) framerate int/float

sets the framerate of the Rpi source.

30 source based on input

defines the source for the input stream.

0 stream_mode bool

controls the exclusive YouTube Mode.

False backend int

selects the backend for OpenCV's VideoCapture class.

0 colorspace str

selects the colorspace of the input stream.

None logging bool

enables/disables logging.

False time_delay int

time delay (in sec) before start reading the frames.

0 options dict

provides ability to alter Tweak Parameters of CamGear, PiGear & Stabilizer.

{} Source code in vidgear/gears/videogear.py
def __init__(\n    self,\n    # VideoGear parameters\n    enablePiCamera=False,\n    stabilize=False,\n    # PiGear parameters\n    camera_num=0,\n    resolution=(640, 480),\n    framerate=30,\n    # CamGear parameters\n    source=0,\n    stream_mode=False,\n    backend=0,\n    # common parameters\n    time_delay=0,\n    colorspace=None,\n    logging=False,\n    **options\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the VideoGear class.\n\n    Parameters:\n        enablePiCamera (bool): provide access to PiGear(if True) or CamGear(if False) APIs respectively.\n        stabilize (bool): enable access to Stabilizer Class for stabilizing frames.\n        camera_num (int): selects the camera module index which will be used as Rpi source.\n        resolution (tuple): sets the resolution (i.e. `(width,height)`) of the Rpi source.\n        framerate (int/float): sets the framerate of the Rpi source.\n        source (based on input): defines the source for the input stream.\n        stream_mode (bool): controls the exclusive YouTube Mode.\n        backend (int): selects the backend for OpenCV's VideoCapture class.\n        colorspace (str): selects the colorspace of the input stream.\n        logging (bool): enables/disables logging.\n        time_delay (int): time delay (in sec) before start reading the frames.\n        options (dict): provides ability to alter Tweak Parameters of CamGear, PiGear & Stabilizer.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # initialize stabilizer\n    self.__stabilization_mode = stabilize\n\n    # reformat dictionary\n    options = {str(k).strip(): v for k, v in options.items()}\n\n    if self.__stabilization_mode:\n        from .stabilizer import Stabilizer\n\n        s_radius = options.pop(\"SMOOTHING_RADIUS\", 25)\n        if not isinstance(s_radius, int):\n            s_radius = 25\n\n        border_size = options.pop(\"BORDER_SIZE\", 0)\n        if not isinstance(border_size, int):\n            border_size = 0\n\n        border_type = options.pop(\"BORDER_TYPE\", \"black\")\n        if not isinstance(border_type, str):\n            border_type = \"black\"\n\n        crop_n_zoom = options.pop(\"CROP_N_ZOOM\", False)\n        if not isinstance(crop_n_zoom, bool):\n            crop_n_zoom = False\n\n        self.__stabilizer_obj = Stabilizer(\n            smoothing_radius=s_radius,\n            border_type=border_type,\n            border_size=border_size,\n            crop_n_zoom=crop_n_zoom,\n            logging=logging,\n        )\n        self.__logging and logger.debug(\n            \"Enabling Stabilization Mode for the current video source!\"\n        )  # log info\n\n    if enablePiCamera:\n        # only import the pigear module only if required\n        from .pigear import PiGear\n\n        # initialize the picamera stream by enabling PiGear API\n        self.stream = PiGear(\n            camera_num=camera_num,\n            resolution=resolution,\n            framerate=framerate,\n            colorspace=colorspace,\n            logging=logging,\n            time_delay=time_delay,\n            **options\n        )\n    else:\n        # otherwise, we are using OpenCV so initialize the webcam\n        # stream by activating CamGear API\n        self.stream = CamGear(\n            source=source,\n            stream_mode=stream_mode,\n            backend=backend,\n            colorspace=colorspace,\n            logging=logging,\n            time_delay=time_delay,\n            **options\n        )\n\n    # initialize framerate variable\n    self.framerate = self.stream.framerate\n
"},{"location":"bonus/reference/videogear/#vidgear.gears.videogear.VideoGear.read","title":"read(self)","text":"

Extracts frames synchronously from selected API's monitored deque, while maintaining a fixed-length frame buffer in the memory, and blocks the thread if the deque is full.

Returns: A n-dimensional numpy array.

Source code in vidgear/gears/videogear.py
def read(self):\n    \"\"\"\n    Extracts frames synchronously from selected API's monitored deque, while maintaining a fixed-length frame\n    buffer in the memory, and blocks the thread if the deque is full.\n\n    **Returns:** A n-dimensional numpy array.\n    \"\"\"\n    while self.__stabilization_mode:\n        frame = self.stream.read()\n        if frame is None:\n            break\n        frame_stab = self.__stabilizer_obj.stabilize(frame)\n        if not (frame_stab is None):\n            return frame_stab\n    return self.stream.read()\n
"},{"location":"bonus/reference/videogear/#vidgear.gears.videogear.VideoGear.start","title":"start(self)","text":"

Launches the internal Threaded Frames Extractor daemon of API in use.

Returns: A reference to the selected class object.

Source code in vidgear/gears/videogear.py
def start(self):\n    \"\"\"\n    Launches the internal *Threaded Frames Extractor* daemon of API in use.\n\n    **Returns:** A reference to the selected class object.\n    \"\"\"\n    self.stream.start()\n    return self\n
"},{"location":"bonus/reference/videogear/#vidgear.gears.videogear.VideoGear.stop","title":"stop(self)","text":"

Safely terminates the thread, and release the respective multi-threaded resources.

Source code in vidgear/gears/videogear.py
def stop(self):\n    \"\"\"\n    Safely terminates the thread, and release the respective multi-threaded resources.\n    \"\"\"\n    self.stream.stop()\n    # logged\n    self.__logging and logger.debug(\"Terminating VideoGear.\")\n    # clean queue\n    self.__stabilization_mode and self.__stabilizer_obj.clean()\n
"},{"location":"bonus/reference/webgear/","title":"WebGear API References","text":"

WebGear API usage examples can be found here \u27b6

WebGear API parameters are explained here \u27b6

WebGear is a powerful ASGI Video-Broadcaster API ideal for transmitting Motion-JPEG-frames from a single source to multiple recipients via the browser.

WebGear API works on Starlette's ASGI application and provides a highly extensible and flexible async wrapper around its complete framework. WebGear can flexibly interact with Starlette's ecosystem of shared middleware, mountable applications, Response classes, Routing tables, Static Files, Templating engine(with Jinja2), etc.

WebGear API uses an intraframe-only compression scheme under the hood where the sequence of video-frames are first encoded as JPEG-DIB (JPEG with Device-Independent Bit compression) and then streamed over HTTP using Starlette's Multipart Streaming Response and a Uvicorn ASGI Server. This method imposes lower processing and memory requirements, but the quality is not the best, since JPEG compression is not very efficient for motion video.

In layman's terms, WebGear acts as a powerful Video Broadcaster that transmits live video-frames to any web-browser in the network. Additionally, WebGear API also provides internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs, thereby granting it exclusive power for transferring frames incoming from any source to the network.

Source code in vidgear/gears/asyncio/webgear.py
class WebGear:\n    \"\"\"\n    WebGear is a powerful ASGI Video-Broadcaster API ideal for transmitting Motion-JPEG-frames from a single source to multiple recipients via the browser.\n\n    WebGear API works on Starlette's ASGI application and provides a highly extensible and flexible async wrapper around its complete framework. WebGear can\n    flexibly interact with Starlette's ecosystem of shared middleware, mountable applications, Response classes, Routing tables, Static Files, Templating\n    engine(with Jinja2), etc.\n\n    WebGear API uses an intraframe-only compression scheme under the hood where the sequence of video-frames are first encoded as JPEG-DIB (JPEG with Device-Independent Bit compression)\n    and then streamed over HTTP using Starlette's Multipart Streaming Response and a Uvicorn ASGI Server. This method imposes lower processing and memory requirements, but the quality\n    is not the best, since JPEG compression is not very efficient for motion video.\n\n    In layman's terms, WebGear acts as a powerful Video Broadcaster that transmits live video-frames to any web-browser in the network. Additionally, WebGear API also provides internal\n    wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs, thereby granting it exclusive power for transferring frames incoming from any source to the network.\n    \"\"\"\n\n    def __init__(\n        self,\n        enablePiCamera=False,\n        stabilize=False,\n        source=None,\n        camera_num=0,\n        stream_mode=False,\n        backend=0,\n        colorspace=None,\n        resolution=(640, 480),\n        framerate=25,\n        logging=False,\n        time_delay=0,\n        **options\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the WebGear class.\n\n        Parameters:\n            enablePiCamera (bool): provide access to PiGear(if True) or CamGear(if False) APIs respectively.\n            stabilize (bool): enable access to Stabilizer Class for stabilizing frames.\n            camera_num (int): selects the camera module index which will be used as Rpi source.\n            resolution (tuple): sets the resolution (i.e. `(width,height)`) of the Rpi source.\n            framerate (int/float): sets the framerate of the Rpi source.\n            source (based on input): defines the source for the input stream.\n            stream_mode (bool): controls the exclusive YouTube Mode.\n            backend (int): selects the backend for OpenCV's VideoCapture class.\n            colorspace (str): selects the colorspace of the input stream.\n            logging (bool): enables/disables logging.\n            time_delay (int): time delay (in sec) before start reading the frames.\n            options (dict): provides ability to alter Tweak Parameters of WebGear, CamGear, PiGear & Stabilizer.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # raise error(s) for critical Class imports\n        import_dependency_safe(\"starlette\" if starlette is None else \"\")\n        import_dependency_safe(\n            \"simplejpeg\" if simplejpeg is None else \"\", min_version=\"1.6.1\"\n        )\n\n        # initialize global params\n        self.__skip_generate_webdata = False  # generate webgear data by default\n        # define frame-compression handler\n        self.__jpeg_compression_quality = 90  # 90% quality\n        self.__jpeg_compression_fastdct = True  # fastest DCT on by default\n        self.__jpeg_compression_fastupsample = False  # fastupsample off by default\n        self.__jpeg_compression_colorspace = \"BGR\"  # use BGR colorspace by default\n        self.__frame_size_reduction = 25  # use 25% reduction\n        # retrieve interpolation for reduction\n        self.__interpolation = retrieve_best_interpolation(\n            [\"INTER_LINEAR_EXACT\", \"INTER_LINEAR\", \"INTER_AREA\"]\n        )\n\n        custom_video_endpoint = \"\"  # custom video endpoint path\n        custom_data_location = \"\"  # path to save data-files to custom location\n        data_path = \"\"  # path to WebGear data-files\n        overwrite_default = False\n        self.__enable_inf = False  # continue frames even when video ends.\n\n        # reformat dictionary\n        options = {str(k).strip(): v for k, v in options.items()}\n\n        # assign values to global variables if specified and valid\n        if options:\n            # check whether to disable Data-Files Auto-Generation WorkFlow\n            if \"skip_generate_webdata\" in options:\n                value = options[\"skip_generate_webdata\"]\n                # enable jpeg fastdct\n                if isinstance(value, bool):\n                    self.__skip_generate_webdata = value\n                else:\n                    logger.warning(\"Skipped invalid `skip_generate_webdata` value!\")\n                del options[\"skip_generate_webdata\"]  # clean\n\n            if \"jpeg_compression_colorspace\" in options:\n                value = options[\"jpeg_compression_colorspace\"]\n                if isinstance(value, str) and value.strip().upper() in [\n                    \"RGB\",\n                    \"BGR\",\n                    \"RGBX\",\n                    \"BGRX\",\n                    \"XBGR\",\n                    \"XRGB\",\n                    \"GRAY\",\n                    \"RGBA\",\n                    \"BGRA\",\n                    \"ABGR\",\n                    \"ARGB\",\n                    \"CMYK\",\n                ]:\n                    # set encoding colorspace\n                    self.__jpeg_compression_colorspace = value.strip().upper()\n                else:\n                    logger.warning(\n                        \"Skipped invalid `jpeg_compression_colorspace` value!\"\n                    )\n                del options[\"jpeg_compression_colorspace\"]  # clean\n\n            if \"jpeg_compression_quality\" in options:\n                value = options[\"jpeg_compression_quality\"]\n                # set valid jpeg quality\n                if isinstance(value, (int, float)) and value >= 10 and value <= 100:\n                    self.__jpeg_compression_quality = int(value)\n                else:\n                    logger.warning(\"Skipped invalid `jpeg_compression_quality` value!\")\n                del options[\"jpeg_compression_quality\"]  # clean\n\n            if \"jpeg_compression_fastdct\" in options:\n                value = options[\"jpeg_compression_fastdct\"]\n                # enable jpeg fastdct\n                if isinstance(value, bool):\n                    self.__jpeg_compression_fastdct = value\n                else:\n                    logger.warning(\"Skipped invalid `jpeg_compression_fastdct` value!\")\n                del options[\"jpeg_compression_fastdct\"]  # clean\n\n            if \"jpeg_compression_fastupsample\" in options:\n                value = options[\"jpeg_compression_fastupsample\"]\n                # enable jpeg  fastupsample\n                if isinstance(value, bool):\n                    self.__jpeg_compression_fastupsample = value\n                else:\n                    logger.warning(\n                        \"Skipped invalid `jpeg_compression_fastupsample` value!\"\n                    )\n                del options[\"jpeg_compression_fastupsample\"]  # clean\n\n            if \"frame_size_reduction\" in options:\n                value = options[\"frame_size_reduction\"]\n                if isinstance(value, (int, float)) and value >= 0 and value <= 90:\n                    self.__frame_size_reduction = value\n                else:\n                    logger.warning(\"Skipped invalid `frame_size_reduction` value!\")\n                del options[\"frame_size_reduction\"]  # clean\n\n            if \"custom_video_endpoint\" in options:\n                value = options[\"custom_video_endpoint\"]\n                if value and isinstance(value, str) and value.strip().isalnum():\n                    custom_video_endpoint = value.strip()\n                    logging and logger.critical(\n                        \"Using custom video endpoint path: `/{}`\".format(\n                            custom_video_endpoint\n                        )\n                    )\n                else:\n                    logger.warning(\"Skipped invalid `custom_video_endpoint` value!\")\n                del options[\"custom_video_endpoint\"]  # clean\n\n            if \"custom_data_location\" in options:\n                value = options[\"custom_data_location\"]\n                if value and isinstance(value, str):\n                    assert os.access(\n                        value, os.W_OK\n                    ), \"[WebGear:ERROR] :: Permission Denied!, cannot write WebGear data-files to '{}' directory!\".format(\n                        value\n                    )\n                    assert os.path.isdir(\n                        os.path.abspath(value)\n                    ), \"[WebGear:ERROR] :: `custom_data_location` value must be the path to a directory and not to a file!\"\n                    custom_data_location = os.path.abspath(value)\n                else:\n                    logger.warning(\"Skipped invalid `custom_data_location` value!\")\n                del options[\"custom_data_location\"]  # clean\n\n            if \"overwrite_default_files\" in options:\n                value = options[\"overwrite_default_files\"]\n                if isinstance(value, bool):\n                    overwrite_default = value\n                else:\n                    logger.warning(\"Skipped invalid `overwrite_default_files` value!\")\n                del options[\"overwrite_default_files\"]  # clean\n\n            if \"enable_infinite_frames\" in options:\n                value = options[\"enable_infinite_frames\"]\n                if isinstance(value, bool):\n                    self.__enable_inf = value\n                else:\n                    logger.warning(\"Skipped invalid `enable_infinite_frames` value!\")\n                del options[\"enable_infinite_frames\"]  # clean\n\n        # check if disable Data-Files Auto-Generation WorkFlow is disabled\n        if not self.__skip_generate_webdata:\n            # check if custom data path is specified\n            if custom_data_location:\n                data_path = generate_webdata(\n                    custom_data_location,\n                    c_name=\"webgear\",\n                    overwrite_default=overwrite_default,\n                    logging=logging,\n                )\n            else:\n                # otherwise generate suitable path\n                data_path = generate_webdata(\n                    os.path.join(expanduser(\"~\"), \".vidgear\"),\n                    c_name=\"webgear\",\n                    overwrite_default=overwrite_default,\n                    logging=logging,\n                )\n\n            # log it\n            self.__logging and logger.debug(\n                \"`{}` is the default location for saving WebGear data-files.\".format(\n                    data_path\n                )\n            )\n            # define Jinja2 templates handler\n            self.__templates = Jinja2Templates(\n                directory=\"{}/templates\".format(data_path)\n            )\n            # define routing tables\n            self.routes = [\n                Route(\"/\", endpoint=self.__homepage),\n                Route(\n                    \"/{}\".format(\n                        custom_video_endpoint if custom_video_endpoint else \"video\"\n                    ),\n                    endpoint=self.__video,\n                ),\n                Mount(\n                    \"/static\",\n                    app=StaticFiles(directory=\"{}/static\".format(data_path)),\n                    name=\"static\",\n                ),\n            ]\n        else:\n            # log it\n            self.__logging and logger.critical(\n                \"WebGear Data-Files Auto-Generation WorkFlow has been manually disabled.\"\n            )\n            # define routing tables\n            self.routes = [\n                Route(\n                    \"/{}\".format(\n                        custom_video_endpoint if custom_video_endpoint else \"video\"\n                    ),\n                    endpoint=self.__video,\n                ),\n            ]\n            # log exceptions\n            self.__logging and logger.warning(\n                \"Only `/video` route is available for this instance.\"\n            )\n\n        # define custom exception handlers\n        self.__exception_handlers = {404: self.__not_found, 500: self.__server_error}\n        # define middleware support\n        self.middleware = []\n        # Handle video source\n        if source is None:\n            self.config = {\"generator\": None}\n            self.__stream = None\n        else:\n            # define stream with necessary params\n            self.__stream = VideoGear(\n                enablePiCamera=enablePiCamera,\n                stabilize=stabilize,\n                source=source,\n                camera_num=camera_num,\n                stream_mode=stream_mode,\n                backend=backend,\n                colorspace=colorspace,\n                resolution=resolution,\n                framerate=framerate,\n                logging=logging,\n                time_delay=time_delay,\n                **options\n            )\n            # define default frame generator in configuration\n            self.config = {\"generator\": self.__producer}\n\n        # log if specified\n        if self.__logging:\n            if source is None:\n                logger.warning(\n                    \"Given source is of NoneType. Therefore, JPEG Frame-Compression is disabled!\"\n                )\n            else:\n                logger.debug(\n                    \"Enabling JPEG Frame-Compression with Colorspace:`{}`, Quality:`{}`%, Fastdct:`{}`, and Fastupsample:`{}`.\".format(\n                        self.__jpeg_compression_colorspace,\n                        self.__jpeg_compression_quality,\n                        \"enabled\" if self.__jpeg_compression_fastdct else \"disabled\",\n                        (\n                            \"enabled\"\n                            if self.__jpeg_compression_fastupsample\n                            else \"disabled\"\n                        ),\n                    )\n                )\n\n        # copying original routing tables for further validation\n        self.__rt_org_copy = self.routes[:]\n        # initialize blank frame\n        self.blank_frame = None\n        # keeps check if producer loop should be running\n        self.__isrunning = True\n\n    def __call__(self):\n        \"\"\"\n        Implements a custom Callable method for WebGear application.\n        \"\"\"\n        # validate routing tables\n        assert not (self.routes is None), \"Routing tables are NoneType!\"\n        if not isinstance(self.routes, list) or not all(\n            x in self.routes for x in self.__rt_org_copy\n        ):\n            raise RuntimeError(\"[WebGear:ERROR] :: Routing tables are not valid!\")\n\n        # validate middlewares\n        assert not (self.middleware is None), \"Middlewares are NoneType!\"\n        if self.middleware and (\n            not isinstance(self.middleware, list)\n            or not all(isinstance(x, Middleware) for x in self.middleware)\n        ):\n            raise RuntimeError(\"[WebGear:ERROR] :: Middlewares are not valid!\")\n\n        # validate assigned frame generator in WebGear configuration\n        if isinstance(self.config, dict) and \"generator\" in self.config:\n            # check if its  assigned value is a asynchronous generator\n            if self.config[\"generator\"] is None or not inspect.isasyncgen(\n                self.config[\"generator\"]()\n            ):\n                # otherwise raise error\n                raise ValueError(\n                    \"[WebGear:ERROR] :: Invalid configuration. Assigned generator must be a asynchronous generator function/method only!\"\n                )\n        else:\n            # raise error if validation fails\n            raise RuntimeError(\"[WebGear:ERROR] :: Assigned configuration is invalid!\")\n\n        # initiate stream\n        self.__logging and logger.debug(\"Initiating Video Streaming.\")\n        if not (self.__stream is None):\n            self.__stream.start()\n        # return Starlette application\n        self.__logging and logger.debug(\"Running Starlette application.\")\n        return Starlette(\n            debug=(True if self.__logging else False),\n            routes=self.routes,\n            middleware=self.middleware,\n            exception_handlers=self.__exception_handlers,\n            lifespan=self.__lifespan,\n        )\n\n    async def __producer(self):\n        \"\"\"\n        WebGear's default asynchronous frame producer/generator.\n        \"\"\"\n        # loop over frames\n        while self.__isrunning:\n            # read frame\n            frame = self.__stream.read()\n\n            # display blank if NoneType\n            if frame is None:\n                frame = (\n                    self.blank_frame\n                    if self.blank_frame is None\n                    else self.blank_frame[:]\n                )\n                if not self.__enable_inf:\n                    self.__isrunning = False\n            else:\n                # create blank\n                if self.blank_frame is None:\n                    self.blank_frame = create_blank_frame(\n                        frame=frame,\n                        text=\"No Input\" if self.__enable_inf else \"The End\",\n                        logging=self.__logging,\n                    )\n\n            # reducer frames size if specified\n            if self.__frame_size_reduction:\n                frame = await reducer(\n                    frame,\n                    percentage=self.__frame_size_reduction,\n                    interpolation=self.__interpolation,\n                )\n\n            # handle JPEG encoding\n            if self.__jpeg_compression_colorspace == \"GRAY\":\n                if frame.ndim == 2:\n                    # patch for https://gitlab.com/jfolz/simplejpeg/-/issues/11\n                    frame = np.expand_dims(frame, axis=2)\n                encodedImage = simplejpeg.encode_jpeg(\n                    frame,\n                    quality=self.__jpeg_compression_quality,\n                    colorspace=self.__jpeg_compression_colorspace,\n                    fastdct=self.__jpeg_compression_fastdct,\n                )\n            else:\n                encodedImage = simplejpeg.encode_jpeg(\n                    frame,\n                    quality=self.__jpeg_compression_quality,\n                    colorspace=self.__jpeg_compression_colorspace,\n                    colorsubsampling=\"422\",\n                    fastdct=self.__jpeg_compression_fastdct,\n                )\n\n            # yield frame in byte format\n            yield (\n                b\"--frame\\r\\nContent-Type:image/jpeg\\r\\n\\r\\n\" + encodedImage + b\"\\r\\n\"\n            )\n            # sleep for sometime.\n            await asyncio.sleep(0)\n\n    async def __video(self, scope):\n        \"\"\"\n        Returns a async video streaming response.\n        \"\"\"\n        assert scope[\"type\"] in [\"http\", \"https\"]\n        return StreamingResponse(\n            self.config[\"generator\"](),\n            media_type=\"multipart/x-mixed-replace; boundary=frame\",\n        )\n\n    async def __homepage(self, request):\n        \"\"\"\n        Returns an HTML index page.\n        \"\"\"\n        return (\n            self.__templates.TemplateResponse(request, \"index.html\")\n            if not self.__skip_generate_webdata\n            else JSONResponse(\n                {\n                    \"detail\": \"MESSAGE : WebGear Data-Files Auto-Generation WorkFlow is disabled!\"\n                },\n                status_code=404,\n            )\n        )\n\n    async def __not_found(self, request, exc):\n        \"\"\"\n        Returns an HTML 404 page.\n        \"\"\"\n        return (\n            self.__templates.TemplateResponse(request, \"404.html\", status_code=404)\n            if not self.__skip_generate_webdata\n            else JSONResponse(\n                {\n                    \"detail\": \"ERROR : {} :: MESSAGE : WebGear Data-Files Auto-Generation WorkFlow is disabled.\".format(\n                        exc.detail\n                    )\n                },\n                status_code=404,\n            )\n        )\n\n    async def __server_error(self, request, exc):\n        \"\"\"\n        Returns an HTML 500 page.\n        \"\"\"\n        return (\n            self.__templates.TemplateResponse(request, \"500.html\", status_code=500)\n            if not self.__skip_generate_webdata\n            else JSONResponse(\n                {\n                    \"detail\": \"ERROR : {} :: MESSAGE : WebGear Data-Files Auto-Generation WorkFlow is disabled.\".format(\n                        exc.detail if hasattr(exc, \"detail\") else repr(exc)\n                    )\n                },\n                status_code=500,\n            )\n        )\n\n    @contextlib.asynccontextmanager\n    async def __lifespan(self, context):\n        try:\n            yield\n        finally:\n            # close Video Server\n            self.shutdown()\n\n    def shutdown(self):\n        \"\"\"\n        Implements a Callable to be run on application shutdown\n        \"\"\"\n        if not (self.__stream is None):\n            self.__logging and logger.debug(\"Closing Video Streaming.\")\n            # stops producer\n            self.__isrunning = False\n            # stops VideoGear stream\n            self.__stream.stop()\n            # prevent any re-iteration\n            self.__stream = None\n

"},{"location":"bonus/reference/webgear/#vidgear.gears.asyncio.webgear.WebGear.__call__","title":"__call__(self) special","text":"

Implements a custom Callable method for WebGear application.

Source code in vidgear/gears/asyncio/webgear.py
def __call__(self):\n    \"\"\"\n    Implements a custom Callable method for WebGear application.\n    \"\"\"\n    # validate routing tables\n    assert not (self.routes is None), \"Routing tables are NoneType!\"\n    if not isinstance(self.routes, list) or not all(\n        x in self.routes for x in self.__rt_org_copy\n    ):\n        raise RuntimeError(\"[WebGear:ERROR] :: Routing tables are not valid!\")\n\n    # validate middlewares\n    assert not (self.middleware is None), \"Middlewares are NoneType!\"\n    if self.middleware and (\n        not isinstance(self.middleware, list)\n        or not all(isinstance(x, Middleware) for x in self.middleware)\n    ):\n        raise RuntimeError(\"[WebGear:ERROR] :: Middlewares are not valid!\")\n\n    # validate assigned frame generator in WebGear configuration\n    if isinstance(self.config, dict) and \"generator\" in self.config:\n        # check if its  assigned value is a asynchronous generator\n        if self.config[\"generator\"] is None or not inspect.isasyncgen(\n            self.config[\"generator\"]()\n        ):\n            # otherwise raise error\n            raise ValueError(\n                \"[WebGear:ERROR] :: Invalid configuration. Assigned generator must be a asynchronous generator function/method only!\"\n            )\n    else:\n        # raise error if validation fails\n        raise RuntimeError(\"[WebGear:ERROR] :: Assigned configuration is invalid!\")\n\n    # initiate stream\n    self.__logging and logger.debug(\"Initiating Video Streaming.\")\n    if not (self.__stream is None):\n        self.__stream.start()\n    # return Starlette application\n    self.__logging and logger.debug(\"Running Starlette application.\")\n    return Starlette(\n        debug=(True if self.__logging else False),\n        routes=self.routes,\n        middleware=self.middleware,\n        exception_handlers=self.__exception_handlers,\n        lifespan=self.__lifespan,\n    )\n
"},{"location":"bonus/reference/webgear/#vidgear.gears.asyncio.webgear.WebGear.__init__","title":"__init__(self, enablePiCamera=False, stabilize=False, source=None, camera_num=0, stream_mode=False, backend=0, colorspace=None, resolution=(640, 480), framerate=25, logging=False, time_delay=0, **options) special","text":"

This constructor method initializes the object state and attributes of the WebGear class.

Parameters:

Name Type Description Default enablePiCamera bool

provide access to PiGear(if True) or CamGear(if False) APIs respectively.

False stabilize bool

enable access to Stabilizer Class for stabilizing frames.

False camera_num int

selects the camera module index which will be used as Rpi source.

0 resolution tuple

sets the resolution (i.e. (width,height)) of the Rpi source.

(640, 480) framerate int/float

sets the framerate of the Rpi source.

25 source based on input

defines the source for the input stream.

None stream_mode bool

controls the exclusive YouTube Mode.

False backend int

selects the backend for OpenCV's VideoCapture class.

0 colorspace str

selects the colorspace of the input stream.

None logging bool

enables/disables logging.

False time_delay int

time delay (in sec) before start reading the frames.

0 options dict

provides ability to alter Tweak Parameters of WebGear, CamGear, PiGear & Stabilizer.

{} Source code in vidgear/gears/asyncio/webgear.py
def __init__(\n    self,\n    enablePiCamera=False,\n    stabilize=False,\n    source=None,\n    camera_num=0,\n    stream_mode=False,\n    backend=0,\n    colorspace=None,\n    resolution=(640, 480),\n    framerate=25,\n    logging=False,\n    time_delay=0,\n    **options\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the WebGear class.\n\n    Parameters:\n        enablePiCamera (bool): provide access to PiGear(if True) or CamGear(if False) APIs respectively.\n        stabilize (bool): enable access to Stabilizer Class for stabilizing frames.\n        camera_num (int): selects the camera module index which will be used as Rpi source.\n        resolution (tuple): sets the resolution (i.e. `(width,height)`) of the Rpi source.\n        framerate (int/float): sets the framerate of the Rpi source.\n        source (based on input): defines the source for the input stream.\n        stream_mode (bool): controls the exclusive YouTube Mode.\n        backend (int): selects the backend for OpenCV's VideoCapture class.\n        colorspace (str): selects the colorspace of the input stream.\n        logging (bool): enables/disables logging.\n        time_delay (int): time delay (in sec) before start reading the frames.\n        options (dict): provides ability to alter Tweak Parameters of WebGear, CamGear, PiGear & Stabilizer.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # raise error(s) for critical Class imports\n    import_dependency_safe(\"starlette\" if starlette is None else \"\")\n    import_dependency_safe(\n        \"simplejpeg\" if simplejpeg is None else \"\", min_version=\"1.6.1\"\n    )\n\n    # initialize global params\n    self.__skip_generate_webdata = False  # generate webgear data by default\n    # define frame-compression handler\n    self.__jpeg_compression_quality = 90  # 90% quality\n    self.__jpeg_compression_fastdct = True  # fastest DCT on by default\n    self.__jpeg_compression_fastupsample = False  # fastupsample off by default\n    self.__jpeg_compression_colorspace = \"BGR\"  # use BGR colorspace by default\n    self.__frame_size_reduction = 25  # use 25% reduction\n    # retrieve interpolation for reduction\n    self.__interpolation = retrieve_best_interpolation(\n        [\"INTER_LINEAR_EXACT\", \"INTER_LINEAR\", \"INTER_AREA\"]\n    )\n\n    custom_video_endpoint = \"\"  # custom video endpoint path\n    custom_data_location = \"\"  # path to save data-files to custom location\n    data_path = \"\"  # path to WebGear data-files\n    overwrite_default = False\n    self.__enable_inf = False  # continue frames even when video ends.\n\n    # reformat dictionary\n    options = {str(k).strip(): v for k, v in options.items()}\n\n    # assign values to global variables if specified and valid\n    if options:\n        # check whether to disable Data-Files Auto-Generation WorkFlow\n        if \"skip_generate_webdata\" in options:\n            value = options[\"skip_generate_webdata\"]\n            # enable jpeg fastdct\n            if isinstance(value, bool):\n                self.__skip_generate_webdata = value\n            else:\n                logger.warning(\"Skipped invalid `skip_generate_webdata` value!\")\n            del options[\"skip_generate_webdata\"]  # clean\n\n        if \"jpeg_compression_colorspace\" in options:\n            value = options[\"jpeg_compression_colorspace\"]\n            if isinstance(value, str) and value.strip().upper() in [\n                \"RGB\",\n                \"BGR\",\n                \"RGBX\",\n                \"BGRX\",\n                \"XBGR\",\n                \"XRGB\",\n                \"GRAY\",\n                \"RGBA\",\n                \"BGRA\",\n                \"ABGR\",\n                \"ARGB\",\n                \"CMYK\",\n            ]:\n                # set encoding colorspace\n                self.__jpeg_compression_colorspace = value.strip().upper()\n            else:\n                logger.warning(\n                    \"Skipped invalid `jpeg_compression_colorspace` value!\"\n                )\n            del options[\"jpeg_compression_colorspace\"]  # clean\n\n        if \"jpeg_compression_quality\" in options:\n            value = options[\"jpeg_compression_quality\"]\n            # set valid jpeg quality\n            if isinstance(value, (int, float)) and value >= 10 and value <= 100:\n                self.__jpeg_compression_quality = int(value)\n            else:\n                logger.warning(\"Skipped invalid `jpeg_compression_quality` value!\")\n            del options[\"jpeg_compression_quality\"]  # clean\n\n        if \"jpeg_compression_fastdct\" in options:\n            value = options[\"jpeg_compression_fastdct\"]\n            # enable jpeg fastdct\n            if isinstance(value, bool):\n                self.__jpeg_compression_fastdct = value\n            else:\n                logger.warning(\"Skipped invalid `jpeg_compression_fastdct` value!\")\n            del options[\"jpeg_compression_fastdct\"]  # clean\n\n        if \"jpeg_compression_fastupsample\" in options:\n            value = options[\"jpeg_compression_fastupsample\"]\n            # enable jpeg  fastupsample\n            if isinstance(value, bool):\n                self.__jpeg_compression_fastupsample = value\n            else:\n                logger.warning(\n                    \"Skipped invalid `jpeg_compression_fastupsample` value!\"\n                )\n            del options[\"jpeg_compression_fastupsample\"]  # clean\n\n        if \"frame_size_reduction\" in options:\n            value = options[\"frame_size_reduction\"]\n            if isinstance(value, (int, float)) and value >= 0 and value <= 90:\n                self.__frame_size_reduction = value\n            else:\n                logger.warning(\"Skipped invalid `frame_size_reduction` value!\")\n            del options[\"frame_size_reduction\"]  # clean\n\n        if \"custom_video_endpoint\" in options:\n            value = options[\"custom_video_endpoint\"]\n            if value and isinstance(value, str) and value.strip().isalnum():\n                custom_video_endpoint = value.strip()\n                logging and logger.critical(\n                    \"Using custom video endpoint path: `/{}`\".format(\n                        custom_video_endpoint\n                    )\n                )\n            else:\n                logger.warning(\"Skipped invalid `custom_video_endpoint` value!\")\n            del options[\"custom_video_endpoint\"]  # clean\n\n        if \"custom_data_location\" in options:\n            value = options[\"custom_data_location\"]\n            if value and isinstance(value, str):\n                assert os.access(\n                    value, os.W_OK\n                ), \"[WebGear:ERROR] :: Permission Denied!, cannot write WebGear data-files to '{}' directory!\".format(\n                    value\n                )\n                assert os.path.isdir(\n                    os.path.abspath(value)\n                ), \"[WebGear:ERROR] :: `custom_data_location` value must be the path to a directory and not to a file!\"\n                custom_data_location = os.path.abspath(value)\n            else:\n                logger.warning(\"Skipped invalid `custom_data_location` value!\")\n            del options[\"custom_data_location\"]  # clean\n\n        if \"overwrite_default_files\" in options:\n            value = options[\"overwrite_default_files\"]\n            if isinstance(value, bool):\n                overwrite_default = value\n            else:\n                logger.warning(\"Skipped invalid `overwrite_default_files` value!\")\n            del options[\"overwrite_default_files\"]  # clean\n\n        if \"enable_infinite_frames\" in options:\n            value = options[\"enable_infinite_frames\"]\n            if isinstance(value, bool):\n                self.__enable_inf = value\n            else:\n                logger.warning(\"Skipped invalid `enable_infinite_frames` value!\")\n            del options[\"enable_infinite_frames\"]  # clean\n\n    # check if disable Data-Files Auto-Generation WorkFlow is disabled\n    if not self.__skip_generate_webdata:\n        # check if custom data path is specified\n        if custom_data_location:\n            data_path = generate_webdata(\n                custom_data_location,\n                c_name=\"webgear\",\n                overwrite_default=overwrite_default,\n                logging=logging,\n            )\n        else:\n            # otherwise generate suitable path\n            data_path = generate_webdata(\n                os.path.join(expanduser(\"~\"), \".vidgear\"),\n                c_name=\"webgear\",\n                overwrite_default=overwrite_default,\n                logging=logging,\n            )\n\n        # log it\n        self.__logging and logger.debug(\n            \"`{}` is the default location for saving WebGear data-files.\".format(\n                data_path\n            )\n        )\n        # define Jinja2 templates handler\n        self.__templates = Jinja2Templates(\n            directory=\"{}/templates\".format(data_path)\n        )\n        # define routing tables\n        self.routes = [\n            Route(\"/\", endpoint=self.__homepage),\n            Route(\n                \"/{}\".format(\n                    custom_video_endpoint if custom_video_endpoint else \"video\"\n                ),\n                endpoint=self.__video,\n            ),\n            Mount(\n                \"/static\",\n                app=StaticFiles(directory=\"{}/static\".format(data_path)),\n                name=\"static\",\n            ),\n        ]\n    else:\n        # log it\n        self.__logging and logger.critical(\n            \"WebGear Data-Files Auto-Generation WorkFlow has been manually disabled.\"\n        )\n        # define routing tables\n        self.routes = [\n            Route(\n                \"/{}\".format(\n                    custom_video_endpoint if custom_video_endpoint else \"video\"\n                ),\n                endpoint=self.__video,\n            ),\n        ]\n        # log exceptions\n        self.__logging and logger.warning(\n            \"Only `/video` route is available for this instance.\"\n        )\n\n    # define custom exception handlers\n    self.__exception_handlers = {404: self.__not_found, 500: self.__server_error}\n    # define middleware support\n    self.middleware = []\n    # Handle video source\n    if source is None:\n        self.config = {\"generator\": None}\n        self.__stream = None\n    else:\n        # define stream with necessary params\n        self.__stream = VideoGear(\n            enablePiCamera=enablePiCamera,\n            stabilize=stabilize,\n            source=source,\n            camera_num=camera_num,\n            stream_mode=stream_mode,\n            backend=backend,\n            colorspace=colorspace,\n            resolution=resolution,\n            framerate=framerate,\n            logging=logging,\n            time_delay=time_delay,\n            **options\n        )\n        # define default frame generator in configuration\n        self.config = {\"generator\": self.__producer}\n\n    # log if specified\n    if self.__logging:\n        if source is None:\n            logger.warning(\n                \"Given source is of NoneType. Therefore, JPEG Frame-Compression is disabled!\"\n            )\n        else:\n            logger.debug(\n                \"Enabling JPEG Frame-Compression with Colorspace:`{}`, Quality:`{}`%, Fastdct:`{}`, and Fastupsample:`{}`.\".format(\n                    self.__jpeg_compression_colorspace,\n                    self.__jpeg_compression_quality,\n                    \"enabled\" if self.__jpeg_compression_fastdct else \"disabled\",\n                    (\n                        \"enabled\"\n                        if self.__jpeg_compression_fastupsample\n                        else \"disabled\"\n                    ),\n                )\n            )\n\n    # copying original routing tables for further validation\n    self.__rt_org_copy = self.routes[:]\n    # initialize blank frame\n    self.blank_frame = None\n    # keeps check if producer loop should be running\n    self.__isrunning = True\n
"},{"location":"bonus/reference/webgear/#vidgear.gears.asyncio.webgear.WebGear.shutdown","title":"shutdown(self)","text":"

Implements a Callable to be run on application shutdown

Source code in vidgear/gears/asyncio/webgear.py
def shutdown(self):\n    \"\"\"\n    Implements a Callable to be run on application shutdown\n    \"\"\"\n    if not (self.__stream is None):\n        self.__logging and logger.debug(\"Closing Video Streaming.\")\n        # stops producer\n        self.__isrunning = False\n        # stops VideoGear stream\n        self.__stream.stop()\n        # prevent any re-iteration\n        self.__stream = None\n
"},{"location":"bonus/reference/webgear_rtc/","title":"WebGear_RTC API References","text":"

WebGear_RTC API usage examples can be found here \u27b6

WebGear_RTC API parameters are explained here \u27b6

WebGear_RTC is similar to WeGear API in many aspects but utilizes WebRTC technology under the hood instead of Motion JPEG, which makes it suitable for building powerful video-streaming solutions for all modern browsers as well as native clients available on all major platforms.

WebGear_RTC is implemented with the help of aiortc library which is built on top of asynchronous I/O framework for Web Real-Time Communication (WebRTC) and Object Real-Time Communication (ORTC) and supports many features like SDP generation/parsing, Interactive Connectivity Establishment with half-trickle and mDNS support, DTLS key and certificate generation, DTLS handshake, etc.

WebGear_RTC can handle multiple consumers seamlessly and provides native support for ICE (Interactive Connectivity Establishment) protocol, STUN (Session Traversal Utilities for NAT), and TURN (Traversal Using Relays around NAT) servers that help us to easily establish direct media connection with the remote peers for uninterrupted data flow. It also allows us to define our custom Server as a source to transform frames easily before sending them across the network(see this doc example).

WebGear_RTC API works in conjunction with Starlette ASGI application and can also flexibly interact with Starlette's ecosystem of shared middleware, mountable applications, Response classes, Routing tables, Static Files, Templating engine(with Jinja2), etc.

Additionally, WebGear_RTC API also provides internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs.

Source code in vidgear/gears/asyncio/webgear_rtc.py
class WebGear_RTC:\n    \"\"\"\n    WebGear_RTC is similar to WeGear API in many aspects but utilizes WebRTC technology under the hood instead of Motion JPEG, which\n    makes it suitable for building powerful video-streaming solutions for all modern browsers as well as native clients available on\n    all major platforms.\n\n    WebGear_RTC is implemented with the help of aiortc library which is built on top of asynchronous I/O framework for Web Real-Time\n    Communication (WebRTC) and Object Real-Time Communication (ORTC) and supports many features like SDP generation/parsing, Interactive\n    Connectivity Establishment with half-trickle and mDNS support, DTLS key and certificate generation, DTLS handshake, etc.\n\n    WebGear_RTC can handle multiple consumers seamlessly and provides native support for ICE (Interactive Connectivity Establishment)\n    protocol, STUN (Session Traversal Utilities for NAT), and TURN (Traversal Using Relays around NAT) servers that help us to easily\n    establish direct media connection with the remote peers for uninterrupted data flow. It also allows us to define our custom Server\n    as a source to transform frames easily before sending them across the network(see this doc example).\n\n    WebGear_RTC API works in conjunction with Starlette ASGI application and can also flexibly interact with Starlette's ecosystem of\n    shared middleware, mountable applications, Response classes, Routing tables, Static Files, Templating engine(with Jinja2), etc.\n\n    Additionally, WebGear_RTC API also provides internal wrapper around VideoGear, which itself provides internal access to both\n    CamGear and PiGear APIs.\n    \"\"\"\n\n    def __init__(\n        self,\n        enablePiCamera=False,\n        stabilize=False,\n        source=None,\n        camera_num=0,\n        stream_mode=False,\n        backend=0,\n        colorspace=None,\n        resolution=(640, 480),\n        framerate=25,\n        logging=False,\n        time_delay=0,\n        **options\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the WebGear_RTC class.\n\n        Parameters:\n            enablePiCamera (bool): provide access to PiGear(if True) or CamGear(if False) APIs respectively.\n            stabilize (bool): enable access to Stabilizer Class for stabilizing frames.\n            camera_num (int): selects the camera module index which will be used as Rpi source.\n            resolution (tuple): sets the resolution (i.e. `(width,height)`) of the Rpi source.\n            framerate (int/float): sets the framerate of the Rpi source.\n            source (based on input): defines the source for the input stream.\n            stream_mode (bool): controls the exclusive YouTube Mode.\n            backend (int): selects the backend for OpenCV's VideoCapture class.\n            colorspace (str): selects the colorspace of the input stream.\n            logging (bool): enables/disables logging.\n            time_delay (int): time delay (in sec) before start reading the frames.\n            options (dict): provides ability to alter Tweak Parameters of WebGear_RTC, CamGear, PiGear & Stabilizer.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # raise error(s) for critical Class imports\n        import_dependency_safe(\"starlette\" if starlette is None else \"\")\n        import_dependency_safe(\"aiortc\" if aiortc is None else \"\")\n\n        # initialize global params\n        custom_data_location = \"\"  # path to save data-files to custom location\n        data_path = \"\"  # path to WebGear_RTC data-files\n        overwrite_default = False\n        self.__relay = None  # act as broadcaster\n\n        # reformat dictionary\n        options = {str(k).strip(): v for k, v in options.items()}\n\n        # assign values to global variables if specified and valid\n        if options:\n            if \"custom_data_location\" in options:\n                value = options[\"custom_data_location\"]\n                if isinstance(value, str):\n                    assert os.access(\n                        value, os.W_OK\n                    ), \"[WebGear_RTC:ERROR] :: Permission Denied!, cannot write WebGear_RTC data-files to '{}' directory!\".format(\n                        value\n                    )\n                    assert os.path.isdir(\n                        os.path.abspath(value)\n                    ), \"[WebGear_RTC:ERROR] :: `custom_data_location` value must be the path to a directory and not to a file!\"\n                    custom_data_location = os.path.abspath(value)\n                else:\n                    logger.warning(\"Skipped invalid `custom_data_location` value!\")\n                del options[\"custom_data_location\"]  # clean\n\n            if \"overwrite_default_files\" in options:\n                value = options[\"overwrite_default_files\"]\n                if isinstance(value, bool):\n                    overwrite_default = value\n                else:\n                    logger.warning(\"Skipped invalid `overwrite_default_files` value!\")\n                del options[\"overwrite_default_files\"]  # clean\n\n            if \"enable_live_broadcast\" in options:\n                value = options[\"enable_live_broadcast\"]\n                if isinstance(value, bool):\n                    if value:\n                        self.__relay = MediaRelay()\n                        options[\"enable_infinite_frames\"] = (\n                            True  # enforce infinite frames\n                        )\n                        logger.critical(\n                            \"Enabled live broadcasting for Peer connection(s).\"\n                        )\n                    else:\n                        None\n                else:\n                    logger.warning(\"Skipped invalid `enable_live_broadcast` value!\")\n                del options[\"enable_live_broadcast\"]  # clean\n\n        # check if custom certificates path is specified\n        if custom_data_location:\n            data_path = generate_webdata(\n                custom_data_location,\n                c_name=\"webgear_rtc\",\n                overwrite_default=overwrite_default,\n                logging=logging,\n            )\n        else:\n            # otherwise generate suitable path\n            data_path = generate_webdata(\n                os.path.join(expanduser(\"~\"), \".vidgear\"),\n                c_name=\"webgear_rtc\",\n                overwrite_default=overwrite_default,\n                logging=logging,\n            )\n\n        # log it\n        self.__logging and logger.debug(\n            \"`{}` is the default location for saving WebGear_RTC data-files.\".format(\n                data_path\n            )\n        )\n\n        # define Jinja2 templates handler\n        self.__templates = Jinja2Templates(directory=\"{}/templates\".format(data_path))\n\n        # define custom exception handlers\n        self.__exception_handlers = {404: self.__not_found, 500: self.__server_error}\n        # define routing tables\n        self.routes = [\n            Route(\"/\", endpoint=self.__homepage),\n            Route(\"/offer\", self.__offer, methods=[\"GET\", \"POST\"]),\n            Mount(\n                \"/static\",\n                app=StaticFiles(directory=\"{}/static\".format(data_path)),\n                name=\"static\",\n            ),\n        ]\n\n        # define middleware support\n        self.middleware = []\n\n        # Handle RTC video server\n        if \"custom_stream\" in options or not (source is None):\n            # Handle video source\n            self.__default_rtc_server = RTC_VideoServer(\n                enablePiCamera=enablePiCamera,\n                stabilize=stabilize,\n                source=source,\n                camera_num=camera_num,\n                stream_mode=stream_mode,\n                backend=backend,\n                colorspace=colorspace,\n                resolution=resolution,\n                framerate=framerate,\n                logging=logging,\n                time_delay=time_delay,\n                **options\n            )\n            # add exclusive reset connection node\n            self.routes.append(\n                Route(\"/close_connection\", self.__reset_connections, methods=[\"POST\"])\n            )\n        else:\n            raise ValueError(\n                \"[WebGear_RTC:ERROR] :: Source cannot be NoneType without Custom Stream(`custom_stream`) defined!\"\n            )\n\n        # copying original routing tables for further validation\n        self.__rt_org_copy = self.routes[:]\n        # collects peer RTC connections\n        self.__pcs = set()\n\n    def __call__(self):\n        \"\"\"\n        Implements a custom Callable method for WebGear_RTC application.\n        \"\"\"\n        # validate routing tables\n        assert not (self.routes is None), \"Routing tables are NoneType!\"\n        if not isinstance(self.routes, list) or not all(\n            x in self.routes for x in self.__rt_org_copy\n        ):\n            raise RuntimeError(\"[WebGear_RTC:ERROR] :: Routing tables are not valid!\")\n\n        # validate middlewares\n        assert not (self.middleware is None), \"Middlewares are NoneType!\"\n        if self.middleware and (\n            not isinstance(self.middleware, list)\n            or not all(isinstance(x, Middleware) for x in self.middleware)\n        ):\n            raise RuntimeError(\"[WebGear_RTC:ERROR] :: Middlewares are not valid!\")\n\n        # return Starlette application\n        self.__logging and logger.debug(\"Running Starlette application.\")\n        return Starlette(\n            debug=(True if self.__logging else False),\n            routes=self.routes,\n            middleware=self.middleware,\n            exception_handlers=self.__exception_handlers,\n            lifespan=self.__lifespan,\n        )\n\n    async def __offer(self, request):\n        \"\"\"\n        Generates JSON Response with a WebRTC Peer Connection of Video Server.\n        \"\"\"\n        # get offer from params\n        params = await request.json()\n        offer = RTCSessionDescription(sdp=params[\"sdp\"], type=params[\"type\"])\n\n        # initiate stream\n        if not (self.__default_rtc_server is None) and not (\n            self.__default_rtc_server.is_launched\n        ):\n            self.__logging and logger.debug(\"Initiating Video Streaming.\")\n            self.__default_rtc_server.launch()\n\n        # setup RTC peer connection - interface represents a WebRTC connection\n        # between the local computer and a remote peer.\n        pc = RTCPeerConnection()\n        self.__pcs.add(pc)\n        self.__logging and logger.info(\"Created WebRTC Peer Connection.\")\n\n        # track ICE connection state changes\n        @pc.on(\"iceconnectionstatechange\")\n        async def on_iceconnectionstatechange():\n            if pc.iceConnectionState == \"failed\":\n                logger.error(\"ICE connection state failed.\")\n                # check if Live Broadcasting is enabled\n                if self.__relay is None:\n                    # if not, close connection.\n                    await pc.close()\n                    self.__pcs.discard(pc)\n            else:\n                logger.debug(\"ICE connection state is %s\" % pc.iceConnectionState)\n\n        # Change the remote description associated with the connection.\n        await pc.setRemoteDescription(offer)\n        # retrieve list of RTCRtpTransceiver objects that are currently attached to the connection\n        for t in pc.getTransceivers():\n            # Increments performance significantly, IDK why this works as H265 codec is not even supported :D\n            capabilities = RTCRtpSender.getCapabilities(\"video\")\n            preferences = list(filter(lambda x: x.name == \"H265\", capabilities.codecs))\n            t.setCodecPreferences(preferences)\n            # add video server to peer track\n            if t.kind == \"video\":\n                pc.addTrack(\n                    self.__relay.subscribe(self.__default_rtc_server)\n                    if not (self.__relay is None)\n                    else self.__default_rtc_server\n                )\n\n        # Create an SDP answer to an offer received from a remote peer\n        answer = await pc.createAnswer()\n\n        # Change the local description for the answer\n        await pc.setLocalDescription(answer)\n\n        # return Starlette json response\n        return JSONResponse(\n            {\"sdp\": pc.localDescription.sdp, \"type\": pc.localDescription.type}\n        )\n\n    async def __homepage(self, request):\n        \"\"\"\n        Return an HTML index page.\n        \"\"\"\n        return self.__templates.TemplateResponse(request, \"index.html\")\n\n    async def __not_found(self, request, exc):\n        \"\"\"\n        Return an HTML 404 page.\n        \"\"\"\n        return self.__templates.TemplateResponse(request, \"404.html\", status_code=404)\n\n    async def __server_error(self, request, exc):\n        \"\"\"\n        Return an HTML 500 page.\n        \"\"\"\n        return self.__templates.TemplateResponse(request, \"500.html\", status_code=500)\n\n    async def __reset_connections(self, request):\n        \"\"\"\n        Resets all connections and recreates VideoServer timestamps\n        \"\"\"\n        # get additional parameter\n        parameter = await request.json()\n        # check if Live Broadcasting is enabled\n        if (\n            self.__relay is None\n            and not (self.__default_rtc_server is None)\n            and (self.__default_rtc_server.is_running)\n        ):\n            logger.critical(\"Resetting Server\")\n            # close old peer connections\n            if parameter != 0:  # disable if specified explicitly\n                coros = [\n                    pc.close() for pc in self.__pcs if pc.iceConnectionState != \"closed\"\n                ]\n                await asyncio.gather(*coros)\n                self.__pcs.clear()\n            await self.__default_rtc_server.reset()\n            return PlainTextResponse(\"OK\")\n        else:\n            # if does, then do nothing\n            return PlainTextResponse(\"DISABLED\")\n\n    @contextlib.asynccontextmanager\n    async def __lifespan(self, context):\n        try:\n            yield\n        finally:\n            # close Video Server\n            self.shutdown()\n            # collects peer RTC connections\n            coros = [\n                pc.close() for pc in self.__pcs if pc.iceConnectionState != \"closed\"\n            ]\n            await asyncio.gather(*coros)\n            self.__pcs.clear()\n\n    def shutdown(self):\n        \"\"\"\n        Gracefully shutdown video-server\n        \"\"\"\n        if not (self.__default_rtc_server is None):\n            self.__logging and logger.debug(\"Closing Video Server.\")\n            self.__default_rtc_server.terminate()\n            self.__default_rtc_server = None\n        # terminate internal server aswell.\n        self.__default_rtc_server = None\n

"},{"location":"bonus/reference/webgear_rtc/#vidgear.gears.asyncio.webgear_rtc.WebGear_RTC.__call__","title":"__call__(self) special","text":"

Implements a custom Callable method for WebGear_RTC application.

Source code in vidgear/gears/asyncio/webgear_rtc.py
def __call__(self):\n    \"\"\"\n    Implements a custom Callable method for WebGear_RTC application.\n    \"\"\"\n    # validate routing tables\n    assert not (self.routes is None), \"Routing tables are NoneType!\"\n    if not isinstance(self.routes, list) or not all(\n        x in self.routes for x in self.__rt_org_copy\n    ):\n        raise RuntimeError(\"[WebGear_RTC:ERROR] :: Routing tables are not valid!\")\n\n    # validate middlewares\n    assert not (self.middleware is None), \"Middlewares are NoneType!\"\n    if self.middleware and (\n        not isinstance(self.middleware, list)\n        or not all(isinstance(x, Middleware) for x in self.middleware)\n    ):\n        raise RuntimeError(\"[WebGear_RTC:ERROR] :: Middlewares are not valid!\")\n\n    # return Starlette application\n    self.__logging and logger.debug(\"Running Starlette application.\")\n    return Starlette(\n        debug=(True if self.__logging else False),\n        routes=self.routes,\n        middleware=self.middleware,\n        exception_handlers=self.__exception_handlers,\n        lifespan=self.__lifespan,\n    )\n
"},{"location":"bonus/reference/webgear_rtc/#vidgear.gears.asyncio.webgear_rtc.WebGear_RTC.__init__","title":"__init__(self, enablePiCamera=False, stabilize=False, source=None, camera_num=0, stream_mode=False, backend=0, colorspace=None, resolution=(640, 480), framerate=25, logging=False, time_delay=0, **options) special","text":"

This constructor method initializes the object state and attributes of the WebGear_RTC class.

Parameters:

Name Type Description Default enablePiCamera bool

provide access to PiGear(if True) or CamGear(if False) APIs respectively.

False stabilize bool

enable access to Stabilizer Class for stabilizing frames.

False camera_num int

selects the camera module index which will be used as Rpi source.

0 resolution tuple

sets the resolution (i.e. (width,height)) of the Rpi source.

(640, 480) framerate int/float

sets the framerate of the Rpi source.

25 source based on input

defines the source for the input stream.

None stream_mode bool

controls the exclusive YouTube Mode.

False backend int

selects the backend for OpenCV's VideoCapture class.

0 colorspace str

selects the colorspace of the input stream.

None logging bool

enables/disables logging.

False time_delay int

time delay (in sec) before start reading the frames.

0 options dict

provides ability to alter Tweak Parameters of WebGear_RTC, CamGear, PiGear & Stabilizer.

{} Source code in vidgear/gears/asyncio/webgear_rtc.py
def __init__(\n    self,\n    enablePiCamera=False,\n    stabilize=False,\n    source=None,\n    camera_num=0,\n    stream_mode=False,\n    backend=0,\n    colorspace=None,\n    resolution=(640, 480),\n    framerate=25,\n    logging=False,\n    time_delay=0,\n    **options\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the WebGear_RTC class.\n\n    Parameters:\n        enablePiCamera (bool): provide access to PiGear(if True) or CamGear(if False) APIs respectively.\n        stabilize (bool): enable access to Stabilizer Class for stabilizing frames.\n        camera_num (int): selects the camera module index which will be used as Rpi source.\n        resolution (tuple): sets the resolution (i.e. `(width,height)`) of the Rpi source.\n        framerate (int/float): sets the framerate of the Rpi source.\n        source (based on input): defines the source for the input stream.\n        stream_mode (bool): controls the exclusive YouTube Mode.\n        backend (int): selects the backend for OpenCV's VideoCapture class.\n        colorspace (str): selects the colorspace of the input stream.\n        logging (bool): enables/disables logging.\n        time_delay (int): time delay (in sec) before start reading the frames.\n        options (dict): provides ability to alter Tweak Parameters of WebGear_RTC, CamGear, PiGear & Stabilizer.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # raise error(s) for critical Class imports\n    import_dependency_safe(\"starlette\" if starlette is None else \"\")\n    import_dependency_safe(\"aiortc\" if aiortc is None else \"\")\n\n    # initialize global params\n    custom_data_location = \"\"  # path to save data-files to custom location\n    data_path = \"\"  # path to WebGear_RTC data-files\n    overwrite_default = False\n    self.__relay = None  # act as broadcaster\n\n    # reformat dictionary\n    options = {str(k).strip(): v for k, v in options.items()}\n\n    # assign values to global variables if specified and valid\n    if options:\n        if \"custom_data_location\" in options:\n            value = options[\"custom_data_location\"]\n            if isinstance(value, str):\n                assert os.access(\n                    value, os.W_OK\n                ), \"[WebGear_RTC:ERROR] :: Permission Denied!, cannot write WebGear_RTC data-files to '{}' directory!\".format(\n                    value\n                )\n                assert os.path.isdir(\n                    os.path.abspath(value)\n                ), \"[WebGear_RTC:ERROR] :: `custom_data_location` value must be the path to a directory and not to a file!\"\n                custom_data_location = os.path.abspath(value)\n            else:\n                logger.warning(\"Skipped invalid `custom_data_location` value!\")\n            del options[\"custom_data_location\"]  # clean\n\n        if \"overwrite_default_files\" in options:\n            value = options[\"overwrite_default_files\"]\n            if isinstance(value, bool):\n                overwrite_default = value\n            else:\n                logger.warning(\"Skipped invalid `overwrite_default_files` value!\")\n            del options[\"overwrite_default_files\"]  # clean\n\n        if \"enable_live_broadcast\" in options:\n            value = options[\"enable_live_broadcast\"]\n            if isinstance(value, bool):\n                if value:\n                    self.__relay = MediaRelay()\n                    options[\"enable_infinite_frames\"] = (\n                        True  # enforce infinite frames\n                    )\n                    logger.critical(\n                        \"Enabled live broadcasting for Peer connection(s).\"\n                    )\n                else:\n                    None\n            else:\n                logger.warning(\"Skipped invalid `enable_live_broadcast` value!\")\n            del options[\"enable_live_broadcast\"]  # clean\n\n    # check if custom certificates path is specified\n    if custom_data_location:\n        data_path = generate_webdata(\n            custom_data_location,\n            c_name=\"webgear_rtc\",\n            overwrite_default=overwrite_default,\n            logging=logging,\n        )\n    else:\n        # otherwise generate suitable path\n        data_path = generate_webdata(\n            os.path.join(expanduser(\"~\"), \".vidgear\"),\n            c_name=\"webgear_rtc\",\n            overwrite_default=overwrite_default,\n            logging=logging,\n        )\n\n    # log it\n    self.__logging and logger.debug(\n        \"`{}` is the default location for saving WebGear_RTC data-files.\".format(\n            data_path\n        )\n    )\n\n    # define Jinja2 templates handler\n    self.__templates = Jinja2Templates(directory=\"{}/templates\".format(data_path))\n\n    # define custom exception handlers\n    self.__exception_handlers = {404: self.__not_found, 500: self.__server_error}\n    # define routing tables\n    self.routes = [\n        Route(\"/\", endpoint=self.__homepage),\n        Route(\"/offer\", self.__offer, methods=[\"GET\", \"POST\"]),\n        Mount(\n            \"/static\",\n            app=StaticFiles(directory=\"{}/static\".format(data_path)),\n            name=\"static\",\n        ),\n    ]\n\n    # define middleware support\n    self.middleware = []\n\n    # Handle RTC video server\n    if \"custom_stream\" in options or not (source is None):\n        # Handle video source\n        self.__default_rtc_server = RTC_VideoServer(\n            enablePiCamera=enablePiCamera,\n            stabilize=stabilize,\n            source=source,\n            camera_num=camera_num,\n            stream_mode=stream_mode,\n            backend=backend,\n            colorspace=colorspace,\n            resolution=resolution,\n            framerate=framerate,\n            logging=logging,\n            time_delay=time_delay,\n            **options\n        )\n        # add exclusive reset connection node\n        self.routes.append(\n            Route(\"/close_connection\", self.__reset_connections, methods=[\"POST\"])\n        )\n    else:\n        raise ValueError(\n            \"[WebGear_RTC:ERROR] :: Source cannot be NoneType without Custom Stream(`custom_stream`) defined!\"\n        )\n\n    # copying original routing tables for further validation\n    self.__rt_org_copy = self.routes[:]\n    # collects peer RTC connections\n    self.__pcs = set()\n
"},{"location":"bonus/reference/webgear_rtc/#vidgear.gears.asyncio.webgear_rtc.WebGear_RTC.shutdown","title":"shutdown(self)","text":"

Gracefully shutdown video-server

Source code in vidgear/gears/asyncio/webgear_rtc.py
def shutdown(self):\n    \"\"\"\n    Gracefully shutdown video-server\n    \"\"\"\n    if not (self.__default_rtc_server is None):\n        self.__logging and logger.debug(\"Closing Video Server.\")\n        self.__default_rtc_server.terminate()\n        self.__default_rtc_server = None\n    # terminate internal server aswell.\n    self.__default_rtc_server = None\n
"},{"location":"bonus/reference/writegear/","title":"WriteGear API References","text":"

WriteGear API usage examples for: Compression Mode \u27b6 and Non-Compression Mode \u27b6

WriteGear API parameters are explained for: Compression Mode \u27b6 and Non-Compression Mode \u27b6

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 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 third-party API.

In addition to this, WriteGear also provides flexible access to OpenCV's VideoWriter API tools for video-frames encoding without compression.

Modes of Operation

WriteGear primarily operates in following modes:

Source code in vidgear/gears/writegear.py
class WriteGear:\n    \"\"\"\n    WriteGear handles various powerful Video-Writer Tools that provide us the freedom to do almost anything imaginable with multimedia data.\n\n    WriteGear API provides a complete, flexible, and robust wrapper around FFmpeg, a leading multimedia framework. WriteGear can process real-time frames into a lossless\n    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\n    Live-Streaming (such as for Twitch) and Multiplexing Video-Audio with real-time frames in way fewer lines of code.\n\n    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\n    third-party API.\n\n    In addition to this, WriteGear also provides flexible access to OpenCV's VideoWriter API tools for video-frames encoding without compression.\n\n    ??? tip \"Modes of Operation\"\n\n        WriteGear primarily operates in following modes:\n\n        * **Compression Mode**: In this mode, WriteGear utilizes powerful **FFmpeg** inbuilt encoders to encode lossless multimedia files.\n                                This mode provides us the ability to exploit almost any parameter available within FFmpeg, effortlessly and flexibly,\n                                and while doing that it robustly handles all errors/warnings quietly.\n\n        * **Non-Compression Mode**: In this mode, WriteGear utilizes basic **OpenCV's inbuilt VideoWriter API** tools. This mode also supports all\n                                    parameters manipulation available within VideoWriter API, but it lacks the ability to manipulate encoding parameters\n                                    and other important features like video compression, audio encoding, etc.\n\n    \"\"\"\n\n    def __init__(\n        self,\n        output=\"\",\n        compression_mode=True,\n        custom_ffmpeg=\"\",\n        logging=False,\n        **output_params\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the WriteGear class.\n\n        Parameters:\n            output (str): sets the valid filename/path/URL for encoding.\n            compression_mode (bool): selects the WriteGear's Primary Mode of Operation.\n            custom_ffmpeg (str): assigns the location of custom path/directory for custom FFmpeg executables.\n            logging (bool): enables/disables logging.\n            output_params (dict): provides the flexibility to control supported internal parameters and FFmpeg properties.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # check if user not using depreciated `output_filename` parameter\n        assert (\n            not \"output_filename\" in output_params\n        ), \"[WriteGear:ERROR] :: The `output_filename` parameter has been renamed to `output`. Refer Docs for more info.\"\n\n        # assign parameter values to class variables\n        # enables compression if enabled\n        self.__compression = (\n            compression_mode if isinstance(compression_mode, bool) else False\n        )\n        # specifies if machine in-use is running Windows OS or not\n        self.__os_windows = True if os.name == \"nt\" else False\n\n        # initialize various important class variables\n        self.__output_parameters = {}  # handles output parameters\n        self.__inputheight = None  # handles input frames height\n        self.__inputwidth = None  # handles input frames width\n        self.__inputchannels = None  # handles input frames channels\n        self.__inputdtype = None  # handles input frames dtype\n        self.__process = None  # handles Encoding class/process\n        self.__ffmpeg = \"\"  # handles valid FFmpeg binaries location\n        self.__initiate_process = (\n            True  # handles initiate one-time process for generating pipeline\n        )\n        self.__ffmpeg_window_disabler_patch = (\n            False  # handles disabling window for ffmpeg subprocess on Windows\n        )\n        self.__out_file = None  # handles output\n        gstpipeline_mode = False  # handles GStreamer Pipeline Mode\n\n        # handles output\n        if not output:\n            # raise error otherwise\n            raise ValueError(\n                \"[WriteGear:ERROR] :: Kindly provide a valid `output` value. Refer Docs for more info.\"\n            )\n        else:\n            # validate output is a system file/directory\n            # and Whether WriteGear has the write rights\n            # to specified file/directory or not\n            abs_path = os.path.abspath(output)\n            if check_WriteAccess(\n                os.path.dirname(abs_path),\n                is_windows=self.__os_windows,\n                logging=self.__logging,\n            ):\n                # check if given path is directory\n                if os.path.isdir(abs_path):\n                    # then, auto-assign valid name and adds it to path\n                    abs_path = os.path.join(\n                        abs_path,\n                        \"VidGear-{}.mp4\".format(time.strftime(\"%Y%m%d-%H%M%S\")),\n                    )\n                # assign output file absolute\n                # path to class variable if valid\n                self.__out_file = abs_path\n            else:\n                # log note otherwise\n                logger.info(\n                    \"`{}` isn't a valid system path or directory. Skipped!\".format(\n                        output\n                    )\n                )\n\n        # cleans and reformat output parameters\n        self.__output_parameters = {\n            str(k).strip(): (v.strip() if isinstance(v, str) else v)\n            for k, v in output_params.items()\n        }\n        # log it if specified\n        self.__logging and logger.debug(\n            \"Output Parameters: `{}`\".format(self.__output_parameters)\n        )\n\n        # handles FFmpeg binaries validity\n        # in Compression mode\n        if self.__compression:\n            # log it if specified\n            self.__logging and logger.debug(\n                \"Compression Mode is enabled therefore checking for valid FFmpeg executable.\"\n            )\n\n            # handles where to save the downloaded FFmpeg Static Binaries\n            # on Windows(if specified)\n            __ffmpeg_download_path = self.__output_parameters.pop(\n                \"-ffmpeg_download_path\", \"\"\n            )\n            # check if value is valid\n            if not isinstance(__ffmpeg_download_path, (str)):\n                # reset improper values\n                __ffmpeg_download_path = \"\"\n\n            # handle user-defined output resolution (must be a tuple or list)\n            # in Compression Mode only.\n            self.__output_dimensions = self.__output_parameters.pop(\n                \"-output_dimensions\", None\n            )\n            # check if value is valid\n            if not isinstance(self.__output_dimensions, (list, tuple)):\n                # reset improper values\n                self.__output_dimensions = None\n\n            # handle user defined input framerate of encoding pipeline\n            # in Compression Mode only.\n            self.__inputframerate = self.__output_parameters.pop(\n                \"-input_framerate\", 0.0\n            )\n            # check if value is valid\n            if not isinstance(self.__inputframerate, (float, int)):\n                # reset improper values\n                self.__inputframerate = 0.0\n            else:\n                # must be float\n                self.__inputframerate = float(self.__inputframerate)\n\n            # handle user-defined input frames pixel-format in Compression Mode only.\n            self.__inputpixfmt = self.__output_parameters.pop(\"-input_pixfmt\", None)\n            # check if value is valid\n            if not isinstance(self.__inputpixfmt, str):\n                # reset improper values\n                self.__inputpixfmt = None\n            else:\n                # must be exact\n                self.__inputpixfmt = self.__inputpixfmt.strip()\n\n            # handle user-defined FFmpeg command pre-headers(must be a list)\n            # in Compression Mode only.\n            self.__ffmpeg_preheaders = self.__output_parameters.pop(\"-ffpreheaders\", [])\n            # check if value is valid\n            if not isinstance(self.__ffmpeg_preheaders, list):\n                # reset improper values\n                self.__ffmpeg_preheaders = []\n\n            # handle the special-case of forced-termination (only for Compression mode)\n            disable_force_termination = self.__output_parameters.pop(\n                \"-disable_force_termination\",\n                False if (\"-i\" in self.__output_parameters) else True,\n            )\n            # check if value is valid\n            if isinstance(disable_force_termination, bool):\n                self.__forced_termination = not (disable_force_termination)\n            else:\n                # handle improper values\n                self.__forced_termination = (\n                    True if (\"-i\" in self.__output_parameters) else False\n                )\n\n            # handles disabling window for ffmpeg subprocess on Windows OS (only for Compression mode)\n            # this patch prevents ffmpeg creation window from opening when building exe files\n            ffmpeg_window_disabler_patch = self.__output_parameters.pop(\n                \"-disable_ffmpeg_window\", False\n            )\n            # check if value is valid\n            if not self.__os_windows or logging:\n                logger.warning(\n                    \"Optional `-disable_ffmpeg_window` flag is only available on Windows OS with `logging=False`. Discarding!\"\n                )\n            elif isinstance(ffmpeg_window_disabler_patch, bool):\n                self.__ffmpeg_window_disabler_patch = ffmpeg_window_disabler_patch\n            else:\n                # handle improper values\n                self.__ffmpeg_window_disabler_patch = False\n\n            # validate the FFmpeg path/binaries and returns valid executable FFmpeg\n            # location/path (also auto-downloads static binaries on Windows OS)\n            self.__ffmpeg = get_valid_ffmpeg_path(\n                custom_ffmpeg,\n                self.__os_windows,\n                ffmpeg_download_path=__ffmpeg_download_path,\n                logging=self.__logging,\n            )\n            # check if valid executable FFmpeg location/path\n            if self.__ffmpeg:\n                # log it if found\n                self.__logging and logger.debug(\n                    \"Found valid FFmpeg executable: `{}`.\".format(self.__ffmpeg)\n                )\n            else:\n                # otherwise disable Compression Mode\n                # and switch to Non-compression mode\n                logger.warning(\n                    \"Disabling Compression Mode since no valid FFmpeg executable found on this machine!\"\n                )\n                if self.__logging and not self.__os_windows:\n                    logger.debug(\n                        \"Kindly install a working FFmpeg module or provide a valid custom FFmpeg binary path. See docs for more info.\"\n                    )\n                # compression mode disabled\n                self.__compression = False\n        else:\n            # handle GStreamer Pipeline Mode (only for Non-compression mode)\n            if \"-gst_pipeline_mode\" in self.__output_parameters:\n                # check if value is valid\n                if isinstance(self.__output_parameters[\"-gst_pipeline_mode\"], bool):\n                    gstpipeline_mode = self.__output_parameters[\n                        \"-gst_pipeline_mode\"\n                    ] and check_gstreamer_support(logging=logging)\n                    self.__logging and logger.debug(\n                        \"GStreamer Pipeline Mode successfully activated!\"\n                    )\n                else:\n                    # reset improper values\n                    gstpipeline_mode = False\n                    # log it\n                    self.__logging and logger.warning(\n                        \"GStreamer Pipeline Mode failed to activate!\"\n                    )\n\n        # handle output differently in Compression/Non-compression Modes\n        if self.__compression and self.__ffmpeg:\n            # check if output falls in exclusive cases\n            if self.__out_file is None:\n                if (\n                    platform.system() == \"Linux\"\n                    and pathlib.Path(output).is_char_device()\n                ):\n                    # check whether output is a Linux video device path (such as `/dev/video0`)\n                    self.__logging and logger.debug(\n                        \"Path:`{}` is a valid Linux Video Device path.\".format(output)\n                    )\n                    self.__out_file = output\n                elif is_valid_url(self.__ffmpeg, url=output, logging=self.__logging):\n                    # check whether output is a valid URL instead\n                    self.__logging and logger.debug(\n                        \"URL:`{}` is valid and successfully configured for streaming.\".format(\n                            output\n                        )\n                    )\n                    self.__out_file = output\n                else:\n                    # raise error otherwise\n                    raise ValueError(\n                        \"[WriteGear:ERROR] :: output value:`{}` is not supported in Compression Mode.\".format(\n                            output\n                        )\n                    )\n            # log if forced termination is enabled\n            self.__forced_termination and logger.debug(\n                \"Forced termination is enabled for this FFmpeg process.\"\n            )\n            # log Compression is enabled\n            self.__logging and logger.debug(\n                \"Compression Mode with FFmpeg backend is configured properly.\"\n            )\n        else:\n            # raise error if not valid input\n            if self.__out_file is None and not gstpipeline_mode:\n                raise ValueError(\n                    \"[WriteGear:ERROR] :: output value:`{}` is not supported in Non-Compression Mode.\".format(\n                        output\n                    )\n                )\n\n            # check if GStreamer Pipeline Mode is enabled\n            if gstpipeline_mode:\n                # enforce GStreamer backend\n                self.__output_parameters[\"-backend\"] = \"CAP_GSTREAMER\"\n                # enforce original output value\n                self.__out_file = output\n\n            # log it\n            self.__logging and logger.debug(\n                \"Non-Compression Mode is successfully configured in GStreamer Pipeline Mode.\"\n            )\n\n            # log if Compression is disabled\n            logger.critical(\n                \"Compression Mode is disabled, Activating OpenCV built-in Writer!\"\n            )\n\n    def write(self, frame, rgb_mode=False):\n        \"\"\"\n        Pipelines `ndarray` frames to respective API _(**FFmpeg** in Compression Mode & **OpenCV's VideoWriter API** in Non-Compression Mode)_.\n\n        Parameters:\n            frame (ndarray): a valid numpy frame\n            rgb_mode (boolean): enable this flag to activate RGB mode _(i.e. specifies that incoming frames are of RGB format(instead of default BGR)_.\n\n        \"\"\"\n        if frame is None:  # None-Type frames will be skipped\n            return\n\n        # get height, width, number of channels, and dtype of current frame\n        height, width = frame.shape[:2]\n        channels = frame.shape[-1] if frame.ndim == 3 else 1\n        dtype = frame.dtype\n\n        # assign values to class variables on first run\n        if self.__initiate_process:\n            self.__inputheight = height\n            self.__inputwidth = width\n            self.__inputchannels = channels\n            self.__inputdtype = dtype\n            self.__logging and logger.debug(\n                \"InputFrame => Height:{} Width:{} Channels:{} Datatype:{}\".format(\n                    self.__inputheight,\n                    self.__inputwidth,\n                    self.__inputchannels,\n                    self.__inputdtype,\n                )\n            )\n\n        # validate frame size\n        if height != self.__inputheight or width != self.__inputwidth:\n            raise ValueError(\n                \"[WriteGear:ERROR] :: All video-frames must have same size!\"\n            )\n        # validate number of channels in frame\n        if channels != self.__inputchannels:\n            raise ValueError(\n                \"[WriteGear:ERROR] :: All video-frames must have same number of channels!\"\n            )\n        # validate frame datatype\n        if dtype != self.__inputdtype:\n            raise ValueError(\n                \"[WriteGear:ERROR] :: All video-frames must have same datatype!\"\n            )\n\n        # checks if compression mode is enabled\n        if self.__compression:\n            # initiate FFmpeg process on first run\n            if self.__initiate_process:\n                # start pre-processing of FFmpeg parameters, and initiate process\n                self.__PreprocessFFParams(channels, dtype=dtype, rgb=rgb_mode)\n                # Check status of the process\n                assert self.__process is not None\n            try:\n                # try writing the frame bytes to the subprocess pipeline\n                self.__process.stdin.write(frame.tobytes())\n            except (OSError, IOError):\n                # log if something is wrong!\n                logger.error(\n                    \"BrokenPipeError caught, Wrong values passed to FFmpeg Pipe. Kindly Refer Docs!\"\n                )\n                raise ValueError  # for testing purpose only\n        else:\n            # otherwise initiate OpenCV's VideoWriter Class process\n            if self.__initiate_process:\n                # start VideoWriter Class process\n                self.__start_CVProcess()\n                # Check status of the process\n                assert self.__process is not None\n                # log one-time OpenCV warning\n                self.__logging and logger.info(\n                    \"RGBA and 16-bit grayscale video frames are not supported by OpenCV yet. Kindly switch on `compression_mode` to use them!\"\n                )\n            # write frame directly to\n            # VideoWriter Class process\n            self.__process.write(frame)\n\n    def __PreprocessFFParams(self, channels, dtype=None, rgb=False):\n        \"\"\"\n        Internal method that pre-processes FFmpeg Parameters before beginning to pipeline frames.\n\n        Parameters:\n            channels (int): Number of channels in input frame.\n            dtype (str): Datatype of input frame.\n            rgb_mode (boolean): Whether to activate `RGB mode`?\n        \"\"\"\n        # turn off initiate flag\n        self.__initiate_process = False\n        # initialize input parameters\n        input_parameters = {}\n\n        # handle output frames dimensions\n        dimensions = \"\"\n        if self.__output_dimensions is None:  # check if dimensions are given\n            dimensions += \"{}x{}\".format(\n                self.__inputwidth, self.__inputheight\n            )  # auto derive from frame\n        else:\n            dimensions += \"{}x{}\".format(\n                self.__output_dimensions[0], self.__output_dimensions[1]\n            )  # apply if defined\n        input_parameters[\"-s\"] = str(dimensions)\n\n        # handles user-defined and auto-assigned input pixel-formats\n        if not (\n            self.__inputpixfmt is None\n        ) and self.__inputpixfmt in get_supported_pixfmts(self.__ffmpeg):\n            # assign directly if valid\n            input_parameters[\"-pix_fmt\"] = self.__inputpixfmt\n        else:\n            # handles pix_fmt based on channels and dtype(HACK)\n            if dtype.kind == \"u\" and dtype.itemsize == 2:\n                # handle pix_fmt for frames with higher than 8-bit depth\n                pix_fmt = None\n                if channels == 1:\n                    pix_fmt = \"gray16\"\n                elif channels == 2:\n                    pix_fmt = \"ya16\"\n                elif channels == 3:\n                    pix_fmt = \"rgb48\" if rgb else \"bgr48\"\n                elif channels == 4:\n                    pix_fmt = \"rgba64\" if rgb else \"bgra64\"\n                else:\n                    # raise error otherwise\n                    raise ValueError(\n                        \"[WriteGear:ERROR] :: Frames with channels outside range 1-to-4 are not supported!\"\n                    )\n                # Add endianness suffix (w.r.t byte-order)\n                input_parameters[\"-pix_fmt\"] = pix_fmt + (\n                    \"be\" if dtype.byteorder == \">\" else \"le\"\n                )\n            else:\n                # handle pix_fmt for frames with exactly 8-bit depth(`uint8`)\n                if channels == 1:\n                    input_parameters[\"-pix_fmt\"] = \"gray\"\n                elif channels == 2:\n                    input_parameters[\"-pix_fmt\"] = \"ya8\"\n                elif channels == 3:\n                    input_parameters[\"-pix_fmt\"] = \"rgb24\" if rgb else \"bgr24\"\n                elif channels == 4:\n                    input_parameters[\"-pix_fmt\"] = \"rgba\" if rgb else \"bgra\"\n                else:\n                    # raise error otherwise\n                    raise ValueError(\n                        \"[WriteGear:ERROR] :: Frames with channels outside range 1-to-4 are not supported!\"\n                    )\n\n        # handles user-defined output video framerate\n        if self.__inputframerate > 0.0:\n            # assign input framerate if valid\n            self.__logging and logger.debug(\n                \"Setting Input framerate: {}\".format(self.__inputframerate)\n            )\n            input_parameters[\"-framerate\"] = str(self.__inputframerate)\n\n        # initiate FFmpeg process\n        self.__start_FFProcess(\n            input_params=input_parameters, output_params=self.__output_parameters\n        )\n\n    def __start_FFProcess(self, input_params, output_params):\n        \"\"\"\n        An Internal method that launches FFmpeg subprocess pipeline in Compression Mode\n        for pipelining frames to `stdin`.\n\n        Parameters:\n            input_params (dict): Input FFmpeg parameters\n            output_params (dict): Output FFmpeg parameters\n        \"\"\"\n        # convert input parameters to argument list\n        input_parameters = dict2Args(input_params)\n\n        # handle output video encoder.\n        # get list of supported video-encoders\n        supported_vcodecs = get_supported_vencoders(self.__ffmpeg)\n        # dynamically select default encoder\n        default_vcodec = [\n            vcodec\n            for vcodec in [\"libx264\", \"libx265\", \"libxvid\", \"mpeg4\"]\n            if vcodec in supported_vcodecs\n        ][0] or \"unknown\"\n        # extract any user-defined encoder\n        if \"-c:v\" in output_params:\n            # assign it to the pipeline\n            output_params[\"-vcodec\"] = output_params.pop(\"-c:v\", default_vcodec)\n        if not \"-vcodec\" in output_params:\n            # auto-assign default video-encoder (if not assigned by user).\n            output_params[\"-vcodec\"] = default_vcodec\n        if (\n            default_vcodec != \"unknown\"\n            and not output_params[\"-vcodec\"] in supported_vcodecs\n        ):\n            # reset to default if not supported\n            logger.critical(\n                \"Provided FFmpeg does not support `{}` video-encoder. Switching to default supported `{}` encoder!\".format(\n                    output_params[\"-vcodec\"], default_vcodec\n                )\n            )\n            output_params[\"-vcodec\"] = default_vcodec\n\n        # assign optimizations based on selected video encoder(if any)\n        if output_params[\"-vcodec\"] in supported_vcodecs:\n            if output_params[\"-vcodec\"] in [\"libx265\", \"libx264\"]:\n                if not \"-crf\" in output_params:\n                    output_params[\"-crf\"] = \"18\"\n                if not \"-preset\" in output_params:\n                    output_params[\"-preset\"] = \"fast\"\n            if output_params[\"-vcodec\"] in [\"libxvid\", \"mpeg4\"]:\n                if not \"-qscale:v\" in output_params:\n                    output_params[\"-qscale:v\"] = \"3\"\n        else:\n            # raise error otherwise\n            raise RuntimeError(\n                \"[WriteGear:ERROR] :: Provided FFmpeg does not support any suitable/usable video-encoders for compression.\"\n                \" Kindly disable compression mode or switch to another FFmpeg binaries(if available).\"\n            )\n\n        # convert output parameters to argument list\n        output_parameters = dict2Args(output_params)\n\n        # format FFmpeg command\n        cmd = (\n            [self.__ffmpeg, \"-y\"]\n            + self.__ffmpeg_preheaders\n            + [\"-f\", \"rawvideo\", \"-vcodec\", \"rawvideo\"]\n            + input_parameters\n            + [\"-i\", \"-\"]\n            + output_parameters\n            + [self.__out_file]\n        )\n        # Launch the process with FFmpeg command\n        if self.__logging:\n            # log command in logging mode\n            logger.debug(\"Executing FFmpeg command: `{}`\".format(\" \".join(cmd)))\n            # In logging mode\n            self.__process = sp.Popen(cmd, stdin=sp.PIPE, stdout=sp.PIPE, stderr=None)\n        else:\n            # In silent mode\n            self.__process = sp.Popen(\n                cmd,\n                stdin=sp.PIPE,\n                stdout=sp.DEVNULL,\n                stderr=sp.STDOUT,\n                creationflags=(  # this prevents ffmpeg creation window from opening when building exe files on Windows\n                    sp.DETACHED_PROCESS if self.__ffmpeg_window_disabler_patch else 0\n                ),\n            )\n\n    def __enter__(self):\n        \"\"\"\n        Handles entry with the `with` statement. See [PEP343 -- The 'with' statement'](https://peps.python.org/pep-0343/).\n\n        **Returns:** Returns a reference to the WriteGear Class\n        \"\"\"\n        return self\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        \"\"\"\n        Handles exit with the `with` statement. See [PEP343 -- The 'with' statement'](https://peps.python.org/pep-0343/).\n        \"\"\"\n        self.close()\n\n    def execute_ffmpeg_cmd(self, command=None):\n        \"\"\"\n\n        Executes user-defined FFmpeg Terminal command, formatted as a python list(in Compression Mode only).\n\n        Parameters:\n            command (list): inputs list data-type command.\n\n        \"\"\"\n        # check if valid command\n        if command is None or not (command):\n            logger.warning(\"Input command is empty, Nothing to execute!\")\n            return\n        else:\n            if not (isinstance(command, list)):\n                raise ValueError(\n                    \"[WriteGear:ERROR] :: Invalid input command datatype! Kindly read docs.\"\n                )\n\n        # check if Compression Mode is enabled\n        if not (self.__compression):\n            # raise error otherwise\n            raise RuntimeError(\n                \"[WriteGear:ERROR] :: Compression Mode is disabled, Kindly enable it to access this function.\"\n            )\n\n        # add configured FFmpeg path\n        cmd = [self.__ffmpeg] + command\n\n        try:\n            # write frames to pipeline\n            if self.__logging:\n                # log command in logging mode\n                logger.debug(\"Executing FFmpeg command: `{}`\".format(\" \".join(cmd)))\n                # In logging mode\n                sp.run(cmd, stdin=sp.PIPE, stdout=sp.PIPE, stderr=None)\n            else:\n                # In silent mode\n                sp.run(cmd, stdin=sp.PIPE, stdout=sp.DEVNULL, stderr=sp.STDOUT)\n        except (OSError, IOError) as e:\n            # re-raise error\n            if self.__logging:\n                raise ValueError(\n                    \"BrokenPipeError caught, Wrong command passed to FFmpeg Pipe, Kindly Refer Docs!\"\n                ) from None\n            else:\n                raise ValueError(\n                    \"BrokenPipeError caught, Wrong command passed to FFmpeg Pipe, Kindly Refer Docs!\"\n                ) from e\n\n    def __start_CVProcess(self):\n        \"\"\"\n        An Internal method that launches OpenCV VideoWriter process in Non-Compression\n        Mode with given settings.\n        \"\"\"\n        # turn off initiate flag\n        self.__initiate_process = False\n\n        # initialize essential variables\n        FPS = 0\n        BACKEND = \"\"\n        FOURCC = 0\n        COLOR = True\n\n        # pre-assign default parameters (if not assigned by user).\n        if \"-fourcc\" not in self.__output_parameters:\n            FOURCC = cv2.VideoWriter_fourcc(*\"MJPG\")\n        if \"-fps\" not in self.__output_parameters:\n            FPS = 25\n\n        # auto-assign frame dimensions\n        HEIGHT = self.__inputheight\n        WIDTH = self.__inputwidth\n\n        # assign dict parameter values to variables\n        try:\n            for key, value in self.__output_parameters.items():\n                if key == \"-fourcc\":\n                    FOURCC = cv2.VideoWriter_fourcc(*(value.upper()))\n                elif key == \"-fps\":\n                    FPS = int(value)\n                elif key == \"-backend\":\n                    BACKEND = capPropId(value.upper())\n                elif key == \"-color\":\n                    COLOR = bool(value)\n                else:\n                    pass\n        except Exception as e:\n            # log and raise error if something is wrong\n            self.__logging and logger.exception(str(e))\n            raise ValueError(\n                \"[WriteGear:ERROR] :: Wrong Values passed to OpenCV Writer, Kindly Refer Docs!\"\n            )\n\n        # log values for debugging\n        self.__logging and logger.debug(\n            \"FILE_PATH: {}, FOURCC = {}, FPS = {}, WIDTH = {}, HEIGHT = {}, BACKEND = {}\".format(\n                self.__out_file, FOURCC, FPS, WIDTH, HEIGHT, BACKEND\n            )\n        )\n        # start different OpenCV VideoCapture processes\n        # for with and without Backend.\n        if BACKEND:\n            self.__process = cv2.VideoWriter(\n                self.__out_file,\n                apiPreference=BACKEND,\n                fourcc=FOURCC,\n                fps=FPS,\n                frameSize=(WIDTH, HEIGHT),\n                isColor=COLOR,\n            )\n        else:\n            self.__process = cv2.VideoWriter(\n                self.__out_file,\n                fourcc=FOURCC,\n                fps=FPS,\n                frameSize=(WIDTH, HEIGHT),\n                isColor=COLOR,\n            )\n        # check if OpenCV VideoCapture is opened successfully\n        assert (\n            self.__process.isOpened()\n        ), \"[WriteGear:ERROR] :: Failed to initialize OpenCV Writer!\"\n\n    def close(self):\n        \"\"\"\n        Safely terminates various WriteGear process.\n        \"\"\"\n        # log termination\n        self.__logging and logger.debug(\"Terminating WriteGear Processes.\")\n        # handle termination separately\n        if self.__compression:\n            # when Compression Mode is enabled\n            if self.__process is None or not (self.__process.poll() is None):\n                # return if no process initiated\n                # at first place\n                return\n            # close `stdin` output\n            self.__process.stdin and self.__process.stdin.close()\n            # close `stdout` output\n            self.__process.stdout and self.__process.stdout.close()\n            # forced termination if specified.\n            self.__forced_termination and self.__process.terminate()\n            # wait if process is still processing\n            self.__process.wait()\n        else:\n            # when Compression Mode is disabled\n            if self.__process is None:\n                # return if no process initiated\n                # at first place\n                return\n            # close it\n            self.__process.release()\n        # discard process\n        self.__process = None\n

"},{"location":"bonus/reference/writegear/#vidgear.gears.writegear.WriteGear.__enter__","title":"__enter__(self) special","text":"

Handles entry with the with statement. See PEP343 -- The 'with' statement'.

Returns: Returns a reference to the WriteGear Class

Source code in vidgear/gears/writegear.py
def __enter__(self):\n    \"\"\"\n    Handles entry with the `with` statement. See [PEP343 -- The 'with' statement'](https://peps.python.org/pep-0343/).\n\n    **Returns:** Returns a reference to the WriteGear Class\n    \"\"\"\n    return self\n
"},{"location":"bonus/reference/writegear/#vidgear.gears.writegear.WriteGear.__exit__","title":"__exit__(self, exc_type, exc_val, exc_tb) special","text":"

Handles exit with the with statement. See PEP343 -- The 'with' statement'.

Source code in vidgear/gears/writegear.py
def __exit__(self, exc_type, exc_val, exc_tb):\n    \"\"\"\n    Handles exit with the `with` statement. See [PEP343 -- The 'with' statement'](https://peps.python.org/pep-0343/).\n    \"\"\"\n    self.close()\n
"},{"location":"bonus/reference/writegear/#vidgear.gears.writegear.WriteGear.__init__","title":"__init__(self, output='', compression_mode=True, custom_ffmpeg='', logging=False, **output_params) special","text":"

This constructor method initializes the object state and attributes of the WriteGear class.

Parameters:

Name Type Description Default output str

sets the valid filename/path/URL for encoding.

'' compression_mode bool

selects the WriteGear's Primary Mode of Operation.

True custom_ffmpeg str

assigns the location of custom path/directory for custom FFmpeg executables.

'' logging bool

enables/disables logging.

False output_params dict

provides the flexibility to control supported internal parameters and FFmpeg properties.

{} Source code in vidgear/gears/writegear.py
def __init__(\n    self,\n    output=\"\",\n    compression_mode=True,\n    custom_ffmpeg=\"\",\n    logging=False,\n    **output_params\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the WriteGear class.\n\n    Parameters:\n        output (str): sets the valid filename/path/URL for encoding.\n        compression_mode (bool): selects the WriteGear's Primary Mode of Operation.\n        custom_ffmpeg (str): assigns the location of custom path/directory for custom FFmpeg executables.\n        logging (bool): enables/disables logging.\n        output_params (dict): provides the flexibility to control supported internal parameters and FFmpeg properties.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # check if user not using depreciated `output_filename` parameter\n    assert (\n        not \"output_filename\" in output_params\n    ), \"[WriteGear:ERROR] :: The `output_filename` parameter has been renamed to `output`. Refer Docs for more info.\"\n\n    # assign parameter values to class variables\n    # enables compression if enabled\n    self.__compression = (\n        compression_mode if isinstance(compression_mode, bool) else False\n    )\n    # specifies if machine in-use is running Windows OS or not\n    self.__os_windows = True if os.name == \"nt\" else False\n\n    # initialize various important class variables\n    self.__output_parameters = {}  # handles output parameters\n    self.__inputheight = None  # handles input frames height\n    self.__inputwidth = None  # handles input frames width\n    self.__inputchannels = None  # handles input frames channels\n    self.__inputdtype = None  # handles input frames dtype\n    self.__process = None  # handles Encoding class/process\n    self.__ffmpeg = \"\"  # handles valid FFmpeg binaries location\n    self.__initiate_process = (\n        True  # handles initiate one-time process for generating pipeline\n    )\n    self.__ffmpeg_window_disabler_patch = (\n        False  # handles disabling window for ffmpeg subprocess on Windows\n    )\n    self.__out_file = None  # handles output\n    gstpipeline_mode = False  # handles GStreamer Pipeline Mode\n\n    # handles output\n    if not output:\n        # raise error otherwise\n        raise ValueError(\n            \"[WriteGear:ERROR] :: Kindly provide a valid `output` value. Refer Docs for more info.\"\n        )\n    else:\n        # validate output is a system file/directory\n        # and Whether WriteGear has the write rights\n        # to specified file/directory or not\n        abs_path = os.path.abspath(output)\n        if check_WriteAccess(\n            os.path.dirname(abs_path),\n            is_windows=self.__os_windows,\n            logging=self.__logging,\n        ):\n            # check if given path is directory\n            if os.path.isdir(abs_path):\n                # then, auto-assign valid name and adds it to path\n                abs_path = os.path.join(\n                    abs_path,\n                    \"VidGear-{}.mp4\".format(time.strftime(\"%Y%m%d-%H%M%S\")),\n                )\n            # assign output file absolute\n            # path to class variable if valid\n            self.__out_file = abs_path\n        else:\n            # log note otherwise\n            logger.info(\n                \"`{}` isn't a valid system path or directory. Skipped!\".format(\n                    output\n                )\n            )\n\n    # cleans and reformat output parameters\n    self.__output_parameters = {\n        str(k).strip(): (v.strip() if isinstance(v, str) else v)\n        for k, v in output_params.items()\n    }\n    # log it if specified\n    self.__logging and logger.debug(\n        \"Output Parameters: `{}`\".format(self.__output_parameters)\n    )\n\n    # handles FFmpeg binaries validity\n    # in Compression mode\n    if self.__compression:\n        # log it if specified\n        self.__logging and logger.debug(\n            \"Compression Mode is enabled therefore checking for valid FFmpeg executable.\"\n        )\n\n        # handles where to save the downloaded FFmpeg Static Binaries\n        # on Windows(if specified)\n        __ffmpeg_download_path = self.__output_parameters.pop(\n            \"-ffmpeg_download_path\", \"\"\n        )\n        # check if value is valid\n        if not isinstance(__ffmpeg_download_path, (str)):\n            # reset improper values\n            __ffmpeg_download_path = \"\"\n\n        # handle user-defined output resolution (must be a tuple or list)\n        # in Compression Mode only.\n        self.__output_dimensions = self.__output_parameters.pop(\n            \"-output_dimensions\", None\n        )\n        # check if value is valid\n        if not isinstance(self.__output_dimensions, (list, tuple)):\n            # reset improper values\n            self.__output_dimensions = None\n\n        # handle user defined input framerate of encoding pipeline\n        # in Compression Mode only.\n        self.__inputframerate = self.__output_parameters.pop(\n            \"-input_framerate\", 0.0\n        )\n        # check if value is valid\n        if not isinstance(self.__inputframerate, (float, int)):\n            # reset improper values\n            self.__inputframerate = 0.0\n        else:\n            # must be float\n            self.__inputframerate = float(self.__inputframerate)\n\n        # handle user-defined input frames pixel-format in Compression Mode only.\n        self.__inputpixfmt = self.__output_parameters.pop(\"-input_pixfmt\", None)\n        # check if value is valid\n        if not isinstance(self.__inputpixfmt, str):\n            # reset improper values\n            self.__inputpixfmt = None\n        else:\n            # must be exact\n            self.__inputpixfmt = self.__inputpixfmt.strip()\n\n        # handle user-defined FFmpeg command pre-headers(must be a list)\n        # in Compression Mode only.\n        self.__ffmpeg_preheaders = self.__output_parameters.pop(\"-ffpreheaders\", [])\n        # check if value is valid\n        if not isinstance(self.__ffmpeg_preheaders, list):\n            # reset improper values\n            self.__ffmpeg_preheaders = []\n\n        # handle the special-case of forced-termination (only for Compression mode)\n        disable_force_termination = self.__output_parameters.pop(\n            \"-disable_force_termination\",\n            False if (\"-i\" in self.__output_parameters) else True,\n        )\n        # check if value is valid\n        if isinstance(disable_force_termination, bool):\n            self.__forced_termination = not (disable_force_termination)\n        else:\n            # handle improper values\n            self.__forced_termination = (\n                True if (\"-i\" in self.__output_parameters) else False\n            )\n\n        # handles disabling window for ffmpeg subprocess on Windows OS (only for Compression mode)\n        # this patch prevents ffmpeg creation window from opening when building exe files\n        ffmpeg_window_disabler_patch = self.__output_parameters.pop(\n            \"-disable_ffmpeg_window\", False\n        )\n        # check if value is valid\n        if not self.__os_windows or logging:\n            logger.warning(\n                \"Optional `-disable_ffmpeg_window` flag is only available on Windows OS with `logging=False`. Discarding!\"\n            )\n        elif isinstance(ffmpeg_window_disabler_patch, bool):\n            self.__ffmpeg_window_disabler_patch = ffmpeg_window_disabler_patch\n        else:\n            # handle improper values\n            self.__ffmpeg_window_disabler_patch = False\n\n        # validate the FFmpeg path/binaries and returns valid executable FFmpeg\n        # location/path (also auto-downloads static binaries on Windows OS)\n        self.__ffmpeg = get_valid_ffmpeg_path(\n            custom_ffmpeg,\n            self.__os_windows,\n            ffmpeg_download_path=__ffmpeg_download_path,\n            logging=self.__logging,\n        )\n        # check if valid executable FFmpeg location/path\n        if self.__ffmpeg:\n            # log it if found\n            self.__logging and logger.debug(\n                \"Found valid FFmpeg executable: `{}`.\".format(self.__ffmpeg)\n            )\n        else:\n            # otherwise disable Compression Mode\n            # and switch to Non-compression mode\n            logger.warning(\n                \"Disabling Compression Mode since no valid FFmpeg executable found on this machine!\"\n            )\n            if self.__logging and not self.__os_windows:\n                logger.debug(\n                    \"Kindly install a working FFmpeg module or provide a valid custom FFmpeg binary path. See docs for more info.\"\n                )\n            # compression mode disabled\n            self.__compression = False\n    else:\n        # handle GStreamer Pipeline Mode (only for Non-compression mode)\n        if \"-gst_pipeline_mode\" in self.__output_parameters:\n            # check if value is valid\n            if isinstance(self.__output_parameters[\"-gst_pipeline_mode\"], bool):\n                gstpipeline_mode = self.__output_parameters[\n                    \"-gst_pipeline_mode\"\n                ] and check_gstreamer_support(logging=logging)\n                self.__logging and logger.debug(\n                    \"GStreamer Pipeline Mode successfully activated!\"\n                )\n            else:\n                # reset improper values\n                gstpipeline_mode = False\n                # log it\n                self.__logging and logger.warning(\n                    \"GStreamer Pipeline Mode failed to activate!\"\n                )\n\n    # handle output differently in Compression/Non-compression Modes\n    if self.__compression and self.__ffmpeg:\n        # check if output falls in exclusive cases\n        if self.__out_file is None:\n            if (\n                platform.system() == \"Linux\"\n                and pathlib.Path(output).is_char_device()\n            ):\n                # check whether output is a Linux video device path (such as `/dev/video0`)\n                self.__logging and logger.debug(\n                    \"Path:`{}` is a valid Linux Video Device path.\".format(output)\n                )\n                self.__out_file = output\n            elif is_valid_url(self.__ffmpeg, url=output, logging=self.__logging):\n                # check whether output is a valid URL instead\n                self.__logging and logger.debug(\n                    \"URL:`{}` is valid and successfully configured for streaming.\".format(\n                        output\n                    )\n                )\n                self.__out_file = output\n            else:\n                # raise error otherwise\n                raise ValueError(\n                    \"[WriteGear:ERROR] :: output value:`{}` is not supported in Compression Mode.\".format(\n                        output\n                    )\n                )\n        # log if forced termination is enabled\n        self.__forced_termination and logger.debug(\n            \"Forced termination is enabled for this FFmpeg process.\"\n        )\n        # log Compression is enabled\n        self.__logging and logger.debug(\n            \"Compression Mode with FFmpeg backend is configured properly.\"\n        )\n    else:\n        # raise error if not valid input\n        if self.__out_file is None and not gstpipeline_mode:\n            raise ValueError(\n                \"[WriteGear:ERROR] :: output value:`{}` is not supported in Non-Compression Mode.\".format(\n                    output\n                )\n            )\n\n        # check if GStreamer Pipeline Mode is enabled\n        if gstpipeline_mode:\n            # enforce GStreamer backend\n            self.__output_parameters[\"-backend\"] = \"CAP_GSTREAMER\"\n            # enforce original output value\n            self.__out_file = output\n\n        # log it\n        self.__logging and logger.debug(\n            \"Non-Compression Mode is successfully configured in GStreamer Pipeline Mode.\"\n        )\n\n        # log if Compression is disabled\n        logger.critical(\n            \"Compression Mode is disabled, Activating OpenCV built-in Writer!\"\n        )\n
"},{"location":"bonus/reference/writegear/#vidgear.gears.writegear.WriteGear.close","title":"close(self)","text":"

Safely terminates various WriteGear process.

Source code in vidgear/gears/writegear.py
def close(self):\n    \"\"\"\n    Safely terminates various WriteGear process.\n    \"\"\"\n    # log termination\n    self.__logging and logger.debug(\"Terminating WriteGear Processes.\")\n    # handle termination separately\n    if self.__compression:\n        # when Compression Mode is enabled\n        if self.__process is None or not (self.__process.poll() is None):\n            # return if no process initiated\n            # at first place\n            return\n        # close `stdin` output\n        self.__process.stdin and self.__process.stdin.close()\n        # close `stdout` output\n        self.__process.stdout and self.__process.stdout.close()\n        # forced termination if specified.\n        self.__forced_termination and self.__process.terminate()\n        # wait if process is still processing\n        self.__process.wait()\n    else:\n        # when Compression Mode is disabled\n        if self.__process is None:\n            # return if no process initiated\n            # at first place\n            return\n        # close it\n        self.__process.release()\n    # discard process\n    self.__process = None\n
"},{"location":"bonus/reference/writegear/#vidgear.gears.writegear.WriteGear.execute_ffmpeg_cmd","title":"execute_ffmpeg_cmd(self, command=None)","text":"

Executes user-defined FFmpeg Terminal command, formatted as a python list(in Compression Mode only).

Parameters:

Name Type Description Default command list

inputs list data-type command.

None Source code in vidgear/gears/writegear.py
def execute_ffmpeg_cmd(self, command=None):\n    \"\"\"\n\n    Executes user-defined FFmpeg Terminal command, formatted as a python list(in Compression Mode only).\n\n    Parameters:\n        command (list): inputs list data-type command.\n\n    \"\"\"\n    # check if valid command\n    if command is None or not (command):\n        logger.warning(\"Input command is empty, Nothing to execute!\")\n        return\n    else:\n        if not (isinstance(command, list)):\n            raise ValueError(\n                \"[WriteGear:ERROR] :: Invalid input command datatype! Kindly read docs.\"\n            )\n\n    # check if Compression Mode is enabled\n    if not (self.__compression):\n        # raise error otherwise\n        raise RuntimeError(\n            \"[WriteGear:ERROR] :: Compression Mode is disabled, Kindly enable it to access this function.\"\n        )\n\n    # add configured FFmpeg path\n    cmd = [self.__ffmpeg] + command\n\n    try:\n        # write frames to pipeline\n        if self.__logging:\n            # log command in logging mode\n            logger.debug(\"Executing FFmpeg command: `{}`\".format(\" \".join(cmd)))\n            # In logging mode\n            sp.run(cmd, stdin=sp.PIPE, stdout=sp.PIPE, stderr=None)\n        else:\n            # In silent mode\n            sp.run(cmd, stdin=sp.PIPE, stdout=sp.DEVNULL, stderr=sp.STDOUT)\n    except (OSError, IOError) as e:\n        # re-raise error\n        if self.__logging:\n            raise ValueError(\n                \"BrokenPipeError caught, Wrong command passed to FFmpeg Pipe, Kindly Refer Docs!\"\n            ) from None\n        else:\n            raise ValueError(\n                \"BrokenPipeError caught, Wrong command passed to FFmpeg Pipe, Kindly Refer Docs!\"\n            ) from e\n
"},{"location":"bonus/reference/writegear/#vidgear.gears.writegear.WriteGear.write","title":"write(self, frame, rgb_mode=False)","text":"

Pipelines ndarray frames to respective API (FFmpeg in Compression Mode & OpenCV's VideoWriter API in Non-Compression Mode).

Parameters:

Name Type Description Default frame ndarray

a valid numpy frame

required rgb_mode boolean

enable this flag to activate RGB mode (i.e. specifies that incoming frames are of RGB format(instead of default BGR).

False Source code in vidgear/gears/writegear.py
def write(self, frame, rgb_mode=False):\n    \"\"\"\n    Pipelines `ndarray` frames to respective API _(**FFmpeg** in Compression Mode & **OpenCV's VideoWriter API** in Non-Compression Mode)_.\n\n    Parameters:\n        frame (ndarray): a valid numpy frame\n        rgb_mode (boolean): enable this flag to activate RGB mode _(i.e. specifies that incoming frames are of RGB format(instead of default BGR)_.\n\n    \"\"\"\n    if frame is None:  # None-Type frames will be skipped\n        return\n\n    # get height, width, number of channels, and dtype of current frame\n    height, width = frame.shape[:2]\n    channels = frame.shape[-1] if frame.ndim == 3 else 1\n    dtype = frame.dtype\n\n    # assign values to class variables on first run\n    if self.__initiate_process:\n        self.__inputheight = height\n        self.__inputwidth = width\n        self.__inputchannels = channels\n        self.__inputdtype = dtype\n        self.__logging and logger.debug(\n            \"InputFrame => Height:{} Width:{} Channels:{} Datatype:{}\".format(\n                self.__inputheight,\n                self.__inputwidth,\n                self.__inputchannels,\n                self.__inputdtype,\n            )\n        )\n\n    # validate frame size\n    if height != self.__inputheight or width != self.__inputwidth:\n        raise ValueError(\n            \"[WriteGear:ERROR] :: All video-frames must have same size!\"\n        )\n    # validate number of channels in frame\n    if channels != self.__inputchannels:\n        raise ValueError(\n            \"[WriteGear:ERROR] :: All video-frames must have same number of channels!\"\n        )\n    # validate frame datatype\n    if dtype != self.__inputdtype:\n        raise ValueError(\n            \"[WriteGear:ERROR] :: All video-frames must have same datatype!\"\n        )\n\n    # checks if compression mode is enabled\n    if self.__compression:\n        # initiate FFmpeg process on first run\n        if self.__initiate_process:\n            # start pre-processing of FFmpeg parameters, and initiate process\n            self.__PreprocessFFParams(channels, dtype=dtype, rgb=rgb_mode)\n            # Check status of the process\n            assert self.__process is not None\n        try:\n            # try writing the frame bytes to the subprocess pipeline\n            self.__process.stdin.write(frame.tobytes())\n        except (OSError, IOError):\n            # log if something is wrong!\n            logger.error(\n                \"BrokenPipeError caught, Wrong values passed to FFmpeg Pipe. Kindly Refer Docs!\"\n            )\n            raise ValueError  # for testing purpose only\n    else:\n        # otherwise initiate OpenCV's VideoWriter Class process\n        if self.__initiate_process:\n            # start VideoWriter Class process\n            self.__start_CVProcess()\n            # Check status of the process\n            assert self.__process is not None\n            # log one-time OpenCV warning\n            self.__logging and logger.info(\n                \"RGBA and 16-bit grayscale video frames are not supported by OpenCV yet. Kindly switch on `compression_mode` to use them!\"\n            )\n        # write frame directly to\n        # VideoWriter Class process\n        self.__process.write(frame)\n
"},{"location":"contribution/PR/","title":"Submitting Pull Request(PR) Guidelines","text":""},{"location":"contribution/PR/#submitting-pull-requestpr-guidelines","title":"Submitting Pull Request(PR) Guidelines:","text":"

The following guidelines tells you how to submit a valid PR for vidGear:

Working on your first Pull Request for VidGear?

"},{"location":"contribution/PR/#clone-testing-branch","title":"Clone Testing branch","text":"

Base Branch must be testing in your Pull Request

Every PR MUST be pushed against VidGear's testing branch only, in order to trigger must needed CI testing workflows. If your's not, then change the base branch to testing \u27b6

Make sure the testing branch of your Forked repository is up-to-date with VidGear, before starting working on Pull Request.

You can clone your Forked remote git to local and create your PR working branch as a sub-branch of latest testing branch as follows:

Functions of different VidGear's Github Branches

Following are the base branches for VidGear's code in its Github Repository:

Master/MainTestingDevelopment

Branch Features:

Cloning:

# clone your forked repository and `cd` inside\ngit clone https://github.com/abhiTronix/vidgear.git && cd vidgear\n

Branch Features:

Cloning:

# clone your forked repository and `cd` inside\ngit clone https://github.com/abhiTronix/vidgear.git && cd vidgear\n\n# checkout the latest testing branch\ngit checkout testing\n

Branch Features:

Cloning:

# clone your forked repository and `cd` inside\ngit clone https://github.com/abhiTronix/vidgear.git && cd vidgear\n\n# checkout the latest development branch\ngit checkout development\n

Workflow:

Typically any feature/improvement/bug-fix code flows as follows:

# clone your forked repository(change with your username) and get inside\ngit clone https://github.com/{YOUR USERNAME}/vidgear.git && cd vidgear\n\n# pull any recent updates\ngit pull\n\n# checkout the latest testing branch\ngit checkout testing\n\n# Now create your new branch with suitable name(such as \"subbranch_of_testing\")\ngit checkout -b subbranch_of_testing\n

Now after working with this newly created branch for your Pull Request, you can commit and push or merge it locally or remotely as usual.

"},{"location":"contribution/PR/#pr-submission-checklist","title":"PR Submission Checklist","text":"

There are some important checks you need to perform while submitting your Pull Request(s) for VidGear library:

"},{"location":"contribution/PR/#testing-formatting-linting","title":"Testing, Formatting & Linting","text":"

All Pull Request(s) must be tested, formatted & linted against our library standards as discussed below:

"},{"location":"contribution/PR/#requirements","title":"Requirements","text":"

Testing VidGear requires additional test dependencies and dataset, which can be handled manually as follows:

"},{"location":"contribution/PR/#running-tests","title":"Running Tests","text":"

All tests can be run with pytest(in VidGear's root folder) as follows:

 pytest -sv  #-sv for verbose output.\n
"},{"location":"contribution/PR/#formatting-linting","title":"Formatting & Linting","text":"

For formatting and linting, following libraries are used:

"},{"location":"contribution/PR/#frequently-asked-questions","title":"Frequently Asked Questions","text":"

Q1. Why do my changes taking so long to be Reviewed and/or Merged?

Submission Aftermaths

Pull requests will be reviewed by the maintainers and the rationale behind the maintainer\u2019s decision to accept or deny the changes will be posted in the pull request. Please wait for our code review and approval, possibly enhancing your change on request.

Q2. Would you accept a huge Pull Request with Lots of Changes?

First, make sure that the changes are somewhat related. Otherwise, please create separate pull requests. Anyway, before submitting a huge change, it's probably a good idea to open an issue in the VidGear Github repository to ask the maintainers if they agree with your proposed changes. Otherwise, they could refuse your proposal after you put all that hard work into making the changes. We definitely don't want you to waste your time!

"},{"location":"contribution/issue/","title":"Submitting an Issue Guidelines","text":""},{"location":"contribution/issue/#submitting-an-issue-guidelines","title":"Submitting an Issue Guidelines","text":"

If you've found a new bug or you've come up with some new feature which can improve the quality of the VidGear, then related issues are welcomed! But, Before you do, please read the following guidelines:

First Issue on GitHub?

You can easily learn about it from creating an issue wiki.

Info

Please note that your issue will be fixed much faster if you spend about half an hour preparing it, including the exact reproduction steps and a demo. If you're in a hurry or don't feel confident, it's fine to report issues with less details, but this makes it less likely they'll get fixed soon.

"},{"location":"contribution/issue/#search-the-docs-and-previous-issues","title":"Search the Docs and Previous Issues","text":""},{"location":"contribution/issue/#gather-required-information","title":"Gather Required Information","text":""},{"location":"contribution/issue/#follow-the-issue-template","title":"Follow the Issue Template","text":""},{"location":"contribution/issue/#raise-the-issue","title":"Raise the Issue","text":""},{"location":"gears/camgear/overview/","title":"Overview","text":""},{"location":"gears/camgear/overview/#camgear-api","title":"CamGear API","text":"CamGear API's generalized workflow"},{"location":"gears/camgear/overview/#overview","title":"Overview","text":"

CamGear supports a diverse range of video streams which can handle/control video stream almost any IP/USB Cameras, multimedia video file format (upto 4k tested), any network stream URL such as http(s), rtp, rtsp, rtmp, mms, etc. In addition to this, it also supports Gstreamer's RAW pipelines and various live video streaming sites like YouTube, Twitch, Dailymotion etc.

CamGear API provides a flexible, high-level multi-threaded wrapper around OpenCV's VideoCapture API with direct access to almost all of its available parameters. It relies on Threaded Queue mode for threaded, error-free and synchronized frame handling.

CamGear internally implements yt_dlp backend class for seamlessly pipelining live video-frames and metadata from various streaming services like YouTube, Twitch, and many more \u27b6

Helpful Tips

"},{"location":"gears/camgear/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through CamGear Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/camgear/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/camgear/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/camgear/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/camgear/params/","title":"Parameters","text":""},{"location":"gears/camgear/params/#camgear-api-parameters","title":"CamGear API Parameters","text":""},{"location":"gears/camgear/params/#source","title":"source","text":"

CamGear API will throw RuntimeError if source provided is invalid.

This parameter defines the source for the input stream.

Data-Type: Based on input.

Default Value: Its default value is 0.

Its valid input can be one of the following:

"},{"location":"gears/camgear/params/#stream_mode","title":"stream_mode","text":"

This parameter controls the Stream Mode, .i.e if enabled(stream_mode=True), the CamGear API will interpret the given source input as YouTube URL address.

Due to a FFmpeg bug that causes video to freeze frequently in OpenCV, It is advised to always use GStreamer backend for any livestream videos. Checkout this FAQ for compiling OpenCV with GStreamer support.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

Supported Streaming Websites

The complete list of all supported Streaming Websites URLs can be found here \u27b6

CamGear(source='https://youtu.be/bvetuLwJIkA', stream_mode=True)\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/camgear/params/#colorspace","title":"colorspace","text":"

This parameter selects the colorspace of the input stream.

Data-Type: String

Default Value: Its default value is None.

Usage:

All supported colorspace values are given here \u27b6

CamGear(source=0, colorspace=\"COLOR_BGR2HSV\")\n

Its complete usage example is given here \u27b6

"},{"location":"gears/camgear/params/#backend","title":"backend","text":"

This parameter manually selects the backend for OpenCV's VideoCapture class (only if specified).

Data-Type: Integer

Default Value: Its default value is 0

Usage:

All supported backends are listed here \u27b6

Its value can be for e.g. backend = cv2.CAP_DSHOW for selecting Direct Show as backend:

CamGear(source=0, backend = cv2.CAP_DSHOW)\n

"},{"location":"gears/camgear/params/#options","title":"options","text":"

This parameter provides the ability to alter various Source Tweak Parameters available within OpenCV's VideoCapture API properties.

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

All supported parameters are listed here \u27b6

The desired parameters can be passed to CamGear API by formatting them as this parameter's attributes, as follows:

# formatting parameters as dictionary attributes\noptions = {\"CAP_PROP_FRAME_WIDTH\":320, \"CAP_PROP_FRAME_HEIGHT\":240, \"CAP_PROP_FPS\":60}\n# assigning it\nCamGear(source=0, **options)\n

"},{"location":"gears/camgear/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

CamGear(source=0, logging=True)\n

"},{"location":"gears/camgear/params/#time_delay","title":"time_delay","text":"

This parameter set the time delay (in seconds) before the CamGear API start reading the frames. This delay is only required if the source required some warm-up delay before starting up.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

CamGear(source=0, time_delay=1) # set 1 seconds time delay\n

"},{"location":"gears/camgear/usage/","title":"Usage Examples","text":""},{"location":"gears/camgear/usage/#camgear-api-usage-examples","title":"CamGear API Usage Examples:","text":"

After going through following Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/camgear/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with CamGear API:

# import required libraries\nfrom vidgear.gears import CamGear\nimport cv2\n\n\n# open any valid video stream(for e.g `myvideo.avi` file)\nstream = CamGear(source=\"myvideo.avi\").start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/camgear/usage/#using-camgear-with-streaming-websites","title":"Using Camgear with Streaming Websites","text":"

CamGear internally implements yt_dlp backend class for seamlessly pipelining live video-frames and metadata from various streaming services like Twitch, Vimeo, Dailymotion, and many more \u27b6. All you have to do is to provide the desired Video's URL to its source parameter, and enable its stream_mode parameter.

The complete usage example for Dailymotion and Twitch URLs are as follows:

Bug in OpenCV's FFmpeg

To workaround a FFmpeg bug that causes video to freeze frequently in OpenCV, It is advised to always use GStreamer backend for Livestream videos.

Checkout this FAQ \u27b6 for compiling OpenCV with GStreamer support.

Not all resolutions are supported with GStreamer Backend. See issue #244

Exclusive CamGear Attributes for yt_dlp backend

CamGear also provides exclusive attributes:

with its options dictionary parameter. More information can be found here \u27b6

Supported Streaming Websites

The list of all supported Streaming Websites URLs can be found here \u27b6

Accessing Stream's Metadata

CamGear now provides ytv_metadata global parameter for accessing given Video's metadata as JSON Object. It can used as follows:

New in v0.2.4

ytv_metadata global parameter was added in v0.2.4.

# import required libraries\nfrom vidgear.gears import CamGear\n\n# Add YouTube Video URL as input source (for e.g https://www.dailymotion.com/video/x2yrnum)\n# and enable Stream Mode (`stream_mode = True`)\nstream = CamGear(\n    source=\"https://www.dailymotion.com/video/x2yrnum\", stream_mode=True, logging=True, **options\n).start()\n\n# get Video's metadata as JSON object\nvideo_metadata =  stream.ytv_metadata\n\n# print all available keys\nprint(video_metadata.keys())\n\n# get data like `title`\nprint(video_metadata[\"title\"])\n
Dailymotion Twitch
# import required libraries\nfrom vidgear.gears import CamGear\nimport cv2\n\n# set desired quality as 720p\noptions = {\"STREAM_RESOLUTION\": \"720p\"}\n\n# Add any desire Video URL as input source\n# for e.g https://vimeo.com/151666798\n# and enable Stream Mode (`stream_mode = True`)\nstream = CamGear(\n    source=\"https://www.dailymotion.com/video/x2yrnum\",\n    stream_mode=True,\n    logging=True,\n    **options\n).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

If Twitch user is offline, CamGear will throw ValueError.

# import required libraries\nfrom vidgear.gears import CamGear\nimport cv2\n\n# set desired quality as 720p\noptions = {\"STREAM_RESOLUTION\": \"720p\"}\n\n# Add any desire Video URL as input source\n# for e.g hhttps://www.twitch.tv/shroud\n# and enable Stream Mode (`stream_mode = True`)\nstream = CamGear(\n    source=\"https://www.twitch.tv/shroud\",\n    stream_mode=True,\n    logging=True,\n    **options\n).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/camgear/usage/#using-camgear-with-youtube-videos","title":"Using Camgear with Youtube Videos","text":"

CamGear API also provides out-of-the-box support for pipelining live video-frames and metadata from YouTube (Livestream + Normal) Videos.

YouTube Playlists are not supported yet.

The complete usage example is as follows:

Bug in OpenCV's FFmpeg

To workaround a FFmpeg bug that causes video to freeze frequently in OpenCV, It is advised to always use GStreamer backend for Livestream videos.

Checkout this FAQ \u27b6 for compiling OpenCV with GStreamer support.

Not all resolutions are supported with GStreamer Backend. See issue #244

Exclusive CamGear Attributes for yt_dlp backend

CamGear also provides exclusive attributes:

with its options dictionary parameter. More information can be found here \u27b6

Accessing Stream's Metadata

CamGear now provides ytv_metadata global parameter for accessing given Video's metadata as JSON Object. It can used as follows:

New in v0.2.4

ytv_metadata global parameter was added in v0.2.4.

# import required libraries\nfrom vidgear.gears import CamGear\n\n# Add YouTube Video URL as input source (for e.g https://youtu.be/uCy5OuSQnyA)\n# and enable Stream Mode (`stream_mode = True`)\nstream = CamGear(\n    source=\"https://youtu.be/uCy5OuSQnyA\", stream_mode=True, logging=True, **options\n).start()\n\n# get Video's metadata as JSON object\nvideo_metadata =  stream.ytv_metadata\n\n# print all available keys\nprint(video_metadata.keys())\n\n# get data like `title`\nprint(video_metadata[\"title\"])\n
# import required libraries\nfrom vidgear.gears import CamGear\nimport cv2\n\n# Add YouTube Video URL as input source (for e.g https://youtu.be/uCy5OuSQnyA)\n# and enable Stream Mode (`stream_mode = True`)\nstream = CamGear(\n    source=\"https://youtu.be/uCy5OuSQnyA\", \n    stream_mode=True,\n    logging=True\n).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/camgear/usage/#using-camgear-with-variable-camera-properties","title":"Using CamGear with Variable Camera Properties","text":"

CamGear API also flexibly support various Source Tweak Parameters available within OpenCV's VideoCapture API. These tweak parameters can be used to transform input source Camera-Device properties (such as its brightness, saturation, framerate, resolution, gain etc.) seamlessly, and can be easily applied in CamGear API through its options dictionary parameter by formatting them as its attributes.

The complete usage example is as follows:

All the supported Source Tweak Parameters can be found here \u27b6

# import required libraries\nfrom vidgear.gears import CamGear\nimport cv2\n\n\n# define suitable tweak parameters for your stream.\noptions = {\n    \"CAP_PROP_FRAME_WIDTH\": 320, # resolution 320x240\n    \"CAP_PROP_FRAME_HEIGHT\": 240,\n    \"CAP_PROP_FPS\": 60, # framerate 60fps\n}\n\n# To open live video stream on webcam at first index(i.e. 0) \n# device and apply source tweak parameters\nstream = CamGear(source=0, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/camgear/usage/#using-camgear-with-direct-colorspace-manipulation","title":"Using Camgear with Direct Colorspace Manipulation","text":"

CamGear API also supports Direct Colorspace Manipulation, which is ideal for changing source colorspace on the run.

A more detailed information on colorspace manipulation can be found here \u27b6

In following example code, we will start with HSV as source colorspace, and then we will switch to GRAY colorspace when W key is pressed, and then LAB colorspace when E key is pressed, finally default colorspace (i.e. BGR) when S key is pressed. Also, quit when Q key is pressed:

Any incorrect or None-type value, will immediately revert the colorspace to default i.e. BGR.

# import required libraries\nfrom vidgear.gears import CamGear\nimport cv2\n\n# Open any source of your choice, like Webcam first index(i.e. 0)\n# and change its colorspace to `HSV`\nstream = CamGear(source=0, colorspace=\"COLOR_BGR2HSV\", logging=True).start()\n\n# loop over\nwhile True:\n\n    # read HSV frames\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the HSV frame here}\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for key if pressed\n    key = cv2.waitKey(1) & 0xFF\n\n    # check if 'w' key is pressed\n    if key == ord(\"w\"):\n        # directly change colorspace at any instant\n        stream.color_space = cv2.COLOR_BGR2GRAY  # Now colorspace is GRAY\n\n    # check for 'e' key is pressed\n    if key == ord(\"e\"):\n        stream.color_space = cv2.COLOR_BGR2LAB  # Now colorspace is CieLAB\n\n    # check for 's' key is pressed\n    if key == ord(\"s\"):\n        stream.color_space = None  # Now colorspace is default(ie BGR)\n\n    # check for 'q' key is pressed\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/camgear/advanced/source_params/","title":"Source Tweak Parameters","text":""},{"location":"gears/camgear/advanced/source_params/#source-tweak-parameters-for-camgear-api","title":"Source Tweak Parameters for CamGear API","text":""},{"location":"gears/camgear/advanced/source_params/#overview","title":"Overview","text":"

With CamGear's options dictionary parameter, the user has the ability to alter various tweak parameters available within OpenCV's VideoCapture Class by formatting them as its attributes.

These tweak parameters can be used to transform input Camera-Source properties (such as its brightness, saturation, resolution, iso, gain etc.) seamlessly. All parameters supported by CamGear API are disscussed in this document.

"},{"location":"gears/camgear/advanced/source_params/#exclusive-camgear-attributes","title":"Exclusive CamGear Attributes","text":"CamGear's Exclusive Attributes

In addition to Source Tweak Parameters, CamGear also provides some exclusive attributes for its options dictionary parameters.

These attributes are as follows:

"},{"location":"gears/camgear/advanced/source_params/#supported-source-tweak-parameters","title":"Supported Source Tweak Parameters","text":"

All Source Tweak Parameters supported by CamGear API are as follows:

Remember, Not all parameters are supported by all cameras devices, which is one of the most troublesome thing with OpenCV library. Each camera type, from android cameras, to USB cameras , to professional ones, offers a different interface to modify its parameters. Therefore, there are many branches in OpenCV code to support as many of them, but of course, not all possible devices are covered, and thereby works. Furthermore, OpenCV does not return any helpful error messages regarding this problem, so it\u2019s pretty much based on trial and error.

You can easily check parameter values supported by your webcam, by hooking it to a Linux machine, and using the command v4l2-ctl -d 0 --list-formats-ext (where 0 is an index of the given camera) to list the supported video parameters and their values. If that doesn't works, refer to its datasheet (if available).

These parameters can be passed to CamGear's options dictionary parameter by formatting them as its string attributes. Its complete usage example is here \u27b6

Values Description CAP_PROP_POS_MSEC Current position of the video file in milliseconds. CAP_PROP_POS_FRAMES 0-based index of the frame to be decoded/captured next. CAP_PROP_POS_AVI_RATIO Relative position of the video file: 0=start of the film, 1=end of the film. CAP_PROP_FRAME_WIDTH Width of the frames in the video stream. CAP_PROP_FRAME_HEIGHT Height of the frames in the video stream. CAP_PROP_FPS Frame rate. CAP_PROP_FOURCC 4-character code of codec. see VideoWriter::fourcc. CAP_PROP_FRAME_COUNT Number of frames in the video file. CAP_PROP_FORMAT Format of the Mat objects returned by VideoCapture::retrieve(). CAP_PROP_MODE Backend-specific value indicating the current capture mode. CAP_PROP_BRIGHTNESS Brightness of the image (only for those cameras that support). CAP_PROP_CONTRAST Contrast of the image (only for cameras). CAP_PROP_SATURATION Saturation of the image (only for cameras). CAP_PROP_HUE Hue of the image (only for cameras). CAP_PROP_GAIN Gain of the image (only for those cameras that support). CAP_PROP_EXPOSURE Exposure (only for those cameras that support). CAP_PROP_CONVERT_RGB Boolean flags indicating whether images should be converted to RGB. CAP_PROP_WHITE_BALANCE_BLUE_U Currently unsupported. CAP_PROP_RECTIFICATION Rectification flag for stereo cameras (note: only supported by DC1394 v 2.x backend currently). CAP_PROP_MONOCHROME CAP_PROP_SHARPNESS CAP_PROP_AUTO_EXPOSURE DC1394: exposure control done by camera, user can adjust reference level using this feature. CAP_PROP_GAMMA CAP_PROP_TEMPERATURE CAP_PROP_TRIGGER CAP_PROP_TRIGGER_DELAY CAP_PROP_WHITE_BALANCE_RED_V CAP_PROP_ZOOM CAP_PROP_FOCUS CAP_PROP_GUID CAP_PROP_ISO_SPEED CAP_PROP_BACKLIGHT CAP_PROP_PAN CAP_PROP_TILT CAP_PROP_ROLL CAP_PROP_IRIS CAP_PROP_SETTINGS Pop up video/camera filter dialog (note: only supported by DSHOW backend currently. The property value is ignored) CAP_PROP_BUFFERSIZE CAP_PROP_AUTOFOCUS CAP_PROP_SAR_NUM Sample aspect ratio: num/den (num) CAP_PROP_SAR_DEN Sample aspect ratio: num/den (den) CAP_PROP_BACKEND Current backend (enum VideoCapture APIs). Read-only property. CAP_PROP_CHANNEL Video input or Channel Number (only for those cameras that support) CAP_PROP_AUTO_WB enable/ disable auto white-balance CAP_PROP_WB_TEMPERATURE white-balance color temperature

"},{"location":"gears/netgear/overview/","title":"Overview","text":""},{"location":"gears/netgear/overview/#netgear-api","title":"NetGear API","text":"NetGear API generalized"},{"location":"gears/netgear/overview/#overview","title":"Overview","text":"

NetGear is exclusively designed to transfer video frames synchronously and asynchronously between interconnecting systems over the network in real-time.

NetGear implements a high-level wrapper around PyZmQ python library that contains python bindings for ZeroMQ - a high-performance asynchronous distributed messaging library that provides a message queue, but unlike message-oriented middleware, its system can run without a dedicated message broker.

NetGear also enables real-time JPEG Frame Compression capabilities for boosting performance significantly while sending video-frames over the network in real-time.

Lazy Pirate pattern in NetGear API

NetGear API now internally implements robust Lazy Pirate pattern (auto-reconnection) for its synchronous messaging patterns(zmq.PAIR & zmq.REQ/zmq.REP) at both Server and Client ends, where its API instead of doing a blocking receive, will:

Netgear API also provides max_retries and request_timeout like attributes for controlling this polling.

NetGear as of now seamlessly supports three ZeroMQ messaging patterns:

whereas the supported protocol are: tcp and ipc.

"},{"location":"gears/netgear/overview/#modes-of-operation","title":"Modes of Operation","text":""},{"location":"gears/netgear/overview/#primary-modes","title":"Primary Modes","text":"

NetGear API primarily has two modes of operations:

"},{"location":"gears/netgear/overview/#exclusive-modes","title":"Exclusive Modes","text":"

In addition to the primary modes, NetGear API also offers application-specific Exclusive Modes:

Also, checkout this compatibility chart for these modes interoperability.

"},{"location":"gears/netgear/overview/#a-multi-servers-mode","title":"A. Multi-Servers Mode","text":""},{"location":"gears/netgear/overview/#b-multi-clients-mode","title":"B. Multi-Clients Mode","text":""},{"location":"gears/netgear/overview/#c-bidirectional-mode","title":"C. Bidirectional Mode","text":""},{"location":"gears/netgear/overview/#d-ssh-tunneling-mode","title":"D. SSH Tunneling Mode","text":""},{"location":"gears/netgear/overview/#e-secure-mode","title":"E. Secure Mode","text":"

Important Information

"},{"location":"gears/netgear/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through NetGear Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/netgear/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/netgear/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/netgear/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/netgear/params/","title":"Parameters","text":""},{"location":"gears/netgear/params/#netgear-api-parameters","title":"NetGear API Parameters","text":""},{"location":"gears/netgear/params/#address","title":"address","text":"

This parameter sets the valid Network IP address for Server/Client. Network addresses are unique identifiers across the network.

Data-Type: String

Default Value: Its default value is based on selected primary mode, i.e 'localhost' for Send Mode and '*' for Receive Mode on a local machine.

Usage:

NetGear(address=\"192.168.0.145\")\n

"},{"location":"gears/netgear/params/#port","title":"port","text":"

This parameter sets the valid Network Port for Server/Client. Network port is a number that identifies one side of a connection between two devices on the network and is used determine to which process or application a message should be delivered.

Exception for Exclusive Modes

In Multi-Servers Mode:

In Multi-Client Mode:

Data-Type: String or List/Tuple

Default Value: Its default value is '5555'

Usage:

NetGear(port=\"5575\")\n

"},{"location":"gears/netgear/params/#protocol","title":"protocol","text":"

This parameter sets the valid messaging protocol between server and client. A network protocol is a set of established rules that dictates how to format, transmit and receive data so computer network devices - from servers and routers to endpoints - can communicate regardless of the differences in their underlying infrastructures, designs or standards. Supported protocol are: 'tcp' and 'ipc'.

Data-Type: String

Default Value: Its default value is 'tcp'

Usage:

NetGear(protocol=\"ipc\")\n

"},{"location":"gears/netgear/params/#pattern","title":"pattern","text":"

This parameter sets the supported messaging pattern(flow of communication) between server and client. Messaging patterns are the network-oriented architectural pattern that describes the flow of communication between interconnecting systems. NetGear provides access to ZeroMQ's pre-optimized sockets which enables you to take advantage of these patterns.

Data-Type: Integer

Default Value: Its default value is 0 (i.e zmq.PAIR).

Supported ZMQ patterns

All supported ZMQ patterns for NetGear are:

Usage:

NetGear(pattern=1) # sets zmq.REQ/zmq.REP pattern\n

"},{"location":"gears/netgear/params/#receive_mode","title":"receive_mode","text":"

This parameter select the Netgear's Mode of operation. It basically activates Receive Mode(if True) and Send Mode(if False). Furthermore, recv() method will only work when this flag is enabled(i.e. Receive Mode), whereas send() method will only work when this flag is disabled(i.e.Send Mode).

Data-Type: Boolean

Default Value: Its default value is False(i.e. Send Mode is activated by default).

Usage:

NetGear(receive_mode=True) # activates Recieve Mode\n

"},{"location":"gears/netgear/params/#options","title":"options","text":"

This parameter provides the flexibility to alter various NetGear API's internal properties, modes, and some PyZMQ flags.

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

Supported dictionary attributes for NetGear API

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

The desired attributes can be passed to NetGear API as follows:

# formatting parameters as dictionary attributes\noptions = {\n    \"secure_mode\": 2,\n    \"custom_cert_location\": \"/home/foo/foo1/foo2\",\n    \"overwrite_cert\": True,\n    \"flag\": 0, \n    \"copy\": True, \n    \"track\": False\n}\n# assigning it\nNetGear(logging=True, **options)\n

"},{"location":"gears/netgear/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

NetGear_Async(logging=True)\n

"},{"location":"gears/netgear/usage/","title":"Usage Examples","text":""},{"location":"gears/netgear/usage/#netgear-api-usage-examples","title":"NetGear API Usage Examples:","text":"

Important Information

After going through following Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/netgear/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with NetGear API:

"},{"location":"gears/netgear/usage/#servers-end","title":"Server's End","text":"

Open your favorite terminal and execute the following python code:

You can terminate both sides anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\n\n# open any valid video stream(for e.g `test.mp4` file)\nstream = VideoGear(source=\"test.mp4\").start()\n\n# Define Netgear Server with default parameters\nserver = NetGear()\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n
"},{"location":"gears/netgear/usage/#clients-end","title":"Client's End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n\n# define Netgear Client with `receive_mode = True` and default parameter\nclient = NetGear(receive_mode=True)\n\n# loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/usage/#using-netgear-with-variable-parameters","title":"Using NetGear with Variable Parameters","text":""},{"location":"gears/netgear/usage/#clients-end_1","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from the Server) and execute the following python code:

Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# define various tweak flags\noptions = {\"flag\": 0, \"copy\": True, \"track\": False}\n\n# Define Netgear Client at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n
"},{"location":"gears/netgear/usage/#servers-end_1","title":"Server's End","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\n\n# define various tweak flags\noptions = {\"flag\": 0, \"copy\": True, \"track\": False}\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = VideoGear(source=0).start()\n\n# Define Netgear server at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/usage/#using-netgear-with-opencv","title":"Using NetGear with OpenCV","text":"

You can easily use NetGear directly with any Video Processing library such as OpenCV itself. The complete usage example is as follows:

"},{"location":"gears/netgear/usage/#clients-end_2","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from the Server) and execute the following python code:

Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# define tweak flags\noptions = {\"flag\": 0, \"copy\": True, \"track\": False}\n\n# Define Netgear Client at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=0,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the received frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n
"},{"location":"gears/netgear/usage/#servers-end_2","title":"Server's End","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = cv2.VideoCapture(0)\n\n# define tweak flags\noptions = {\"flag\": 0, \"copy\": True, \"track\": False}\n\n# Define Netgear Client at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=0,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        (grabbed, frame) = stream.read()\n\n        # check for frame if not grabbed\n        if not grabbed:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.release()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/usage/#using-netgear-with-other-videocapture-gears","title":"Using NetGear with Other VideoCapture Gears","text":"

You can use any VideoCapture Gear in the similar manner. Let's implement given usage example with ScreenGear:

"},{"location":"gears/netgear/usage/#clients-end_3","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from the Server) and execute the following python code:

Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# define various tweak flags\noptions = {\"flag\": 0, \"copy\": True, \"track\": False}\n\n# Define Netgear Client at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n
"},{"location":"gears/netgear/usage/#servers-end_3","title":"Server's End","text":"

Now, Open the terminal on another Server System (let's say you want to transmit Monitor Screen Frames from a Laptop), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import ScreenGear\nfrom vidgear.gears import NetGear\n\n# define various tweak flags\noptions = {\"flag\": 0, \"copy\": True, \"track\": False}\n\n# Start capturing live Monitor screen frames with default settings\nstream = ScreenGear().start()\n\n# Define Netgear server at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/bidirectional_mode/","title":"Bidirectional Mode","text":""},{"location":"gears/netgear/advanced/bidirectional_mode/#bidirectional-mode-for-netgear-api","title":"Bidirectional Mode for NetGear API","text":"NetGear's Bidirectional Mode"},{"location":"gears/netgear/advanced/bidirectional_mode/#overview","title":"Overview","text":"

Bidirectional Mode enables seamless support for Bidirectional data transmission between Client/Consumer and Sender/Publisher along with video-frames through its synchronous messaging patterns such as zmq.PAIR (ZMQ Pair Pattern) & zmq.REQ/zmq.REP (ZMQ Request/Reply Pattern).

In Bidirectional Mode, we utilizes the NetGear API's message parameter of send() method for sending data from Server-to-Client, and return_data parameter of recv() method to return data back from Client-to-Server all while transferring frames in real-time.

This mode can be easily activated in NetGear through bidirectional_mode attribute of its options dictionary parameter during initialization.

Important Information regarding Bidirectional Mode

"},{"location":"gears/netgear/advanced/bidirectional_mode/#features-of-bidirectional-mode","title":"Features of Bidirectional Mode","text":"

"},{"location":"gears/netgear/advanced/bidirectional_mode/#exclusive-parameters","title":"Exclusive Parameters","text":"

To send data bidirectionally, NetGear API provides two exclusive parameters for its methods:

"},{"location":"gears/netgear/advanced/bidirectional_mode/#usage-examples","title":"Usage Examples","text":""},{"location":"gears/netgear/advanced/bidirectional_mode/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with Bidirectional Mode in NetGear API:

"},{"location":"gears/netgear/advanced/bidirectional_mode/#server-end","title":"Server End","text":"

Open your favorite terminal and execute the following python code:

You can terminate both sides anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\n\n# open any valid video stream(for e.g `test.mp4` file)\nstream = VideoGear(source=\"test.mp4\").start()\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# Define NetGear Server with defined parameters\nserver = NetGear(logging=True, **options)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # send frame & data and also receive data from Client\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # print data just received from Client\n        if not (recv_data is None):\n            print(recv_data)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.
"},{"location":"gears/netgear/advanced/bidirectional_mode/#client-end","title":"Client End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# define NetGear Client with `receive_mode = True` and defined parameter\nclient = NetGear(receive_mode=True, logging=True, **options)\n\n# loop over\nwhile True:\n\n    # prepare data to be sent\n    target_data = \"Hi, I am a Client here.\"\n\n    # receive data from server and also send our data\n    data = client.recv(return_data=target_data)\n\n    # check for data if None\n    if data is None:\n        break\n\n    # extract server_data & frame from data\n    server_data, frame = data\n\n    # again check for frame if None\n    if frame is None:\n        break\n\n    # {do something with the extracted frame and data here}\n\n    # lets print extracted server data\n    if not (server_data is None):\n        print(server_data)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/bidirectional_mode/#using-bidirectional-mode-with-variable-parameters","title":"Using Bidirectional Mode with Variable Parameters","text":""},{"location":"gears/netgear/advanced/bidirectional_mode/#clients-end","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from the Server) and execute the following python code:

Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# Define NetGear Client at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# loop over\nwhile True:\n\n    # prepare data to be sent\n    target_data = \"Hi, I am a Client here.\"\n\n    # receive data from server and also send our data\n    data = client.recv(return_data=target_data)\n\n    # check for data if None\n    if data is None:\n        break\n\n    # extract server_data & frame from data\n    server_data, frame = data\n\n    # again check for frame if None\n    if frame is None:\n        break\n\n    # {do something with the extracted frame and data here}\n\n    # lets print received server data\n    if not (server_data is None):\n        print(server_data)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/bidirectional_mode/#server-end_1","title":"Server End","text":"

Now, Open the terminal on another Server System (a Raspberry Pi with Camera Module), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this backend, otherwise nothing will work.

New Picamera2 backendLegacy Picamera backend
# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import PiGear\nfrom libcamera import Transform\n\n# add various Picamera2 API tweaks\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# Define NetGear server at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # send frame & data and also receive data from Client\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # print data just received from Client\n        if not (recv_data is None):\n            print(recv_data)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import PiGear\n\n# add various Picamera tweak parameters to dictionary\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# Define NetGear server at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # send frame & data and also receive data from Client\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # print data just received from Client\n        if not (recv_data is None):\n            print(recv_data)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.

"},{"location":"gears/netgear/advanced/bidirectional_mode/#using-bidirectional-mode-for-video-frames-transfer","title":"Using Bidirectional Mode for Video-Frames Transfer","text":"

In this example we are going to implement a bare-minimum example, where we will be sending video-frames (3-Dimensional numpy arrays) of the same Video bidirectionally at the same time, for testing the real-time performance and synchronization between the Server and the Client using this(Bidirectional) Mode.

This example is useful for building applications like Real-Time Video Chat.

We're also using reducer() method for reducing frame-size on-the-go for additional performance.

Remember, Sending large HQ video-frames may required more network bandwidth and packet size which may lead to video latency!

"},{"location":"gears/netgear/advanced/bidirectional_mode/#server-end_2","title":"Server End","text":"

Open your favorite terminal and execute the following python code:

You can terminate both side anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears.helper import reducer\nimport numpy as np\nimport cv2\n\n# open any valid video stream(for e.g `test.mp4` file)\nstream = cv2.VideoCapture(\"test.mp4\")\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# Define NetGear Server with defined parameters\nserver = NetGear(pattern=1, logging=True, **options)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        (grabbed, frame) = stream.read()\n\n        # check for frame if not grabbed\n        if not grabbed:\n            break\n\n        # reducer frames size if you want more performance, otherwise comment this line\n        frame = reducer(frame, percentage=30)  # reduce frame by 30%\n\n        # {do something with the frame here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # send frame & data and also receive data from Client\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # check data just received from Client is of numpy datatype\n        if not (recv_data is None) and isinstance(recv_data, np.ndarray):\n\n            # {do something with received numpy array here}\n\n            # Let's show it on output window\n            cv2.imshow(\"Received Frame\", recv_data)\n            key = cv2.waitKey(1) & 0xFF\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.release()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.

"},{"location":"gears/netgear/advanced/bidirectional_mode/#client-end_1","title":"Client End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears.helper import reducer\nimport cv2\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# again open the same video stream\nstream = cv2.VideoCapture(\"test.mp4\")\n\n# define NetGear Client with `receive_mode = True` and defined parameter\nclient = NetGear(receive_mode=True, pattern=1, logging=True, **options)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n        break\n\n    # reducer frames size if you want more performance, otherwise comment this line\n    frame = reducer(frame, percentage=30)  # reduce frame by 30%\n\n    # receive data from server and also send our data\n    data = client.recv(return_data=frame)\n\n    # check for data if None\n    if data is None:\n        break\n\n    # extract server_data & frame from data\n    server_data, frame = data\n\n    # again check for frame if None\n    if frame is None:\n        break\n\n    # {do something with the extracted frame and data here}\n\n    # lets print extracted server data\n    if not (server_data is None):\n        print(server_data)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.release()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/bidirectional_mode/#using-bidirectional-mode-for-video-frames-transfer-with-frame-compression","title":"Using Bidirectional Mode for Video-Frames Transfer with Frame Compression","text":"

This usage examples can be found here \u27b6

  1. Additional data of numpy.ndarray data-type is ONLY SUPPORTED at Client's end with its return_data parameter.

    \u21a9\u21a9

"},{"location":"gears/netgear/advanced/compression/","title":"Frame Compression","text":""},{"location":"gears/netgear/advanced/compression/#frame-compression-for-netgear-api","title":"Frame Compression for NetGear API","text":""},{"location":"gears/netgear/advanced/compression/#overview","title":"Overview","text":"

NetGear API enables real-time JPEG Frame Compression capabilities for optimizing performance significantly while sending frames over the network.

For enabling Frame Compression, NetGear uses powerful simplejpeg library at its backend, which is based on recent versions of libjpeg-turbo JPEG image codec, to accelerate baseline JPEG compression and decompression on all modern systems. NetGear API employs its exposed decode_jpeg and encode_jpeg methods to encode video-frames to JFIF format before sending it at Server, and cleverly decode it at the Client(s) all in real-time, thereby leveraging performance at cost of minor loss in frame quality.

Frame Compression is enabled by default in NetGear, and can be easily controlled through jpeg_compression_quality, jpeg_compression_fastdct, jpeg_compression_fastupsample like attributes of its options dictionary parameter during initialization.

Useful Information about Frame Compression

Frame Compression is primarily controlled by Server end. That means, if Frame Compression is enabled at Server, then Client(s) will automatically enforce the Frame Compression with defined performance attributes. Otherwise if it is disabled, then Client(s) disables it too.

"},{"location":"gears/netgear/advanced/compression/#exclusive-attributes","title":"Exclusive Attributes","text":"

For implementing Frame Compression, NetGear API currently provide following exclusive attribute for its options dictionary parameter to leverage performance with Frame Compression:

"},{"location":"gears/netgear/advanced/compression/#performance-attributes","title":"Performance Attributes","text":""},{"location":"gears/netgear/advanced/compression/#usage-examples","title":"Usage Examples","text":""},{"location":"gears/netgear/advanced/compression/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with Frame Compression in NetGear API:

"},{"location":"gears/netgear/advanced/compression/#server-end","title":"Server End","text":"

Open your favorite terminal and execute the following python code:

You can terminate both sides anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\nimport cv2\n\n# open any valid video stream(for e.g `test.mp4` file)\nstream = VideoGear(source=\"test.mp4\").start()\n\n# activate jpeg encoding and specify other related parameters\noptions = {\n    \"jpeg_compression\": True,\n    \"jpeg_compression_quality\": 90,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# Define NetGear Server with defined parameters\nserver = NetGear(pattern=1, logging=True, **options)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if None-type\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/compression/#client-end","title":"Client End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

You can terminate client anytime by pressing Ctrl+C on your keyboard!

If compression is enabled at Server, then Client will automatically enforce Frame Compression with its performance attributes.

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# define NetGear Client with `receive_mode = True` and defined parameter\nclient = NetGear(receive_mode=True, pattern=1, logging=True)\n\n# loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/compression/#bare-minimum-usage-with-variable-colorspace","title":"Bare-Minimum Usage with Variable Colorspace","text":"

Frame Compression also supports specify incoming frames colorspace with compression. In following bare-minimum code, we will be sending GRAY frames from Server to Client:

New in v0.2.2

This example was added in v0.2.2.

This example works in conjunction with Source ColorSpace manipulation for VideoCapture Gears \u27b6

Supported colorspace values are RGB, BGR, RGBX, BGRX, XBGR, XRGB, GRAY, RGBA, BGRA, ABGR, ARGB, CMYK. More information can be found here \u27b6

"},{"location":"gears/netgear/advanced/compression/#server-end_1","title":"Server End","text":"

Open your favorite terminal and execute the following python code:

You can terminate both sides anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\nimport cv2\n\n# open any valid video stream(for e.g `test.mp4` file) and change its colorspace to grayscale\nstream = VideoGear(source=\"test.mp4\", colorspace=\"COLOR_BGR2GRAY\").start()\n\n# activate jpeg encoding and specify other related parameters\noptions = {\n    \"jpeg_compression\": \"GRAY\", # set grayscale\n    \"jpeg_compression_quality\": 90,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# Define NetGear Server with defined parameters\nserver = NetGear(pattern=1, logging=True, **options)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read grayscale frames from stream\n        frame = stream.read()\n\n        # check for frame if None-type\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send grayscale frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/compression/#client-end_1","title":"Client End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

You can terminate client anytime by pressing Ctrl+C on your keyboard!

If compression is enabled at Server, then Client will automatically enforce Frame Compression with its performance attributes.

Client's end also automatically enforces Server's colorspace, there's no need to define it again.

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# define NetGear Client with `receive_mode = True` and defined parameter\nclient = NetGear(receive_mode=True, pattern=1, logging=True)\n\n# loop over\nwhile True:\n\n    # receive grayscale frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the grayscale frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Grayscale Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/compression/#using-frame-compression-with-variable-parameters","title":"Using Frame Compression with Variable Parameters","text":""},{"location":"gears/netgear/advanced/compression/#clients-end","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from the Server) and execute the following python code:

Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

If compression is enabled at Server, then Client will automatically enforce Frame Compression with its performance attributes.

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n\n# Define NetGear Client at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n#  loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/compression/#server-end_2","title":"Server End","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate jpeg encoding and specify other related parameters\noptions = {\n    \"jpeg_compression\": True,\n    \"jpeg_compression_quality\": 90,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = VideoGear(source=0).start()\n\n# Define NetGear server at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/compression/#using-bidirectional-mode-for-video-frames-transfer-with-frame-compression","title":"Using Bidirectional Mode for Video-Frames Transfer with Frame Compression","text":"

NetGear now supports Dual Frame Compression for transferring video-frames with its exclusive Bidirectional Mode for achieving unmatchable performance bidirectionally. You can easily enable Frame Compression with its performance attributes at both ends to boost performance bidirectionally.

In this example we are going to implement a bare-minimum example, where we will be sending video-frames (3-Dimensional numpy arrays) of the same Video bidirectionally at the same time for testing the real-time performance and synchronization between the Server and Client using Bidirectional Mode. Furthermore, we're going to use optimal Dual Frame Compression Setting for Sending and Receiving frames at both Server and Client end.

This example is great for building applications like Real-time Video Chat System.

This Dual Frame Compression feature also available for Multi-Clients Mode.

We're also using reducer() Helper method for reducing frame-size on-the-go for additional performance.

Remember to define Frame Compression's performance attributes both on Server and Client ends in Dual Frame Compression to boost performance bidirectionally!

"},{"location":"gears/netgear/advanced/compression/#server-end_3","title":"Server End","text":"

Open your favorite terminal and execute the following python code:

You can terminate both side anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears.helper import reducer\nimport numpy as np\nimport cv2\n\n# open any valid video stream(for e.g `test.mp4` file)\nstream = cv2.VideoCapture(\"test.mp4\")\n\n# activate Bidirectional mode and Frame Compression\noptions = {\n    \"bidirectional_mode\": True,\n    \"jpeg_compression\": True,\n    \"jpeg_compression_quality\": 95,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# Define NetGear Server with defined parameters\nserver = NetGear(pattern=1, logging=True, **options)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        (grabbed, frame) = stream.read()\n\n        # check for frame if not grabbed\n        if not grabbed:\n            break\n\n        # reducer frames size if you want even more performance, otherwise comment this line\n        frame = reducer(frame, percentage=20)  # reduce frame by 20%\n\n        # {do something with the frame here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # send frame & data and also receive data from Client\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # check data just received from Client is of numpy datatype\n        if not (recv_data is None) and isinstance(recv_data, np.ndarray):\n\n            # {do something with received numpy array here}\n\n            # Let's show it on output window\n            cv2.imshow(\"Received Frame\", recv_data)\n            key = cv2.waitKey(1) & 0xFF\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.release()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.

"},{"location":"gears/netgear/advanced/compression/#client-end_2","title":"Client End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears.helper import reducer\nimport cv2\n\n# activate Bidirectional mode and Frame Compression\noptions = {\n    \"bidirectional_mode\": True,\n    \"jpeg_compression\": True,\n    \"jpeg_compression_quality\": 95,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# again open the same video stream\nstream = cv2.VideoCapture(\"test.mp4\")\n\n# define NetGear Client with `receive_mode = True` and defined parameter\nclient = NetGear(receive_mode=True, pattern=1, logging=True, **options)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n        break\n\n    # reducer frames size if you want even more performance, otherwise comment this line\n    frame = reducer(frame, percentage=20)  # reduce frame by 20%\n\n    # receive data from server and also send our data\n    data = client.recv(return_data=frame)\n\n    # check for data if None\n    if data is None:\n        break\n\n    # extract server_data & frame from data\n    server_data, frame = data\n\n    # again check for frame if None\n    if frame is None:\n        break\n\n    # {do something with the extracted frame and data here}\n\n    # lets print extracted server data\n    if not (server_data is None):\n        print(server_data)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.release()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/multi_client/","title":"Multi-Clients Mode","text":""},{"location":"gears/netgear/advanced/multi_client/#multi-clients-mode-for-netgear-api","title":"Multi-Clients Mode for NetGear API","text":"NetGear's Multi-Clients Mode"},{"location":"gears/netgear/advanced/multi_client/#overview","title":"Overview","text":"

In Multi-Clients Mode, NetGear robustly handles Multiple Clients at once thereby able to broadcast frames and data across multiple Clients/Consumers in the network at same time. This mode works contrary to Multi-Servers Mode such that every new Client that connects to single Server can be identified by its unique port address on the network.

The supported patterns for this mode are Publish/Subscribe (zmq.PUB/zmq.SUB) and Request/Reply(zmq.REQ/zmq.REP) and can be easily activated in NetGear API through multiclient_mode attribute of its options dictionary parameter during initialization.

Multi-Clients Mode is best for broadcasting Meta-Data with Video-frames to specific limited number of clients in real time. But if you're looking to scale broadcast to a very large pool of clients, then see our WebGear or WebGear_RTC APIs.

Important Information regarding Multi-Clients Mode

"},{"location":"gears/netgear/advanced/multi_client/#features-of-multi-clients-mode","title":"Features of Multi-Clients Mode","text":"

"},{"location":"gears/netgear/advanced/multi_client/#usage-examples","title":"Usage Examples","text":"

Important

"},{"location":"gears/netgear/advanced/multi_client/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

In this example, we will capturing live video-frames from a source (a.k.a Server) with a webcam connected to it. Afterwards, those captured frame will be sent over the network to two independent system (a.k.a Clients) using this Multi-Clients Mode in NetGear API. Finally, both Clients will be displaying received frames in Output Windows in real time.

This example is useful for building applications like Real-Time Video Broadcasting to multiple clients in local network.

"},{"location":"gears/netgear/advanced/multi_client/#servers-end","title":"Server's End","text":"

Now, Open the terminal on a Server System (with a webcam connected to it at index 0). Now execute the following python code:

Important Notes

Frame/Data transmission will NOT START untill all given Client(s) are connected to this Server.

You can terminate streaming anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import CamGear\n\n# Open suitable video stream (webcam on first index in our case)\nstream = CamGear(source=0).start()\n\n# activate multiclient_mode mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple of\n# all unique Server((5577,5578) in our case) and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.x.x\",\n    port=(5567, 5577),\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# Define received data dictionary\ndata_dict = {}\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if not None-type\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame and also receive data from Client(s)\n        recv_data = server.send(frame)\n\n        # check if valid data received\n        if not (recv_data is None):\n            # extract unique port address and its respective data\n            unique_address, data = recv_data\n            # update the extracted data in the data dictionary\n            data_dict[unique_address] = data\n\n        if data_dict:\n            # print data just received from Client(s)\n            for key, value in data_dict.items():\n                print(\"Client at port {} said: {}\".format(key, value))\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#client-1s-end","title":"Client-1's End","text":"

Now, Open a terminal on another Client System (where you want to display the input frames received from Server), let's name it Client-1. Execute the following python code:

Replace the IP address in the following code with Server's IP address you noted earlier and also assign a unique port address (required by Server to identify this system).

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate Multi-Clients mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at Server's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5567\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n) \n\n# loop over\nwhile True:\n    # receive data from server\n    frame = client.recv()\n\n    # check for frame if None\n    if frame is None:\n        break\n\n    # {do something with frame here}\n\n    # Show output window\n    cv2.imshow(\"Client 5567 Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#client-2s-end","title":"Client-2's End","text":"

Finally, Open a terminal on another Client System (where you want to display the input frames received from Server), let's name it Client-2. Execute the following python code:

Replace the IP address in the following code with Server's IP address you noted earlier and also assign a unique port address (required by Server to identify this system).

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate Multi-Clients mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at Server's IP address and assign a unique port address and other parameters\n # !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5577\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# loop over\nwhile True:\n\n    # receive data from server\n    frame = client.recv()\n\n    # check for frame if None\n    if frame is None:\n        break\n\n    # {do something with frame here}\n\n    # Show output window\n    cv2.imshow(\"Client 5577 Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#bare-minimum-usage-with-opencv","title":"Bare-Minimum Usage with OpenCV","text":"

In this example, we will be re-implementing previous bare-minimum example with OpenCV and NetGear API.

"},{"location":"gears/netgear/advanced/multi_client/#servers-end_1","title":"Server's End","text":"

Now, Open the terminal on a Server System (with a webcam connected to it at index 0). Now execute the following python code:

Important Notes

Frame/Data transmission will NOT START untill all given Client(s) are connected to this Server.

You can terminate streaming anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# Open suitable video stream (webcam on first index in our case)\nstream = cv2.VideoCapture(0)\n\n# activate multiclient_mode mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple of all unique Server((5577,5578) in our case) and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.x.x\",\n    port=(5567, 5577),\n    protocol=\"tcp\",\n    pattern=2,\n    logging=True,\n    **options\n)\n\n# Define received data dictionary\ndata_dict = {}\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        (grabbed, frame) = stream.read()\n\n        # check for frame if not grabbed\n        if not grabbed:\n            break\n\n        # {do something with the frame here}\n\n        # send frame and also receive data from Client(s)\n        recv_data = server.send(frame)\n\n        # check if valid data received\n        if not (recv_data is None):\n            # extract unique port address and its respective data\n            unique_address, data = recv_data\n            # update the extracted data in the data dictionary\n            data_dict[unique_address] = data\n\n        if data_dict:\n            # print data just received from Client(s)\n            for key, value in data_dict.items():\n                print(\"Client at port {} said: {}\".format(key, value))\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.release()\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#client-1s-end_1","title":"Client-1's End","text":"

Now, Open a terminal on another Client System (where you want to display the input frames received from Server), let's name it Client-1. Execute the following python code:

Replace the IP address in the following code with Server's IP address you noted earlier and also assign a unique port address (required by Server to identify this system).

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate Multi-Clients mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at Server's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5567\",\n    protocol=\"tcp\",\n    pattern=2,\n    receive_mode=True,\n    logging=True,\n    **options\n) \n\n# loop over\nwhile True:\n    # receive data from server\n    frame = client.recv()\n\n    # check for frame if None\n    if frame is None:\n        break\n\n    # {do something with frame here}\n\n    # Show output window\n    cv2.imshow(\"Client 5567 Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#client-2s-end_1","title":"Client-2's End","text":"

Finally, Open a terminal on another Client System (also, where you want to display the input frames received from Server), let's name it Client-2. Execute the following python code:

Replace the IP address in the following code with Server's IP address you noted earlier and also assign a unique port address (required by Server to identify this system).

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate Multi-Clients mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at Server's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5577\",\n    protocol=\"tcp\",\n    pattern=2,\n    receive_mode=True,\n    logging=True,\n    **options\n) \n\n# loop over\nwhile True:\n    # receive data from server\n    frame = client.recv()\n\n    # check for frame if None\n    if frame is None:\n        break\n\n    # {do something with frame here}\n\n    # Show output window\n    cv2.imshow(\"Client 5577 Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#using-multi-clients-mode-for-unidirectional-custom-data-transfer","title":"Using Multi-Clients Mode for Unidirectional Custom Data Transfer","text":"

Abstract

With Multi-Clients Mode, you can also send additional data of any data-type (such as list, tuple, string, int, ndarray etc.) along with frame, from all connected Clients(s) back to a Server unidirectionally.

In Multi-Clients Mode, unidirectional data transfer ONLY works with pattern 1 (i.e. Request/Reply zmq.REQ/zmq.REP), and NOT with pattern 2 (i.e. Publish/Subscribe zmq.PUB/zmq.SUB)!

In this example, We will be transferring video-frames from a single Server (consisting of Raspberry Pi with Camera Module) over the network to two independent Client for displaying them in real-time. At the same time, we will be sending data (a Text String, for the sake of simplicity) from both the Client(s) back to our Server, which will be printed onto the terminal.

"},{"location":"gears/netgear/advanced/multi_client/#servers-end_2","title":"Server's End","text":"

Now, Open the terminal on a Server System (with a webcam connected to it at index 0). Now execute the following python code:

Important Notes

Frame/Data transmission will NOT START untill all given Client(s) are connected to this Server.

You can terminate streaming anytime by pressing Ctrl+C on your keyboard!

Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this backend, otherwise nothing will work.

New Picamera2 backendLegacy Picamera backend
# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import NetGear\n\n# add various Picamera2 tweak parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate multiclient_mode mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple of all unique Server((5577,5578) in our case) and other parameters\nserver = NetGear(\n    address=\"192.168.x.x\",\n    port=(5577, 5578),\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)  # !!! change following IP address '192.168.x.xxx' with yours !!!\n\n# Define received data dictionary\ndata_dict = {}\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame and also receive data from Client(s)\n        recv_data = server.send(frame)\n\n        # check if valid data received\n        if not (recv_data is None):\n            # extract unique port address and its respective data\n            unique_address, data = recv_data\n            # update the extracted data in the data dictionary\n            data_dict[unique_address] = data\n\n        if data_dict:\n            # print data just received from Client(s)\n            for key, value in data_dict.items():\n                print(\"Client at port {} said: {}\".format(key, value))\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import NetGear\n\n# add various Picamera tweak parameters to dictionary\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate multiclient_mode mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple of all unique Server((5577,5578) in our case) and other parameters\nserver = NetGear(\n    address=\"192.168.x.x\",\n    port=(5577, 5578),\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)  # !!! change following IP address '192.168.x.xxx' with yours !!!\n\n# Define received data dictionary\ndata_dict = {}\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame and also receive data from Client(s)\n        recv_data = server.send(frame)\n\n        # check if valid data received\n        if not (recv_data is None):\n            # extract unique port address and its respective data\n            unique_address, data = recv_data\n            # update the extracted data in the data dictionary\n            data_dict[unique_address] = data\n\n        if data_dict:\n            # print data just received from Client(s)\n            for key, value in data_dict.items():\n                print(\"Client at port {} said: {}\".format(key, value))\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#client-1s-end_2","title":"Client-1's End","text":"

Now, Open a terminal on another Client System (where you want to display the input frames received from Server), let's name it Client-1. Execute the following python code:

Replace the IP address in the following code with Server's IP address you noted earlier and also assign a unique port address (required by Server to identify this system).

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate Multi-Clients mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at Server's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5577\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# loop over\nwhile True:\n\n    # prepare data to be sent\n    target_data = \"Hi, I am 5577 Client here.\"\n\n    # receive data from server and also send our data\n    frame = client.recv(return_data=target_data)\n\n    # check for frame if None\n    if frame is None:\n        break\n\n    # {do something with frame here}\n\n    # Show output window\n    cv2.imshow(\"Client 5577 Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#client-2s-end_2","title":"Client-2's End","text":"

Finally, Open a terminal on another Client System (also, where you want to display the input frames received from Server), let's name it Client-2. Execute the following python code:

Replace the IP address in the following code with Server's IP address you noted earlier and also assign a unique port address (required by Server to identify this system).

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate Multi-Clients mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at Server's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5578\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n) \n\n# loop over\nwhile True:\n\n    # prepare data to be sent\n    target_data = \"Hi, I am 5578 Client here.\"\n\n    # receive data from server and also send our data\n    frame = client.recv(return_data=target_data)\n\n    # check for frame if None\n    if frame is None:\n        break\n\n    # {do something with frame here}\n\n    # Show output window\n    cv2.imshow(\"Client 5578 Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#using-multi-clients-mode-with-bidirectional-mode","title":"Using Multi-Clients Mode with Bidirectional Mode","text":"

Abstract

Multi-Clients Mode now also compatible with Bidirectional Mode, which lets you send additional data of any datatype1 along with frame in real-time bidirectionally between a single Server and all connected Client(s).

Important Information

New in v0.2.5

This example was added in v0.2.5.

In this example, We will be transferring video-frames and data (a Text String, for the sake of simplicity) from a single Server (In this case, Raspberry Pi with Camera Module) over the network to two independent Clients for displaying them both in real-time. At the same time, we will be sending data (a Text String, for the sake of simplicity) back from both the Client(s) to our Server, which will be printed onto the terminal.

"},{"location":"gears/netgear/advanced/multi_client/#servers-end_3","title":"Server's End","text":"

Now, Open the terminal on a Server System (with a webcam connected to it at index 0). Now execute the following python code:

Important Notes

Frame/Data transmission will NOT START untill all given Client(s) are connected to this Server.

You can terminate streaming anytime by pressing Ctrl+C on your keyboard!

Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this backend, otherwise nothing will work.

New Picamera2 backendLegacy Picamera backend
# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import NetGear\nfrom libcamera import Transform\n\n# add various Picamera2 tweak parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate both multiclient and bidirectional modes\noptions = {\"multiclient_mode\": True, \"bidirectional_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple of \n# all unique Server((5577,5578) in our case) and other parameters\nserver = NetGear(\n    address=\"192.168.x.x\",\n    port=(5577, 5578),\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)  # !!! change following IP address '192.168.x.xxx' with yours !!!\n\n# Define received data dictionary\ndata_dict = {}\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # send frame & data and also receive data from Client(s)\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # check if valid data received\n        if not (recv_data is None):\n            # extract unique port address and its respective data\n            unique_address, data = recv_data\n            # update the extracted data in the data dictionary\n            data_dict[unique_address] = data\n\n        if data_dict:\n            # print data just received from Client(s)\n            for key, value in data_dict.items():\n                print(\"Client at port {} said: {}\".format(key, value))\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import NetGear\n\n# add various Picamera tweak parameters to dictionary\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate both multiclient and bidirectional modes\noptions = {\"multiclient_mode\": True, \"bidirectional_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple of \n# all unique Server((5577,5578) in our case) and other parameters\nserver = NetGear(\n    address=\"192.168.x.x\",\n    port=(5577, 5578),\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)  # !!! change following IP address '192.168.x.xxx' with yours !!!\n\n# Define received data dictionary\ndata_dict = {}\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # send frame & data and also receive data from Client(s)\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # check if valid data received\n        if not (recv_data is None):\n            # extract unique port address and its respective data\n            unique_address, data = recv_data\n            # update the extracted data in the data dictionary\n            data_dict[unique_address] = data\n\n        if data_dict:\n            # print data just received from Client(s)\n            for key, value in data_dict.items():\n                print(\"Client at port {} said: {}\".format(key, value))\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.

"},{"location":"gears/netgear/advanced/multi_client/#client-1s-end_3","title":"Client-1's End","text":"

Now, Open a terminal on another Client System (where you want to display the input frames received from Server), let's name it Client-1. Execute the following python code:

Replace the IP address in the following code with Server's IP address you noted earlier and also assign a unique port address (required by Server to identify this system).

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate both multiclient and bidirectional modes\noptions = {\"multiclient_mode\": True, \"bidirectional_mode\": True}\n\n# Define NetGear Client at Server's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5577\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# loop over\nwhile True:\n\n    # prepare data to be sent\n    target_data = \"Hi, I am 5577 Client here.\"\n\n    # receive data from server and also send our data\n    data = client.recv(return_data=target_data)\n\n    # check for data if None\n    if data is None:\n        break\n\n    # extract server_data & frame from data\n    server_data, frame = data\n\n    # again check for frame if None\n    if frame is None:\n        break\n\n    # {do something with the extracted frame and data here}\n\n    # lets print extracted server data\n    if not (server_data is None):\n        print(server_data)\n\n    # Show output window\n    cv2.imshow(\"Client 5577 Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#client-2s-end_3","title":"Client-2's End","text":"

Finally, Open a terminal on another Client System (also, where you want to display the input frames received from Server), let's name it Client-2. Execute the following python code:

Replace the IP address in the following code with Server's IP address you noted earlier and also assign a unique port address (required by Server to identify this system).

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate both multiclient and bidirectional modes\noptions = {\"multiclient_mode\": True, \"bidirectional_mode\": True}\n\n# Define NetGear Client at Server's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5578\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n) \n\n# loop over\nwhile True:\n\n    # prepare data to be sent\n    target_data = \"Hi, I am 5578 Client here.\"\n\n    # receive data from server and also send our data\n    data = client.recv(return_data=target_data)\n\n    # check for data if None\n    if data is None:\n        break\n\n    # extract server_data & frame from data\n    server_data, frame = data\n\n    # again check for frame if None\n    if frame is None:\n        break\n\n    # {do something with the extracted frame and data here}\n\n    # lets print extracted server data\n    if not (server_data is None):\n        print(server_data)\n\n    # Show output window\n    cv2.imshow(\"Client 5578 Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

  1. Additional data of numpy.ndarray data-type is NOT SUPPORTED at Server's end with its message parameter.

    \u21a9

"},{"location":"gears/netgear/advanced/multi_server/","title":"Multi-Servers Mode","text":""},{"location":"gears/netgear/advanced/multi_server/#multi-servers-mode-for-netgear-api","title":"Multi-Servers Mode for NetGear API","text":"NetGear's Multi-Servers Mode"},{"location":"gears/netgear/advanced/multi_server/#overview","title":"Overview","text":"

In Multi-Servers Mode, NetGear API robustly handles Multiple Servers at once, thereby providing seamless access to frames and unidirectional data transfer across multiple Publishers/Servers in the network at the same time. Each new server connects to a single client can be identified by its unique port address on the network.

The supported patterns for this mode are Publish/Subscribe (zmq.PUB/zmq.SUB) and Request/Reply(zmq.REQ/zmq.REP) and can be easily activated in NetGear API through multiserver_mode attribute of its options dictionary parameter during initialization.

Important Information regarding Multi-Servers Mode

"},{"location":"gears/netgear/advanced/multi_server/#key-features","title":"Key Features","text":"

"},{"location":"gears/netgear/advanced/multi_server/#usage-examples","title":"Usage Examples","text":"

Example Assumptions

"},{"location":"gears/netgear/advanced/multi_server/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

In this example, we will capturing live video-frames on two independent sources (a.k.a Servers), each with a webcam connected to it. Afterwards, these frames will be sent over the network to a single system (a.k.a Client) using this Multi-Servers Mode in NetGear API in real time, and will be displayed as a live montage.

This example is useful for building applications like Real-Time Security System with multiple cameras.

"},{"location":"gears/netgear/advanced/multi_server/#clients-end","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from Multiple Servers) and execute the following python code:

Important Notes

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom imutils import build_montages # (1)\nimport cv2\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple \n# of all unique Server((5566,5567) in our case) and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=(5566, 5567),\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    **options\n)\n\n# Define received frame dictionary\nframe_dict = {}\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # receive data from network\n        data = client.recv()\n\n        # check if data received isn't None\n        if data is None:\n            break\n\n        # extract unique port address and its respective frame\n        unique_address, frame = data\n\n        # {do something with the extracted frame here}\n\n        # get extracted frame's shape\n        (h, w) = frame.shape[:2]\n\n        # update the extracted frame in the received frame dictionary\n        frame_dict[unique_address] = frame\n\n        # build a montage using data dictionary\n        montages = build_montages(frame_dict.values(), (w, h), (2, 1))\n\n        # display the montage(s) on the screen\n        for (i, montage) in enumerate(montages):\n\n            cv2.imshow(\"Montage Footage {}\".format(i), montage)\n\n        # check for 'q' key if pressed\n        key = cv2.waitKey(1) & 0xFF\n        if key == ord(\"q\"):\n            break\n\n    except KeyboardInterrupt:\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n
  1. For building Frames Montage you'll need imutils python library. Install it with pip install imutils command.

"},{"location":"gears/netgear/advanced/multi_server/#server-1s-end","title":"Server-1's End","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and let's called it Server-1. Now execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier and also assign a unique port address (required by Client to identify this system).

You can terminate stream anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import CamGear\n\n# Open suitable video stream (webcam on first index in our case)\nstream = CamGear(source=0).start()\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.x.x\", port=\"5566\", protocol=\"tcp\", pattern=1, **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if not None-type\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/multi_server/#server-2s-end","title":"Server-2's End","text":"

Finally, Open the terminal on another Server System (also with a webcam connected to it at index 0), and let's called it Server-2. Now execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier and also assign a unique port address (required by Client to identify this system).

You can terminate stream anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import CamGear\n\n# Open suitable video stream (webcam on first index in our case)\nstream = CamGear(source=0).start()\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.x.x\", port=\"5567\", protocol=\"tcp\", pattern=1, **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if not None-type\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/multi_server/#bare-minimum-usage-with-opencv","title":"Bare-Minimum Usage with OpenCV","text":"

In this example, we will be re-implementing previous bare-minimum example with OpenCV and NetGear API.

"},{"location":"gears/netgear/advanced/multi_server/#clients-end_1","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from Mutiple Servers) and execute the following python code:

Important Notes

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom imutils import build_montages # (1)\nimport cv2\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple of all \n# unique Server((5566,5567) in our case) and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=(5566, 5567),\n    protocol=\"tcp\",\n    pattern=2,\n    receive_mode=True,\n    **options\n)\n\n# Define received frame dictionary\nframe_dict = {}\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # receive data from network\n        data = client.recv()\n\n        # check if data received isn't None\n        if data is None:\n            break\n\n        # extract unique port address and its respective frame\n        unique_address, frame = data\n\n        # {do something with the extracted frame here}\n\n        # get extracted frame's shape\n        (h, w) = frame.shape[:2]\n\n        # update the extracted frame in the received frame dictionary\n        frame_dict[unique_address] = frame\n\n        # build a montage using data dictionary\n        montages = build_montages(frame_dict.values(), (w, h), (2, 1))\n\n        # display the montage(s) on the screen\n        for (i, montage) in enumerate(montages):\n\n            cv2.imshow(\"Montage Footage {}\".format(i), montage)\n\n        # check for 'q' key if pressed\n        key = cv2.waitKey(1) & 0xFF\n        if key == ord(\"q\"):\n            break\n\n    except KeyboardInterrupt:\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n
  1. For building Frames Montage you'll need imutils python library. Install it with pip install imutils command.

"},{"location":"gears/netgear/advanced/multi_server/#server-1s-end_1","title":"Server-1's End","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and let's called it Server-1. Now execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier and also assign a unique port address (required by Client to identify this system).

You can terminate stream anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# Open suitable video stream (webcam on first index in our case)\nstream = cv2.VideoCapture(0)\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameter\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.x.x\", port=\"5566\", protocol=\"tcp\", pattern=2, **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        (grabbed, frame) = stream.read()\n\n        # check for frame if not grabbed\n        if not grabbed:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.release()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/multi_server/#server-2s-end_1","title":"Server-2's End","text":"

Finally, Open the terminal on another Server System (also with a webcam connected to it at index 0), and let's called it Server-2. Now execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier and also assign a unique port address (required by Client to identify this system).

You can terminate stream anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# Open suitable video stream (webcam on first index in our case)\nstream = cv2.VideoCapture(0)\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.x.x\", port=\"5567\", protocol=\"tcp\", pattern=2, **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        (grabbed, frame) = stream.read()\n\n        # check for frame if not grabbed\n        if not grabbed:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.release()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/multi_server/#using-multi-servers-mode-for-unidirectional-custom-data-transfer","title":"Using Multi-Servers Mode for Unidirectional Custom Data Transfer","text":"

Abstract

With Multi-Servers Mode, you can send additional data of any datatype1 along with frame with frame in real-time, from all connected Server(s) to a single Client unidirectionally.

But numpy.ndarray data-type is NOT supported as data.

In this example, We will be transferring video-frames and data (a Text String, for the sake of simplicity) from two Servers (consisting of a Raspberry Pi with Camera Module & a Laptop with webcam) to a single Client over the network in real-time. The received video-frames at Client's end will displayed as a live montage, whereas the received data will be printed to the terminal.

"},{"location":"gears/netgear/advanced/multi_server/#clients-end_2","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from Mutiple Servers) and execute the following python code:

Important Notes

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom imutils import build_montages # (1)\nimport cv2\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple of all unique Server((5577,5578) in our case) and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=(5577, 5578),\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)  \n# Define received frame dictionary\nframe_dict = {}\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # receive data from network\n        data = client.recv()\n\n        # check if data received isn't None\n        if data is None:\n            break\n\n        # extract unique port address and its respective frame and received data\n        unique_address, extracted_data, frame = data\n\n        # {do something with the extracted frame and data here}\n        # let's display extracted data on our extracted frame\n        cv2.putText(\n            frame,\n            extracted_data,\n            (10, frame.shape[0] - 10),\n            cv2.FONT_HERSHEY_SIMPLEX,\n            0.6,\n            (0, 255, 0),\n            2,\n        )\n\n        # get extracted frame's shape\n        (h, w) = frame.shape[:2]\n\n        # update the extracted frame in the frame dictionary\n        frame_dict[unique_address] = frame\n\n        # build a montage using data dictionary\n        montages = build_montages(frame_dict.values(), (w, h), (2, 1))\n\n        # display the montage(s) on the screen\n        for (i, montage) in enumerate(montages):\n\n            cv2.imshow(\"Montage Footage {}\".format(i), montage)\n\n        # check for 'q' key if pressed\n        key = cv2.waitKey(1) & 0xFF\n        if key == ord(\"q\"):\n            break\n\n    except KeyboardInterrupt:\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n
  1. For building Frames Montage you'll need imutils python library. Install it with pip install imutils command.

"},{"location":"gears/netgear/advanced/multi_server/#server-1s-end_2","title":"Server-1's End","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and let's called it Server-1. Now execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier and also assign a unique port address (required by Client to identify this system).

You can terminate stream anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import VideoGear\nimport cv2\n\n# Open suitable video stream (webcam on first index in our case)\nstream = VideoGear(source=0).start()\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5577\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with frame and data(to be sent) here}\n\n        # let's prepare a text string as data\n        target_data = \"I'm Server-1 at Port: 5577\"\n\n        # send frame and data through server\n        server.send(frame, message=target_data) # (1)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.

"},{"location":"gears/netgear/advanced/multi_server/#server-2s-end_2","title":"Server-2's End","text":"

Finally, Open the terminal on another Server System (this time a Raspberry Pi with Camera Module connected to it), and let's called it Server-2. Now execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier and also assign a unique port address (required by Client to identify this system).

You can terminate stream anytime by pressing Ctrl+C on your keyboard!

Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this backend, otherwise nothing will work.

New Picamera2 backendLegacy Picamera backend
# import libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import PiGear\nfrom libcamera import Transform\nimport cv2\n\n# add various Picamera tweak parameters to dictionary\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.1.xxx\",\n    port=\"5578\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with frame and data(to be sent) here}\n\n        # let's prepare a text string as data\n        text = \"I'm Server-2 at Port: 5578\"\n\n        # send frame and data through server\n        server.send(frame, message=text)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream.\nstream.stop()\n\n# safely close server\nserver.close()\n
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import PiGear\nimport cv2\n\n# add various Picamera tweak parameters to dictionary\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.1.xxx\",\n    port=\"5578\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with frame and data(to be sent) here}\n\n        # let's prepare a text string as data\n        text = \"I'm Server-2 at Port: 5578\"\n\n        # send frame and data through server\n        server.send(frame, message=text)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream.\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/multi_server/#using-multi-servers-mode-with-bidirectional-mode","title":"Using Multi-Servers Mode with Bidirectional Mode","text":"

Abstract

Multi-Servers Mode now also compatible with Bidirectional Mode, which lets you send additional data of any datatype1 along with frame in real-time bidirectionally between a single Client and all connected Server(s).

Important Information

New in v0.2.5

This example was added in v0.2.5.

In this example, We will be transferring video-frames and data (a Text String, for the sake of simplicity) from two Servers (consisting of a Raspberry Pi with Camera Module & a Laptop with webcam) to a single Client, and at same time sending back data (a Text String, for the sake of simplicity) to them over the network all in real-time. The received video-frames at Client's end will displayed as a live montage, whereas the received data will be printed to the terminal.

"},{"location":"gears/netgear/advanced/multi_server/#clients-end_3","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from Mutiple Servers) and execute the following python code:

Important Notes

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom imutils import build_montages # (1)\nimport cv2\n\n# activate both multiserver and bidirectional modes\noptions = {\"multiserver_mode\": True, \"bidirectional_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple of all unique Server((5577,5578) in our case) and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=(5577, 5578),\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)  \n# Define received frame dictionary\nframe_dict = {}\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # prepare data to be sent\n        target_data = \"Hi, I am a Client here.\"\n\n        # receive data from server(s) and also send our data\n        data = client.recv(return_data=target_data)\n\n        # check if data received isn't None\n        if data is None:\n            break\n\n        # extract unique port address and its respective frame and received data\n        unique_address, extracted_data, frame = recv_data\n\n        # {do something with the extracted frame and data here}\n        # let's display extracted data on our extracted frame\n        cv2.putText(\n            frame,\n            extracted_data,\n            (10, frame.shape[0] - 10),\n            cv2.FONT_HERSHEY_SIMPLEX,\n            0.6,\n            (0, 255, 0),\n            2,\n        )\n\n        # get extracted frame's shape\n        (h, w) = frame.shape[:2]\n\n        # update the extracted frame in the frame dictionary\n        frame_dict[unique_address] = frame\n\n        # build a montage using data dictionary\n        montages = build_montages(frame_dict.values(), (w, h), (2, 1))\n\n        # display the montage(s) on the screen\n        for (i, montage) in enumerate(montages):\n\n            cv2.imshow(\"Montage Footage {}\".format(i), montage)\n\n        # check for 'q' key if pressed\n        key = cv2.waitKey(1) & 0xFF\n        if key == ord(\"q\"):\n            break\n\n    except KeyboardInterrupt:\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n
  1. For building Frames Montage you'll need imutils python library. Install it with pip install imutils command.

"},{"location":"gears/netgear/advanced/multi_server/#server-1s-end_3","title":"Server-1's End","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and let's called it Server-1. Now execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier and also assign a unique port address (required by Client to identify this system).

You can terminate stream anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import VideoGear\nimport cv2\n\n# Open suitable video stream (webcam on first index in our case)\nstream = VideoGear(source=0).start()\n\n# activate both multiserver and bidirectional modes\noptions = {\"multiserver_mode\": True, \"bidirectional_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5577\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with frame and data(to be sent) here}\n\n        # let's prepare a text string as data\n        target_data = \"I'm Server-1 at Port: 5577\"\n\n        # send frame & data and also receive data from Client\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # print data just received from Client\n        if not (recv_data is None):\n            print(recv_data)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.

"},{"location":"gears/netgear/advanced/multi_server/#server-2s-end_3","title":"Server-2's End","text":"

Finally, Open the terminal on another Server System (this time a Raspberry Pi with Camera Module connected to it), and let's called it Server-2. Now execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier and also assign a unique port address (required by Client to identify this system).

You can terminate stream anytime by pressing Ctrl+C on your keyboard!

Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this backend, otherwise nothing will work.

New Picamera2 backendLegacy Picamera backend
# import libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import PiGear\nfrom libcamera import Transform\nimport cv2\n\n# add various Picamera2 tweak parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate both multiserver and bidirectional modes\noptions = {\"multiserver_mode\": True, \"bidirectional_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.1.xxx\",\n    port=\"5578\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with frame and data(to be sent) here}\n\n        # let's prepare a text string as data\n        target_data = \"I'm Server-2 at Port: 5578\"\n\n        # send frame & data and also receive data from Client\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # print data just received from Client\n        if not (recv_data is None):\n            print(recv_data)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream.\nstream.stop()\n\n# safely close server\nserver.close()\n
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import PiGear\nimport cv2\n\n# add various Picamera tweak parameters to dictionary\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate both multiserver and bidirectional modes\noptions = {\"multiserver_mode\": True, \"bidirectional_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.1.xxx\",\n    port=\"5578\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with frame and data(to be sent) here}\n\n        # let's prepare a text string as data\n        target_data = \"I'm Server-2 at Port: 5578\"\n\n        # send frame & data and also receive data from Client\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # print data just received from Client\n        if not (recv_data is None):\n            print(recv_data)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream.\nstream.stop()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.

  1. Additional data of numpy.ndarray data-type is NOT SUPPORTED at Server(s) with their message parameter.

    \u21a9\u21a9\u21a9

"},{"location":"gears/netgear/advanced/secure_mode/","title":"Secure Mode","text":""},{"location":"gears/netgear/advanced/secure_mode/#secure-mode-for-netgear-api","title":"Secure Mode for NetGear API","text":""},{"location":"gears/netgear/advanced/secure_mode/#overview","title":"Overview","text":"

Secure Mode provides easy access to powerful, smart & secure ZeroMQ's Security Layers in NetGear API that enables strong encryption on data, and unbreakable authentication between the Server and the Client with the help of custom Certificates/keys and brings cheap, standardized privacy and authentication for distributed systems over the network.

Secure Mode uses a new wire protocol, ZMTP 3.0 that adds a security handshake to all ZeroMQ connections and a new security protocol, CurveZMQ, that implements \"perfect forward security\" between two ZeroMQ peers over a TCP connection.

Secure Mode can be easily activated in NetGear API through secure_mode attribute of its options dictionary parameter, during initialization. Furthermore, for managing this mode, NetGear API provides additional custom_cert_location & overwrite_cert like attribute too.

"},{"location":"gears/netgear/advanced/secure_mode/#supported-zmq-security-layers","title":"Supported ZMQ Security Layers","text":"

Secure mode supports the two most powerful ZMQ security layers:

Important Information regarding Secure Mode

"},{"location":"gears/netgear/advanced/secure_mode/#features","title":"Features","text":"

"},{"location":"gears/netgear/advanced/secure_mode/#exclusive-attributes","title":"Exclusive Attributes","text":"

For implementing Secure Mode, NetGear API currently provide following exclusive attribute for its options dictionary parameter:

"},{"location":"gears/netgear/advanced/secure_mode/#usage-examples","title":"Usage Examples","text":""},{"location":"gears/netgear/advanced/secure_mode/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with Secure Mode in NetGear API:

In Secure Mode, Client's end MUST run before the Server's end to establish a secure connection!

"},{"location":"gears/netgear/advanced/secure_mode/#clients-end","title":"Client's End","text":"

Open your favorite terminal and execute the following python code:

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate StoneHouse security mechanism\noptions = {\"secure_mode\": 1}\n\n# define NetGear Client with `receive_mode = True` and defined parameter\nclient = NetGear(pattern=1, receive_mode=True, logging=True, **options)\n\n# loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n
"},{"location":"gears/netgear/advanced/secure_mode/#servers-end","title":"Server's End","text":"

Then open another terminal on the same system and execute the following python code to send the frames to our client:

You can terminate both sides anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\n\n# open any valid video stream(for e.g `test.mp4` file)\nstream = VideoGear(source=\"test.mp4\").start()\n\n# activate StoneHouse security mechanism\noptions = {\"secure_mode\": 1}\n\n# Define NetGear Server with defined parameters\nserver = NetGear(pattern=1, logging=True, **options)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/secure_mode/#using-secure-mode-with-variable-parameters","title":"Using Secure Mode with Variable Parameters","text":""},{"location":"gears/netgear/advanced/secure_mode/#clients-end_1","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from the Server) and execute the following python code:

In Secure Mode, Client's end MUST run before the Server's end to establish a secure connection!

Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

You need to paste the Public+Secret Keypairs (generated at the Server End) at the $HOME/.vidgear/keys directory of your Client machine for a successful authentication!

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate IronHouse security mechanism\noptions = {\"secure_mode\": 2}\n\n# Define NetGear Client at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=2,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/secure_mode/#servers-end_1","title":"Server's End","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

You also need to copy the Public+Secret Keypairs (generated on running this example code) present in the $HOME/.vidgear/keys directory, and make available at Client's end for a successful authentication.

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\n\n# activate IronHouse security mechanism, and \n# [BEWARE!!!] generating new Keypairs for this example !!!\noptions = {\"secure_mode\": 2, \"overwrite_cert\": True}\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = VideoGear(source=0).start()\n\n# Define NetGear server at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=2,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/ssh_tunnel/","title":"SSH Tunneling Mode","text":""},{"location":"gears/netgear/advanced/ssh_tunnel/#ssh-tunneling-mode-for-netgear-api","title":"SSH Tunneling Mode for NetGear API","text":"NetGear's Bidirectional Mode"},{"location":"gears/netgear/advanced/ssh_tunnel/#overview","title":"Overview","text":"New in v0.2.2

This document was added in v0.2.2.

SSH Tunneling Mode allows you to connect NetGear client and server via secure SSH connection over the untrusted network and access its intranet services across firewalls. This mode works with pyzmq's zmq.ssh module for tunneling ZeroMQ connections over ssh.

This mode implements SSH Remote Port Forwarding which enables accessing Host(client) machine outside the network by exposing port to the public Internet. Thereby, once you have established the tunnel, connections to local machine will actually be connections to remote machine as seen from the server.

Beware \u2620\ufe0f

Cybercriminals or malware could exploit SSH tunnels to hide their unauthorized communications, or to exfiltrate stolen data from the network. More information can be found here \u27b6

All patterns are valid for this mode and it can be easily activated in NetGear API at server end through ssh_tunnel_mode string attribute of its options dictionary parameter during initialization.

Important

Useful Tips

"},{"location":"gears/netgear/advanced/ssh_tunnel/#prerequisites","title":"Prerequisites","text":"

SSH Tunnel Mode requires pexpect or paramiko as an additional dependency which is not part of standard VidGear package. It can be easily installed via pypi as follows:

PramikoPexpect

paramiko is compatible with all platforms.

paramiko support is automatically enabled in ZeroMQ if installed.

# install paramiko\npip install paramiko\n

pexpect is NOT compatible with Windows Machines.

# install pexpect\npip install pexpect\n

"},{"location":"gears/netgear/advanced/ssh_tunnel/#exclusive-attributes","title":"Exclusive Attributes","text":"

All these attributes will work on Server end only whereas Client end will simply discard them.

For implementing SSH Tunneling Mode, NetGear API currently provide following exclusive attribute for its options dictionary parameter:

"},{"location":"gears/netgear/advanced/ssh_tunnel/#usage-example","title":"Usage Example","text":"Assumptions for this Example

In this particular example, we assume that:

"},{"location":"gears/netgear/advanced/ssh_tunnel/#clients-end","title":"Client's End","text":"

Open a terminal on Client System (A Regular PC where you want to display the input frames received from the Server) and execute the following python code:

Requirements for Client's End

To ensure a successful Remote NetGear Connection with Server:

Finding Public IP Address

Only IPv4 IP-addresses are supported

Enabling Dynamic DNS

SSH tunneling requires public IP address to able to access host on public Internet. Thereby, if it's troublesome to remember Public IP address or your IP address change constantly, then you can use dynamic DNS services like https://www.noip.com/

How to TCP Port Forward in your Router

For more information on Forwarding Port in Popular Home Routers. See this document \u27b6

Secsh channel X open FAILED: open failed: Administratively prohibited

Error: This error means that installed OpenSSH is preventing connections to forwarded ports from outside your Client Machine.

Solution: You need to change GatewayPorts no option to GatewayPorts yes in the OpenSSH server configuration file sshd_config to allows anyone to connect to the forwarded ports on Client Machine.

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# Define NetGear Client at given IP address and define parameters \nclient = NetGear(\n    address=\"127.0.0.1\", # don't change this\n    port=\"5454\",\n    pattern=2,\n    receive_mode=True,\n    logging=True,\n)\n\n# loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/ssh_tunnel/#servers-end","title":"Server's End","text":"

Now, Open the terminal on Remote Server System (A Raspberry Pi with a webcam connected to it at index 0), and execute the following python code:

Make sure to replace the Client's Public IP Address and Forwarded TCP port(default is 22) in SSH URL with yours in the following example.

On Server end, NetGear automatically validates if the port is open at specified Client's Public IP Address or not, and if it fails (i.e. port is closed), NetGear will throw AssertionError!

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\n\n# activate SSH tunneling with SSH URL, and\n# [BEWARE!!!] Change SSH URL and SSH password with yours for this example !!!\noptions = {\n    \"ssh_tunnel_mode\": \"test@52.155.1.89\", # defaults to port 22\n    \"ssh_tunnel_pwd\": \"pas$wd\",\n}\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = VideoGear(source=0).start()\n\n# Define NetGear server at given IP address and define parameters\nserver = NetGear(\n    address=\"127.0.0.1\", # don't change this\n    port=\"5454\",\n    pattern=2, \n    logging=True, \n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear_async/overview/","title":"Overview","text":""},{"location":"gears/netgear_async/overview/#netgear_async-api","title":"NetGear_Async API","text":""},{"location":"gears/netgear_async/overview/#overview","title":"Overview","text":"

NetGear_Async can generate the same performance as NetGear API at about one-third the memory consumption, and also provide complete server-client handling with various options to use variable protocols/patterns similar to NetGear, but lacks in term of flexibility as it supports only a few NetGear's Exclusive Modes.

NetGear_Async is built on zmq.asyncio, and powered by a high-performance asyncio event loop called uvloop to achieve unmatchable high-speed and lag-free video streaming over the network with minimal resource constraints. NetGear_Async can transfer thousands of frames in just a few seconds without causing any significant load on your system.

NetGear_Async provides complete server-client handling and options to use variable protocols/patterns similar to NetGear API. Furthermore, NetGear_Async allows us to define our custom Server as source to transform frames easily before sending them across the network(see this doc example).

NetGear_Async now supports additional bidirectional data transmission between receiver(client) and sender(server) while transferring frames. Users can easily build complex applications such as like Real-Time Video Chat in just few lines of code.

In addition to all this, NetGear_Async API also provides internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs, thereby granting it exclusive power for transferring frames incoming from any source to the network.

NetGear_Async as of now supports four ZeroMQ messaging patterns:

Whereas supported protocol are: tcp and ipc.

Helpful Tips

"},{"location":"gears/netgear_async/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through NetGear_Async Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/netgear_async/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/netgear_async/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/netgear_async/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/netgear_async/params/","title":"Parameters","text":""},{"location":"gears/netgear_async/params/#netgear_async-api-parameters","title":"NetGear_Async API Parameters","text":"

NetGear_Async provides a special internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs and their parameters.

"},{"location":"gears/netgear_async/params/#enablepicamera","title":"enablePiCamera","text":"

This parameter provide access to PiGear or CamGear APIs respectively. This means the if enablePiCamera flag is True, the PiGear API will be accessed, and if False, the CamGear API will be accessed.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

NetGear_Async(enablePiCamera=True) # enable access to PiGear API\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/netgear_async/params/#address","title":"address","text":"

This parameter sets the valid network address of the Server/Client. Network addresses unique identifiers across the network.

Data-Type: String

Default Value: Its default value is based on selected primary mode, i.e 'localhost' for Send Mode and '*' for Receive Mode.

Usage:

NetGear_Async(address=\"192.168.0.145\")\n

"},{"location":"gears/netgear_async/params/#port","title":"port","text":"

This parameter sets the valid Network Port of the Server/Client. A network port is a number that identifies one side of a connection between two devices on the network and is used determine to which process or application a message should be delivered.

Data-Type: String

Default Value: Its default value is '5555'

Usage:

NetGear_Async(port=\"5575\")\n

"},{"location":"gears/netgear_async/params/#protocol","title":"protocol","text":"

This parameter sets the valid messaging protocol between Server/Client. A network protocol is a set of established rules that dictates how to format, transmit and receive data so computer network devices - from servers and routers to endpoints - can communicate regardless of the differences in their underlying infrastructures, designs or standards. Supported protocol are: 'tcp' and 'ipc'.

Data-Type: String

Default Value: Its default value is 'tcp'

Usage:

NetGear_Async(protocol=\"ipc\")\n

"},{"location":"gears/netgear_async/params/#pattern","title":"pattern","text":"

This parameter sets the supported messaging pattern(flow of communication) between Server/Client. Messaging patterns are the network-oriented architectural pattern that describes the flow of communication between interconnecting systems. NetGear provides access to ZeroMQ's pre-optimized sockets which enables you to take advantage of these patterns.

Data-Type: Integer

Default Value: Its default value is 0 (i.e zmq.PAIR).

All supported ZMQ patterns for NetGear_Async are:

Usage:

NetGear_Async(pattern=1) # sets zmq.REQ/zmq.REP pattern\n

"},{"location":"gears/netgear_async/params/#receive_mode","title":"receive_mode","text":"

This parameter select the Netgear's Mode of operation. It basically activates Receive Mode(if True) and Send Mode(if False). Furthermore, recv() method will only work when this flag is enabled(i.e. Receive Mode), whereas send() method will only work when this flag is disabled(i.e.Send Mode).

Data-Type: Boolean

Default Value: Its default value is False(i.e. Send Mode is activated by default).

Usage:

NetGear_Async(receive_mode=True) # activates Recieve Mode\n

"},{"location":"gears/netgear_async/params/#timeout","title":"timeout","text":"

In NetGear_Async, the Receiver-end keeps tracks if frames are received from Server-end within this specified timeout value (in seconds), Otherwise TimeoutError will be raised, which helps to close the Receiver-end safely if the Server has lost connection prematurely. This parameter controls that timeout value (i.e. the maximum waiting time (in seconds)) after which Client exit itself with a TimeoutError to save resources. Its minimum value is 0.0 but no max limit.

Data-Type: Float/Integer

Default Value: Its default value is 10.0.

Usage:

NetGear_Async(timeout=5.0) # sets 5secs timeout\n
"},{"location":"gears/netgear_async/params/#options","title":"options","text":"

This parameter provides the flexibility to alter various NetGear_Async API's internal properties and modes.

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

Supported dictionary attributes for NetGear_Async API

"},{"location":"gears/netgear_async/params/#parameters-for-stabilizer-backend","title":"Parameters for Stabilizer Backend","text":"

Enable this backend with stabilize=True in NetGear_Async.

"},{"location":"gears/netgear_async/params/#stabilize","title":"stabilize","text":"

This parameter enable access to Stabilizer Class for stabilizing frames, i.e. can be set to True(to enable) or unset to False(to disable).

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

NetGear_Async(stabilize=True) # enable stablization\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/netgear_async/params/#options_1","title":"options","text":"

This parameter can be used in addition, to pass user-defined parameters supported by Stabilizer Class. These parameters can be formatted as this parameter's attribute.

Supported dictionary attributes for Stabilizer Class are:

"},{"location":"gears/netgear_async/params/#parameters-for-camgear-backend","title":"Parameters for CamGear backend","text":"

Enable this backend with enablePiCamera=False in NetGear_Async. Default is also False.

"},{"location":"gears/netgear_async/params/#source","title":"source","text":"

NetGear_Async API will throw RuntimeError if source provided is invalid.

This parameter defines the source for the input stream.

Data-Type: Based on input.

Default Value: Its default value is 0.

Its valid input can be one of the following:

"},{"location":"gears/netgear_async/params/#stream_mode","title":"stream_mode","text":"

This parameter controls the Stream Mode, .i.e if enabled(stream_mode=True), the CamGear API will interpret the given source input as YouTube URL address.

Due to a FFmpeg bug that causes video to freeze frequently in OpenCV, It is advised to always use GStreamer backend for any livestream videos. Checkout this FAQ for compiling OpenCV with GStreamer support.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

Supported Streaming Websites

The complete list of all supported Streaming Websites URLs can be found here \u27b6

NetGear_Async(source='https://youtu.be/bvetuLwJIkA', stream_mode=True)\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/netgear_async/params/#backend","title":"backend","text":"

This parameter manually selects the backend for OpenCV's VideoCapture class (only if specified).

Data-Type: Integer

Default Value: Its default value is 0

Usage:

All supported backends are listed here \u27b6

Its value can be for e.g. backend = cv2.CAP_DSHOW for selecting Direct Show as backend:

NetGear_Async(source=0, backend = cv2.CAP_DSHOW)\n

"},{"location":"gears/netgear_async/params/#options_2","title":"options","text":"

This parameter provides the ability to alter various Source Tweak Parameters available within OpenCV's VideoCapture API properties.

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

All supported parameters are listed here \u27b6

The desired parameters can be passed to NetGear_Async API by formatting them as this parameter's attributes, as follows:

# formatting parameters as dictionary attributes\noptions = {\"CAP_PROP_FRAME_WIDTH\":320, \"CAP_PROP_FRAME_HEIGHT\":240, \"CAP_PROP_FPS\":60}\n# assigning it\nNetGear_Async(source=0, **options)\n

"},{"location":"gears/netgear_async/params/#parameters-for-pigear-backend","title":"Parameters for PiGear backend","text":"

Enable this backend with enablePiCamera=True in NetGear_Async.

"},{"location":"gears/netgear_async/params/#camera_num","title":"camera_num","text":"

This parameter selects the camera index to be used as the source, allowing you to drive these multiple cameras simultaneously from within a single Python session. Its value can only be zero or greater, otherwise, NetGear_Async API will throw ValueError for any negative value.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

# select Camera Module at index `1`\nNetGear_Async(enablePiCamera=True, camera_num=1)\n

The complete usage example demonstrating the usage of the camera_num parameter is available here \u27b6.

"},{"location":"gears/netgear_async/params/#resolution","title":"resolution","text":"

This parameter controls the resolution - a tuple (i.e. (width,height)) of two values giving the width and height of the output frames.

Make sure both width and height values should be at least 64.

When using the Picamera2 backend, the resolution parameter will be OVERRIDDEN, if the user explicitly defines the output_size property of the sensor configurational parameter.

Data-Type: Tuple

Default Value: Its default value is (640,480).

Usage:

NetGear_Async(enablePiCamera=True, resolution=(1280,720)) # sets 1280x720 resolution\n

"},{"location":"gears/netgear_async/params/#framerate","title":"framerate","text":"

This parameter controls the framerate of the source.

Data-Type: integer/float

Default Value: Its default value is 30.

Usage:

NetGear_Async(enablePiCamera=True, framerate=60) # sets 60fps framerate\n

"},{"location":"gears/netgear_async/params/#options_3","title":"options","text":"

This dictionary parameter in the internal PiGear API backend allows you to control various camera settings for both the picamera2 and legacy picamera backends and some internal API tasks. These settings include:

"},{"location":"gears/netgear_async/params/#a-configurational-camera-parameters","title":"A. Configurational Camera Parameters","text":""},{"location":"gears/netgear_async/params/#b-user-defined-parameters","title":"B. User-defined Parameters","text":""},{"location":"gears/netgear_async/params/#common-parameters","title":"Common Parameters","text":"

These are common parameters that works with every backend in NetGear_Async.

"},{"location":"gears/netgear_async/params/#colorspace","title":"colorspace","text":"

This parameter selects the colorspace of the source stream.

Data-Type: String

Default Value: Its default value is None.

Usage:

All supported colorspace values are given here \u27b6

NetGear_Async(colorspace=\"COLOR_BGR2HSV\")\n

Its complete usage example is given here \u27b6

"},{"location":"gears/netgear_async/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

NetGear_Async(logging=True)\n

"},{"location":"gears/netgear_async/params/#time_delay","title":"time_delay","text":"

This parameter set the time delay (in seconds) before the NetGear_Async API start reading the frames. This delay is only required if the source required some warm-up delay before starting up.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

NetGear_Async(time_delay=1)  # set 1 seconds time delay\n

"},{"location":"gears/netgear_async/usage/","title":"Usage Examples","text":""},{"location":"gears/netgear_async/usage/#netgear_async-api-usage-examples","title":"NetGear_Async API Usage Examples:","text":"

Helpful Tips

After going through following Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/netgear_async/usage/#requirement","title":"Requirement","text":"

NetGear_Async API is the part of asyncio package of VidGear, thereby you need to install VidGear with asyncio support as follows:

pip install vidgear[asyncio]\n

"},{"location":"gears/netgear_async/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with NetGear_Async API:

"},{"location":"gears/netgear_async/usage/#servers-end","title":"Server's End","text":"

Open your favorite terminal and execute the following python code:

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nimport asyncio\n\n# initialize Server with suitable source\nserver = NetGear_Async(source=\"/home/foo/foo1.mp4\").launch()\n\nif __name__ == \"__main__\":\n    # set event loop\n    asyncio.set_event_loop(server.loop)\n    try:\n        # run your main function task until it is complete\n        server.loop.run_until_complete(server.task)\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    finally:\n        # finally close the server\n        server.close()\n
"},{"location":"gears/netgear_async/usage/#clients-end","title":"Client's End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

Client will throw TimeoutError if it fails to connect to the Server in given timeout value!

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nimport cv2, asyncio\n\n# define and launch Client with `receive_mode=True`\nclient = NetGear_Async(receive_mode=True).launch()\n\n# Create a async function where you want to show/manipulate your received frames\nasync def main():\n    # loop over Client's Asynchronous Frame Generator\n    async for frame in client.recv_generator():\n\n        # do something with received frames here\n\n        # Show output window\n        cv2.imshow(\"Output Frame\", frame)\n        key = cv2.waitKey(1) & 0xFF\n\n        # await before continuing\n        await asyncio.sleep(0)\n\nif __name__ == \"__main__\":\n    # Set event loop to client's\n    asyncio.set_event_loop(client.loop)\n    try:\n        # run your main function task until it is complete\n        client.loop.run_until_complete(main())\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n\n    # close all output window\n    cv2.destroyAllWindows()\n    # safely close client\n    client.close()\n

"},{"location":"gears/netgear_async/usage/#using-netgear_async-with-variable-parameters","title":"Using NetGear_Async with Variable Parameters","text":""},{"location":"gears/netgear_async/usage/#clients-end_1","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from the Server) and execute the following python code:

Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

Client will throw TimeoutError if it fails to connect to the Server in given timeout value!

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nimport cv2, asyncio\n\n# define and launch Client with `receive_mode=True`. #change following IP address '192.168.x.xxx' with yours\nclient = NetGear_Async(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=2,\n    receive_mode=True,\n    logging=True,\n).launch()\n\n\n# Create a async function where you want to show/manipulate your received frames\nasync def main():\n    # loop over Client's Asynchronous Frame Generator\n    async for frame in client.recv_generator():\n\n        # do something with received frames here\n\n        # Show output window\n        cv2.imshow(\"Output Frame\", frame)\n        key = cv2.waitKey(1) & 0xFF\n\n        # await before continuing\n        await asyncio.sleep(0)\n\n\nif __name__ == \"__main__\":\n    # Set event loop to client's\n    asyncio.set_event_loop(client.loop)\n    try:\n        # run your main function task until it is complete\n        client.loop.run_until_complete(main())\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n\n    # close all output window\n    cv2.destroyAllWindows()\n    # safely close client\n    client.close()\n
"},{"location":"gears/netgear_async/usage/#servers-end_1","title":"Server's End","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nimport asyncio\n\n# initialize Server with suitable source\nserver = NetGear_Async(\n    source=0,\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=2,\n    logging=True,\n).launch()\n\nif __name__ == \"__main__\":\n    # set event loop\n    asyncio.set_event_loop(server.loop)\n    try:\n        # run your main function task until it is complete\n        server.loop.run_until_complete(server.task)\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    finally:\n        # finally close the server\n        server.close()\n

"},{"location":"gears/netgear_async/usage/#using-netgear_async-with-a-custom-sourceopencv","title":"Using NetGear_Async with a Custom Source(OpenCV)","text":"

NetGear_Async allows you to easily define your own custom Source at Server-end that you want to use to transform your frames before sending them onto the network.

Let's implement a bare-minimum example with a Custom Source using NetGear_Async API and OpenCV:

"},{"location":"gears/netgear_async/usage/#servers-end_2","title":"Server's End","text":"

Open your favorite terminal and execute the following python code:

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import library\nfrom vidgear.gears.asyncio import NetGear_Async\nimport cv2, asyncio\n\n# initialize Server without any source\nserver = NetGear_Async(source=None, logging=True)\n\n# !!! define your own video source here !!!\n# Open any video stream such as live webcam\n# video stream on first index(i.e. 0) device\nstream = cv2.VideoCapture(0)\n\n# Create a async frame generator as custom source\nasync def my_frame_generator():\n\n    # loop over stream until its terminated\n    while True:\n\n        # read frames\n        (grabbed, frame) = stream.read()\n\n        # check if frame empty\n        if not grabbed:\n            break\n\n        # do something with the frame to be sent here\n\n        # yield frame\n        yield frame\n        # sleep for sometime\n        await asyncio.sleep(0)\n\n\nif __name__ == \"__main__\":\n    # set event loop\n    asyncio.set_event_loop(server.loop)\n    # Add your custom source generator to Server configuration\n    server.config[\"generator\"] = my_frame_generator()\n    # Launch the Server\n    server.launch()\n    try:\n        # run your main function task until it is complete\n        server.loop.run_until_complete(server.task)\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    finally:\n        # close stream\n        stream.release()\n        # finally close the server\n        server.close()\n
"},{"location":"gears/netgear_async/usage/#clients-end_2","title":"Client's End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

Client will throw TimeoutError if it fails to connect to the Server in given timeout value!

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nimport cv2, asyncio\n\n# define and launch Client with `receive_mode=True`\nclient = NetGear_Async(receive_mode=True, logging=True).launch()\n\n\n# Create a async function where you want to show/manipulate your received frames\nasync def main():\n    # loop over Client's Asynchronous Frame Generator\n    async for frame in client.recv_generator():\n\n        # {do something with received frames here}\n\n        # Show output window\n        cv2.imshow(\"Output Frame\", frame)\n        key = cv2.waitKey(1) & 0xFF\n\n        # await before continuing\n        await asyncio.sleep(0)\n\n\nif __name__ == \"__main__\":\n    # Set event loop to client's\n    asyncio.set_event_loop(client.loop)\n    try:\n        # run your main function task until it is complete\n        client.loop.run_until_complete(main())\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n\n    # close all output window\n    cv2.destroyAllWindows()\n    # safely close client\n    client.close()\n

"},{"location":"gears/netgear_async/usage/#using-netgear_async-with-other-gears","title":"Using NetGear_Async with Other Gears","text":"

NetGear_Async can be used with any other Gears without any compatibility issues.

Let's implement a bare-minimum example where we are sending Stabilized frames from Server-end and saving them at Client's end with WriteGear as follows:

"},{"location":"gears/netgear_async/usage/#servers-end_3","title":"Server's End","text":"

Open your favorite terminal and execute the following python code:

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nimport asyncio\n\n# initialize Server with suitable source and enable stabilization\nserver = NetGear_Async(\n    source=\"/home/foo/foo1.mp4\", stabilize=True, logging=True\n).launch()\n\nif __name__ == \"__main__\":\n    # set event loop\n    asyncio.set_event_loop(server.loop)\n    try:\n        # run your main function task until it is complete\n        server.loop.run_until_complete(server.task)\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    finally:\n        # finally close the server\n        server.close()\n
"},{"location":"gears/netgear_async/usage/#clients-end_3","title":"Client's End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

Client will throw TimeoutError if it fails to connect to the Server in given timeout value!

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nfrom vidgear.gears import WriteGear\nimport cv2, asyncio\n\n# define and launch Client with `receive_mode=True`\nclient = NetGear_Async(receive_mode=True).launch()\n\n# Define writer with output filename 'Output.mp4'\nwriter = WriteGear(output=\"Output.mp4\", logging=True)\n\n# Create a async function where you want to show/manipulate your received frames\nasync def main():\n    # loop over Client's Asynchronous Frame Generator\n    async for frame in client.recv_generator():\n\n        # {do something with received frames here}\n\n        # write a modified frame to writer\n        writer.write(frame)\n\n        # Show output window\n        cv2.imshow(\"Output Frame\", frame)\n        key = cv2.waitKey(1) & 0xFF\n\n        # await before continuing\n        await asyncio.sleep(0)\n\n\nif __name__ == \"__main__\":\n    # Set event loop to client's\n    asyncio.set_event_loop(client.loop)\n    try:\n        # run your main function task until it is complete\n        client.loop.run_until_complete(main())\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n\n    # close all output window\n    cv2.destroyAllWindows()\n    # safely close client\n    client.close()\n    # safely close writer\n    writer.close()\n

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/","title":"Bidirectional Mode","text":""},{"location":"gears/netgear_async/advanced/bidirectional_mode/#bidirectional-mode-for-netgear_async-api","title":"Bidirectional Mode for NetGear_Async API","text":"NetGear_Async's Bidirectional Mode"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#overview","title":"Overview","text":"New in v0.2.2

This document was added in v0.2.2.

Bidirectional Mode enables seamless support for Bidirectional data transmission between Client and Sender along with video-frames through its synchronous messaging patterns such as zmq.PAIR (ZMQ Pair Pattern) & zmq.REQ/zmq.REP (ZMQ Request/Reply Pattern) in NetGear_Async API.

In Bidirectional Mode, we utilizes the NetGear_Async API's transceive_data method for transmitting data (at Client's end) and receiving data (in Server's end) all while transferring frames in real-time.

This mode can be easily activated in NetGear_Async through bidirectional_mode attribute of its options dictionary parameter during initialization.

Important

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#exclusive-method-and-parameter","title":"Exclusive Method and Parameter","text":"

To send data bidirectionally, NetGear_Async API provides following exclusive method and parameter:

transceive_data only works when Bidirectional Mode is enabled.

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#usage-examples","title":"Usage Examples","text":"

For Bidirectional Mode, NetGear_Async must need User-defined Custom Source at its Server end otherwise it will throw ValueError.

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#bare-minimum-usage-with-opencv","title":"Bare-Minimum Usage with OpenCV","text":"

Following is the bare-minimum code you need to get started with Bidirectional Mode over Custom Source Server built using OpenCV and NetGear_Async API:

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#server-end","title":"Server End","text":"

Open your favorite terminal and execute the following python code:

You can terminate both sides anytime by pressing Ctrl+C on your keyboard!

# import library\nfrom vidgear.gears.asyncio import NetGear_Async\nimport cv2, asyncio\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# initialize Server without any source\nserver = NetGear_Async(source=None, logging=True, **options)\n\n# Create a async frame generator as custom source\nasync def my_frame_generator():\n\n    # !!! define your own video source here !!!\n    # Open any valid video stream(for e.g `foo.mp4` file)\n    stream = cv2.VideoCapture(\"foo.mp4\")\n\n    # loop over stream until its terminated\n    while True:\n        # read frames\n        (grabbed, frame) = stream.read()\n\n        # check for empty frame\n        if not grabbed:\n            break\n\n        # {do something with the frame to be sent here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # receive data from Client\n        recv_data = await server.transceive_data()\n\n        # print data just received from Client\n        if not (recv_data is None):\n            print(recv_data)\n\n        # send our frame & data\n        yield (target_data, frame) # (1)\n\n        # sleep for sometime\n        await asyncio.sleep(0)\n\n    # safely close video stream\n    stream.release()\n\n\nif __name__ == \"__main__\":\n    # set event loop\n    asyncio.set_event_loop(server.loop)\n    # Add your custom source generator to Server configuration\n    server.config[\"generator\"] = my_frame_generator()\n    # Launch the Server\n    server.launch()\n    try:\n        # run your main function task until it is complete\n        server.loop.run_until_complete(server.task)\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    finally:\n        # finally close the server\n        server.close()\n
  1. Everything except numpy.ndarray datatype data is accepted in target_data.
"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#client-end","title":"Client End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nimport cv2, asyncio\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# define and launch Client with `receive_mode=True`\nclient = NetGear_Async(receive_mode=True, logging=True, **options).launch()\n\n\n# Create a async function where you want to show/manipulate your received frames\nasync def main():\n    # loop over Client's Asynchronous Frame Generator\n    async for (data, frame) in client.recv_generator():\n\n        # do something with receive data from server\n        if not (data is None):\n            # let's print it\n            print(data)\n\n        # {do something with received frames here}\n\n        # Show output window(comment these lines if not required)\n        cv2.imshow(\"Output Frame\", frame)\n        cv2.waitKey(1) & 0xFF\n\n        # prepare data to be sent\n        target_data = \"Hi, I am a Client here.\"\n        # send our data to server\n        await client.transceive_data(data=target_data)\n\n        # await before continuing\n        await asyncio.sleep(0)\n\n\nif __name__ == \"__main__\":\n    # Set event loop to client's\n    asyncio.set_event_loop(client.loop)\n    try:\n        # run your main function task until it is complete\n        client.loop.run_until_complete(main())\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n\n    # close all output window\n    cv2.destroyAllWindows()\n\n    # safely close client\n    client.close()\n

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#using-bidirectional-mode-with-variable-parameters","title":"Using Bidirectional Mode with Variable Parameters","text":""},{"location":"gears/netgear_async/advanced/bidirectional_mode/#clients-end","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from the Server) and execute the following python code:

Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nimport cv2, asyncio\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# Define NetGear_Async Client at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear_Async(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# Create a async function where you want to show/manipulate your received frames\nasync def main():\n    # loop over Client's Asynchronous Frame Generator\n    async for (data, frame) in client.recv_generator():\n\n        # do something with receive data from server\n        if not (data is None):\n            # let's print it\n            print(data)\n\n        # {do something with received frames here}\n\n        # Show output window(comment these lines if not required)\n        cv2.imshow(\"Output Frame\", frame)\n        cv2.waitKey(1) & 0xFF\n\n        # prepare data to be sent\n        target_data = \"Hi, I am a Client here.\"\n        # send our data to server\n        await client.transceive_data(data=target_data)\n\n        # await before continuing\n        await asyncio.sleep(0)\n\n\nif __name__ == \"__main__\":\n    # Set event loop to client's\n    asyncio.set_event_loop(client.loop)\n    try:\n        # run your main function task until it is complete\n        client.loop.run_until_complete(main())\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n\n    # close all output window\n    cv2.destroyAllWindows()\n\n    # safely close client\n    client.close()\n

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#server-end_1","title":"Server End","text":"

Now, Open the terminal on another Server System (a Raspberry Pi with Camera Module), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this backend, otherwise nothing will work.

New Picamera2 backendLegacy Picamera backend
# import libs\nfrom vidgear.gears.asyncio import NetGear_Async\nfrom vidgear.gears import VideoGear\nfrom libcamera import Transform\nimport cv2, asyncio\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# initialize Server without any source at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear_Async(\n    source=None,\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# Create a async frame generator as custom source\nasync def my_frame_generator():\n\n    # !!! define your own video source below !!!\n\n    # define various Picamera2 tweak parameters\n    options = {\n        \"queue\": True,\n        \"buffer_count\": 4,\n        \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n        \"transform\": Transform(hflip=1),\n        \"auto_align_output_config\": True,  # auto-align camera configuration\n    }\n\n    # open pi video stream with defined parameters\n    stream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n    # loop over stream until its terminated\n    while True:\n        # read frames\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame to be sent here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # receive data from Client\n        recv_data = await server.transceive_data()\n\n        # print data just received from Client\n        if not (recv_data is None):\n            print(recv_data)\n\n        # send our frame & data\n        yield (target_data, frame) # (1)\n\n        # sleep for sometime\n        await asyncio.sleep(0)\n\n    # safely close video stream\n    stream.stop()\n\n\nif __name__ == \"__main__\":\n    # set event loop\n    asyncio.set_event_loop(server.loop)\n    # Add your custom source generator to Server configuration\n    server.config[\"generator\"] = my_frame_generator()\n    # Launch the Server\n    server.launch()\n    try:\n        # run your main function task until it is complete\n        server.loop.run_until_complete(server.task)\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    finally:\n        # finally close the server\n        server.close()\n
  1. Everything except numpy.ndarray datatype data is accepted in target_data.
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import library\nfrom vidgear.gears.asyncio import NetGear_Async\nfrom vidgear.gears import VideoGear\nimport cv2, asyncio\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# initialize Server without any source at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear_Async(\n    source=None,\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# Create a async frame generator as custom source\nasync def my_frame_generator():\n\n    # !!! define your own video source below !!!\n\n    # define various Picamera tweak parameters\n    options = {\n        \"hflip\": True,\n        \"exposure_mode\": \"auto\",\n        \"iso\": 800,\n        \"exposure_compensation\": 15,\n        \"awb_mode\": \"horizon\",\n        \"sensor_mode\": 0,\n    }\n\n    # open pi video stream with defined parameters\n    stream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n    # loop over stream until its terminated\n    while True:\n        # read frames\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame to be sent here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # receive data from Client\n        recv_data = await server.transceive_data()\n\n        # print data just received from Client\n        if not (recv_data is None):\n            print(recv_data)\n\n        # send our frame & data\n        yield (target_data, frame) # (1)\n\n        # sleep for sometime\n        await asyncio.sleep(0)\n\n    # safely close video stream\n    stream.stop()\n\n\nif __name__ == \"__main__\":\n    # set event loop\n    asyncio.set_event_loop(server.loop)\n    # Add your custom source generator to Server configuration\n    server.config[\"generator\"] = my_frame_generator()\n    # Launch the Server\n    server.launch()\n    try:\n        # run your main function task until it is complete\n        server.loop.run_until_complete(server.task)\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    finally:\n        # finally close the server\n        server.close()\n
  1. Everything except numpy.ndarray datatype data is accepted in target_data.

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#using-bidirectional-mode-for-video-frames-transfer","title":"Using Bidirectional Mode for Video-Frames Transfer","text":"

In this example we are going to implement a bare-minimum example, where we will be sending video-frames (3-Dimensional numpy arrays) of the same Video bidirectionally at the same time, for testing the real-time performance and synchronization between the Server and the Client using this(Bidirectional) Mode.

This feature is great for building applications like Real-Time Video Chat.

We're also using reducer() method for reducing frame-size on-the-go for additional performance.

Remember, Sending large HQ video-frames may required more network bandwidth and packet size which may lead to video latency!

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#server-end_2","title":"Server End","text":"

Open your favorite terminal and execute the following python code:

You can terminate both side anytime by pressing Ctrl+C on your keyboard!

Server end can only send numpy.ndarray datatype as frame but not as data.

# import library\nfrom vidgear.gears.asyncio import NetGear_Async\nfrom vidgear.gears.asyncio.helper import reducer\nimport cv2, asyncio\nimport numpy as np\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# Define NetGear Server without any source and with defined parameters\nserver = NetGear_Async(source=None, pattern=1, logging=True, **options)\n\n# Create a async frame generator as custom source\nasync def my_frame_generator():\n    # !!! define your own video source here !!!\n    # Open any valid video stream(for e.g `foo.mp4` file)\n    stream = cv2.VideoCapture(\"foo.mp4\")\n    # loop over stream until its terminated\n    while True:\n\n        # read frames\n        (grabbed, frame) = stream.read()\n\n        # check for empty frame\n        if not grabbed:\n            break\n\n        # reducer frames size if you want more performance, otherwise comment this line\n        frame = await reducer(frame, percentage=30)  # reduce frame by 30%\n\n        # {do something with the frame to be sent here}\n\n        # send frame & data and also receive data from Client\n        recv_data = await server.transceive_data()\n\n        # receive data from Client\n        if not (recv_data is None):\n            # check data is a numpy frame\n            if isinstance(recv_data, np.ndarray):\n\n                # {do something with received numpy frame here}\n\n                # Let's show it on output window\n                cv2.imshow(\"Received Frame\", recv_data)\n                cv2.waitKey(1) & 0xFF\n            else:\n                # otherwise just print data\n                print(recv_data)\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # send our frame & data to client\n        yield (target_data, frame) # (1)\n\n        # sleep for sometime\n        await asyncio.sleep(0)\n\n    # safely close video stream\n    stream.release()\n\n\nif __name__ == \"__main__\":\n    # set event loop\n    asyncio.set_event_loop(server.loop)\n    # Add your custom source generator to Server configuration\n    server.config[\"generator\"] = my_frame_generator()\n    # Launch the Server\n    server.launch()\n    try:\n        # run your main function task until it is complete\n        server.loop.run_until_complete(server.task)\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    finally:\n        # finally close the server\n        server.close()\n
  1. Everything except numpy.ndarray datatype data is accepted in target_data.

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#client-end_1","title":"Client End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nfrom vidgear.gears.asyncio.helper import reducer\nimport cv2, asyncio\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# define and launch Client with `receive_mode=True`\nclient = NetGear_Async(pattern=1, receive_mode=True, logging=True, **options).launch()\n\n# Create a async function where you want to show/manipulate your received frames\nasync def main():\n    # !!! define your own video source here !!!\n    # again open the same video stream for comparison\n    stream = cv2.VideoCapture(\"foo.mp4\")\n    # loop over Client's Asynchronous Frame Generator\n    async for (server_data, frame) in client.recv_generator():\n\n        # check for server data\n        if not (server_data is None):\n\n            # {do something with the server data here}\n\n            # lets print extracted server data\n            print(server_data)\n\n        # {do something with received frames here}\n\n        # Show output window\n        cv2.imshow(\"Output Frame\", frame)\n        key = cv2.waitKey(1) & 0xFF\n\n        # read frame target data from stream to be sent to server\n        (grabbed, target_data) = stream.read()\n        # check for frame\n        if grabbed:\n            # reducer frames size if you want more performance, otherwise comment this line\n            target_data = await reducer(\n                target_data, percentage=30\n            )  # reduce frame by 30%\n            # send our frame data\n            await client.transceive_data(data=target_data)\n\n        # await before continuing\n        await asyncio.sleep(0)\n\n    # safely close video stream\n    stream.release()\n\n\nif __name__ == \"__main__\":\n    # Set event loop to client's\n    asyncio.set_event_loop(client.loop)\n    try:\n        # run your main function task until it is complete\n        client.loop.run_until_complete(main())\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    # close all output window\n    cv2.destroyAllWindows()\n    # safely close client\n    client.close()\n

  1. Additional data of numpy.ndarray datatype is ONLY SUPPORTED at Client's end with transceive_data method using its data parameter. Whereas Server end can only send numpy.ndarray datatype as frame but not as data.

    \u21a9\u21a9

"},{"location":"gears/pigear/overview/","title":"Overview","text":""},{"location":"gears/pigear/overview/#pigear-api","title":"PiGear API","text":"Raspberry Pi Camera Module"},{"location":"gears/pigear/overview/#overview","title":"Overview","text":"

PiGear is a specialized API similar to the CamGear API but optimized for Raspberry Pi Boards, offering comprehensive support for camera modules (e.g., OmniVision OV5647, Sony IMX219), along with limited compatibility for USB cameras.

PiGear implements a seamless and robust wrapper around the picamera2 python library, simplifying integration with minimal code changes and ensuring a smooth transition for developers already familiar with the Picamera2 API. PiGear leverages the libcamera API under the hood with multi-threading, providing high-performance , enhanced control and functionality for Raspberry Pi camera modules.

PiGear handles common configuration parameters and non-standard settings for various camera types, simplifying the integration process. PiGear currently supports PiCamera2 API parameters such as sensor, controls, transform, and format etc., with internal type and sanity checks for robust performance.

While primarily focused on Raspberry Pi camera modules, PiGear also provides basic functionality for USB webcams only with Picamera2 API, along with the ability to accurately differentiate between USB and Raspberry Pi cameras using metadata.

Backward compatibility with picamera library

PiGear seamlessly switches to the legacy picamera library if the picamera2 library is unavailable, ensuring seamless backward compatibility. For this, PiGear also provides a flexible multi-threaded framework around complete picamera API, allowing developers to effortlessly exploit a wide range of parameters, such as brightness, saturation, sensor_mode, iso, exposure, and more.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

Furthermore, PiGear supports the use of multiple camera modules, including those found on Raspberry Pi Compute Module IO boards and USB cameras (only with Picamera2 API).

Threaded Internal Timer

PiGear ensures proper resource release during the termination of the API, preventing potential issues or resource leaks. PiGear API internally implements a Threaded Internal Timer that silently keeps active track of any frozen-threads or hardware-failures and exits safely if any do occur. This means that if you're running the PiGear API in your script and someone accidentally pulls the Camera-Module cable out, instead of going into a possible kernel panic, the API will exit safely to save resources.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this API, otherwise nothing will work.

Helpful Tips

"},{"location":"gears/pigear/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through PiGear Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/pigear/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/pigear/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/pigear/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/pigear/params/","title":"Parameters","text":""},{"location":"gears/pigear/params/#pigear-api-parameters","title":"PiGear API Parameters","text":""},{"location":"gears/pigear/params/#camera_num","title":"camera_num","text":"

This parameter selects the camera index to be used as the source, allowing you to drive these multiple cameras simultaneously from within a single Python session. Its value can only be zero or greater, otherwise, PiGear API will throw ValueError for any negative value.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

# select Camera Module at index `1`\nPiGear(camera_num=1)\n

The complete usage example demonstrating the usage of the camera_num parameter is available here \u27b6.

"},{"location":"gears/pigear/params/#resolution","title":"resolution","text":"

This parameter controls the resolution - a tuple (i.e. (width,height)) of two values giving the width and height of the output frames.

Make sure both width and height values should be at least 64.

When using the Picamera2 backend, the resolution parameter will be OVERRIDDEN, if the user explicitly defines the output_size property of the sensor configurational parameter in PiGear API.

Data-Type: Tuple

Default Value: Its default value is (640,480).

Usage:

PiGear(resolution=(1280,720)) # sets 1280x720 resolution\n

"},{"location":"gears/pigear/params/#framerate","title":"framerate","text":"

This parameter controls the framerate of the source.

Data-Type: integer/float

Default Value: Its default value is 30.

Usage:

PiGear(framerate=60) # sets 60fps framerate\n

"},{"location":"gears/pigear/params/#colorspace","title":"colorspace","text":"

This parameter controls the colorspace of the output frames.

With the Picamera2 backend, you can also define a custom format (format of output frame pixels) in PiGear API. Checkout this bonus example \u27b6

Data-Type: String

Default Value: Its default value is None (i.e. Default BGR colorspace).

Usage:

All supported colorspace values are described here \u27b6

PiGear(colorspace=\"COLOR_BGR2HSV\")\n

Its complete usage example is given here \u27b6

"},{"location":"gears/pigear/params/#options","title":"options","text":"

This dictionary parameter in the PiGear API allows you to control various camera settings for both the picamera2 and legacy picamera backends and some internal API tasks. These settings include:

"},{"location":"gears/pigear/params/#a-configurational-camera-parameters","title":"A. Configurational Camera Parameters","text":""},{"location":"gears/pigear/params/#b-user-defined-parameters","title":"B. User-defined Parameters","text":"

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

The complete usage example demonstrating the usage of the options parameter is available here \u27b6.

You can format these user-defined and configurational parameters as attributes of this options dictionary parameter as follows:

New Picamera2 backendLegacy Picamera backend
# formulate various Picamera2 API parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"exposure_compensation\": 15,\n    \"sensor\": {\"output_size\": (480, 320)},  # !!! will override `resolution` !!!\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n
# formulate various Picamera API parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n

"},{"location":"gears/pigear/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

PiGear(logging=True)\n

"},{"location":"gears/pigear/params/#time_delay","title":"time_delay","text":"

This parameter set the time delay (in seconds) before the PiGear API start reading the frames. This delay is only required if the source required some warm-up delay before starting up.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

PiGear(time_delay=1)  # set 1 seconds time delay\n

"},{"location":"gears/pigear/usage/","title":"Usage Examples","text":""},{"location":"gears/pigear/usage/#pigear-api-usage-examples","title":"PiGear API Usage Examples:","text":"

PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this API, otherwise nothing will work.

After going through following Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/pigear/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with PiGear API:

Under the hood, PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, PiGear API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

Disabling common libcamera API messages in silent mode.

The picamera2 backend can be a bit verbose with logging messages from the underlying libcamera library, even when logging is disabled (logging=False) in the PiGear API.

Linux Windows (Powershell) MacOS
export LIBCAMERA_LOG_LEVELS=2\n
$Env:LIBCAMERA_LOG_LEVELS=2\n
export LIBCAMERA_LOG_LEVELS=2\n
# import required libraries\nfrom vidgear.gears import PiGear\nimport cv2\n\n# open stream with default parameters\nstream = PiGear().start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/pigear/usage/#using-pigear-with-variable-camera-properties","title":"Using PiGear with Variable Camera Properties","text":"New Picamera2 backendLegacy Picamera backend

PiGear provides a user-friendly interface for the underlying picamera2 library, offering access to almost all of its important configurational parameters. It simplifies configuration for developers with even basic knowledge of Raspberry Pi camera modules, allowing them to easily configure and control the camera functionality with just a few lines of code.

This example doc showcases the capabilities of PiGear and demonstrates how it simplifies camera configuration with Picamera2 API backend.

All supported Picamera2 Library Configurational Parameters [IMPORTANT]

Following are the list of Picamera2 parameters, i.e. if supported, can be applied to the source stream in PiGear API through its options dictionary parameter by formatting them as its attributes.

Few Important points Parameters Datatype Description Supported Supported on USB Cameras Remarks buffer_count int, >=1 number of sets of buffers to allocate for the camera system Read Docs here \u27b6 queue bool whether the system is allowed to queue up a frame ready for a capture request Read Docs here \u27b6 controls dict specify a set of runtime controls that can be regarded as part of the camera configuration Read Docs here \u27b6 sensor dict allow to select a particular mode of operation for the sensor Read Docs here \u27b6 format str Pixel formats Read Docs here \u27b6 and see Bonus example \u27b6 transform Transform1 The 2D plane transform that is applied to all images from all the configured streams. Read Docs here \u27b6 colour_space colour space of the output images Handled by colorspace parameter of PiGear API size A tuple of two values giving the width and height of the output image. (Both numbers should be no less than 64) Handled by resolution parameter of PiGear API display name of the stream that will be displayed in the preview window. Not-Required encode name of the stream that will be used for video recording. Not-Required Limited support for USB Cameras

This example also works with USB Cameras, However:

Enabling verbose logs for backend PiCamera2 Library

The PiGear API allows you to enable more detailed logging from the picamera2 backend library using the enable_verbose_logs user-defined optional parameter attribute. This can be used in conjunction with enabling general logging (logging=True) in the PiGear API for even more granular control over logging output.

PiGear also support changing parameter at runtime. Checkout this bonus example here \u27b6

# import required libraries\nfrom vidgear.gears import PiGear\nfrom libcamera import Transform\nimport cv2\n\n# formulate various Picamera2 API \n# configurational parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"sensor\": {\"output_size\": (480, 320)},  # !!! will override `resolution` !!!\n    \"auto_align_output_size\": True,  # auto-align output size\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

PiGear API switches to the legacy picamerabackend if the picamera2 library is unavailable.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

PiGear also supports almost every parameter available within picamera python library. These parameters can be easily applied to the source stream in PiGear API through its options dictionary parameter by formatting them as its attributes. The complete usage example is as follows:

All supported parameters are listed in PiCamera Docs \u27b6

PiGear also support changing parameter at runtime. Checkout this bonus example here \u27b6

# import required libraries\nfrom vidgear.gears import PiGear\nimport cv2\n\n# formulate various Picamera API \n# configurational parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/pigear/usage/#using-pigear-with-direct-colorspace-manipulation","title":"Using PiGear with Direct Colorspace Manipulation","text":"

PiGear API also supports Direct Colorspace Manipulation, which is ideal for changing source colorspace on the run.

A more detailed information on colorspace manipulation can be found here \u27b6

In following example code, we will start with HSV as source colorspace, and then we will switch to GRAY colorspace when W key is pressed, and then LAB colorspace when E key is pressed, finally default colorspace (i.e. BGR) when S key is pressed. Also, quit when Q key is pressed:

Any incorrect or None-Type value will immediately revert the colorspace to default (i.e. BGR).

# import required libraries\nfrom vidgear.gears import PiGear\nimport cv2\n\n# open pi video stream with defined parameters and change colorspace to `HSV`\nstream = PiGear(\n    resolution=(640, 480),\n    framerate=60,\n    colorspace=\"COLOR_BGR2HSV\",\n    logging=True\n).start()\n\n\n# loop over\nwhile True:\n\n    # read HSV frames\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the HSV frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for key if pressed\n    key = cv2.waitKey(1) & 0xFF\n\n    # check if 'w' key is pressed\n    if key == ord(\"w\"):\n        # directly change colorspace at any instant\n        stream.color_space = cv2.COLOR_BGR2GRAY  # Now colorspace is GRAY\n\n    # check for 'e' key is pressed\n    if key == ord(\"e\"):\n        stream.color_space = cv2.COLOR_BGR2LAB  # Now colorspace is CieLAB\n\n    # check for 's' key is pressed\n    if key == ord(\"s\"):\n        stream.color_space = None  # Now colorspace is default(ie BGR)\n\n    # check for 'q' key is pressed\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/pigear/usage/#using-pigear-with-writegear-api","title":"Using PiGear with WriteGear API","text":"

PiGear can be easily used with WriteGear API directly without any compatibility issues. The suitable example is as follows:

New Picamera2 backendLegacy Picamera backend
# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import WriteGear\nfrom libcamera import Transform\nimport cv2\n\n# formulate various Picamera2 API \n# configurational parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"sensor\": {\"output_size\": (480, 320)},  # will override `resolution`\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# define suitable (Codec,CRF,preset) FFmpeg parameters for writer\noutput_params = {\"-vcodec\": \"libx264\", \"-crf\": 0, \"-preset\": \"fast\"}\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n    # lets convert frame to gray for this example\n    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n\n    # write gray frame to writer\n    writer.write(gray)\n\n    # Show output window\n    cv2.imshow(\"Output Gray Frame\", gray)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n
PiGear API switches to the legacy picamerabackend if the picamera2 library is unavailable.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# formulate various Picamera API \n# configurational parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# define suitable (Codec,CRF,preset) FFmpeg parameters for writer\noutput_params = {\"-vcodec\": \"libx264\", \"-crf\": 0, \"-preset\": \"fast\"}\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n    # lets convert frame to gray for this example\n    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n\n    # write gray frame to writer\n    writer.write(gray)\n\n   # Show output window\n    cv2.imshow(\"Output Gray Frame\", gray)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

  1. A custom libcamera API class. Must be imported as from libcamera import Transform.\u00a0\u21a9

"},{"location":"gears/screengear/overview/","title":"Overview","text":""},{"location":"gears/screengear/overview/#screengear-api","title":"ScreenGear API","text":"ScreenGear API in action"},{"location":"gears/screengear/overview/#overview","title":"Overview","text":"

ScreenGear is designed exclusively for targeting rapid Screencasting Capabilities, which means it can grab frames from your monitor in real-time, either by defining an area on the computer screen or full-screen, at the expense of inconsiderable latency. ScreenGear also seamlessly support frame capturing from multiple monitors as well as supports multiple backends.

ScreenGear API implements a multi-threaded wrapper around dxcam, pyscreenshot & python-mss python library, and also flexibly supports its internal parameter.

Helpful Tips

"},{"location":"gears/screengear/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through ScreenGear Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/screengear/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/screengear/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/screengear/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/screengear/params/","title":"Parameters","text":""},{"location":"gears/screengear/params/#screengear-api-parameters","title":"ScreenGear API Parameters","text":""},{"location":"gears/screengear/params/#monitor","title":"monitor","text":"

This parameter enforces dxcam (if installed) and mss (otherwise) usage, and it is suitable for selecting index of a specific screen/monitor device (from where you want retrieve frames) in multi-monitor setup. For example, its value can be assign to 2, to fetch frames from a secondary monitor screen.

Implication of using monitor parameter

Any value on monitor parameter other than None in ScreenGear API:

Data-Type: Integer, Tuple (only if dxcam backend on Windows)

Default Value: Its default value is None (i.e. disabled by default).

Usage:

With dxcam on Windows With mss backend Using GPU acceleration on Windows

With dxcam library backend, you can also assign which GPU devices ids to use along with monitor device ids as tuple (monitor_idx, gpu_idx), as follows:

# open video stream with defined parameters with \n# monitor at index `1` and GPU at index `0`.\nstream = ScreenGear(monitor=(1,0), logging=True).start()\n

Getting a complete list of monitor devices and GPUs

To get a complete list of monitor devices and outputs(GPUs), you can use dxcam library itself:

>>> import dxcam\n>>> dxcam.device_info()\n'Device[0]:<Device Name:NVIDIA GeForce RTX 3090 Dedicated VRAM:24348Mb VendorId:4318>\\n'\n>>> dxcam.output_info()\n'Device[0] Output[0]: Res:(1920, 1080) Rot:0 Primary:True\\nDevice[0] Output[1]: Res:(1920, 1080) Rot:0 Primary:False\\n'\n

# open video stream with defined parameters \n# with monitor at index `1` selected\nScreenGear(monitor=1)\n

With mss library backend, You can also assign monitor value to -1 to fetch frames from all connected multiple monitor screens with mss backend.

With mss library backend, API will output BGRA colorspace frames instead of default BGR.

# open video stream with defined parameters \n# with monitor at index `1` selected\nScreenGear(monitor=1)\n

"},{"location":"gears/screengear/params/#backend","title":"backend","text":"

This parameter enables pyscreenshot usage and select suitable backend for extracting frames in ScreenGear. The user have the authority of selecting suitable backend which generates best performance as well as the most compatible with their machines. The possible values are: dxcam (Windows only), pil, mss, scrot, maim, imagemagick, pyqt5, pyqt, pyside2, pyside, wx, pygdk3, mac_screencapture, mac_quartz, gnome_dbus, gnome-screenshot, kwin_dbus.

Performance Benchmarking of all backend can be found here \u27b6 and here \u27b6

Remember to install backend library and all of its dependencies you're planning to use with ScreenGear API.

Any value on monitor parameter will disable the backend parameter. You cannot use both parameters at same time.

Backend defaults to dxcam library on Windows (if installed), and pyscreenshot otherwise.

Data-Type: String

Default Value: Its default value is \"\" (i.e. default backend).

Usage:

ScreenGear(backend=\"pil\") # to enforce `pil` as backend for extracting frames.\n

"},{"location":"gears/screengear/params/#colorspace","title":"colorspace","text":"

This parameter selects the colorspace of the source stream.

Data-Type: String

Default Value: Its default value is None.

Usage:

All supported colorspace values are given here \u27b6.

ScreenGear(colorspace=\"COLOR_BGR2HSV\")\n

Its complete usage example is given here \u27b6

"},{"location":"gears/screengear/params/#options","title":"options","text":"

This parameter provides the flexibility to manually set the dimensions of capture screen area.

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, defined below:

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

The desired dimensional coordinates parameters can be passed to ScreenGear API by formatting them as attributes, as follows:

# formatting dimensional parameters as dictionary attributes\noptions = {'top': 40, 'left': 0, 'width': 100, 'height': 100}\n# assigning it\nScreenGear(**options)\n

"},{"location":"gears/screengear/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

ScreenGear(logging=True)\n

"},{"location":"gears/screengear/usage/","title":"Usage Examples","text":""},{"location":"gears/screengear/usage/#screengear-api-usage-examples","title":"ScreenGear API Usage Examples:","text":"

After going through ScreenGear Usage Examples, Checkout more of its advanced configurations here \u27b6

Recommended: Install DXcam library on Windows Machines

On Windows Machines, if installed, ScreenGear API uses dxcam backend machines for higher FPS performance. Thereby, it is highly recommended to install it via pip as follows:

pip install dxcam\n

"},{"location":"gears/screengear/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with ScreenGear API:

# import required libraries\nfrom vidgear.gears import ScreenGear\nimport cv2\n\n# open video stream with default parameters\nstream = ScreenGear().start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/screengear/usage/#using-screengear-with-variable-screen-dimensions","title":"Using ScreenGear with Variable Screen Dimensions","text":"

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 dictionary parameter by formatting them as its attributes.

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, defined below:

The complete usage example is as follows:

# import required libraries\nfrom vidgear.gears import ScreenGear\nimport cv2\n\n# define dimensions of screen w.r.t to given monitor to be captured\noptions = {\"top\": 40, \"left\": 0, \"width\": 100, \"height\": 100}\n\n# open video stream with defined parameters\nstream = ScreenGear(logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/screengear/usage/#using-screengear-with-multiple-screens","title":"Using ScreenGear with Multiple Screens","text":"

ScreenGear API provides us the flexibility to select any connected display for fetching frames, with its monitor parameter:

Implication of using monitor parameter

Any value on monitor parameter other than None in ScreenGear API:

With dxcam on Windows With mss backend Using GPU acceleration on Windows

With dxcam library backend, you can also assign which GPU devices ids to use along with monitor device ids as tuple (monitor_idx, gpu_idx), as follows:

# open video stream with defined parameters with \n# monitor at index `1` and GPU at index `0`.\nstream = ScreenGear(monitor=(1,0), logging=True).start()\n

Getting a complete list of monitor devices and GPUs

To get a complete list of monitor devices and outputs(GPUs), you can use dxcam library itself:

>>> import dxcam\n>>> dxcam.device_info()\n'Device[0]:<Device Name:NVIDIA GeForce RTX 3090 Dedicated VRAM:24348Mb VendorId:4318>\\n'\n>>> dxcam.output_info()\n'Device[0] Output[0]: Res:(1920, 1080) Rot:0 Primary:True\\nDevice[0] Output[1]: Res:(1920, 1080) Rot:0 Primary:False\\n'\n

# import required libraries\nfrom vidgear.gears import ScreenGear\nimport cv2\n\n# open video stream with defined parameters with monitor at index `1` selected\nstream = ScreenGear(monitor=1, logging=True).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

With mss library backend, You can also assign monitor value to -1 to fetch frames from all connected multiple monitor screens with mss backend.

With mss library backend, API will output BGRA colorspace frames instead of default BGR.

# import required libraries\nfrom vidgear.gears import ScreenGear\nimport cv2\n\n# open video stream with defined parameters with monitor at index `1` selected\nstream = ScreenGear(monitor=1, logging=True).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/screengear/usage/#using-screengear-with-variable-backend","title":"Using ScreenGear with Variable Backend","text":"

With ScreenGear API, you can select from many different backends that generates best performance as well as the most compatible with our machine by employing its backend parameter that supports many different backends:

Supported backend values

Its possible values are: dxcam (Windows only), pil, mss, scrot, maim, imagemagick, pyqt5, pyqt, pyside2, pyside, wx, pygdk3, mac_screencapture, mac_quartz, gnome_dbus, gnome-screenshot, kwin_dbus.

Remember to install backend library and all of its dependencies you're planning to use with ScreenGear API. More information on all these backends (except dxcam) can be found here \u27b6

Backend defaults to dxcam library on Windows (if installed), and pyscreenshot otherwise.

Any value on monitor parameter will disable the backend parameter. You cannot use them simultaneously.

# import required libraries\nfrom vidgear.gears import ScreenGear\nimport cv2\n\n# open video stream with defined parameters and `mss` backend \n# for extracting frames.\nstream = ScreenGear(backend=\"mss\", logging=True).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/screengear/usage/#using-screengear-with-direct-colorspace-manipulation","title":"Using ScreenGear with Direct Colorspace Manipulation","text":"

ScreenGear API also supports Direct Colorspace Manipulation, which is ideal for changing source colorspace on the run.

A more detailed information on colorspace manipulation can be found here \u27b6

In following example code, we will start with HSV as source colorspace, and then we will switch to GRAY colorspace when W key is pressed, and then LAB colorspace when E key is pressed, finally default colorspace (i.e. BGR) when S key is pressed. Also, quit when Q key is pressed:

Any incorrect or None-type value, will immediately revert the colorspace to default i.e. BGR.

# import required libraries\nfrom vidgear.gears import ScreenGear\nimport cv2\n\n# Change colorspace to `HSV`\nstream = ScreenGear(colorspace=\"COLOR_BGR2HSV\", logging=True).start()\n\n# loop over\nwhile True:\n\n    # read HSV frames\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the HSV frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for key if pressed\n    key = cv2.waitKey(1) & 0xFF\n\n    # check if 'w' key is pressed\n    if key == ord(\"w\"):\n        # directly change colorspace at any instant\n        stream.color_space = cv2.COLOR_BGR2GRAY  # Now colorspace is GRAY\n\n    # check for 'e' key is pressed\n    if key == ord(\"e\"):\n        stream.color_space = cv2.COLOR_BGR2LAB  # Now colorspace is CieLAB\n\n    # check for 's' key is pressed\n    if key == ord(\"s\"):\n        stream.color_space = None  # Now colorspace is default(ie BGR)\n\n    # check for 'q' key is pressed\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/screengear/usage/#using-screengear-with-writegear-api","title":"Using ScreenGear with WriteGear API","text":"

ScreenGear can be used in conjunction with WriteGear API directly without any compatibility issues. The suitable example is as follows:

# import required libraries\nfrom vidgear.gears import ScreenGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n\n# define dimensions of screen w.r.t to given monitor to be captured\noptions = {\"top\": 40, \"left\": 0, \"width\": 100, \"height\": 100}\n\n# define suitable (Codec,CRF,preset) FFmpeg parameters for writer\noutput_params = {\"-vcodec\": \"libx264\", \"-crf\": 0, \"-preset\": \"fast\"}\n\n# open video stream with defined parameters\nstream = ScreenGear(monitor=1, logging=True, **options).start()\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n    # lets convert frame to gray for this example\n    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n\n    # write gray frame to writer\n    writer.write(gray)\n\n    # Show output window\n    cv2.imshow(\"Output Gray Frame\", gray)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/stabilizer/overview/","title":"Overview","text":""},{"location":"gears/stabilizer/overview/#stabilizer-class","title":"Stabilizer Class","text":"

VidGear's Stabilizer in Action(Video Credits @SIGGRAPH2013)

This video is transcoded with StreamGear API and hosted on GitHub Repository and served with raw.githack.com

"},{"location":"gears/stabilizer/overview/#overview","title":"Overview","text":"

Stabilizer is an auxiliary class that enables Video Stabilization for vidgear with minimalistic latency, and at the expense of little to no additional computational requirements.

The basic idea behind it is to tracks and save the salient feature array for the given number of frames and then uses these anchor point to cancel out all perturbations relative to it for the incoming frames in the queue. This class relies on Fixed-Size Python Queues for error-free & ultra-fast frame handling.

For more detailed information on Stabilizer working, See this blogpost \u27b6

"},{"location":"gears/stabilizer/overview/#features","title":"Features","text":"

Important

"},{"location":"gears/stabilizer/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through Stabilizer Class Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/stabilizer/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/stabilizer/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/stabilizer/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/stabilizer/params/","title":"Parameters","text":""},{"location":"gears/stabilizer/params/#stabilizer-class-parameters","title":"Stabilizer Class Parameters","text":""},{"location":"gears/stabilizer/params/#smoothing_radius","title":"smoothing_radius","text":"

This parameter can be used to alter averaging window size. It basically handles the quality of stabilization at the expense of latency and sudden panning. Larger its value, less will be panning, more will be latency and vice-versa.

Data-Type: Integer

Default Value: Its default value is 25.

Usage:

You can easily pass this parameter as follows:

Stabilizer(smoothing_radius=30)\n

"},{"location":"gears/stabilizer/params/#border_size","title":"border_size","text":"

This parameter enables and set the value for extended border size that compensates for reduction of black borders during stabilization.

Data-Type: Integer

Default Value: Its default value is 0(no borders).

Usage:

You can easily pass this parameter as follows:

Stabilizer(border_size=10)\n

"},{"location":"gears/stabilizer/params/#crop_n_zoom","title":"crop_n_zoom","text":"

This parameter enables cropping and zooming of frames (to original size) to reduce the black borders from being too noticeable (similar to the Stabilized, cropped and Auto-Scaled feature available in Adobe AfterEffects) during stabilization. It simply works in conjunction with the border_size parameter, i.e. when this parameter is enabled, border_size will be used for cropping border instead of extending them.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

You can easily pass this parameter as follows:

Stabilizer(border_size=10, crop_n_zoom=True)\n

"},{"location":"gears/stabilizer/params/#border_type","title":"border_type","text":"

This parameter can be used to change the extended border type. Valid border types are 'black', 'reflect', 'reflect_101', 'replicate' and 'wrap', learn more about it here.

Altering border_type parameter is DISABLED when crop_n_zoom is enabled!

Data-Type: String

Default Value: Its default value is 'black'.

Usage:

You can easily pass this parameter as follows:

Stabilizer(border_type='reflect')\n

"},{"location":"gears/stabilizer/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

Stabilizer(logging=True)\n

"},{"location":"gears/stabilizer/usage/","title":"Usage Examples","text":""},{"location":"gears/stabilizer/usage/#stabilizer-class-usage-examples","title":"Stabilizer Class Usage Examples:","text":"

The stabilizer may not perform well against High-frequency jitter in video. Use at your own risk!

The stabilizer might be slower for High-Quality/Resolution videos-frames.

It is advised to enable logging on the first run for easily identifying any runtime errors.

After going through Stabilizer Class Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/stabilizer/usage/#bare-minimum-usage-with-videocapture-gears","title":"Bare-Minimum Usage with VideoCapture Gears","text":"

Following is the bare-minimum code you need to get started with Stabilizer Class and various VideoCapture Gears:

You can use any VideoCapture Gear instead of CamGear in the similar manner, as shown in this usage example.

# import required libraries\nfrom vidgear.gears.stabilizer import Stabilizer\nfrom vidgear.gears import CamGear\nimport cv2\n\n# To open live video stream on webcam at first index(i.e. 0) device\nstream = CamGear(source=0).start()\n\n# initiate stabilizer object with default parameters\nstab = Stabilizer()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # send current frame to stabilizer for processing\n    stabilized_frame = stab.stabilize(frame)\n\n    # wait for stabilizer which still be initializing\n    if stabilized_frame is None:\n        continue\n\n    # {do something with the stabilized frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Stabilized Frame\", stabilized_frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# clear stabilizer resources\nstab.clean()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/stabilizer/usage/#bare-minimum-usage-with-opencv","title":"Bare-Minimum Usage with OpenCV","text":"

The VidGear's stabilizer class can also work standalone easily with any Computer Vision library such as OpenCV itself. Following is the bare-minimum code you need to get started with Stabilizer Class and OpenCV:

# import required libraries\nfrom vidgear.gears.stabilizer import Stabilizer\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = cv2.VideoCapture(0)\n\n# initiate stabilizer object with default parameters\nstab = Stabilizer()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n        break\n\n    # send current frame to stabilizer for processing\n    stabilized_frame = stab.stabilize(frame)\n\n    # wait for stabilizer which still be initializing\n    if stabilized_frame is None:\n        continue\n\n    # {do something with the stabilized frame here}\n\n    # Show output window\n    cv2.imshow(\"Stabilized Frame\", stabilized_frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# clear stabilizer resources\nstab.clean()\n\n# safely close video stream\nstream.release()\n

"},{"location":"gears/stabilizer/usage/#using-stabilizer-with-variable-parameters","title":"Using Stabilizer with Variable Parameters","text":"

Stabilizer class provide certain parameters which you can use to tweak its internal properties. The complete usage example is as follows:

# import required libraries\nfrom vidgear.gears.stabilizer import Stabilizer\nfrom vidgear.gears import CamGear\nimport cv2\n\n# To open live video stream on webcam at first index(i.e. 0) device\nstream = CamGear(source=0).start()\n\n# initiate stabilizer object with defined parameters\nstab = Stabilizer(smoothing_radius=30, crop_n_zoom=True, border_size=5, logging=True)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # send current frame to stabilizer for processing\n    stabilized_frame = stab.stabilize(frame)\n\n    # wait for stabilizer which still be initializing\n    if stabilized_frame is None:\n        continue\n\n    # {do something with the stabilized frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Stabilized Frame\", stabilized_frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# clear stabilizer resources\nstab.clean()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/stabilizer/usage/#using-stabilizer-with-writegear","title":"Using Stabilizer with WriteGear","text":"

VideoGear's stabilizer can be used in conjunction with WriteGear API directly without any compatibility issues. The complete usage example is as follows:

You can also add live audio input to WriteGear pipeline. See this bonus example \u27b6

# import required libraries\nfrom vidgear.gears.stabilizer import Stabilizer\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# Open suitable video stream\nstream = CamGear(source=\"unstabilized_stream.mp4\").start()\n\n# initiate stabilizer object with default parameters\nstab = Stabilizer()\n\n# Define writer with default parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\")\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if not None-type\n    if frame is None:\n        break\n\n    # send current frame to stabilizer for processing\n    stabilized_frame = stab.stabilize(frame)\n\n    # wait for stabilizer which still be initializing\n    if stabilized_frame is None:\n        continue\n\n    # {do something with the stabilized frame here}\n\n    # write stabilized frame to writer\n    writer.write(stabilized_frame)\n\n    # Show output window\n    cv2.imshow(\"Stabilized Frame\", stabilized_frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# clear stabilizer resources\nstab.clean()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/stabilizer/usage/#using-videogear-with-stabilizer-backend","title":"Using VideoGear with Stabilizer backend","text":"

VideoGear API provides a special internal wrapper around Stabilizer class that enables easy stabilization for various video-streams (real-time or not) with minimum effort and writing way fewer lines of code.

The complete usage example can be found here \u27b6

"},{"location":"gears/streamgear/ffmpeg_install/","title":"FFmpeg Installation","text":""},{"location":"gears/streamgear/ffmpeg_install/#ffmpeg-installation-instructions","title":"FFmpeg Installation Instructions","text":"

StreamGear must requires FFmpeg executables for transcoding Media Chunks. You can following machine-specific instructions for its installation:

StreamGear API will throw RuntimeError, if it fails to detect valid FFmpeg executables on your system.

Enable logging (logging=True) for debugging FFmpeg validation process.

"},{"location":"gears/streamgear/ffmpeg_install/#linux-ffmpeg-installation","title":"Linux FFmpeg Installation","text":"

The StreamGear API supports Auto-Detection and Manual Configuration methods on a Linux machine:

"},{"location":"gears/streamgear/ffmpeg_install/#a-auto-detection","title":"A. Auto-Detection","text":"

This is a recommended approach on Linux Machines

If StreamGear API not receives any input from the user on custom_ffmpeg parameter, then on Linux system, it tries to auto-detects the required FFmpeg installed binaries through validation test that employs subprocess python module.

Installation: You can install easily install official FFmpeg according to your Linux Distro by following this post \u27b6

"},{"location":"gears/streamgear/ffmpeg_install/#b-manual-configuration","title":"B. Manual Configuration","text":"

"},{"location":"gears/streamgear/ffmpeg_install/#windows-ffmpeg-installation","title":"Windows FFmpeg Installation","text":"

The StreamGear API supports Auto-Installation and Manual Configuration methods on Windows systems.

"},{"location":"gears/streamgear/ffmpeg_install/#a-auto-installation","title":"A. Auto-Installation","text":"

This is a recommended approach on Windows Machines

If StreamGear API not receives any input from the user on custom_ffmpeg parameter, then on Windows system StreamGear API auto-generates the required FFmpeg Static Binaries from a dedicated Github Server into the temporary directory (for e.g. C:\\Temp) of your machine.

Warning

"},{"location":"gears/streamgear/ffmpeg_install/#b-manual-configuration_1","title":"B. Manual Configuration","text":"

"},{"location":"gears/streamgear/ffmpeg_install/#macos-ffmpeg-installation","title":"MacOS FFmpeg Installation","text":"

The StreamGear API supports Auto-Detection and Manual Configuration methods on a macOS machine.

"},{"location":"gears/streamgear/ffmpeg_install/#a-auto-detection_1","title":"A. Auto-Detection","text":"

This is a recommended approach on MacOS Machines

If StreamGear API not receives any input from the user on custom_ffmpeg parameter, then on macOS system, it tries to auto-detects the required FFmpeg installed binaries through validation test that employs subprocess python module.

Installation: You can easily install FFmpeg on your macOS machine by following this tutorial \u27b6

"},{"location":"gears/streamgear/ffmpeg_install/#b-manual-configuration_2","title":"B. Manual Configuration","text":"

"},{"location":"gears/streamgear/introduction/","title":"Introduction","text":""},{"location":"gears/streamgear/introduction/#streamgear-api","title":"StreamGear API","text":"StreamGear API's generalized workflow"},{"location":"gears/streamgear/introduction/#overview","title":"Overview","text":"

StreamGear streamlines and simplifies the transcoding workflow to generate Ultra-Low Latency, High-Quality, Dynamic & Adaptive Streaming Formats like MPEG-DASH and Apple HLS with just a few lines of Python code, allowing developers to focus on their application logic rather than dealing with the complexities of transcoding and chunking media files.

StreamGear API provides a standalone, highly extensible, and flexible wrapper around the FFmpeg multimedia framework for generating chunk-encoded media segments from your multimedia content effortlessly.

With StreamGear, you can transcode source video/audio files and real-time video frames into a sequence of multiple smaller chunks/segments of suitable lengths. These segments facilitate streaming at different quality levels (bitrates or spatial resolutions) and allow for seamless switching between quality levels during playback based on available bandwidth. You can serve these segments on a web server, making them easily accessible via standard HTTP GET requests.

SteamGear currently supports both MPEG-DASH (Dynamic Adaptive Streaming over HTTP, ISO/IEC 23009-1) and Apple HLS (HTTP Live Streaming).

Additionally, StreamGear generates a manifest file (such as MPD for DASH) or a master playlist (such as M3U8 for Apple HLS) alongside the segments. These files contain essential segment information, including timing, URLs, and media characteristics like video resolution and adaptive bitrate. They are provided to the client before the streaming session begins.

For streaming with older traditional protocols such as RTMP, RTSP/RTP you could use WriteGear API instead.

New in v0.2.2

Apple HLS support was added in v0.2.2.

Important

Useful Links

"},{"location":"gears/streamgear/introduction/#mode-of-operations","title":"Mode of Operations","text":"

StreamGear primarily operates in following independent modes for transcoding:

Real-time Frames Mode itself is NOT Live-Streaming

To enable live-streaming in Real-time Frames Mode, use the exclusive -livestream attribute of the stream_params dictionary parameter in the StreamGear API. Checkout this usage example \u27b6 for more information.

"},{"location":"gears/streamgear/introduction/#watch-demo","title":"Watch Demo","text":"Watch MPEG-DASH StreamWatch APPLE HLS Stream

Watch StreamGear transcoded MPEG-DASH Stream:

Powered by clappr & shaka-player

This video assets (Manifest and segments) are hosted on GitHub Repository and served with raw.githack.com

Video Credits: \"Tears of Steel\" - Project Mango Teaser

Watch StreamGear transcoded APPLE HLS Stream:

Powered by clappr & HlsjsPlayback

This video assets (Playlist and segments) are hosted on GitHub Repository and served with raw.githack.com

Video Credits: \"Sintel\" - Project Durian Teaser

"},{"location":"gears/streamgear/introduction/#recommended-players","title":"Recommended Players","text":"GUI PlayersCommand-Line PlayersOnline Players

To run Online players locally, you'll need a HTTP server. For creating one yourself, See this well-curated list \u27b6

"},{"location":"gears/streamgear/introduction/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/streamgear/introduction/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/streamgear/introduction/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/streamgear/params/","title":"Parameters","text":""},{"location":"gears/streamgear/params/#streamgear-api-parameters","title":"StreamGear API Parameters","text":""},{"location":"gears/streamgear/params/#output","title":"output","text":"

This parameter sets the valid filename/path for storing the StreamGear assets, including Manifest file (such as MPD in case of DASH) or a Master Playlist (such as M3U8 in case of Apple HLS) and generated sequence of chunks/segments.

StreamGear API will throw ValueError if the provided output is empty or invalid.

Make sure to provide a valid filename with a valid file extension for the selected format value (such as .mpd for MPEG-DASH and .m3u8 for APPLE-HLS), otherwise StreamGear will throw AssertionError.

You can easily delete all previous assets at the output location by using the -clear_prev_assets attribute of the stream_params dictionary parameter.

Data-Type: String

Usage:

Its valid input can be one of the following:

"},{"location":"gears/streamgear/params/#format","title":"format","text":"

This parameter enables the adaptive HTTP streaming format. This parameter currently supported these formats: dash (i.e MPEG-DASH) and hls (i.e Apple HLS).

Make sure to provide a valid filename with a valid file extension in the output parameter for the selected format value (i.e., .mpd for MPEG-DASH and .m3u8 for APPLE-HLS), otherwise StreamGear will throw an AssertionError.

Any improper value assigned to format parameter will result in a ValueError!

Data-Type: String

Default Value: Its default value is dash

Usage:

DASHHLS
# Define streamer with DASH format\nStreamGear(output = \"output_dash.mpd\", format=\"dash\")\n
# Define streamer with HLS format\nStreamGear(output = \"output_hls.m3u8\", format=\"hls\")\n

"},{"location":"gears/streamgear/params/#custom_ffmpeg","title":"custom_ffmpeg","text":"

This parameter assigns the custom path/directory where the custom/downloaded FFmpeg executables are located.

Behavior on Windows Systems

On Windows, if a custom FFmpeg executable's path/directory is not provided through this custom_ffmpeg parameter, the StreamGear API will automatically attempt to download and extract suitable Static FFmpeg binaries at a suitable location on your Windows machine. More information can be found here \u27b6.

Data-Type: String

Default Value: Its default value is None.

Usage:

# Define streamer with custom ffmpeg binary\nStreamGear(output = 'output_foo.mpd', custom_ffmpeg=\"C://foo//bar//ffmpeg.exe\")\n

"},{"location":"gears/streamgear/params/#stream_params","title":"stream_params","text":"

This parameter allows developers to leverage nearly all FFmpeg options, providing effortless and flexible control over its internal settings for transcoding and generating high-quality streams. All supported parameters can be formatted as attributes within this dictionary parameter.

Please read the FFmpeg Documentation carefully before passing any additional values to the stream_params parameter. Incorrect values may cause errors or result in no output.

Data-Type: Dictionary

Default Value: Its default value is {}.

"},{"location":"gears/streamgear/params/#supported-parameters","title":"Supported Parameters","text":""},{"location":"gears/streamgear/params/#a-exclusive-parameters","title":"A. Exclusive Parameters","text":"

StreamGear API provides some exclusive internal parameters to easily generate Streaming Assets and effortlessly tweak its internal properties. These parameters are discussed below:

"},{"location":"gears/streamgear/params/#b-ffmpeg-parameters","title":"B. FFmpeg Parameters","text":"

Almost all FFmpeg parameters can be passed as dictionary attributes in stream_params. For example, to use the libx264 encoder to produce a lossless output video, you can pass the required FFmpeg parameters as dictionary attributes as follows:

Please check the H.264 documentation \u27b6 and FFmpeg Documentation \u27b6 for more information on following parameters.

All FFmpeg parameters are case-sensitive. Double-check each parameter if any errors occur.

In addition to these parameters, almost any FFmpeg parameter (supported by the installed FFmpeg) is also supported. Be sure to read the FFmpeg Documentation carefully first.

# libx264 encoder and its supported parameters\nstream_params = {\"-vcodec\":\"libx264\", \"-crf\": 0, \"-preset\": \"fast\", \"-tune\": \"zerolatency\"} \n

"},{"location":"gears/streamgear/params/#supported-encoders-and-decoders","title":"Supported Encoders and Decoders","text":"

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:

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 for creating HLS/DASH chunks of the primary stream efficiently. However, consider the following points:

# for checking encoder\nffmpeg -encoders           # use `ffmpeg.exe -encoders` on windows\n# for checking decoders\nffmpeg -decoders           # use `ffmpeg.exe -decoders` on windows\n

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

"},{"location":"gears/streamgear/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

StreamGear(logging=True)\n

  1. In Real-time Frames Mode, the Primary Stream's framerate defaults to -input_framerate attribute value, if defined, else it will be 25fps.\u00a0\u21a9

"},{"location":"gears/streamgear/rtfm/overview/","title":"Overview","text":""},{"location":"gears/streamgear/rtfm/overview/#streamgear-api-real-time-frames-mode","title":"StreamGear API: Real-time Frames Mode","text":"Real-time Frames Mode generalized workflow"},{"location":"gears/streamgear/rtfm/overview/#overview","title":"Overview","text":"

When no valid input is received on -video_source attribute of stream_params dictionary parameter, StreamGear API activates this mode where it directly transcodes real-time numpy.ndarray video-frames (as opposed to a entire video file) into a sequence of multiple smaller chunks/segments for adaptive streaming.

This mode works exceptionally well when you desire to flexibility manipulate or transform video-frames in real-time before sending them onto FFmpeg Pipeline for processing. But on the downside, StreamGear DOES NOT automatically maps video-source's audio to generated streams with this mode. You need to manually assign separate audio-source through -audio attribute of stream_params dictionary parameter.

SteamGear supports both MPEG-DASH (Dynamic Adaptive Streaming over HTTP, ISO/IEC 23009-1) and Apple HLS (HTTP Live Streaming) with this mode.

For this mode, StreamGear API provides exclusive stream() method for directly trancoding video-frames into streamable chunks.

New in v0.2.2

Apple HLS support was added in v0.2.2.

Real-time Frames Mode itself is NOT Live-Streaming

To enable live-streaming in Real-time Frames Mode, use the exclusive -livestream attribute of the stream_params dictionary parameter in the StreamGear API. Checkout this usage example \u27b6 for more information.

Please Remember

"},{"location":"gears/streamgear/rtfm/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through StreamGear Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/streamgear/rtfm/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/streamgear/rtfm/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/streamgear/rtfm/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/streamgear/rtfm/usage/","title":"Usage Examples","text":""},{"location":"gears/streamgear/rtfm/usage/#streamgear-api-usage-examples-real-time-frames-mode","title":"StreamGear API Usage Examples: Real-time Frames Mode","text":"

Real-time Frames Mode itself is NOT Live-Streaming

To enable live-streaming in Real-time Frames Mode, use the exclusive -livestream attribute of the stream_params dictionary parameter in the StreamGear API. Checkout following usage example \u27b6 for more information.

Important Information

DEPRECATION NOTICES for v0.3.3 and above

After going through following Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/streamgear/rtfm/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with StreamGear API in Real-time Frames Mode:

We are using CamGear in this Bare-Minimum example, but any VideoCapture Gear will work in the similar manner.

In this mode, StreamGear DOES NOT automatically maps video-source audio to generated streams. You need to manually assign separate audio-source through -audio attribute of stream_params dictionary parameter.

DASHHLS
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# open any valid video stream(for e.g `foo1.mp4` file)\nstream = CamGear(source='foo1.mp4').start() \n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"dash_out.mpd\")\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# open any valid video stream(for e.g `foo1.mp4` file)\nstream = CamGear(source='foo1.mp4').start() \n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\")\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n

After running this bare-minimum example, StreamGear will produce a Manifest file (dash.mpd) with streamable chunks that contains information about a Primary Stream of same resolution and framerate1 as input (without any audio).

"},{"location":"gears/streamgear/rtfm/usage/#bare-minimum-usage-with-controlled-input-framerate","title":"Bare-Minimum Usage with controlled Input-framerate","text":"

In Real-time Frames Mode, StreamGear API provides the exclusive -input_framerate attribute for the stream_params dictionary parameter, which allows you to set the assumed constant framerate for incoming frames.

In this example, we will retrieve the framerate from a webcam video stream and set it as the value for the -input_framerate attribute in StreamGear.

Remember, the input framerate defaults to 25.0 fps if the -input_framerate attribute value is not defined in Real-time Frames mode.

DASHHLS
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = CamGear(source=0).start()\n\n# retrieve framerate from CamGear Stream and pass it as `-input_framerate` value\nstream_params = {\"-input_framerate\":stream.framerate}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = CamGear(source=0).start()\n\n# retrieve framerate from CamGear Stream and pass it as `-input_framerate` value\nstream_params = {\"-input_framerate\":stream.framerate}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n

"},{"location":"gears/streamgear/rtfm/usage/#bare-minimum-usage-with-live-streaming","title":"Bare-Minimum Usage with Live-Streaming","text":"

You can easily activate Low-latency Live-Streaming in Real-time Frames Mode, where chunks will contain information for new frames only and forget previous ones, using the exclusive -livestream attribute of the stream_params dictionary parameter. The complete example is as follows:

In this mode, StreamGear DOES NOT automatically maps video-source audio to generated streams. You need to manually assign separate audio-source through -audio attribute of stream_params dictionary parameter.

DASHHLS

Controlling chunk size in DASH

To control the number of frames kept in Chunks for the DASH stream (controlling latency), you can use the -window_size and -extra_window_size FFmpeg parameters. Lower values for these parameters will result in lower latency.

After every few chunks (equal to the sum of -window_size and -extra_window_size values), all chunks will be overwritten while Live-Streaming. This means that newer chunks in the manifest will contain NO information from older chunks, and the resulting DASH stream will only play the most recent frames, reducing latency.

# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# open any valid video stream(from web-camera attached at index `0`)\nstream = CamGear(source=0).start()\n\n# enable livestreaming and retrieve framerate from CamGear Stream and\n# pass it as `-input_framerate` parameter for controlled framerate\nstream_params = {\"-input_framerate\": stream.framerate, \"-livestream\": True}\n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n

Controlling chunk size in HLS

To control the number of frames kept in Chunks for the HLS stream (controlling latency), you can use the -hls_init_time & -hls_time FFmpeg parameters. Lower values for these parameters will result in lower latency.

After every few chunks (equal to the sum of -hls_init_time & -hls_time values), all chunks will be overwritten while Live-Streaming. This means that newer chunks in the master playlist will contain NO information from older chunks, and the resulting HLS stream will only play the most recent frames, reducing latency.

# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# open any valid video stream(from web-camera attached at index `0`)\nstream = CamGear(source=0).start()\n\n# enable livestreaming and retrieve framerate from CamGear Stream and\n# pass it as `-input_framerate` parameter for controlled framerate\nstream_params = {\"-input_framerate\": stream.framerate, \"-livestream\": True}\n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n

"},{"location":"gears/streamgear/rtfm/usage/#bare-minimum-usage-with-opencv","title":"Bare-Minimum Usage with OpenCV","text":"

You can easily use the StreamGear API directly with any other Video Processing library (for e.g. OpenCV) in Real-time Frames Mode.

The following is a complete StreamGear API usage example with OpenCV:

This is a bare-minimum example with OpenCV, but any other Real-time Frames Mode feature or example will work in a similar manner.

DASHHLS
# import required libraries\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = cv2.VideoCapture(0) \n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"dash_out.mpd\")\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n      break\n\n    # {do something with the frame here}\n    # lets convert frame to gray for this example\n    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n\n    # send frame to streamer\n    streamer.stream(gray)\n\n    # Show output window\n    cv2.imshow(\"Output Gray Frame\", gray)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.release()\n\n# safely close streamer\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = cv2.VideoCapture(0) \n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\")\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n      break\n\n    # {do something with the frame here}\n    # lets convert frame to gray for this example\n    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n\n    # send frame to streamer\n    streamer.stream(gray)\n\n    # Show output window\n    cv2.imshow(\"Output Gray Frame\", gray)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.release()\n\n# safely close streamer\nstreamer.close()\n

"},{"location":"gears/streamgear/rtfm/usage/#usage-with-additional-streams","title":"Usage with Additional Streams","text":"

Similar to Single-Source Mode, in addition to the Primary Stream, you can easily generate any number of additional Secondary Streams with variable bitrate or spatial resolution, using the exclusive -streams attribute of the stream_params dictionary parameter.

To generate Secondary Streams, add each desired resolution and bitrate/framerate as a list of dictionaries to the -streams attribute. StreamGear will handle the rest automatically. The complete example is as follows:

A more detailed information on -streams attribute can be found here \u27b6

In this mode, StreamGear DOES NOT automatically maps video-source audio to generated streams. You need to manually assign separate audio-source through -audio attribute of stream_params dictionary parameter.

Important Information about -streams attribute

Always use the -streams attribute to define additional streams safely. Duplicate or incorrect definitions can break the transcoding pipeline and corrupt the output chunks.

DASHHLS
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = CamGear(source=0).start() \n\n# define various streams\nstream_params = {\n    \"-streams\": [\n        {\"-resolution\": \"1280x720\", \"-framerate\": 30.0},  # Stream1: 1280x720 at 30fps framerate\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream2: 640x360 at 60fps framerate\n        {\"-resolution\": \"320x240\", \"-video_bitrate\": \"500k\"},  # Stream3: 320x240 at 500kbs bitrate\n    ],\n}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"dash_out.mpd\")\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = CamGear(source=0).start() \n\n# define various streams\nstream_params = {\n    \"-streams\": [\n        {\"-resolution\": \"1280x720\", \"-framerate\": 30.0},  # Stream1: 1280x720 at 30fps framerate\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream2: 640x360 at 60fps framerate\n        {\"-resolution\": \"320x240\", \"-video_bitrate\": \"500k\"},  # Stream3: 320x240 at 500kbs bitrate\n    ],\n}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\")\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n

"},{"location":"gears/streamgear/rtfm/usage/#usage-with-file-audio-input","title":"Usage with File Audio-Input","text":"

In Real-time Frames Mode, if you want to add audio to your streams, you need to use the exclusive -audio attribute of the stream_params dictionary parameter.

To add a audio source, provide the path to your audio file as a string to the -audio attribute. The API will automatically validate and map the audio to all generated streams. The complete example is as follows:

Ensure the provided -audio audio source is compatible with the input video source. Incompatibility can cause multiple errors or result in no output at all.

You MUST use -input_framerate attribute to set exact value of input framerate when using external audio in Real-time Frames mode, otherwise audio delay will occur in output streams.

You can also assign a valid audio URL as input instead of a file path. More details can be found here \u27b6

DASHHLS
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# open any valid video stream(for e.g `foo1.mp4` file)\nstream = CamGear(source='foo1.mp4').start() \n\n# add various streams, along with custom audio\nstream_params = {\n    \"-streams\": [\n        {\"-resolution\": \"1920x1080\", \"-video_bitrate\": \"4000k\"},  # Stream1: 1920x1080 at 4000kbs bitrate\n        {\"-resolution\": \"1280x720\", \"-framerate\": 30.0},  # Stream2: 1280x720 at 30fps\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream3: 640x360 at 60fps\n    ],\n    \"-input_framerate\": stream.framerate, # controlled framerate for audio-video sync !!! don't forget this line !!!\n    \"-audio\": \"/home/foo/foo1.aac\" # assign external audio-source\n}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# open any valid video stream(for e.g `foo1.mp4` file)\nstream = CamGear(source='foo1.mp4').start() \n\n# add various streams, along with custom audio\nstream_params = {\n    \"-streams\": [\n        {\"-resolution\": \"1920x1080\", \"-video_bitrate\": \"4000k\"},  # Stream1: 1920x1080 at 4000kbs bitrate\n        {\"-resolution\": \"1280x720\", \"-framerate\": 30.0},  # Stream2: 1280x720 at 30fps\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream3: 640x360 at 60fps\n    ],\n    \"-input_framerate\": stream.framerate, # controlled framerate for audio-video sync !!! don't forget this line !!!\n    \"-audio\": \"/home/foo/foo1.aac\" # assign external audio-source\n}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n

"},{"location":"gears/streamgear/rtfm/usage/#usage-with-device-audio-input","title":"Usage with Device Audio-Input","text":"

In Real-time Frames Mode, you can also use the exclusive -audio attribute of the stream_params dictionary parameter for streaming live audio from an external device.

To stream live audio, format your audio device name followed by a suitable demuxer as a list, and assign it to the -audio attribute. The API will automatically validate and map the audio to all generated streams. The complete example is as follows:

Example Assumptions

Using devices sources with -audio attribute on different OS platforms

To use device sources with the -audio attribute on different OS platforms, follow these instructions:

Windows Linux MacOS

Windows OS users can use the dshow (DirectShow) to list audio input device which is the preferred option for Windows users. You can refer following steps to identify and specify your sound card:

If audio still doesn't work then checkout this troubleshooting guide \u27b6 or reach us out on Gitter \u27b6 Community channel

Linux OS users can use the alsa to list input device to capture live audio input such as from a webcam. You can refer following steps to identify and specify your sound card:

If audio still doesn't work then reach us out on Gitter \u27b6 Community channel

MAC OS users can use the avfoundation to list input devices for grabbing audio from integrated iSight cameras as well as cameras connected via USB or FireWire. You can refer following steps to identify and specify your sound card on MacOS/OSX machines:

If audio still doesn't work then reach us out on Gitter \u27b6 Community channel

It is advised to use this example with live-streaming enabled(True) by using StreamGear API's exclusive -livestream attribute of stream_params dictionary parameter.

Ensure the provided -audio audio source is compatible with the video source device. Incompatibility can cause multiple errors or result in no output at all.

You MUST use -input_framerate attribute to set exact value of input framerate when using external audio in Real-time Frames mode, otherwise audio delay will occur in output streams.

DASHHLS
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# open any valid DEVICE video stream\nstream = CamGear(source=0).start()\n\n# add various streams, along with custom audio\nstream_params = {\n    \"-streams\": [\n        {\n            \"-resolution\": \"640x360\",\n            \"-video_bitrate\": \"4000k\",\n        },  # Stream1: 640x360 at 4000kbs bitrate\n        {\"-resolution\": \"320x240\", \"-framerate\": 30.0},  # Stream2: 320x240 at 30fps\n    ],\n    \"-input_framerate\": stream.framerate,  # controlled framerate for audio-video sync !!! don't forget this line !!!\n    \"-livestream\": True,\n    \"-audio\": [\n        \"-f\",\n        \"dshow\",\n        \"-i\",\n        \"audio=Microphone (USB2.0 Camera)\",\n    ],  # assign appropriate input audio-source device(compatible with video source) and its demuxer\n}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# open any valid DEVICE video stream\nstream = CamGear(source=0).start()\n\n# add various streams, along with custom audio\nstream_params = {\n    \"-streams\": [\n        {\n            \"-resolution\": \"640x360\",\n            \"-video_bitrate\": \"4000k\",\n        },  # Stream1: 640x360 at 4000kbs bitrate\n        {\"-resolution\": \"320x240\", \"-framerate\": 30.0},  # Stream2: 320x240 at 30fps\n    ],\n    \"-input_framerate\": stream.framerate,  # controlled framerate for audio-video sync !!! don't forget this line !!!\n    \"-livestream\": True,\n    \"-audio\": [\n        \"-f\",\n        \"dshow\",\n        \"-i\",\n        \"audio=Microphone (USB2.0 Camera)\",\n    ],  # assign appropriate input audio-source device(compatible with video source) and its demuxer\n}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"hls_out.m3u8\", format=\"hls\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n

"},{"location":"gears/streamgear/rtfm/usage/#usage-with-hardware-video-encoder","title":"Usage with Hardware Video-Encoder","text":"

In Real-time Frames Mode, you can easily change the video encoder according to your requirements by passing the -vcodec FFmpeg parameter as an attribute in the stream_params dictionary parameter. Additionally, you can specify additional properties, features, and optimizations for your system's GPU.

In this example, we will be using h264_vaapi as our Hardware Encoder and specifying the device hardware's location and compatible video filters by formatting them as attributes in the stream_params dictionary parameter.

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.

Checking VAAPI Support for Hardware Encoding

To use VAAPI (Video Acceleration API) as a hardware encoder in this example, follow these steps to ensure your FFmpeg supports VAAPI:

ffmpeg  -hide_banner -encoders | grep vaapi \n\n V..... h264_vaapi           H.264/AVC (VAAPI) (codec h264)\n V..... hevc_vaapi           H.265/HEVC (VAAPI) (codec hevc)\n V..... mjpeg_vaapi          MJPEG (VAAPI) (codec mjpeg)\n V..... mpeg2_vaapi          MPEG-2 (VAAPI) (codec mpeg2video)\n V..... vp8_vaapi            VP8 (VAAPI) (codec vp8)\n

Please read the FFmpeg Documentation carefully before passing any additional values to the stream_params parameter. Incorrect values may cause errors or result in no output.

DASHHLS
# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = VideoGear(source=0).start() \n\n# add various streams with custom Video Encoder and optimizations\nstream_params = {\n    \"-streams\": [\n        {\"-resolution\": \"1920x1080\", \"-video_bitrate\": \"4000k\"},  # Stream1: 1920x1080 at 4000kbs bitrate\n        {\"-resolution\": \"1280x720\", \"-framerate\": 30.0},  # Stream2: 1280x720 at 30fps\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream3: 640x360 at 60fps\n    ],\n    \"-vcodec\": \"h264_vaapi\", # define custom Video encoder\n    \"-vaapi_device\": \"/dev/dri/renderD128\", # define device location\n    \"-vf\": \"format=nv12,hwupload\",  # define video filters\n}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = VideoGear(source=0).start() \n\n# add various streams with custom Video Encoder and optimizations\nstream_params = {\n    \"-streams\": [\n        {\"-resolution\": \"1920x1080\", \"-video_bitrate\": \"4000k\"},  # Stream1: 1920x1080 at 4000kbs bitrate\n        {\"-resolution\": \"1280x720\", \"-framerate\": 30.0},  # Stream2: 1280x720 at 30fps\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream3: 640x360 at 60fps\n    ],\n    \"-vcodec\": \"h264_vaapi\", # define custom Video encoder\n    \"-vaapi_device\": \"/dev/dri/renderD128\", # define device location\n    \"-vf\": \"format=nv12,hwupload\",  # define video pixformat\n}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n

  1. In Real-time Frames Mode, the Primary Stream's framerate defaults to the value of the -input_framerate attribute, if defined. Otherwise, it will be set to 25 fps.\u00a0\u21a9\u21a9\u21a9

"},{"location":"gears/streamgear/ssm/overview/","title":"Overview","text":""},{"location":"gears/streamgear/ssm/overview/#streamgear-api-single-source-mode","title":"StreamGear API: Single-Source Mode","text":"Single-Source Mode generalized workflow"},{"location":"gears/streamgear/ssm/overview/#overview","title":"Overview","text":"

In this mode, StreamGear transcodes entire audio-video file (as opposed to frames-by-frame) into a sequence of multiple smaller chunks/segments for adaptive streaming.

This mode works exceptionally well when you're transcoding long-duration lossless videos(with audio) files for streaming that requires no interruptions. But on the downside, the provided source cannot be flexibly manipulated or transformed before sending onto FFmpeg Pipeline for processing.

SteamGear supports both MPEG-DASH (Dynamic Adaptive Streaming over HTTP, ISO/IEC 23009-1) and Apple HLS (HTTP Live Streaming) with this mode.

For this mode, StreamGear API provides exclusive transcode_source() method to easily process audio-video files into streamable chunks.

This mode can be easily activated by assigning suitable video path as input to -video_source attribute of stream_params dictionary parameter, during StreamGear initialization.

New in v0.2.2

Apple HLS support was added in v0.2.2.

Please Remember

"},{"location":"gears/streamgear/ssm/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through StreamGear Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/streamgear/ssm/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/streamgear/ssm/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/streamgear/ssm/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/streamgear/ssm/usage/","title":"Usage Examples","text":""},{"location":"gears/streamgear/ssm/usage/#streamgear-api-usage-examples-single-source-mode","title":"StreamGear API Usage Examples: Single-Source Mode","text":"

Important Information

DEPRECATION NOTICES for v0.3.3 and above 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:

After going through following Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/streamgear/ssm/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with StreamGear API in Single-Source Mode:

If input video-source (i.e. -video_source) contains any audio stream/channel, then it automatically gets mapped to all generated streams.

DASHHLS
# import required libraries\nfrom vidgear.gears import StreamGear\n\n# activate Single-Source Mode with valid video input\nstream_params = {\"-video_source\": \"foo.mp4\"}\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n# transcode source\nstreamer.transcode_source()\n# close\nstreamer.close()\n

After running this bare-minimum example, StreamGear will produce a Manifest file (dash_out.mpd) with streamable chunks, containing information about a Primary Stream with the same resolution and framerate as the input.

# import required libraries\nfrom vidgear.gears import StreamGear\n\n# activate Single-Source Mode with valid video input\nstream_params = {\"-video_source\": \"foo.mp4\"}\n# describe a suitable master playlist location/name and assign params\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", **stream_params)\n# transcode source\nstreamer.transcode_source()\n# close\nstreamer.close()\n

After running this bare-minimum example, StreamGear will produce a Master Playlist file (hls_out.mpd) with streamable chunks, containing information about a Primary Stream with the same resolution and framerate as the input.

"},{"location":"gears/streamgear/ssm/usage/#usage-with-additional-streams","title":"Usage with Additional Streams","text":"

In addition to the Primary Stream, you can easily generate any number of additional Secondary Streams with variable bitrate or spatial resolutions, using the exclusive -streams attribute of the stream_params dictionary parameter.

To generate Secondary Streams, add each desired resolution and bitrate/framerate as a list of dictionaries to the -streams attribute. StreamGear will handle the rest automatically. The complete example is as follows:

A more detailed information on -streams attribute can be found here \u27b6

If input video-source (i.e. -video_source) contains any audio stream/channel, then it automatically gets mapped to all generated streams without any extra efforts.

Important Information about -streams attribute

Always use the -streams attribute to define additional streams safely. Duplicate or incorrect definitions can break the transcoding pipeline and corrupt the output chunks.

DASHHLS
# import required libraries\nfrom vidgear.gears import StreamGear\n\n# activate Single-Source Mode and also define various streams\nstream_params = {\n    \"-video_source\": \"foo.mp4\",\n    \"-streams\": [\n        {\"-resolution\": \"1920x1080\", \"-video_bitrate\": \"4000k\"},  # Stream1: 1920x1080 at 4000kbs bitrate\n        {\"-resolution\": \"1280x720\", \"-framerate\": 30.0},  # Stream2: 1280x720 at 30fps framerate\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream3: 640x360 at 60fps framerate\n        {\"-resolution\": \"320x240\", \"-video_bitrate\": \"500k\"},  # Stream3: 320x240 at 500kbs bitrate\n    ],\n}\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n# transcode source\nstreamer.transcode_source()\n# close\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import StreamGear\n\n# activate Single-Source Mode and also define various streams\nstream_params = {\n    \"-video_source\": \"foo.mp4\",\n    \"-streams\": [\n        {\"-resolution\": \"1920x1080\", \"-video_bitrate\": \"4000k\"},  # Stream1: 1920x1080 at 4000kbs bitrate\n        {\"-resolution\": \"1280x720\", \"-framerate\": 30.0},  # Stream2: 1280x720 at 30fps framerate\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream3: 640x360 at 60fps framerate\n        {\"-resolution\": \"320x240\", \"-video_bitrate\": \"500k\"},  # Stream3: 320x240 at 500kbs bitrate\n    ],\n}\n# describe a suitable master playlist location/name and assign params\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", **stream_params)\n# transcode source\nstreamer.transcode_source()\n# close\nstreamer.close()\n

"},{"location":"gears/streamgear/ssm/usage/#usage-with-custom-audio-input","title":"Usage with Custom Audio-Input","text":"

In single source mode, by default, if the input video source (i.e., -video_source) contains audio, it gets automatically mapped to all generated streams. However, if you want to add a custom audio source, you can use the exclusive -audio attribute of the stream_params dictionary parameter.

To add a custom audio source, provide the path to your audio file as a string to the -audio attribute. The API will automatically validate and map the audio to all generated streams. The complete example is as follows:

Ensure the provided -audio audio source is compatible with the input video source (-video_source). Incompatibility can cause multiple errors or result in no output at all.

You can also assign a valid audio URL as input instead of a file path. More details can be found here \u27b6

DASHHLS
# import required libraries\nfrom vidgear.gears import StreamGear\n\n# activate Single-Source Mode and various streams, along with custom audio\nstream_params = {\n    \"-video_source\": \"foo.mp4\",\n    \"-streams\": [\n        {\"-resolution\": \"1280x720\", \"-video_bitrate\": \"4000k\"},  # Stream1: 1280x720 at 4000kbs bitrate\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream2: 640x360 at 60fps\n    ],\n    \"-audio\": \"/home/foo/foo1.aac\", # define custom audio-source\n    \"-acodec\": \"copy\", # define copy audio encoder\n}\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n# transcode source\nstreamer.transcode_source()\n# close\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import StreamGear\n\n# activate Single-Source Mode and various streams, along with custom audio\nstream_params = {\n    \"-video_source\": \"foo.mp4\",\n    \"-streams\": [\n        {\"-resolution\": \"1280x720\", \"-video_bitrate\": \"4000k\"},  # Stream1: 1280x720 at 4000kbs bitrate\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream2: 640x360 at 60fps\n    ],\n    \"-audio\": \"/home/foo/foo1.aac\",  # define custom audio-source\n    \"-acodec\": \"copy\", # define copy audio encoder\n}\n# describe a suitable master playlist location/name and assign params\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", **stream_params)\n# transcode source\nstreamer.transcode_source()\n# close\nstreamer.close()\n

"},{"location":"gears/streamgear/ssm/usage/#usage-with-variable-ffmpeg-parameters","title":"Usage with Variable FFmpeg Parameters","text":"

For fine-grained control over the transcoding process, StreamGear provides a highly extensible and flexible wrapper around FFmpeg library and access to almost all of its configurational parameter.

In this example, we'll use the H.265/HEVC video encoder and AAC audio encoder, apply various optimal FFmpeg configurational parameters.

This example assumes that the given input video source (-video_source) contains at least one audio stream.

This example is just conveying the idea on how to use FFmpeg's internal encoders/parameters with StreamGear API. You can use any FFmpeg parameter in the similar manner.

Please read the FFmpeg Documentation carefully before passing any additional values to the stream_params parameter. Incorrect values may cause errors or result in no output.

DASHHLS
# import required libraries\nfrom vidgear.gears import StreamGear\n\n# activate Single-Source Mode and various other parameters\nstream_params = {\n    \"-video_source\": \"foo.mp4\", # define Video-Source\n    \"-vcodec\": \"libx265\", # specify H.265/HEVC video encoder\n    \"-x265-params\": \"lossless=1\", # enables Lossless encoding\n    \"-bpp\": 0.15, # Bits-Per-Pixel(BPP), an Internal StreamGear parameter to ensure good quality of high motion scenes\n    \"-streams\": [\n        {\"-resolution\": \"640x360\", \"-video_bitrate\": \"4000k\"}, # Stream1: 1280x720 at 4000kbs bitrate\n        {\"-resolution\": \"320x240\", \"-framerate\": 60.0},  # Stream2: 640x360 at 60fps\n    ],\n    \"-acodec\": \"aac\", # specify AAC audio encoder\n}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"dash_out.mpd\", logging=True, **stream_params)\n# transcode source\nstreamer.transcode_source()\n# close\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import StreamGear\n\nstream_params = {\n    \"-video_source\": \"foo.mp4\", # define Video-Source\n    \"-vcodec\": \"libx265\", # specify H.265/HEVC video encoder\n    \"-x265-params\": \"lossless=1\", # enables Lossless encoding\n    \"-bpp\": 0.15, # Bits-Per-Pixel(BPP), an Internal StreamGear parameter to ensure good quality of high motion scenes\n    \"-streams\": [\n        {\"-resolution\": \"640x360\", \"-video_bitrate\": \"4000k\"}, # Stream1: 1280x720 at 4000kbs bitrate\n        {\"-resolution\": \"320x240\", \"-framerate\": 60.0},  # Stream2: 640x360 at 60fps\n    ],\n    \"-acodec\": \"aac\", # specify AAC audio encoder\n}\n\n# describe a suitable master playlist file location/name and assign params\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", logging=True, **stream_params)\n# transcode source\nstreamer.transcode_source()\n# close\nstreamer.close()\n

  1. In Real-time Frames Mode, the Primary Stream's framerate defaults to -input_framerate attribute value, if defined, else it will be 25fps.\u00a0\u21a9

"},{"location":"gears/videogear/overview/","title":"Overview","text":""},{"location":"gears/videogear/overview/#videogear-api","title":"VideoGear API","text":"VideoGear API's generalized workflow"},{"location":"gears/videogear/overview/#overview","title":"Overview","text":"

VideoGear API provides a special internal wrapper around VidGear's exclusive Video Stabilizer class.

VideoGear also acts as a Common Video-Capture API that provides internal access for both CamGear and PiGear APIs and their parameters with an exclusive enablePiCamera boolean flag.

VideoGear is ideal when you need to switch to different video sources without changing your code much. Also, it enables easy stabilization for various video-streams (real-time or not) with minimum effort and writing way fewer lines of code.

Helpful Tips

"},{"location":"gears/videogear/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through VideoGear Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/videogear/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/videogear/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/videogear/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/videogear/params/","title":"Parameters","text":""},{"location":"gears/videogear/params/#videogear-api-parameters","title":"VideoGear API Parameters","text":"

VideoGear acts as a Common Video-Capture API that provides internal access for both CamGear and PiGear APIs and their parameters.

"},{"location":"gears/videogear/params/#enablepicamera","title":"enablePiCamera","text":"

This parameter provide direct access to PiGear or CamGear APIs respectively in VideoGear. This means the if enablePiCamera flag is True, the PiGear API will be accessed, and if False, the CamGear API will be accessed.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

VideoGear(enablePiCamera=True) # enable access to PiGear API\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/videogear/params/#parameters-for-stabilizer-backend","title":"Parameters for Stabilizer Backend","text":"

Enable this backend with stabilize=True in VideoGear.

"},{"location":"gears/videogear/params/#stabilize","title":"stabilize","text":"

This parameter enable access to Stabilizer Class for stabilizing frames, i.e. can be set to True(to enable) or unset to False(to disable).

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

VideoGear(stabilize=True) # enable stablization\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/videogear/params/#options","title":"options","text":"

This parameter can be used in addition, to pass user-defined parameters supported by Stabilizer Class. These parameters can be formatted as this parameter's attribute.

Supported dictionary attributes for Stabilizer Class are:

"},{"location":"gears/videogear/params/#parameters-for-camgear-backend","title":"Parameters for CamGear backend","text":"

Enable this backend with enablePiCamera=False in VideoGear. Default is also False.

"},{"location":"gears/videogear/params/#source","title":"source","text":"

VideoGear API will throw RuntimeError if source provided is invalid.

This parameter defines the source for the input stream.

Data-Type: Based on input.

Default Value: Its default value is 0.

Its valid input can be one of the following:

"},{"location":"gears/videogear/params/#stream_mode","title":"stream_mode","text":"

This parameter controls the Stream Mode, .i.e if enabled(stream_mode=True), the VideoGear API will interpret the given source input as YouTube URL address.

Due to a FFmpeg bug that causes video to freeze frequently in OpenCV, It is advised to always use GStreamer backend (backend=cv2.CAP_GSTREAMER) for any livestreams (such as Twitch).

VideoGear automatically enforce GStreamer backend (backend=cv2.CAP_GSTREAMER) for YouTube-livestreams!

VideoGear will exit with RuntimeError for YouTube livestreams, if OpenCV is not compiled with GStreamer(>=v1.0.0) support. Checkout this FAQ for compiling OpenCV with GStreamer support.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

VideoGear(source='https://youtu.be/bvetuLwJIkA', stream_mode=True)\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/videogear/params/#backend","title":"backend","text":"

This parameter manually selects the backend for OpenCV's VideoCapture class (only if specified).

Data-Type: Integer

Default Value: Its default value is 0

Usage:

All supported backends are listed here \u27b6

Its value can be for e.g. backend = cv2.CAP_DSHOW for selecting Direct Show as backend:

VideoGear(source=0, backend = cv2.CAP_DSHOW)\n

"},{"location":"gears/videogear/params/#options_1","title":"options","text":"

This parameter provides the ability to alter various Source Tweak Parameters available within OpenCV's VideoCapture API properties.

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

All supported parameters are listed here \u27b6

The desired parameters can be passed to VideoGear API by formatting them as this parameter's attributes, as follows:

# formatting parameters as dictionary attributes\noptions = {\"CAP_PROP_FRAME_WIDTH\":320, \"CAP_PROP_FRAME_HEIGHT\":240, \"CAP_PROP_FPS\":60}\n# assigning it\nVideoGear(source=0, **options)\n

"},{"location":"gears/videogear/params/#parameters-for-pigear-backend","title":"Parameters for PiGear backend","text":"

Enable this backend with enablePiCamera=True in VideoGear.

"},{"location":"gears/videogear/params/#camera_num","title":"camera_num","text":"

This parameter selects the camera index to be used as the source, allowing you to drive these multiple cameras simultaneously from within a single Python session. Its value can only be zero or greater, otherwise, VideoGear API will throw ValueError for any negative value.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

# select Camera Module at index `1`\nVideoGear(enablePiCamera=True, camera_num=1)\n

The complete usage example demonstrating the usage of the camera_num parameter is available here \u27b6.

"},{"location":"gears/videogear/params/#resolution","title":"resolution","text":"

This parameter controls the resolution - a tuple (i.e. (width,height)) of two values giving the width and height of the output frames.

Make sure both width and height values should be at least 64.

When using the Picamera2 backend, the resolution parameter will be OVERRIDDEN, if the user explicitly defines the output_size property of the sensor configurational parameter.

Data-Type: Tuple

Default Value: Its default value is (640,480).

Usage:

VideoGear(enablePiCamera=True, resolution=(1280,720)) # sets 1280x720 resolution\n

"},{"location":"gears/videogear/params/#framerate","title":"framerate","text":"

This parameter controls the framerate of the source.

Data-Type: integer/float

Default Value: Its default value is 30.

Usage:

VideoGear(enablePiCamera=True, framerate=60) # sets 60fps framerate\n

"},{"location":"gears/videogear/params/#options_2","title":"options","text":"

This dictionary parameter in the internal PiGear API backend allows you to control various camera settings for both the picamera2 and legacy picamera backends and some internal API tasks. These settings include:

"},{"location":"gears/videogear/params/#a-configurational-camera-parameters","title":"A. Configurational Camera Parameters","text":""},{"location":"gears/videogear/params/#b-user-defined-parameters","title":"B. User-defined Parameters","text":"

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

The complete usage example demonstrating the usage of the options parameter is available here \u27b6.

You can format these user-defined and configurational parameters as attributes of this options dictionary parameter as follows:

New Picamera2 backendLegacy Picamera backend
# formulate various Picamera2 API parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"exposure_compensation\": 15,\n    \"sensor\": {\"output_size\": (480, 320)},  # !!! will override `resolution` !!!\n}\n\n# open pi video stream with defined parameters\nstream = VideoGear(enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options).start()\n
# formulate various Picamera API parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = VideoGear(enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options).start()\n

"},{"location":"gears/videogear/params/#common-parameters","title":"Common Parameters","text":"

These are common parameters that works with every backend in VideoGear.

"},{"location":"gears/videogear/params/#colorspace","title":"colorspace","text":"

This parameter selects the colorspace of the source stream.

Data-Type: String

Default Value: Its default value is None.

Usage:

All supported colorspace values are given here \u27b6

VideoGear(colorspace=\"COLOR_BGR2HSV\")\n

Its complete usage example is given here \u27b6

"},{"location":"gears/videogear/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

VideoGear(logging=True)\n

"},{"location":"gears/videogear/params/#time_delay","title":"time_delay","text":"

This parameter set the time delay (in seconds) before the VideoGear API start reading the frames. This delay is only required if the source required some warm-up delay before starting up.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

VideoGear(time_delay=1)  # set 1 seconds time delay\n

"},{"location":"gears/videogear/usage/","title":"Usage Examples","text":""},{"location":"gears/videogear/usage/#videogear-api-usage-examples","title":"VideoGear API Usage Examples:","text":"

After going through following Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/videogear/usage/#bare-minimum-usage-with-camgear-backend","title":"Bare-Minimum Usage with CamGear backend","text":"

VideoGear by default provides direct internal access to CamGear API.

Following is the bare-minimum code you need to access CamGear API with VideoGear:

# import required libraries\nfrom vidgear.gears import VideoGear\nimport cv2\n\n\n# open any valid video stream(for e.g `myvideo.avi` file)\nstream = VideoGear(source=\"myvideo.avi\").start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/videogear/usage/#bare-minimum-usage-with-pigear-backend","title":"Bare-Minimum Usage with PiGear backend","text":"

VideoGear contains a special enablePiCamera flag that when True provides internal access to PiGear API.

Following is the bare-minimum code you need to access PiGear API with VideoGear:

Under the hood, PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, PiGear API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this API, otherwise nothing will work.

# import required libraries\nfrom vidgear.gears import VideoGear\nimport cv2\n\n# enable enablePiCamera boolean flag to access PiGear API backend\nstream = VideoGear(enablePiCamera=True).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/videogear/usage/#using-videogear-with-video-stabilizer-backend","title":"Using VideoGear with Video Stabilizer backend","text":"

VideoGear API provides a special internal wrapper around VidGear's Exclusive Video Stabilizer class and provides easy way of activating stabilization for various video-streams (real-time or not) with its stabilize boolean parameter during initialization.

The usage example is as follows:

For a more detailed information on Video-Stabilizer Class, Read here \u27b6

The stabilizer might be slower for High-Quality/Resolution videos-frames.

# import required libraries\nfrom vidgear.gears import VideoGear\nimport numpy as np\nimport cv2\n\n# open any valid video stream with stabilization enabled(`stabilize = True`)\nstream_stab = VideoGear(source=\"test.mp4\", stabilize=True).start()\n\n# loop over\nwhile True:\n\n    # read stabilized frames\n    frame_stab = stream_stab.read()\n\n    # check for stabilized frame if None-type\n    if frame_stab is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Stabilized Output\", frame_stab)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close streams\nstream_stab.stop()\n

"},{"location":"gears/videogear/usage/#advanced-videogear-usage-with-camgear-backend","title":"Advanced VideoGear usage with CamGear Backend","text":"

VideoGear provides internal access to both CamGear and PiGear APIs, and thereby all additional parameters of PiGear API or CamGear API are also easily accessible within VideoGear API.

The usage example of VideoGear API with Variable Camera Properties is as follows:

This example demonstrates how to use the VideoGear API in a similar manner to the CamGear's example for controlling variable source properties. Any CamGear usage example can be implemented using the VideoGear API in a similar way.

All the supported Source Tweak Parameters can be found here \u27b6

# import required libraries\nfrom vidgear.gears import VideoGear\nimport cv2\n\n\n# define suitable tweak parameters for your stream.\noptions = {\n    \"CAP_PROP_FRAME_WIDTH\": 320, # resolution 320x240\n    \"CAP_PROP_FRAME_HEIGHT\": 240,\n    \"CAP_PROP_FPS\": 60, # framerate 60fps\n}\n\n# To open live video stream on webcam at first index(i.e. 0) \n# device and apply source tweak parameters\nstream = VideoGear(source=0, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/videogear/usage/#advanced-videogear-usage-with-pigear-backend","title":"Advanced VideoGear usage with PiGear Backend","text":"

VideoGear provides internal access to both CamGear and PiGear APIs, and thereby all additional parameters of PiGear API or CamGear API are also easily accessible within VideoGear API.

The usage example of VideoGear API with Variable Camera Properties is as follows:

This example demonstrates how to use the VideoGear API in a similar manner to the PiGear's example for using variable camera properties. Any PiGear usage example can be implemented using the VideoGear API in a similar way.

Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this backend, otherwise nothing will work.

New Picamera2 backendLegacy Picamera backend
# import required libraries\nfrom vidgear.gears import VideoGear\nfrom libcamera import Transform\nimport cv2\n\n# formulate various Picamera2 API \n# configurational parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream with defined parameters\nstream = VideoGear(enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import required libraries\nfrom vidgear.gears import VideoGear\nimport cv2\n\n# formulate various Picamera API \n# configurational parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = VideoGear(enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/videogear/usage/#using-videogear-with-colorspace-manipulation","title":"Using VideoGear with Colorspace Manipulation","text":"

VideoGear API also supports Colorspace Manipulation but NOT Direct like other VideoCapture Gears.

Important: color_space global variable is NOT Supported in VideoGear API

In following example code, we will convert source colorspace to HSV on initialization:

# import required libraries\nfrom vidgear.gears import VideoGear\nimport cv2\n\n# Open any source of your choice, like Webcam first index(i.e. 0) and change its colorspace to `HSV`\nstream = VideoGear(source=0, colorspace=\"COLOR_BGR2HSV\", logging=True).start()\n\n# loop over\nwhile True:\n\n    # read HSV frames\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the HSV frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for key if pressed\n    key = cv2.waitKey(1) & 0xFF\n\n    # check for 'q' key is pressed\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/videogear/usage/#bonus-examples","title":"Bonus Examples","text":"

Checkout more advanced VideoGear examples with unusual configuration here \u27b6

"},{"location":"gears/webgear/advanced/","title":"Advanced Usages","text":""},{"location":"gears/webgear/advanced/#webgear-api-advanced-usage","title":"WebGear API Advanced Usage:","text":"

This is a continuation of the WebGear doc \u27b6. Thereby, It's advised to first get familiarize with this API, and its requirements.

After going through following Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/webgear/advanced/#using-webgear-with-variable-colorspace","title":"Using WebGear with Variable Colorspace","text":"

WebGear by default only supports \"BGR\" colorspace frames as input, but you can use jpeg_compression_colorspace string attribute through its options dictionary parameter to specify incoming frames colorspace.

Let's implement a bare-minimum example using WebGear, where we will be sending GRAY frames to client browser:

New in v0.2.2

This example was added in v0.2.2.

This example works in conjunction with Source ColorSpace manipulation for VideoCapture Gears \u27b6

Supported jpeg_compression_colorspace colorspace values are RGB, BGR, RGBX, BGRX, XBGR, XRGB, GRAY, RGBA, BGRA, ABGR, ARGB, CMYK. More information can be found here \u27b6

# import required libraries\nimport uvicorn\nfrom vidgear.gears.asyncio import WebGear\n\n# various performance tweaks and enable grayscale input\noptions = {\n    \"frame_size_reduction\": 25,\n    \"jpeg_compression_colorspace\": \"GRAY\",  # set grayscale\n    \"jpeg_compression_quality\": 90,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# initialize WebGear app and change its colorspace to grayscale\nweb = WebGear(\n    source=\"foo.mp4\", colorspace=\"COLOR_BGR2GRAY\", logging=True, **options\n)\n\n# run this app on Uvicorn server at address http://0.0.0.0:8000/\nuvicorn.run(web(), host=\"0.0.0.0\", port=8000)\n\n# close app safely\nweb.shutdown()\n

And that's all, Now you can see output at http://localhost:8000/ address on your local machine.

"},{"location":"gears/webgear/advanced/#using-webgear-with-a-custom-sourceopencv","title":"Using WebGear with a Custom Source(OpenCV)","text":"New in v0.2.1

This example was added in v0.2.1.

WebGear allows you to easily define your own custom Source that you want to use to transform your frames before sending them onto the browser.

JPEG Frame-Compression and all of its performance enhancing attributes are disabled with a Custom Source!

Let's implement a bare-minimum example with a Custom Source using WebGear API and OpenCV:

# import necessary libs\nimport uvicorn, asyncio, cv2\nfrom vidgear.gears.asyncio import WebGear\nfrom vidgear.gears.asyncio.helper import reducer\n\n# initialize WebGear app without any source\nweb = WebGear(logging=True)\n\n# create your own custom frame producer\nasync def my_frame_producer():\n\n    # !!! define your own video source here !!!\n    # Open any video stream such as live webcam \n    # video stream on first index(i.e. 0) device\n    stream = cv2.VideoCapture(0)\n    # loop over frames\n    while True:\n        # read frame from provided source\n        (grabbed, frame) = stream.read()\n        # break if NoneType\n        if not grabbed:\n            break\n\n        # do something with your OpenCV frame here\n\n        # reducer frames size if you want more performance otherwise comment this line\n        frame = await reducer(frame, percentage=30, interpolation=cv2.INTER_AREA)  # reduce frame by 30%\n        # handle JPEG encoding\n        encodedImage = cv2.imencode(\".jpg\", frame)[1].tobytes()\n        # yield frame in byte format\n        yield (b\"--frame\\r\\nContent-Type:image/jpeg\\r\\n\\r\\n\" + encodedImage + b\"\\r\\n\")\n        await asyncio.sleep(0)\n    # close stream\n    stream.release()\n\n\n# add your custom frame producer to config\nweb.config[\"generator\"] = my_frame_producer\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

And that's all, Now you can see output at http://localhost:8000/ address.

"},{"location":"gears/webgear/advanced/#using-webgear-with-custom-mounting-points","title":"Using WebGear with Custom Mounting Points","text":"

With our highly extensible WebGear API, you can add your own mounting points, where additional files located, as follows:

# import libs\nimport uvicorn\nfrom starlette.routing import Mount\nfrom starlette.staticfiles import StaticFiles\nfrom vidgear.gears.asyncio import WebGear\n\n# various performance tweaks\noptions = {\n    \"frame_size_reduction\": 40,\n    \"jpeg_compression_quality\": 80,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": False,\n}\n\n# initialize WebGear app\nweb = WebGear(\n    source=\"foo.mp4\", logging=True, **options\n)  # enable source i.e. `test.mp4` and enable `logging` for debugging\n\n# append new route i.e. mount another folder called `test` located at `/home/foo/.vidgear/test` directory\nweb.routes.append(\n    Mount(\"/test\", app=StaticFiles(directory=\"/home/foo/.vidgear/test\"), name=\"test\")\n)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

Then you can use this folder in your HTML page, to host data-files. For example, if we have jQuery script jquery-3.3.1.slim.min.js in this folder and want to integrate it, then, we can do something like this:

<script src=\"{{ url_for('test', path='jquery-3.3.1.slim.min.js') }}\"></script>\n

"},{"location":"gears/webgear/advanced/#using-webgear-with-custom-webpage-routes","title":"Using WebGear with Custom Webpage Routes","text":"

With Webgear's flexible API, you can even add your additional HTML Static webpages without any extra efforts.

Suppose we want to add a simple hello world webpage to our WebGear server. So let's create a bare-minimum hello.html file with HTML code as follows:

<html>\n   <header>\n      <title>This is Hello world page</title>\n   </header>\n   <body>\n      <h1>Hello World</h1>\n      <p>how ya doing?</p>\n   </body>\n</html>\n

Then in our application code, we can integrate this webpage route, as follows:

# import libs\nimport uvicorn, asyncio\nfrom starlette.templating import Jinja2Templates\nfrom starlette.routing import Route\nfrom vidgear.gears.asyncio import WebGear\n\n# Build out Jinja2 template render at `/home/foo/.vidgear/custom_template` path in which our `hello.html` file is located\ntemplate = Jinja2Templates(directory=\"/home/foo/.vidgear/custom_template\")\n\n# render and return our webpage template\nasync def hello_world(request):\n    page = \"hello.html\"\n    context = {\"request\": request}\n    return template.TemplateResponse(page, context)\n\n\n# add various performance tweaks as usual\noptions = {\n    \"frame_size_reduction\": 40,\n    \"jpeg_compression_quality\": 80,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": False,\n}\n\n# initialize WebGear app with a valid source\nweb = WebGear(\n    source=\"/home/foo/foo1.mp4\", logging=True, **options\n)  # enable source i.e. `test.mp4` and enable `logging` for debugging\n\n# append new route to point our rendered webpage\nweb.routes.append(Route(\"/hello\", endpoint=hello_world))\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n
And that's all, Now you can see output at http://localhost:8000/hello address.

"},{"location":"gears/webgear/advanced/#using-webgear-with-middlewares","title":"Using WebGear with MiddleWares","text":"

WebGear natively supports ASGI middleware classes with Starlette for implementing behavior that is applied across your entire ASGI application easily.

New in v0.2.2

This example was added in v0.2.2.

All supported middlewares can be found here \u27b6

For this example, let's use CORSMiddleware for implementing appropriate CORS headers to outgoing responses in our application in order to allow cross-origin requests from browsers, as follows:

The default parameters used by the CORSMiddleware implementation are restrictive by default, so you'll need to explicitly enable particular origins, methods, or headers, in order for browsers to be permitted to use them in a Cross-Domain context.

Starlette provides several arguments for enabling origins, methods, or headers for CORSMiddleware API. More information can be found here \u27b6

# import libs\nimport uvicorn, asyncio\nfrom starlette.middleware import Middleware\nfrom starlette.middleware.cors import CORSMiddleware\nfrom vidgear.gears.asyncio import WebGear\n\n# add various performance tweaks as usual\noptions = {\n    \"frame_size_reduction\": 40,\n    \"jpeg_compression_quality\": 80,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": False,\n}\n\n# initialize WebGear app with a valid source\nweb = WebGear(\n    source=\"/home/foo/foo1.mp4\", logging=True, **options\n)  # enable source i.e. `test.mp4` and enable `logging` for debugging\n\n# define and assign suitable cors middlewares\nweb.middleware = [\n    Middleware(\n        CORSMiddleware,\n        allow_origins=[\"*\"],\n        allow_credentials=True,\n        allow_methods=[\"*\"],\n        allow_headers=[\"*\"],\n    )\n]\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n
And that's all, Now you can see output at http://localhost:8000 address.

"},{"location":"gears/webgear/advanced/#rules-for-altering-webgear-files-and-folders","title":"Rules for Altering WebGear Files and Folders","text":"

WebGear gives us complete freedom of altering data files generated in Auto-Generation Process, But you've to keep the following rules in mind:

"},{"location":"gears/webgear/advanced/#rules-for-altering-data-files","title":"Rules for Altering Data Files","text":""},{"location":"gears/webgear/advanced/#rules-for-altering-data-folders","title":"Rules for Altering Data Folders","text":""},{"location":"gears/webgear/advanced/#bonus-examples","title":"Bonus Examples","text":"

Checkout more advanced WebGear examples with unusual configuration here \u27b6

"},{"location":"gears/webgear/overview/","title":"Overview","text":""},{"location":"gears/webgear/overview/#webgear-api","title":"WebGear API","text":"WebGear API's Video Server running at http://localhost:8000/ address."},{"location":"gears/webgear/overview/#overview","title":"Overview","text":"

WebGear is a powerful ASGI Video-Broadcaster API ideal for transmitting Motion-JPEG-frames from a single source to multiple recipients via the browser.

WebGear API works on Starlette's ASGI application and provides a highly extensible and flexible async wrapper around its complete framework. WebGear can flexibly interact with Starlette's ecosystem of shared middleware, mountable applications, Response classes, Routing tables, Static Files, Templating engine(with Jinja2), etc.

WebGear API uses an intraframe-only compression scheme under the hood where the sequence of video-frames are first encoded as JPEG-DIB (JPEG with Device-Independent Bit compression) and then streamed over HTTP using Starlette's Multipart Streaming Response and a Uvicorn ASGI Server. This method imposes lower processing and memory requirements, but the quality is not the best, since JPEG compression is not very efficient for motion video.

In layman's terms, WebGear acts as a powerful Video Broadcaster that transmits live video-frames to any web-browser in the network. Additionally, WebGear API also provides internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs, thereby granting it exclusive power for transferring frames incoming from any source to the network.

"},{"location":"gears/webgear/overview/#data-files-auto-generation-workflow-for-webgear","title":"Data-Files Auto-Generation WorkFlow for WebGear","text":"Disabling Auto-Generation process in WebGear

Starting with vidgear v0.3.0, you can now completely disable Auto-Generation process in WebGear API using skip_generate_webdata optional boolean attribute. When {skip_generate_webdata:True}, no default data files will be downloaded or validated during initialization.

Only /video route is available when {skip_generate_webdata:True} in WebGear API. All other default routes will be JSONResponses with 404/500 status codes.

Customizing default video endpoint path

Starting with vidgear v0.3.1, you can change default /video video endpoint path to any alphanumeric string value, using custom_video_endpoint optional string attribute. For example:

Only alphanumeric string with no space in between are allowed as custom_video_endpoint value. Any other value will be discarded.

WebGear's Default Theme which expects only default /video video endpoint path, will fail to work, if it is customized to any other value using this custom_video_endpoint attribute.

# custom alphanumeric video endpoint string\noptions = {\"custom_video_endpoint\": \"xyz\"}\n\n# initialize WebGear app\nweb = WebGear(source=\"foo.mp4\", logging=True, **options)\n
Hence, default video endpoint will now be available at /xyz path.

On initializing WebGear API, it automatically checks for three critical data files(i.e index.html, 404.html & 500.html) inside the templates folder of the webgear directory at the default location which gives rise to the following two possible scenario:

"},{"location":"gears/webgear/overview/#default-location","title":"Default Location","text":"

Identifying Default location

You can set logging=True during initialization, for easily identifying the selected default location, which will be something like this on a Linux machine:

WebGear :: DEBUG :: `/home/foo/.vidgear` is the default location for saving WebGear data-files.\n
"},{"location":"gears/webgear/overview/#auto-generation-process","title":"Auto-Generation process","text":"

Info

"},{"location":"gears/webgear/overview/#webgears-default-template","title":"WebGear's Default Template","text":"New in v0.2.1

New Standalone WebGear's Default Theme was added in v0.2.1.

The WebGear API by default uses simple & elegant WebGear's Default Theme which looks like something as follows:

"},{"location":"gears/webgear/overview/#indexhtml","title":"Index.html","text":"

Can be accessed by visiting WebGear app server, running at http://localhost:8000/:

"},{"location":"gears/webgear/overview/#404html","title":"404.html","text":"

Appears when respective URL is not found, for example http://localhost:8000/ok:

"},{"location":"gears/webgear/overview/#500html","title":"500.html","text":"

Appears when an API Error is encountered:

If logging is enabled and an error occurs, then instead of displaying this 500 handler, WebGear will respond with a traceback response.

"},{"location":"gears/webgear/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through WebGear Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/webgear/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/webgear/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/webgear/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/webgear/params/","title":"Parameters","text":""},{"location":"gears/webgear/params/#webgear-api-parameters","title":"WebGear API Parameters","text":"

WebGear provides a special internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs and their parameters.

"},{"location":"gears/webgear/params/#enablepicamera","title":"enablePiCamera","text":"

This parameter provide direct access to PiGear or CamGear APIs respectively in WebGear. This means the if enablePiCamera flag is True, the PiGear API will be accessed, and if False, the CamGear API will be accessed.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

WebGear(enablePiCamera=True) # enable access to PiGear API\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/webgear/params/#options","title":"options","text":"

This parameter can be used to pass user-defined parameter to WebGear API by formatting them as this parameter's attribute.

Data-Type: Dictionary

Default Value: Its default value is {}

"},{"location":"gears/webgear/params/#webgear-specific-attributes","title":"WebGear Specific attributes","text":"

"},{"location":"gears/webgear/params/#parameters-for-stabilizer-backend","title":"Parameters for Stabilizer Backend","text":"

Enable this backend with stabilize=True in WebGear.

"},{"location":"gears/webgear/params/#stabilize","title":"stabilize","text":"

This parameter enable access to Stabilizer Class for stabilizing frames, i.e. can be set to True(to enable) or unset to False(to disable).

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

WebGear(stabilize=True) # enable stablization\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/webgear/params/#options_1","title":"options","text":"

This parameter can be used in addition, to pass user-defined parameters supported by Stabilizer Class. These parameters can be formatted as this parameter's attribute.

Supported dictionary attributes for Stabilizer Class are:

"},{"location":"gears/webgear/params/#parameters-for-camgear-backend","title":"Parameters for CamGear backend","text":"

Enable this backend with enablePiCamera=False in WebGear. Default is also False.

"},{"location":"gears/webgear/params/#source","title":"source","text":"

WebGear API will throw RuntimeError if source provided is invalid.

This parameter defines the source for the input stream.

Data-Type: Based on input.

Default Value: Its default value is 0.

Its valid input can be one of the following:

"},{"location":"gears/webgear/params/#stream_mode","title":"stream_mode","text":"

This parameter controls the Stream Mode, .i.e if enabled(stream_mode=True), the CamGear API will interpret the given source input as YouTube URL address.

Due to a FFmpeg bug that causes video to freeze frequently in OpenCV, It is advised to always use GStreamer backend for any livestream videos. Checkout this FAQ for compiling OpenCV with GStreamer support.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

Supported Streaming Websites

The complete list of all supported Streaming Websites URLs can be found here \u27b6

WebGear(source='https://youtu.be/bvetuLwJIkA', stream_mode=True)\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/webgear/params/#backend","title":"backend","text":"

This parameter manually selects the backend for OpenCV's VideoCapture class (only if specified).

Data-Type: Integer

Default Value: Its default value is 0

Usage:

All supported backends are listed here \u27b6

Its value can be for e.g. backend = cv2.CAP_DSHOW for selecting Direct Show as backend:

WebGear(source=0, backend = cv2.CAP_DSHOW)\n

"},{"location":"gears/webgear/params/#options_2","title":"options","text":"

This parameter provides the ability to alter various Source Tweak Parameters available within OpenCV's VideoCapture API properties.

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

All supported parameters are listed here \u27b6

The desired parameters can be passed to WebGear API by formatting them as this parameter's attributes, as follows:

# formatting parameters as dictionary attributes\noptions = {\"CAP_PROP_FRAME_WIDTH\":320, \"CAP_PROP_FRAME_HEIGHT\":240, \"CAP_PROP_FPS\":60}\n# assigning it\nWebGear(source=0, **options)\n

"},{"location":"gears/webgear/params/#parameters-for-pigear-backend","title":"Parameters for PiGear backend","text":"

Enable this backend with enablePiCamera=True in WebGear.

"},{"location":"gears/webgear/params/#camera_num","title":"camera_num","text":"

This parameter selects the camera index to be used as the source, allowing you to drive these multiple cameras simultaneously from within a single Python session. Its value can only be zero or greater, otherwise, WebGear API will throw ValueError for any negative value.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

# select Camera Module at index `1`\nWebGear(enablePiCamera=True, camera_num=1)\n

The complete usage example demonstrating the usage of the camera_num parameter is available here \u27b6.

"},{"location":"gears/webgear/params/#resolution","title":"resolution","text":"

This parameter controls the resolution - a tuple (i.e. (width,height)) of two values giving the width and height of the output frames.

Make sure both width and height values should be at least 64.

When using the Picamera2 backend, the resolution parameter will be OVERRIDDEN, if the user explicitly defines the output_size property of the sensor configurational parameter.

Data-Type: Tuple

Default Value: Its default value is (640,480).

Usage:

WebGear(enablePiCamera=True, resolution=(1280,720)) # sets 1280x720 resolution\n

"},{"location":"gears/webgear/params/#framerate","title":"framerate","text":"

This parameter controls the framerate of the source.

Data-Type: integer/float

Default Value: Its default value is 30.

Usage:

WebGear(enablePiCamera=True, framerate=60) # sets 60fps framerate\n

"},{"location":"gears/webgear/params/#options_3","title":"options","text":"

This dictionary parameter in the internal PiGear API backend allows you to control various camera settings for both the picamera2 and legacy picamera backends and some internal API tasks. These settings include:

"},{"location":"gears/webgear/params/#a-configurational-camera-parameters","title":"A. Configurational Camera Parameters","text":""},{"location":"gears/webgear/params/#b-user-defined-parameters","title":"B. User-defined Parameters","text":"

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

The complete usage example demonstrating the usage of the options parameter is available here \u27b6.

You can format these user-defined and configurational parameters as attributes of this options dictionary parameter as follows:

New Picamera2 backendLegacy Picamera backend
# formulate various Picamera2 API parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"exposure_compensation\": 15,\n    \"sensor\": {\"output_size\": (480, 320)},  # !!! will override `resolution` !!!\n}\n\n# open pi video stream with defined parameters\nstream = WebGear(enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options).start()\n
# formulate various Picamera API parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = WebGear(enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options).start()\n

"},{"location":"gears/webgear/params/#common-parameters","title":"Common Parameters","text":"

These are common parameters that works with every backend in WebGear.

"},{"location":"gears/webgear/params/#colorspace","title":"colorspace","text":"

This parameter selects the colorspace of the source stream.

Data-Type: String

Default Value: Its default value is None.

Usage:

All supported colorspace values are given here \u27b6

WebGear(colorspace=\"COLOR_BGR2HSV\")\n

Its complete usage example is given here \u27b6

"},{"location":"gears/webgear/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

WebGear(logging=True)\n

"},{"location":"gears/webgear/params/#time_delay","title":"time_delay","text":"

This parameter set the time delay (in seconds) before the WebGear API start reading the frames. This delay is only required if the source required some warm-up delay before starting up.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

WebGear(time_delay=1)  # set 1 seconds time delay\n

"},{"location":"gears/webgear/usage/","title":"Usage Examples","text":""},{"location":"gears/webgear/usage/#webgear-api-usage-examples","title":"WebGear API Usage Examples:","text":""},{"location":"gears/webgear/usage/#requirements","title":"Requirements","text":""},{"location":"gears/webgear/usage/#installation-with-asyncio-support","title":"Installation with Asyncio Support","text":"

WebGear API is the part of asyncio package of VidGear, thereby you need to install VidGear with asyncio support as follows:

pip install vidgear[asyncio]\n
"},{"location":"gears/webgear/usage/#asgi-server","title":"ASGI Server","text":"

You'll also need to install an ASGI Server to run following WebGear usage examples, and by default WebGear ships the state-of-the-art uvicorn Server. But you can also use other ASGI server such as daphne, or hypercorn with it.

"},{"location":"gears/webgear/usage/#performance-enhancements","title":"Performance Enhancements","text":"

WebGear provides certain performance enhancing attributes for its options dictionary parameter to cope with performance-throttling.

Performance Enhancing Attributes

"},{"location":"gears/webgear/usage/#bare-minimum-usage-with-performance-enhancements","title":"Bare-Minimum Usage with Performance Enhancements","text":"

Let's implement our Bare-Minimum usage example with these Performance Enhancing Attributes \u27b6 for speeding up the output.

"},{"location":"gears/webgear/usage/#running-programmatically","title":"Running Programmatically","text":"

You can access and run WebGear VideoStreamer Server programmatically in your python script in just a few lines of code, as follows:

For accessing WebGear on different Client Devices on the network, use \"0.0.0.0\" as host value instead of \"localhost\" on Host Machine. More information can be found here \u27b6

# import required libraries\nimport uvicorn\nfrom vidgear.gears.asyncio import WebGear\n\n# various performance tweaks\noptions = {\n    \"frame_size_reduction\": 40,\n    \"jpeg_compression_quality\": 80,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": False,\n}\n\n# initialize WebGear app\nweb = WebGear(source=\"foo.mp4\", logging=True, **options)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

which can be accessed on any browser on your machine at http://localhost:8000/.

"},{"location":"gears/webgear/usage/#running-from-terminal","title":"Running from Terminal","text":"

You can also access and run WebGear Server directly from the terminal commandline. The following command will run a WebGear VideoStreamer server at http://localhost:8000/:

Make sure your PYTHON_PATH is set to python 3.7+ versions only.

If you're using --options/-op flag, then kindly wrap your dictionary value in single '' quotes.

python3 -m vidgear.gears.asyncio --source test.avi --logging True --options '{\"frame_size_reduction\": 50, \"jpeg_compression_quality\": 80, \"jpeg_compression_fastdct\": True, \"jpeg_compression_fastupsample\": False}'\n

which can also be accessed on any browser on the network at http://localhost:8000/.

Advanced Usage from Terminal

You can run python3 -m vidgear.gears.asyncio -h help command to see all the advanced settings, as follows:

usage: python -m vidgear.gears.asyncio [-h] [-m MODE] [-s SOURCE] [-ep ENABLEPICAMERA] [-S STABILIZE]\n            [-cn CAMERA_NUM] [-yt stream_mode] [-b BACKEND] [-cs COLORSPACE]\n            [-r RESOLUTION] [-f FRAMERATE] [-td TIME_DELAY]\n            [-ip IPADDRESS] [-pt PORT] [-l LOGGING] [-op OPTIONS]\n\nRuns WebGear/WebGear_RTC Video Server through terminal.\n\noptional arguments:\n  -h, --help            show this help message and exit\n  -m {mjpeg,webrtc}, --mode {mjpeg,webrtc}\n                        Whether to use \"MJPEG\" or \"WebRTC\" mode for streaming.\n  -s SOURCE, --source SOURCE\n                        Path to input source for CamGear API.\n  -ep ENABLEPICAMERA, --enablePiCamera ENABLEPICAMERA\n                        Sets the flag to access PiGear(if True) or otherwise\n                        CamGear API respectively.\n  -S STABILIZE, --stabilize STABILIZE\n                        Enables/disables real-time video stabilization.\n  -cn CAMERA_NUM, --camera_num CAMERA_NUM\n                        Sets the camera module index that will be used by\n                        PiGear API.\n  -yt STREAM_MODE, --stream_mode STREAM_MODE\n                        Enables YouTube Mode in CamGear API.\n  -b BACKEND, --backend BACKEND\n                        Sets the backend of the video source in CamGear API.\n  -cs COLORSPACE, --colorspace COLORSPACE\n                        Sets the colorspace of the output video stream.\n  -r RESOLUTION, --resolution RESOLUTION\n                        Sets the resolution (width,height) for camera module\n                        in PiGear API.\n  -f FRAMERATE, --framerate FRAMERATE\n                        Sets the framerate for camera module in PiGear API.\n  -td TIME_DELAY, --time_delay TIME_DELAY\n                        Sets the time delay(in seconds) before start reading\n                        the frames.\n  -ip IPADDRESS, --ipaddress IPADDRESS\n                        Uvicorn binds the socket to this ipaddress.\n  -pt PORT, --port PORT\n                        Uvicorn binds the socket to this port.\n  -l LOGGING, --logging LOGGING\n                        Enables/disables error logging, essential for\n                        debugging.\n  -op OPTIONS, --options OPTIONS\n                        Sets the parameters supported by APIs(whichever being\n                        accessed) to the input videostream, But make sure to\n                        wrap your dict value in single or double quotes.\n

"},{"location":"gears/webgear_rtc/advanced/","title":"Advanced Usages","text":""},{"location":"gears/webgear_rtc/advanced/#webgear_rtc-api-advanced-usage","title":"WebGear_RTC API Advanced Usage:","text":"

This is a continuation of the WebGear_RTC doc \u27b6. Thereby, It's advised to first get familiarize with this API, and its requirements.

After going through following Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/webgear_rtc/advanced/#using-webgear_rtc-as-real-time-broadcaster","title":"Using WebGear_RTC as Real-time Broadcaster","text":"

WebGear_RTC by default only supports one-to-one peer connection with a single consumer or client. But you can use enable_live_broadcast boolean attribute through its options dictionary parameter to easily enable live broadcast/stream to multiple peer consumers/clients at the same time.

Let's implement a bare-minimum example using WebGear_RTC as Real-time Broadcaster:

enable_infinite_frames is enforced by default with this(enable_live_broadcast) attribute.

For accessing WebGear_RTC on different Client Devices on the network, we use \"0.0.0.0\" as host value instead of \"localhost\" on Host Machine. More information can be found here \u27b6

# import required libraries\nimport uvicorn\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# various performance tweaks and enable live broadcasting\noptions = {\n    \"frame_size_reduction\": 25,\n    \"enable_live_broadcast\": True,\n}\n\n# initialize WebGear_RTC app\nweb = WebGear_RTC(source=\"foo.mp4\", logging=True, **options)\n\n# run this app on Uvicorn server at address http://0.0.0.0:8000/\nuvicorn.run(web(), host=\"0.0.0.0\", port=8000)\n\n# close app safely\nweb.shutdown()\n

And that's all, Now you can see output at http://localhost:8000/ address on your local machine.

"},{"location":"gears/webgear_rtc/advanced/#using-webgear_rtc-with-a-custom-sourceopencv","title":"Using WebGear_RTC with a Custom Source(OpenCV)","text":"

WebGear_RTC provides custom_stream attribute with its options parameter that allows you to easily define your own Custom Streaming Class with suitable source that you want to use to transform your frames before sending them onto the browser.

Let's implement a bare-minimum example with a Custom Source using WebGear_RTC API and OpenCV:

New in v0.2.4

This implementation was added in v0.2.4.

Auto-Reconnection or Auto-Refresh works out-of-the-box with this implementation.

Make sure your Custom Streaming Class at-least implements read() and stop() methods as shown in following example, otherwise WebGear_RTC will throw ValueError!

Using Vidgear's VideoCapture APIs instead of OpenCV

You can directly replace Custom Streaming Class(Custom_Stream_Class in following example) with any VideoCapture APIs. These APIs implements read() and stop() methods by-default, so they're also supported out-of-the-box.

See this example \u27b6 for more information.

# import necessary libs\nimport uvicorn, cv2\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# create your own custom streaming class\nclass Custom_Stream_Class:\n    \"\"\"\n    Custom Streaming using OpenCV\n    \"\"\"\n\n    def __init__(self, source=0):\n\n        # !!! define your own video source here !!!\n        self.source = cv2.VideoCapture(source)\n\n        # define running flag\n        self.running = True\n\n    def read(self):\n\n        # don't forget this function!!!\n\n        # check if source was initialized or not\n        if self.source is None:\n            return None\n        # check if we're still running\n        if self.running:\n            # read frame from provided source\n            (grabbed, frame) = self.source.read()\n            # check if frame is available\n            if grabbed:\n\n                # do something with your OpenCV frame here\n\n                # lets convert frame to gray for this example\n                gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n\n                # return our gray frame\n                return gray\n            else:\n                # signal we're not running now\n                self.running = False\n        # return None-type\n        return None\n\n    def stop(self):\n\n        # don't forget this function!!!\n\n        # flag that we're not running\n        self.running = False\n        # close stream\n        if not self.source is None:\n            self.source.release()\n\n# assign your Custom Streaming Class with adequate source (for e.g. foo.mp4) \n# to `custom_stream` attribute in options parameter\noptions = {\"custom_stream\": Custom_Stream_Class(source=\"foo.mp4\")}\n\n# initialize WebGear_RTC app without any source\nweb = WebGear_RTC(logging=True, **options)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

And that's all, Now you can see output at http://localhost:8000/ address.

"},{"location":"gears/webgear_rtc/advanced/#using-webgear_rtc-with-custom-mounting-points","title":"Using WebGear_RTC with Custom Mounting Points","text":"

With our highly extensible WebGear_RTC API, you can add your own mounting points, where additional files located, as follows:

# import libs\nimport uvicorn\nfrom starlette.routing import Mount\nfrom starlette.staticfiles import StaticFiles\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# various performance tweaks\noptions = {\n    \"frame_size_reduction\": 25,\n}\n\n# initialize WebGear_RTC app\nweb = WebGear_RTC(\n    source=\"foo.mp4\", logging=True, **options\n)  # enable source i.e. `test.mp4` and enable `logging` for debugging\n\n# append new route i.e. mount another folder called `test` located at `/home/foo/.vidgear/test` directory\nweb.routes.append(\n    Mount(\"/test\", app=StaticFiles(directory=\"/home/foo/.vidgear/test\"), name=\"test\")\n)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

Then you can use this folder in your HTML page, to host data-files. For example, if we have jQuery script jquery-3.3.1.slim.min.js in this folder and want to integrate it, then, we can do something like this:

<script src=\"{{ url_for('test', path='jquery-3.3.1.slim.min.js') }}\"></script>\n

"},{"location":"gears/webgear_rtc/advanced/#using-webgear_rtc-with-custom-webpage-routes","title":"Using WebGear_RTC with Custom Webpage Routes","text":"

With Webgear_RTC's flexible API, you can even add your additional HTML Static webpages without any extra efforts.

Suppose we want to add a simple hello world webpage to our WebGear_RTC server. So let's create a bare-minimum hello.html file with HTML code as follows:

<html>\n   <header>\n      <title>This is Hello world page</title>\n   </header>\n   <body>\n      <h1>Hello World</h1>\n      <p>how ya doing?</p>\n   </body>\n</html>\n

Then in our application code, we can integrate this webpage route, as follows:

# import libs\nimport uvicorn, asyncio\nfrom starlette.templating import Jinja2Templates\nfrom starlette.routing import Route\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# Build out Jinja2 template render at `/home/foo/.vidgear/custom_template` path in which our `hello.html` file is located\ntemplate = Jinja2Templates(directory=\"/home/foo/.vidgear/custom_template\")\n\n# render and return our webpage template\nasync def hello_world(request):\n    page = \"hello.html\"\n    context = {\"request\": request}\n    return template.TemplateResponse(page, context)\n\n\n# add various performance tweaks as usual\noptions = {\n    \"frame_size_reduction\": 25,\n}\n\n# initialize WebGear_RTC app with a valid source\nweb = WebGear_RTC(\n    source=\"/home/foo/foo1.mp4\", logging=True, **options\n)  # enable source i.e. `test.mp4` and enable `logging` for debugging\n\n# append new route to point our rendered webpage\nweb.routes.append(Route(\"/hello\", endpoint=hello_world))\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n
And that's all, Now you can see output at http://localhost:8000/hello address.

"},{"location":"gears/webgear_rtc/advanced/#using-webgear_rtc-with-middlewares","title":"Using WebGear_RTC with MiddleWares","text":"

WebGear_RTC also natively supports ASGI middleware classes with Starlette for implementing behavior that is applied across your entire ASGI application easily.

New in v0.2.2

This example was added in v0.2.2.

All supported middlewares can be found here \u27b6

For this example, let's use CORSMiddleware for implementing appropriate CORS headers to outgoing responses in our application in order to allow cross-origin requests from browsers, as follows:

The default parameters used by the CORSMiddleware implementation are restrictive by default, so you'll need to explicitly enable particular origins, methods, or headers, in order for browsers to be permitted to use them in a Cross-Domain context.

Starlette provides several arguments for enabling origins, methods, or headers for CORSMiddleware API. More information can be found here \u27b6

# import libs\nimport uvicorn, asyncio\nfrom starlette.middleware import Middleware\nfrom starlette.middleware.cors import CORSMiddleware\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# add various performance tweaks as usual\noptions = {\n    \"frame_size_reduction\": 25,\n}\n\n# initialize WebGear_RTC app with a valid source\nweb = WebGear_RTC(\n    source=\"/home/foo/foo1.mp4\", logging=True, **options\n)  # enable source i.e. `test.mp4` and enable `logging` for debugging\n\n# define and assign suitable cors middlewares\nweb.middleware = [\n    Middleware(\n        CORSMiddleware,\n        allow_origins=[\"*\"],\n        allow_credentials=True,\n        allow_methods=[\"*\"],\n        allow_headers=[\"*\"],\n    )\n]\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

And that's all, Now you can see output at http://localhost:8000 address.

"},{"location":"gears/webgear_rtc/advanced/#rules-for-altering-webgear_rtc-files-and-folders","title":"Rules for Altering WebGear_RTC Files and Folders","text":"

WebGear_RTC gives us complete freedom of altering data files generated in Auto-Generation Process, But you've to keep the following rules in mind:

"},{"location":"gears/webgear_rtc/advanced/#rules-for-altering-data-files","title":"Rules for Altering Data Files","text":""},{"location":"gears/webgear_rtc/advanced/#rules-for-altering-data-folders","title":"Rules for Altering Data Folders","text":""},{"location":"gears/webgear_rtc/advanced/#bonus-examples","title":"Bonus Examples","text":"

Checkout more advanced WebGear_RTC examples with unusual configuration here \u27b6

"},{"location":"gears/webgear_rtc/overview/","title":"Overview","text":""},{"location":"gears/webgear_rtc/overview/#webgear_rtc-api","title":"WebGear_RTC API","text":"WebGear_RTC API's Video Server running at http://localhost:8000/ address."},{"location":"gears/webgear_rtc/overview/#overview","title":"Overview","text":"

WebGear_RTC is similar to WeGear API in many aspects but utilizes WebRTC technology under the hood instead of Motion JPEG, which makes it suitable for building powerful video-streaming solutions for all modern browsers as well as native clients available on all major platforms.

New in v0.2.1

WebGear_RTC API was added in v0.2.1.

WebGear_RTC is implemented with the help of aiortc library which is built on top of asynchronous I/O framework for Web Real-Time Communication (WebRTC) and Object Real-Time Communication (ORTC) and supports many features like SDP generation/parsing, Interactive Connectivity Establishment with half-trickle and mDNS support, DTLS key and certificate generation, DTLS handshake, etc.

WebGear_RTC can handle multiple consumers seamlessly and provides native support for ICE (Interactive Connectivity Establishment) protocol, STUN (Session Traversal Utilities for NAT), and TURN (Traversal Using Relays around NAT) servers that help us to seamlessly establish direct media connection with the remote peers for uninterrupted data flow. It also allows us to define our custom streaming class with suitable source to transform frames easily before sending them across the network(see this doc example).

WebGear_RTC API works in conjunction with Starlette ASGI application and can also flexibly interact with Starlette's ecosystem of shared middleware, mountable applications, Response classes, Routing tables, Static Files, Templating engine(with Jinja2), etc.

Additionally, WebGear_RTC API also provides internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs.

"},{"location":"gears/webgear_rtc/overview/#data-files-auto-generation-workflow-for-webgear_rtc","title":"Data-Files Auto-Generation WorkFlow for WebGear_RTC","text":"

Same as WebGear, WebGear_RTC API automatically checks for three critical data files(i.e index.html, 404.html & 500.html) on initialization inside the templates folder of the webgear_rtc directory at the default location which gives rise to the following two possible scenario:

"},{"location":"gears/webgear_rtc/overview/#default-location","title":"Default Location","text":"

Identifying Default location

You can set logging=True during initialization, for easily identifying the selected default location, which will be something like this on a Linux machine:

WebGear_RTC :: DEBUG :: `/home/foo/.vidgear` is the default location for saving WebGear_RTC data-files.\n
"},{"location":"gears/webgear_rtc/overview/#auto-generation-process","title":"Auto-Generation process","text":"

Info

"},{"location":"gears/webgear_rtc/overview/#webgear_rtcs-default-template","title":"WebGear_RTC's Default Template","text":"

The WebGear_RTC API by default uses simple & elegant WebGear_RTC's Default Theme which looks like something as follows:

"},{"location":"gears/webgear_rtc/overview/#indexhtml","title":"Index.html","text":"

Can be accessed by visiting WebGear_RTC app server, running at http://localhost:8000/:

"},{"location":"gears/webgear_rtc/overview/#404html","title":"404.html","text":"

Appears when respective URL is not found, for example http://localhost:8000/ok:

"},{"location":"gears/webgear_rtc/overview/#500html","title":"500.html","text":"

Appears when an API Error is encountered:

If logging is enabled and an error occurs, then instead of displaying this 500 handler, WebGear_RTC will respond with a traceback response.

"},{"location":"gears/webgear_rtc/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through WebGear_RTC Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/webgear_rtc/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/webgear_rtc/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/webgear_rtc/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/webgear_rtc/params/","title":"Parameters","text":""},{"location":"gears/webgear_rtc/params/#webgear_rtc-api-parameters","title":"WebGear_RTC API Parameters","text":"

WebGear_RTC provides a special internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs and their parameters.

"},{"location":"gears/webgear_rtc/params/#enablepicamera","title":"enablePiCamera","text":"

This parameter provide direct access to PiGear or CamGear APIs respectively in WebGear_RTC. This means the if enablePiCamera flag is True, the PiGear API will be accessed, and if False, the CamGear API will be accessed.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

WebGear_RTC(enablePiCamera=True) # enable access to PiGear API\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/webgear_rtc/params/#options","title":"options","text":"

This parameter can be used to pass user-defined parameter to WebGear_RTC API by formatting them as this parameter's attribute.

Data-Type: Dictionary

Default Value: Its default value is {}

"},{"location":"gears/webgear_rtc/params/#webgear_rtc-specific-attributes","title":"WebGear_RTC Specific attributes","text":"

"},{"location":"gears/webgear_rtc/params/#parameters-for-stabilizer-backend","title":"Parameters for Stabilizer Backend","text":"

Enable this backend with stabilize=True in WebGear_RTC. Default is also False.

"},{"location":"gears/webgear_rtc/params/#stabilize","title":"stabilize","text":"

This parameter enable access to Stabilizer Class for stabilizing frames, i.e. can be set to True(to enable) or unset to False(to disable).

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

WebGear_RTC(stabilize=True) # enable stablization\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/webgear_rtc/params/#options_1","title":"options","text":"

This parameter can be used in addition, to pass user-defined parameters supported by Stabilizer Class. These parameters can be formatted as this parameter's attribute.

Supported dictionary attributes for Stabilizer Class are:

"},{"location":"gears/webgear_rtc/params/#parameters-for-camgear-backend","title":"Parameters for CamGear backend","text":"

Enable this backend with enablePiCamera=False in WebGear_RTC.

"},{"location":"gears/webgear_rtc/params/#source","title":"source","text":"

WebGear_RTC API will throw RuntimeError if source provided is invalid.

This parameter defines the source for the input stream.

Data-Type: Based on input.

Default Value: Its default value is 0.

Its valid input can be one of the following:

"},{"location":"gears/webgear_rtc/params/#stream_mode","title":"stream_mode","text":"

This parameter controls the Stream Mode, .i.e if enabled(stream_mode=True), the CamGear API will interpret the given source input as YouTube URL address.

Due to a FFmpeg bug that causes video to freeze frequently in OpenCV, It is advised to always use GStreamer backend for any livestream videos. Checkout this FAQ for compiling OpenCV with GStreamer support.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

Supported Streaming Websites

The complete list of all supported Streaming Websites URLs can be found here \u27b6

WebGear_RTC(source='https://youtu.be/bvetuLwJIkA', stream_mode=True)\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/webgear_rtc/params/#backend","title":"backend","text":"

This parameter manually selects the backend for OpenCV's VideoCapture class (only if specified).

Data-Type: Integer

Default Value: Its default value is 0

Usage:

All supported backends are listed here \u27b6

Its value can be for e.g. backend = cv2.CAP_DSHOW for selecting Direct Show as backend:

WebGear_RTC(source=0, backend = cv2.CAP_DSHOW)\n

"},{"location":"gears/webgear_rtc/params/#options_2","title":"options","text":"

This parameter provides the ability to alter various Source Tweak Parameters available within OpenCV's VideoCapture API properties.

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

All supported parameters are listed here \u27b6

The desired parameters can be passed to WebGear_RTC API by formatting them as this parameter's attributes, as follows:

# formatting parameters as dictionary attributes\noptions = {\"CAP_PROP_FRAME_WIDTH\":320, \"CAP_PROP_FRAME_HEIGHT\":240, \"CAP_PROP_FPS\":60}\n# assigning it\nWebGear_RTC(source=0, **options)\n

"},{"location":"gears/webgear_rtc/params/#parameters-for-pigear-backend","title":"Parameters for PiGear backend","text":"

Enable this backend with enablePiCamera=True in WebGear_RTC.

"},{"location":"gears/webgear_rtc/params/#camera_num","title":"camera_num","text":"

This parameter selects the camera index to be used as the source, allowing you to drive these multiple cameras simultaneously from within a single Python session. Its value can only be zero or greater, otherwise, WebGear_RTC API will throw ValueError for any negative value.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

# select Camera Module at index `1`\nWebGear_RTC(enablePiCamera=True, camera_num=1)\n

The complete usage example demonstrating the usage of the camera_num parameter is available here \u27b6.

"},{"location":"gears/webgear_rtc/params/#resolution","title":"resolution","text":"

This parameter controls the resolution - a tuple (i.e. (width,height)) of two values giving the width and height of the output frames.

Make sure both width and height values should be at least 64.

When using the Picamera2 backend, the resolution parameter will be OVERRIDDEN, if the user explicitly defines the output_size property of the sensor configurational parameter.

Data-Type: Tuple

Default Value: Its default value is (640,480).

Usage:

WebGear_RTC(enablePiCamera=True, resolution=(1280,720)) # sets 1280x720 resolution\n

"},{"location":"gears/webgear_rtc/params/#framerate","title":"framerate","text":"

This parameter controls the framerate of the source.

Data-Type: integer/float

Default Value: Its default value is 30.

Usage:

WebGear_RTC(enablePiCamera=True, framerate=60) # sets 60fps framerate\n

"},{"location":"gears/webgear_rtc/params/#options_3","title":"options","text":"

This dictionary parameter in the internal PiGear API backend allows you to control various camera settings for both the picamera2 and legacy picamera backends and some internal API tasks. These settings include:

"},{"location":"gears/webgear_rtc/params/#a-configurational-camera-parameters","title":"A. Configurational Camera Parameters","text":""},{"location":"gears/webgear_rtc/params/#b-user-defined-parameters","title":"B. User-defined Parameters","text":"

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

The complete usage example demonstrating the usage of the options parameter is available here \u27b6.

You can format these user-defined and configurational parameters as attributes of this options dictionary parameter as follows:

New Picamera2 backendLegacy Picamera backend
# formulate various Picamera2 API parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"exposure_compensation\": 15,\n    \"sensor\": {\"output_size\": (480, 320)},  # !!! will override `resolution` !!!\n}\n\n# open pi video stream with defined parameters\nstream = WebGear_RTC(enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options).start()\n
# formulate various Picamera API parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = WebGear_RTC(enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options).start()\n

"},{"location":"gears/webgear_rtc/params/#common-parameters","title":"Common Parameters","text":"

These are common parameters that works with every backend in WebGear_RTC.

"},{"location":"gears/webgear_rtc/params/#colorspace","title":"colorspace","text":"

This parameter selects the colorspace of the source stream.

Data-Type: String

Default Value: Its default value is None.

Usage:

All supported colorspace values are given here \u27b6

WebGear_RTC(colorspace=\"COLOR_BGR2HSV\")\n

Its complete usage example is given here \u27b6

"},{"location":"gears/webgear_rtc/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

WebGear_RTC(logging=True)\n

"},{"location":"gears/webgear_rtc/params/#time_delay","title":"time_delay","text":"

This parameter set the time delay (in seconds) before the WebGear_RTC API start reading the frames. This delay is only required if the source required some warm-up delay before starting up.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

WebGear_RTC(time_delay=1)  # set 1 seconds time delay\n

"},{"location":"gears/webgear_rtc/usage/","title":"Usage Examples","text":""},{"location":"gears/webgear_rtc/usage/#webgear_rtc-api-usage-examples","title":"WebGear_RTC API Usage Examples:","text":""},{"location":"gears/webgear_rtc/usage/#requirements","title":"Requirements","text":""},{"location":"gears/webgear_rtc/usage/#installation-with-asyncio-support","title":"Installation with Asyncio Support","text":"

WebGear_RTC API is the part of asyncio package of VidGear, thereby you need to install VidGear with asyncio support as follows:

pip install vidgear[asyncio]\n
"},{"location":"gears/webgear_rtc/usage/#aiortc","title":"Aiortc","text":"

Must Required with WebGear_RTC API. You can easily install it via pip:

Microsoft Visual C++ 14.0 is required.

Installing aiortc on windows requires Microsoft Build Tools for Visual C++ libraries installed. You can easily fix this error by installing any ONE of these choices:

While the error is calling for VC++ 14.0 - but newer versions of Visual C++ libraries works as well.

Afterwards, Select: Workloads \u2192 Desktop development with C++, then for Individual Components, select only:

Finally, proceed installing aiortc via pip.

  pip install aiortc\n
"},{"location":"gears/webgear_rtc/usage/#asgi-server","title":"ASGI Server","text":"

You'll also need to install an ASGI Server to run following WebGear_RTC usage examples, and by default WebGear_RTC ships the state-of-the-art uvicorn Server. But you can also use other ASGI server such as daphne, or hypercorn with it.

"},{"location":"gears/webgear_rtc/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Let's implement a Bare-Minimum usage example:

"},{"location":"gears/webgear_rtc/usage/#running-programmatically","title":"Running Programmatically","text":"

You can access and run WebGear_RTC VideoStreamer Server programmatically in your python script in just a few lines of code, as follows:

For accessing WebGear_RTC on different Client Devices on the network, use \"0.0.0.0\" as host value instead of \"localhost\" on Host Machine. More information can be found here \u27b6

We are using frame_size_reduction attribute for frame size reduction (in percentage) to be streamed with its options dictionary parameter to cope with performance-throttling in this example.

# import required libraries\nimport uvicorn\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# various performance tweaks\noptions = {\n    \"frame_size_reduction\": 25,\n}\n\n# initialize WebGear_RTC app\nweb = WebGear_RTC(source=\"foo.mp4\", logging=True, **options)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

which can be accessed on any browser on your machine at http://localhost:8000/.

"},{"location":"gears/webgear_rtc/usage/#running-from-terminal","title":"Running from Terminal","text":"

You can also access and run WebGear_RTC Server directly from the terminal commandline. The following command will run a WebGear_RTC VideoStreamer server at http://localhost:8000/:

Make sure your PYTHON_PATH is set to python 3.7+ versions only.

If you're using --options/-op flag, then kindly wrap your dictionary value in single '' quotes.

python3 -m vidgear.gears.asyncio --mode webrtc --source test.avi --logging True --options '{\"frame_size_reduction\": 50, \"frame_jpeg_quality\": 80, \"frame_jpeg_optimize\": True, \"frame_jpeg_progressive\": False}'\n

which can also be accessed on any browser on the network at http://localhost:8000/.

Advanced Usage from Terminal

You can run python3 -m vidgear.gears.asyncio -h help command to see all the advanced settings, as follows:

usage: python -m vidgear.gears.asyncio [-h] [-m MODE] [-s SOURCE] [-ep ENABLEPICAMERA] [-S STABILIZE]\n            [-cn CAMERA_NUM] [-yt stream_mode] [-b BACKEND] [-cs COLORSPACE]\n            [-r RESOLUTION] [-f FRAMERATE] [-td TIME_DELAY]\n            [-ip IPADDRESS] [-pt PORT] [-l LOGGING] [-op OPTIONS]\n\nRuns WebGear/WebGear_RTC Video Server through terminal.\n\noptional arguments:\n  -h, --help            show this help message and exit\n  -m {mjpeg,webrtc}, --mode {mjpeg,webrtc}\n                        Whether to use \"MJPEG\" or \"WebRTC\" mode for streaming.\n  -s SOURCE, --source SOURCE\n                        Path to input source for CamGear API.\n  -ep ENABLEPICAMERA, --enablePiCamera ENABLEPICAMERA\n                        Sets the flag to access PiGear(if True) or otherwise\n                        CamGear API respectively.\n  -S STABILIZE, --stabilize STABILIZE\n                        Enables/disables real-time video stabilization.\n  -cn CAMERA_NUM, --camera_num CAMERA_NUM\n                        Sets the camera module index that will be used by\n                        PiGear API.\n  -yt STREAM_MODE, --stream_mode STREAM_MODE\n                        Enables YouTube Mode in CamGear API.\n  -b BACKEND, --backend BACKEND\n                        Sets the backend of the video source in CamGear API.\n  -cs COLORSPACE, --colorspace COLORSPACE\n                        Sets the colorspace of the output video stream.\n  -r RESOLUTION, --resolution RESOLUTION\n                        Sets the resolution (width,height) for camera module\n                        in PiGear API.\n  -f FRAMERATE, --framerate FRAMERATE\n                        Sets the framerate for camera module in PiGear API.\n  -td TIME_DELAY, --time_delay TIME_DELAY\n                        Sets the time delay(in seconds) before start reading\n                        the frames.\n  -ip IPADDRESS, --ipaddress IPADDRESS\n                        Uvicorn binds the socket to this ipaddress.\n  -pt PORT, --port PORT\n                        Uvicorn binds the socket to this port.\n  -l LOGGING, --logging LOGGING\n                        Enables/disables error logging, essential for\n                        debugging.\n  -op OPTIONS, --options OPTIONS\n                        Sets the parameters supported by APIs(whichever being\n                        accessed) to the input videostream, But make sure to\n                        wrap your dict value in single or double quotes.\n

"},{"location":"gears/writegear/introduction/","title":"Introduction","text":""},{"location":"gears/writegear/introduction/#writegear-api","title":"WriteGear API","text":"WriteGear API generalized workflow"},{"location":"gears/writegear/introduction/#overview","title":"Overview","text":"

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 specifications (such asbitrate, codec, framerate, resolution, subtitles, etc.).

WriteGear also supports streaming with traditional protocols such as RTSP/RTP, RTMP. It is powerful enough to perform complex tasks such as Live-Streaming (such as for Twitch, YouTube etc.) and Multiplexing Video-Audio with real-time frames in just few lines of code.

Best of all, WriteGear grants users the complete freedom to play with any FFmpeg parameter with its exclusive Custom Commands function (see this doc) without relying on any third-party API.

In addition to this, WriteGear also provides flexible access to OpenCV's VideoWriter API tools for video-frames encoding without compression.

"},{"location":"gears/writegear/introduction/#modes-of-operation","title":"Modes of Operation","text":"

WriteGear primarily operates in following modes:

Helpful Tips

"},{"location":"gears/writegear/introduction/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/writegear/compression/overview/","title":"Overview","text":""},{"location":"gears/writegear/compression/overview/#writegear-api-compression-mode","title":"WriteGear API: Compression Mode","text":"WriteGear API's Compression Mode generalized workflow"},{"location":"gears/writegear/compression/overview/#overview","title":"Overview","text":"

When compression_mode parameter is enabled (.i.e compression_mode = True), WriteGear API provides a complete, flexible & robust wrapper around FFmpeg to encode lossless & compressed multimedia files.

This mode can process real-time video frames into a lossless compressed format with any suitable setting video/audio properties such as bitrate, codec, framerate, resolution, subtitles, and much more in just a few easy lines of code. It can also perform complex tasks such as Live-Streaming (such as for Twitch), multiplexing video with audio in real-time (see this usage example) while handling all errors robustly.

Important Information

You can speed up the execution time by disabling logging (.i.e logging = False) for production use, and by tweaking FFmpeg parameters in output_params values. Look into FFmpeg docs \u27b6 for such hacks.

"},{"location":"gears/writegear/compression/overview/#custom-ffmpeg-commands-in-writegear-api","title":"Custom FFmpeg Commands in WriteGear API","text":"

WriteGear API now provides the execute_ffmpeg_cmd Function in Compression Mode, that enables the user to pass any custom CLI commands as an input to its internal FFmpeg Pipeline by formating it as a list.

This function opens endless possibilities of exploiting any FFmpeg supported parameter within WriteGear, without relying on a third-party library/API to do the same, and while doing that it robustly handles all errors/warnings quietly.

A complete guide on execute_ffmpeg_cmd Function can be found here \u27b6

"},{"location":"gears/writegear/compression/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through WriteGear Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/writegear/compression/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/writegear/compression/params/","title":"Parameters","text":""},{"location":"gears/writegear/compression/params/#writegear-api-parameters-compression-mode","title":"WriteGear API Parameters: Compression Mode","text":""},{"location":"gears/writegear/compression/params/#output","title":"output","text":"

This parameter sets the valid filename/path/URL for the video output.

Warning

WriteGear API will throw ValueError if output provided is empty or invalid.

Data-Type: String

Usage:

Its valid input can be one of the following:

"},{"location":"gears/writegear/compression/params/#compression_mode","title":"compression_mode","text":"

This parameter selects the WriteGear's Primary Mode of Operation, i.e. if this parameter is enabled (.i.e compression_mode = True) WriteGear will use FFmpeg to encode output video, and if disabled (.i.e compression_mode = False), the OpenCV's VideoWriter API will be used for encoding files/streams.

Data-Type: Boolean

Default Value: Its default value is True.

Usage:

WriteGear(output = 'output.mp4', compression_mode=True)\n

"},{"location":"gears/writegear/compression/params/#custom_ffmpeg","title":"custom_ffmpeg","text":"

This parameter assigns the custom path/directory where the custom FFmpeg executables are located in Compression Mode only.

Compression Mode Behavior on Windows

In Compression Mode, if a custom FFmpeg executable's path | directory is not provided through custom_ffmpeg parameter on Windows machine, then WriteGear API will automatically attempt to download and extract suitable Static FFmpeg binaries at suitable location on your windows machine. More information can be found here \u27b6.

Data-Type: String

Default Value: Its default value is None.

Usage:

# if ffmpeg executables are located at \"/foo/foo1/FFmpeg\"\nWriteGear(output = 'output.mp4', custom_ffmpeg=\"/foo/foo1/FFmpeg\")\n

"},{"location":"gears/writegear/compression/params/#output_params","title":"output_params","text":"

This parameter allows us to exploit almost all FFmpeg supported parameters effortlessly and flexibly for encoding in Compression Mode, by formatting desired FFmpeg Parameters as this parameter's attributes. All supported parameters and encoders for compression mode discussed below:

Kindly read FFmpeg Docs carefully, before passing any values to output_param dictionary parameter. Wrong values may result in undesired Errors or no output at all.

Data-Type: Dictionary

Default Value: Its default value is {}.

"},{"location":"gears/writegear/compression/params/#supported-parameters","title":"Supported Parameters","text":""},{"location":"gears/writegear/compression/params/#supported-encoders","title":"Supported Encoders","text":"

All the encoders that are compiled with FFmpeg in use, are supported by WriteGear API. You can easily check the compiled encoders by running following command in your terminal:

Similarily, supported demuxers and filters depends upons compiled FFmpeg in use.

ffmpeg -encoders           # use `ffmpeg.exe -encoders` on windows\n

"},{"location":"gears/writegear/compression/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

WriteGear(output = 'output.mp4', logging=True)\n

"},{"location":"gears/writegear/compression/usage/","title":"Usage Examples","text":""},{"location":"gears/writegear/compression/usage/#writegear-api-usage-examples-compression-mode","title":"WriteGear API Usage Examples: Compression Mode","text":"

Important Information

After going through WriteGear Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/writegear/compression/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with WriteGear API in Compression Mode:

# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# open any valid video stream(for e.g `myvideo.avi` file)\nstream = CamGear(source=\"myvideo.avi\").start()\n\n# Define writer with default parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\")\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/compression/usage/#using-compression-mode-in-rgb-mode","title":"Using Compression Mode in RGB Mode","text":"

In Compression Mode, WriteGear API contains rgb_mode boolean parameter for RGB Mode, which when enabled (i.e. rgb_mode=True), specifies that incoming frames are of RGB format (instead of default BGR format). This mode makes WriteGear directly compatible with libraries that only supports RGB format.

The complete usage example is as follows:

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = VideoGear(source=0).start()\n\n# Define writer with default parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\")\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # simulating RGB frame for example\n    frame_rgb = frame[:, :, ::-1]\n\n    # writing RGB frame to writer\n    writer.write(frame_rgb, rgb_mode=True)  # activate RGB Mode\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/compression/usage/#using-compression-mode-with-controlled-framerate","title":"Using Compression Mode with controlled FrameRate","text":"

WriteGear API provides -input_framerate attribute for its options dictionary parameter in Compression Mode, which allow us to control/set the constant framerate of the output video.

Advanced Tip for setting constant framerate

If -input_framerate attribute doesn't works for you, then define it in conjunction with another -r FFmpeg parameter as attribute:

# set output constant framerate to (say 60 fps)\noutput_params = {\"-input_framerate\":60, \"-r\":60}\n# assign that to WriteGear\nwriter = WriteGear(output=\"out.mp4\", logging =True, **output_params)\n

But make sure you MUST set value of -r and -input_framerate parameter less than or equal to your input source framerate.

In this code we will retrieve framerate from video stream, and set it as -input_framerate attribute for option parameter in WriteGear API:

# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = CamGear(source=0).start()\n\n# retrieve framerate from CamGear Stream and pass it as `-input_framerate` parameter\noutput_params = {\"-input_framerate\": stream.framerate}\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\", **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if None-type\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/compression/usage/#using-compression-mode-for-live-streaming","title":"Using Compression Mode for live streaming","text":"

In Compression Mode, WriteGear also allows URL strings (as output) for live streaming realtime frames with its output parameter.

In this example, we will stream live camera frames directly to Twitch :

For streaming with traditional protocols such as RTSP/RTP, Checkout this WriteGear's Bonus Examples \u27b6.

YouTube-Live Streaming example code also available in WriteGear's Bonus Examples \u27b6

This example assume you already have a Twitch Account for publishing video.

Make sure to change Twitch Stream Key with yours in following code before running!

# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# Open live webcam video stream on first index(i.e. 0) device\nstream = CamGear(source=0, logging=True).start()\n\n# define required FFmpeg optimizing parameters for your writer\noutput_params = {\n    \"-preset:v\": \"veryfast\",\n    \"-g\": 60,\n    \"-keyint_min\": 60,\n    \"-sc_threshold\": 0,\n    \"-bufsize\": \"2500k\",\n    \"-f\": \"flv\",\n}\n\n# [WARNING] Change your Twitch Stream Key here:\nTWITCH_KEY = \"live_XXXXXXXXXX~XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\"\n\n# Define writer with defined parameters and\nwriter = WriteGear(\n    output=\"rtmp://live.twitch.tv/app/{}\".format(TWITCH_KEY),\n    logging=True,\n    **output_params\n)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/compression/usage/#using-compression-mode-with-hardware-encoders","title":"Using Compression Mode with Hardware encoders","text":"

By default, WriteGear API uses libx264 encoder for encoding output files in Compression Mode. But you can easily change encoder to your suitable supported encoder by passing -vcodec FFmpeg parameter as an attribute with its output_param dictionary parameter. In addition to this, you can also specify the additional properties/features of your system's GPU easily.

User Discretion Advised

This example is just conveying the idea on how to use FFmpeg's hardware encoders with WriteGear API in Compression mode, which MAY/MAY NOT suit your system. Kindly use suitable parameters based your system hardware settings only.

In this example, we will be using h264_vaapi as our hardware encoder and also optionally be specifying our device hardware's location (i.e. '-vaapi_device':'/dev/dri/renderD128') and other features such as '-vf':'format=nv12,hwupload':

Remember to 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:

ffmpeg  -hide_banner -encoders | grep vaapi \n\n V..... h264_vaapi           H.264/AVC (VAAPI) (codec h264)\n V..... hevc_vaapi           H.265/HEVC (VAAPI) (codec hevc)\n V..... mjpeg_vaapi          MJPEG (VAAPI) (codec mjpeg)\n V..... mpeg2_vaapi          MPEG-2 (VAAPI) (codec mpeg2video)\n V..... vp8_vaapi            VP8 (VAAPI) (codec vp8)\n
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# Open live webcam video stream on first index(i.e. 0) device\nstream = CamGear(source=0, logging=True).start()\n\n# define required FFmpeg parameters for your writer\noutput_params = {\n    \"-vcodec\": \"h264_vaapi\",\n    \"-vaapi_device\": \"/dev/dri/renderD128\",\n    \"-vf\": \"format=nv12,hwupload\",\n}\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\", **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/compression/usage/#using-compression-mode-with-opencv","title":"Using Compression Mode with OpenCV","text":"

You can easily use WriterGear API directly with any Video Processing library(For e.g OpenCV itself) in Compression Mode. The complete usage example is as follows:

# import required libraries\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# define suitable (Codec,CRF,preset) FFmpeg parameters for writer\noutput_params = {\"-vcodec\": \"libx264\", \"-crf\": 0, \"-preset\": \"fast\"}\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = cv2.VideoCapture(0)\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n        break\n\n    # {do something with the frame here}\n    # lets convert frame to gray for this example\n    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n\n    # write gray frame to writer\n    writer.write(gray)\n\n    # Show output window\n    cv2.imshow(\"Output Gray Frame\", gray)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.release()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/compression/usage/#using-compression-mode-with-live-audio-input","title":"Using Compression Mode with Live Audio Input","text":"

In Compression Mode, WriteGear API allows us to exploit almost all FFmpeg supported parameters that you can think of in its Compression Mode. Hence, combining audio with live video frames is pretty easy.

In this example code, we will merging the audio from a Audio Device (for e.g. Webcam inbuilt mic) to live frames incoming from the Video Source (for e.g external webcam), and save the output as a compressed video file, all in real time:

Example Assumptions

Identifying and Specifying sound card on different OS platforms Windows Linux MacOS

Windows OS users can use the dshow (DirectShow) to list audio input device which is the preferred option for Windows users. You can refer following steps to identify and specify your sound card:

If audio still doesn't work then checkout this troubleshooting guide \u27b6 or reach us out on Gitter \u27b6 Community channel

Linux OS users can use the alsa to list input device to capture live audio input such as from a webcam. You can refer following steps to identify and specify your sound card:

If audio still doesn't work then reach us out on Gitter \u27b6 Community channel

MAC OS users can use the avfoundation to list input devices for grabbing audio from integrated iSight cameras as well as cameras connected via USB or FireWire. You can refer following steps to identify and specify your sound card on MacOS/OSX machines:

If audio still doesn't work then reach us out on Gitter \u27b6 Community channel

Make sure this -i audio-source it compatible with provided video-source, otherwise you could encounter multiple errors or no output at all.

You MUST use -input_framerate attribute to set exact value of input framerate when using external audio in Real-time Frames mode, otherwise audio delay will occur in output streams.

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = VideoGear(source=0).start()\n\n# change with your webcam soundcard, plus add additional required FFmpeg parameters for your writer\noutput_params = {\n    \"-input_framerate\": stream.framerate,\n    \"-thread_queue_size\": \"512\",\n    \"-ac\": \"2\",\n    \"-ar\": \"48000\",\n    \"-f\": \"alsa\", # !!! warning: always keep this line above \"-i\" parameter !!!\n    \"-i\": \"hw:1\",\n}\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4\nwriter = WriteGear(output=\"Output.mp4\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/compression/advanced/cciw/","title":"Custom FFmpeg Commands","text":""},{"location":"gears/writegear/compression/advanced/cciw/#custom-ffmpeg-commands-in-writegear-api","title":"Custom FFmpeg Commands in WriteGear API","text":"

WriteGear API now provides the execute_ffmpeg_cmd Method in Compression Mode that enables the user to pass any custom FFmpeg CLI (Command Line Interface) commands as input to its internal FFmpeg Pipeline by formating it as a list.

This opens endless possibilities of exploiting every FFmpeg params within WriteGear without relying on a third-party API to do the same and while doing that it robustly handles all errors/warnings quietly.

Important Information

"},{"location":"gears/writegear/compression/advanced/cciw/#features","title":"Features","text":"

"},{"location":"gears/writegear/compression/advanced/cciw/#methods","title":"Methods","text":""},{"location":"gears/writegear/compression/advanced/cciw/#execute_ffmpeg_cmd","title":"execute_ffmpeg_cmd","text":"

This method allows the users to pass the custom FFmpeg terminal commands as a formatted list directly to WriteGear API's FFmpeg pipeline for processing/execution. Its usage is as follows:

# format FFmpeg terminal command `ffmpeg -y -i source_video -acodec copy input_audio.aac` as a list\nffmpeg_command = [\"-y\", \"-i\", source_video, \"-acodec\", \"copy\", \"input_audio.aac\"]\n\n# execute this list using this function\nexecute_ffmpeg_cmd(ffmpeg_command)\n

"},{"location":"gears/writegear/compression/advanced/cciw/#usage-examples","title":"Usage Examples","text":"

Following usage examples is just an idea of what can be done with this powerful function. So just Tinker with various FFmpeg parameters/commands yourself and see it working. Also, if you're unable to run any terminal FFmpeg command, then report an issue.

"},{"location":"gears/writegear/compression/advanced/cciw/#using-writegear-to-separate-audio-from-video","title":"Using WriteGear to separate Audio from Video","text":"

In this example, we will extract and save audio from a URL stream:

# import required libraries\nfrom vidgear.gears import WriteGear\n\n# define a valid url\nurl_to_stream = (\n    \"http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4\"\n)\n\n# Define writer with default parameters\nwriter = WriteGear(output=\"Output.mp4\", logging=True)\n\n# format command to convert stream audio as 'output_audio.aac' as list\nffmpeg_command_to_save_audio = [\n    \"-y\",\n    \"-i\",\n    url_to_stream,\n    \"output_audio.aac\",\n]  # `-y` parameter is to overwrite outputfile if exists\n\n# execute FFmpeg command\nwriter.execute_ffmpeg_cmd(ffmpeg_command_to_save_audio)\n\n# safely close writer\nwriter.close()\n

After running this script, You will get the final 'output_audio.aac' audio file.

"},{"location":"gears/writegear/compression/advanced/cciw/#using-writegear-to-merge-audio-with-video","title":"Using WriteGear to merge Audio with Video","text":"

In this example, we will merge audio with video:

You can also directly add external audio input to video-frames in WriteGear. For more information, See this FAQ example \u27b6

Example Assumptions

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import WriteGear\nimport cv2\nimport time\n\n# Open input video stream\nstream = VideoGear(source=\"input-video.mp4\").start()\n\n# set input audio stream path\ninput_audio = \"input-audio.aac\"\n\n# define your parameters\noutput_params = {\n    \"-input_framerate\": stream.framerate\n}  # output framerate must match source framerate\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\", **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n\n\n# sleep 1 sec as the above video might still be rendering\ntime.sleep(1)\n\n\n# format FFmpeg command to generate `Output_with_audio.mp4` by merging input_audio in above rendered `Output.mp4`\nffmpeg_command = [\n    \"-y\",\n    \"-i\",\n    \"Output.mp4\",\n    \"-i\",\n    input_audio,\n    \"-c:v\",\n    \"copy\",\n    \"-c:a\",\n    \"copy\",\n    \"-map\",\n    \"0:v:0\",\n    \"-map\",\n    \"1:a:0\",\n    \"-shortest\",\n    \"Output_with_audio.mp4\",\n]  # `-y` parameter is to overwrite outputfile if exists\n\n# execute FFmpeg command\nwriter.execute_ffmpeg_cmd(ffmpeg_command)\n

After running this script, You will get the final 'Output_with_audio.mp4' file with both video and audio merged.

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/","title":"FFmpeg Installation","text":""},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#ffmpeg-installation-instructions","title":"FFmpeg Installation Instructions","text":"

WriteGear must requires FFmpeg executables for its Compression capabilities in Compression Mode. You can following machine-specific instructions for its installation:

In case WriteGear API fails to detect valid FFmpeg executables on your system (even if Compression Mode is enabled), it automatically fallbacks to Non-Compression Mode.

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#linux-ffmpeg-installation","title":"Linux FFmpeg Installation","text":"

The WriteGear API supports Auto-Detection and Manual Configuration methods on a Linux machine:

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#a-auto-detection","title":"A. Auto-Detection","text":"

This is a recommended approach on Linux Machines

If WriteGear API not receives any input from the user on custom_ffmpeg parameter, then on Linux system, it tries to auto-detects the required FFmpeg installed binaries through validation test that employs subprocess python module.

Installation: You can install easily install official FFmpeg according to your Linux Distro by following this post \u27b6

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#b-manual-configuration","title":"B. Manual Configuration","text":"

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#windows-ffmpeg-installation","title":"Windows FFmpeg Installation","text":"

The WriteGear API supports Auto-Installation and Manual Configuration methods on Windows systems.

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#a-auto-installation","title":"A. Auto-Installation","text":"

This is a recommended approach on Windows Machines

If WriteGear API not receives any input from the user on custom_ffmpeg parameter, then on Windows system WriteGear API auto-generates the required FFmpeg Static Binaries from a dedicated Github Server into the temporary directory (for e.g. C:\\Temp) of your machine.

Warning

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#b-manual-configuration_1","title":"B. Manual Configuration","text":"

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#macos-ffmpeg-installation","title":"MacOS FFmpeg Installation","text":"

The WriteGear API supports Auto-Detection and Manual Configuration methods on a macOS machine.

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#a-auto-detection_1","title":"A. Auto-Detection","text":"

This is a recommended approach on MacOS Machines

If WriteGear API not receives any input from the user on custom_ffmpeg parameter, then on macOS system, it tries to auto-detects the required FFmpeg installed binaries through validation test that employs subprocess python module.

Installation: You can easily install FFmpeg on your macOS machine by following this tutorial \u27b6

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#b-manual-configuration_2","title":"B. Manual Configuration","text":"

"},{"location":"gears/writegear/non_compression/overview/","title":"Overview","text":""},{"location":"gears/writegear/non_compression/overview/#writegear-api-non-compression-mode","title":"WriteGear API: Non-Compression Mode","text":"WriteGear API's Non-Compression Mode generalized workflow"},{"location":"gears/writegear/non_compression/overview/#overview","title":"Overview","text":"

When compression_mode parameter is disabled (.i.e compression_mode = False), WriteGear API uses basic OpenCV's inbuilt VideoWriter API tools for encoding multimedia files but without compression.

This mode provides flexible access to OpenCV's VideoWriter API,and also supports various parameters available within this API, but lacks the ability to control output quality, compression, and other important features like lossless video compression, audio encoding, etc. which are only available in Compression Mode. Thereby, the resultant output video-file size will be many times larger as compared to Compression Mode.

Important Information

"},{"location":"gears/writegear/non_compression/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through WriteGear Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/writegear/non_compression/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/writegear/non_compression/params/","title":"Parameters","text":""},{"location":"gears/writegear/non_compression/params/#writegear-api-parameters-non-compression-mode","title":"WriteGear API Parameters: Non-Compression Mode","text":""},{"location":"gears/writegear/non_compression/params/#output","title":"output","text":"

This parameter sets the valid output Video filename/path for the output video.

WriteGear API will throw RuntimeError if output provided is empty or invalid.

Data-Type: String

Default Value: Its default value is 0.

Usage:

Make sure to provide valid filename with valid file-extension based on the encoder in use (default is .mp4).

Its valid input can be one of the following:

"},{"location":"gears/writegear/non_compression/params/#compression_mode","title":"compression_mode","text":"

This parameter selects the WriteGear's Primary Mode of Operation, i.e. if this parameter is enabled (.i.e compression_mode = True) WriteGear will use FFmpeg to encode output video, and if disabled (.i.e compression_mode = False), the OpenCV's VideoWriter API will be used for encoding files/streams.

Data-Type: Boolean

Default Value: Its default value is True.

Usage:

WriteGear(output = 'output.mp4', compression_mode=False)\n

"},{"location":"gears/writegear/non_compression/params/#custom_ffmpeg","title":"custom_ffmpeg","text":"

Not supported in Non-Compression Mode!

"},{"location":"gears/writegear/non_compression/params/#output_params","title":"output_params","text":"

This parameter allows us to exploit almost all OpenCV's VideoWriter API supported parameters effortlessly and flexibly for video-encoding in Non-Compression Mode, by formatting desired FFmpeg Parameters as this parameter's attributes. All supported parameters and FOURCC codecs for compression mode discussed below:

Remember, Non-Compression mode lacks the ability to control output quality and other important features like lossless video compression, audio encoding, etc., which are available with WriteGear's Compression Mode only.

Data-Type: Dictionary

Default Value: Its default value is {}.

"},{"location":"gears/writegear/non_compression/params/#supported-attributes","title":"Supported Attributes","text":"

Non-Compression Mode only gives access to a limited number of Parameters through its output_params parameter's attributes, which are as follows:

"},{"location":"gears/writegear/non_compression/params/#a-opencv-parameters","title":"A. OpenCV Parameters","text":"

WriteGear provides access to all available OpenCV's VideoWriter API parameters in Non-Compression Mode.

Parameters Description -fourcc 4-character code of codec used to encode frames -fps controls the framerate of output video(Default value: 25) -backend (optional) In case of multiple backends, this parameter allows us to specify VideoWriter API's backends to use. Its valid values are CAP_FFMPEG or CAP_GSTREAMER(if enabled) -color (optional) If it is not zero(0), the encoder will expect and encode color frames, otherwise it will work with grayscale frames (the flag is currently supported on Windows only)

-height and -width parameter are no longer supported and are automatically derived from the input frames.

"},{"location":"gears/writegear/non_compression/params/#b-exclusive-parameters","title":"B. Exclusive Parameters","text":"

In addition to OpenCV Parameters, WriteGear API also provides few exclusive attribute, which are as follows:

Usage:

To assign desired parameters in Non-Compression Mode, you can format it as dictionary attribute and pass through this(output_params) parameter as follows:

# format parameter as dictionary attribute\noutput_params = {\"-fps\":30} \n# and then, assign it\nWriteGear(output = 'output.mp4', compression_mode=False, **output_params)\n

Its usage example can be found here \u27b6.

"},{"location":"gears/writegear/non_compression/params/#supported-fourcc-codecs","title":"Supported FOURCC Codecs","text":"

FOURCC is a 4-character code of the codec used to encode video in Non-Compression Mode(OpenCV's VideoWriter API) without compression.

List of all supported FOURCC codecs can found here \u27b6

Usage:

To select desired FOURCC codec in Non-Compression Mode, you can format it as dictionary attribute and pass through this(output_params) parameter. For example, using MJPG as codec, we can:

# format codec as dictionary attribute\noutput_params = {\"-fourcc\":\"MJPG\"} \n# and then, assign it\nWriteGear(output = 'output.mp4', compression_mode=False, **output_params)\n

Its usage example can be found here \u27b6.

"},{"location":"gears/writegear/non_compression/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

WriteGear(output = 'output.mp4', compression_mode=False, logging=True)\n

"},{"location":"gears/writegear/non_compression/usage/","title":"Usage Examples","text":""},{"location":"gears/writegear/non_compression/usage/#writegear-api-usage-examples-non-compression-mode","title":"WriteGear API Usage Examples: Non-Compression Mode","text":"

Important Information

After going through WriteGear Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/writegear/non_compression/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with WriteGear API in Non-Compression Mode:

# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# open any valid video stream(for e.g `myvideo.avi` file)\nstream = CamGear(source=\"myvideo.avi\").start()\n\n# Define writer with Non-compression mode and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\", compression_mode=False)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/non_compression/usage/#using-non-compression-mode-with-videocapture-gears","title":"Using Non-Compression Mode with VideoCapture Gears","text":"

In Non-Compression mode, WriteGear API provides flexible control over OpenCV's VideoWriter API parameters through its output_param dictionary parameter by formating them as dictionary attributes. Moreover, WriteGear API can be used in conjunction with any other Gears/APIs effortlessly.

All supported attributes for output_param can be found here \u27b6

The complete usage example is as follows:

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# define suitable tweak parameters for writer\noutput_params = {\"-fourcc\": \"MJPG\", \"-fps\": 30}\n\n# open live video stream on webcam at first index(i.e. 0) device\nstream = VideoGear(source=0, logging=True).start()\n\n# Define writer with defined parameters and suitable output filename \n# for e.g. `Output.mp4`\nwriter = WriteGear(\n    output=\"Output.mp4\", compression_mode=False, logging=True, **output_params\n)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n    # lets convert frame to gray for this example\n    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n\n    # write gray frame to writer\n    writer.write(gray)\n\n    # Show output window\n    cv2.imshow(\"Output Gray Frame\", gray)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/non_compression/usage/#using-non-compression-mode-with-opencv","title":"Using Non-Compression Mode with OpenCV","text":"

You can easily use WriterGear API directly with any Video Processing library(For e.g OpenCV itself) in Non-Compression Mode. The complete usage example is as follows:

# import required libraries\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# define suitable tweak parameters for writer\noutput_params = {\"-fourcc\": \"MJPG\", \"-fps\": 30}\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = cv2.VideoCapture(0)\n\n# Define writer with defined parameters and suitable output filename \n# for e.g. `Output.mp4`\nwriter = WriteGear(\n    output=\"Output.mp4\", compression_mode=False, logging=True, **output_params\n)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n        break\n\n    # {do something with the frame here}\n    # lets convert frame to gray for this example\n    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n\n    # write gray frame to writer\n    writer.write(gray)\n\n    # Show output window\n    cv2.imshow(\"Output Gray Frame\", gray)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.release()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/non_compression/usage/#using-non-compression-mode-with-gstreamer-pipeline","title":"Using Non-Compression Mode with GStreamer Pipeline","text":"

WriteGear API's Non-Compression Mode also supports GStreamer Pipeline as input to its output parameter, when GStreamer Pipeline Mode is enabled. This provides flexible way to write video frames to file or network stream with controlled framerate and bitrate. The complete usage example is as follows:

Requirement for GStreamer Pipelining

GStreamer Pipelining in WriteGear requires your OpenCV to be built with GStreamer support. Checkout this FAQ for compiling OpenCV with GStreamer support.

New in v0.2.5

This example was added in v0.2.5.

In this example we will be constructing GStreamer pipeline to write video-frames into a file(foo.mp4) at 1M video-bitrate.

# import required libraries\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# enable GStreamer Pipeline Mode for writer\noutput_params = {\"-gst_pipeline_mode\": True}\n\n# open live video stream on webcam at first index(i.e. 0) device\nstream = cv2.VideoCapture(0)\n\n# gst pipeline to write to a file `foo.mp4` at 1M video-bitrate\nGSTPipeline = \"appsrc ! videoconvert ! avenc_mpeg4 bitrate=100000 ! mp4mux ! filesink location={}\".format(\n    \"foo.mp4\"\n)\n\n# Define writer with defined parameters and with our Gstreamer pipeline\nwriter = WriteGear(\n    output=GSTPipeline, compression_mode=False, logging=True, **output_params\n)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.release()\n\n# safely close writer\nwriter.close()\n

"},{"location":"help/camgear_ex/","title":"Bonus Examples","text":""},{"location":"help/camgear_ex/#camgear-examples","title":"CamGear Examples","text":""},{"location":"help/camgear_ex/#synchronizing-two-sources-in-camgear","title":"Synchronizing Two Sources in CamGear","text":"

In this example both streams and corresponding frames will be processed synchronously i.e. with no delay:

Using same source with more than one instances of CamGear can lead to Global Interpreter Lock (GIL) that degrades performance even when it is not a bottleneck.

# import required libraries\nfrom vidgear.gears import CamGear\nimport cv2\nimport time\n\n# define and start the stream on first source ( For e.g #0 index device)\nstream1 = CamGear(source=0, logging=True).start() \n\n# define and start the stream on second source ( For e.g #1 index device)\nstream2 = CamGear(source=1, logging=True).start() \n\n# infinite loop\nwhile True:\n\n    frameA = stream1.read()\n    # read frames from stream1\n\n    frameB = stream2.read()\n    # read frames from stream2\n\n    # check if any of two frame is None\n    if frameA is None or frameB is None:\n        #if True break the infinite loop\n        break\n\n    # do something with both frameA and frameB here\n    cv2.imshow(\"Output Frame1\", frameA)\n    cv2.imshow(\"Output Frame2\", frameB)\n    # Show output window of stream1 and stream 2 separately\n\n    key = cv2.waitKey(1) & 0xFF\n    # check for 'q' key-press\n    if key == ord(\"q\"):\n        #if 'q' key-pressed break out\n        break\n\n    if key == ord(\"w\"):\n        #if 'w' key-pressed save both frameA and frameB at same time\n        cv2.imwrite(\"Image-1.jpg\", frameA)\n        cv2.imwrite(\"Image-2.jpg\", frameB)\n        #break   #uncomment this line to break out after taking images\n\ncv2.destroyAllWindows()\n# close output window\n\n# safely close both video streams\nstream1.stop()\nstream2.stop()\n

"},{"location":"help/camgear_ex/#using-variable-yt_dlp-parameters-in-camgear","title":"Using variable yt_dlp parameters in CamGear","text":"

CamGear provides exclusive attributes STREAM_RESOLUTION (for specifying stream resolution) & STREAM_PARAMS (for specifying underlying API(e.g. yt_dlp) parameters) with its options dictionary parameter.

The complete usage example is as follows:

More information on STREAM_RESOLUTION & STREAM_PARAMS attributes can be found here \u27b6

# import required libraries\nfrom vidgear.gears import CamGear\nimport cv2\n\n# specify attributes\noptions = {\"STREAM_RESOLUTION\": \"720p\", \"STREAM_PARAMS\": {\"nocheckcertificate\": True}}\n\n# Add YouTube Video URL as input source (for e.g https://youtu.be/bvetuLwJIkA)\n# and enable Stream Mode (`stream_mode = True`)\nstream = CamGear(\n    source=\"https://youtu.be/bvetuLwJIkA\", stream_mode=True, logging=True, **options\n).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"help/camgear_ex/#using-camgear-for-capturing-rtsprtmp-urls","title":"Using CamGear for capturing RTSP/RTMP URLs","text":"

You can open any network stream (such as RTSP/RTMP) just by providing its URL directly to CamGear's source parameter.

Here's a high-level wrapper code around CamGear API to enable auto-reconnection during capturing:

New in v0.2.2

This example was added in v0.2.2.

Enforcing UDP stream

You can easily enforce UDP for RTSP streams inplace of default TCP, by putting following lines of code on the top of your existing code:

# import required libraries\nimport os\n\n# enforce UDP\nos.environ[\"OPENCV_FFMPEG_CAPTURE_OPTIONS\"] = \"rtsp_transport;udp\"\n

Finally, use backend parameter value as backend=cv2.CAP_FFMPEG in CamGear.

from vidgear.gears import CamGear\nimport cv2\nimport datetime\nimport time\n\n\nclass Reconnecting_CamGear:\n    def __init__(self, cam_address, reset_attempts=50, reset_delay=5):\n        self.cam_address = cam_address\n        self.reset_attempts = reset_attempts\n        self.reset_delay = reset_delay\n        self.source = CamGear(source=self.cam_address).start()\n        self.running = True\n\n    def read(self):\n        if self.source is None:\n            return None\n        if self.running and self.reset_attempts > 0:\n            frame = self.source.read()\n            if frame is None:\n                self.source.stop()\n                self.reset_attempts -= 1\n                print(\n                    \"Re-connection Attempt-{} occured at time:{}\".format(\n                        str(self.reset_attempts),\n                        datetime.datetime.now().strftime(\"%m-%d-%Y %I:%M:%S%p\"),\n                    )\n                )\n                time.sleep(self.reset_delay)\n                self.source = CamGear(source=self.cam_address).start()\n                # return previous frame\n                return self.frame\n            else:\n                self.frame = frame\n                return frame\n        else:\n            return None\n\n    def stop(self):\n        self.running = False\n        self.reset_attempts = 0\n        self.frame = None\n        if not self.source is None:\n            self.source.stop()\n\n\nif __name__ == \"__main__\":\n    # open any valid video stream\n    stream = Reconnecting_CamGear(\n        cam_address=\"rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov\",\n        reset_attempts=20,\n        reset_delay=5,\n    )\n\n    # loop over\n    while True:\n\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if None-type\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # Show output window\n        cv2.imshow(\"Output\", frame)\n\n        # check for 'q' key if pressed\n        key = cv2.waitKey(1) & 0xFF\n        if key == ord(\"q\"):\n            break\n\n    # close output window\n    cv2.destroyAllWindows()\n\n    # safely close video stream\n    stream.stop()\n

"},{"location":"help/camgear_faqs/","title":"FAQs","text":""},{"location":"help/camgear_faqs/#camgear-faqs","title":"CamGear FAQs","text":""},{"location":"help/camgear_faqs/#what-is-camgear-api-and-what-does-it-do","title":"What is CamGear API and what does it do?","text":"

Answer: CamGear supports a diverse range of video streams which can handle/control video stream almost any IP/USB Cameras, multimedia video file format (upto 4k tested), any network stream URL such as http(s), rtp, rtsp, rtmp, mms, etc. In addition to this, it also supports live Gstreamer's RAW pipelines and YouTube video/livestreams URLs. For more info. see CamGear doc \u27b6.

"},{"location":"help/camgear_faqs/#im-only-familiar-with-opencv-how-to-get-started-with-camgear-api","title":"I'm only familiar with OpenCV, how to get started with CamGear API?","text":"

Answer: Answer: First, refer to the Switching from OpenCV guide, then go through CamGear documentation \u27b6. If you still have doubts, ask us on Gitter \u27b6 Community channel.

"},{"location":"help/camgear_faqs/#how-to-change-opencv-source-backend-in-camgear-api","title":"How to change OpenCV source backend in CamGear API?","text":"

Answer: See its Parameters \u27b6. Its, backend(int) parameter sets the backend of the source. Its value can be for e.g. backend = cv2.CAP_DSHOW in case of Direct Show.

"},{"location":"help/camgear_faqs/#how-to-get-framerate-of-the-source-in-camgear-api","title":"How to get framerate of the source in CamGear API?","text":"

Answer: CamGear's framerate global variable can be used to retrieve framerate of the input video stream. See this example \u27b6.

"},{"location":"help/camgear_faqs/#how-to-compile-opencv-with-gstreamer-support","title":"How to compile OpenCV with GStreamer support?","text":"

Answer: For compiling OpenCV with GSstreamer(>=v1.0.0) support:

Linux Windows MacOS

"},{"location":"help/camgear_faqs/#how-to-change-quality-and-parameters-of-youtube-streams-with-camgear","title":"How to change quality and parameters of YouTube Streams with CamGear?","text":"

Answer: CamGear provides exclusive attributes STREAM_RESOLUTION (for specifying stream resolution) & STREAM_PARAMS (for specifying underlying API(e.g. yt_dlp) parameters) with its options dictionary parameter. See this bonus example \u27b6.

"},{"location":"help/camgear_faqs/#how-to-open-rtsp-network-streams-with-camgear","title":"How to open RTSP network streams with CamGear?","text":"

Answer: You can open any local network stream (such as RTSP) just by providing its URL directly to CamGear's source parameter. See this bonus example \u27b6.

"},{"location":"help/camgear_faqs/#how-to-set-camera-settings-with-camgear","title":"How to set Camera Settings with CamGear?","text":"

Answer: See this usage example \u27b6.

"},{"location":"help/camgear_faqs/#can-i-play-4k8k-video-with-camgear-api","title":"Can I play 4K/8k video with CamGear API?","text":"

Answer: Yes, you can if your System Hardware supports it.

"},{"location":"help/camgear_faqs/#how-to-synchronize-between-two-cameras","title":"How to synchronize between two cameras?","text":"

Answer: See this bonus example \u27b6.

"},{"location":"help/camgear_faqs/#can-i-use-gpu-to-decode-the-video-source","title":"Can I use GPU to decode the video source?","text":"

Answer: See this issue comment \u27b6.

"},{"location":"help/camgear_faqs/#why-camgear-is-throwing-warning-that-threaded-queue-mode-is-disabled","title":"Why CamGear is throwing warning that Threaded Queue Mode is disabled?","text":"

Answer: That's a normal behavior. Please read about Threaded Queue Mode \u27b6

"},{"location":"help/general_faqs/","title":"General FAQs","text":""},{"location":"help/general_faqs/#general-faqs","title":"General FAQs","text":""},{"location":"help/general_faqs/#im-new-to-python-programming-or-its-usage-in-opencv-library-how-to-use-vidgear-in-my-projects","title":"\"I'm new to Python Programming or its usage in OpenCV Library\", How to use vidgear in my projects?","text":"

Answer: Before using vidgear, It's recommended to first go through the following dedicated blog sites and learn how OpenCV-Python syntax works (with examples):

Once done, visit Switching from OpenCV \u27b6 to easily replace OpenCV APIs with suitable Gears \u27b6 in your project. All the best!

If you run into any trouble or have any questions, then refer our Help section.

"},{"location":"help/general_faqs/#vidgear-is-using-multi-threading-but-python-is-notorious-for-its-poor-performance-in-multithreading","title":"\"VidGear is using Multi-threading, but Python is notorious for its poor performance in multithreading?\"","text":"

Answer: Refer vidgear's Threaded-Queue-Mode \u27b6

"},{"location":"help/general_faqs/#modulenotfounderror-no-module-named-vidgeargears-vidgear-is-not-a-package","title":"ModuleNotFoundError: No module named 'vidgear.gears'. 'vidgear' is not a package?","text":"

Answer: This error means you either have a file named vidgear.py in your python path or you've named your python script vidgear.py. Replace vidgear name with anything else to fix this error.

"},{"location":"help/general_faqs/#how-to-log-to-a-file-in-vidgear","title":"How to log to a file in VidGear?","text":"

Answer: VidGear provides exclusive VIDGEAR_LOGFILE environment variable to enable logging to a file while logging is enabled (i.e. logging=True) on respective Gear. You just have to set directory pathname (automatically creates vidgear.log file) or a log file pathname itself as value for this environment variable. This can be done on various Operating Systems as follows:

Remember enabling this logging to a file will completely disable any output on the terminal.

Linux Windows (Powershell) MacOS
# path to file\nexport VIDGEAR_LOGFILE=\"$HOME/foo.log\"\n\n# or just directory path \n# !!! Make sure `foo` path already exists !!!\nexport VIDGEAR_LOGFILE=\"$HOME/foo\"\n\n# to remove\nunset VIDGEAR_LOGFILE\n
# path to file\n$Env:VIDGEAR_LOGFILE = \"D:\\foo.log\"\n\n# or just directory path \n# !!! Make sure `foo` path already exists !!!\n$Env:VIDGEAR_LOGFILE = \"D:\\foo\"\n\n# to remove\n$Env:VIDGEAR_LOGFILE = \"\"\n
# path to file\nexport VIDGEAR_LOGFILE=\"$HOME/foo.log\"\n\n# or just directory path \n# !!! Make sure `foo` path already exists !!!\nexport VIDGEAR_LOGFILE=\"$HOME/foo\"\n\n# to remove\nunset VIDGEAR_LOGFILE\n

"},{"location":"help/general_faqs/#can-i-perform-deep-learning-task-with-vidgear","title":"Can I perform Deep Learning task with VidGear?","text":"

Answer: VidGear is a powerful Video Processing library (similar to OpenCV, FFmpeg, etc.) that can read, write, process, send & receive a sequence of video-frames in an optimized manner. But for Deep Learning or Machine Learning tasks, you have to use a third-party library. That being said, all VidGear's APIs can be used with any third-party Library(such as PyTorch, Tensorflow, etc.) that can leverage the overall performance if you're processing video/audio streams/frames in your application with Deep Learning tasks. Also, it eases the workflow since you have to write way fewer lines of code to read/store/process output videos.

"},{"location":"help/general_faqs/#can-i-ask-my-question-directly-without-raising-an-issue","title":"Can I ask my question directly without raising an issue?","text":"

Answer: Yes, please join our Gitter \u27b6 Community channel.

"},{"location":"help/general_faqs/#how-to-contribute-to-vidgear-development","title":"How to contribute to VidGear development?","text":"

Answer: See our Contribution Guidelines \u27b6

"},{"location":"help/general_faqs/#what-oses-are-supported-by-vidgear","title":"What OSes are supported by VidGear?","text":"

Answer: See Supported Systems \u27b6

"},{"location":"help/general_faqs/#what-python-versions-are-supported-by-vidgear","title":"What Python versions are supported by VidGear?","text":"

Answer: See Supported Python legacies \u27b6

"},{"location":"help/general_faqs/#can-i-include-vidgear-in-my-project-commercially-or-not","title":"Can I include VidGear in my project commercially or not?","text":"

Answer: Yes, you can, but strictly under the Terms and Conditions given in VidGear License \u27b6

"},{"location":"help/general_faqs/#i-love-using-vidgear-for-my-projects-how-can-i-support-it","title":"\"I Love using VidGear for my projects\", How can I support it?","text":"

Answer: See Helping VidGear \u27b6

"},{"location":"help/get_help/","title":"Getting Help","text":""},{"location":"help/get_help/#getting-help","title":"Getting Help","text":"Courtesy - Pinterest

Would you like to get help with VidGear?

There are several ways such as:

"},{"location":"help/get_help/#frequently-asked-questions","title":"Frequently Asked Questions","text":"

Got a question related to VidGear Working?

Checkout the Frequently Asked Questions - a curated list of all the questions with adequate answer that we commonly receive for quickly troubleshooting your problems:

"},{"location":"help/get_help/#bonus-examples","title":"Bonus Examples","text":"

How we do this with that API?

Checkout the Bonus Examples - a curated list of all experimental examples with unusual configuration that aren't included in general usage examples:

"},{"location":"help/get_help/#join-our-gitter-community-channel","title":"Join our Gitter Community channel","text":"

Have you come up with some new idea \ud83d\udca1 or looking for the fastest way troubleshoot your problems

Join and chat on our Gitter Community channel:

There you can ask quick questions, swiftly troubleshoot your problems, help others, share ideas & information, etc.

"},{"location":"help/get_help/#this-is-what-you-do-when","title":"This is what you do when...","text":""},{"location":"help/get_help/#reporting-an-issues","title":"Reporting an issues","text":"

Want to report a bug? Suggest a new feature?

Before you do, please read our guidelines \u27b6

"},{"location":"help/get_help/#preparing-a-pull-request","title":"Preparing a Pull Request","text":"

Interested in contributing to VidGear?

Before you do, please read our guidelines \u27b6

"},{"location":"help/netgear_async_ex/","title":"Bonus Examples","text":""},{"location":"help/netgear_async_ex/#netgear_async-examples","title":"NetGear_Async Examples","text":""},{"location":"help/netgear_async_ex/#using-netgear_async-with-webgear","title":"Using NetGear_Async with WebGear","text":"

The complete usage example is as follows:

New in v0.2.2

This example was added in v0.2.2.

"},{"location":"help/netgear_async_ex/#client-webgear-server","title":"Client + WebGear Server","text":"

Open a terminal on Client System where you want to display the input frames (and setup WebGear server) received from the Server and execute the following python code:

After running this code, Make sure to open Browser immediately otherwise NetGear_Async will soon exit with TimeoutError. You can also try setting timeout parameter to a higher value to extend this timeout.

Make sure you use different port value for NetGear_Async and WebGear API.

High CPU utilization may occur on Client's end. User discretion is advised.

Note down the IP-address of this system (required at Server's end) by executing the hostname -I command and also replace it in the following code.\"

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nfrom vidgear.gears.asyncio import WebGear\nfrom vidgear.gears.asyncio.helper import reducer\nimport uvicorn, asyncio, cv2\n\n# Define NetGear_Async Client at given IP address and define parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear_Async(\n    receive_mode=True,\n    pattern=1,\n    logging=True,\n).launch()\n\n# create your own custom frame producer\nasync def my_frame_producer():\n\n    # loop over Client's Asynchronous Frame Generator\n    async for frame in client.recv_generator():\n\n        # {do something with received frames here}\n\n        # reducer frames size if you want more performance otherwise comment this line\n        frame = await reducer(\n            frame, percentage=30, interpolation=cv2.INTER_AREA\n        )  # reduce frame by 30%\n\n        # handle JPEG encoding\n        encodedImage = cv2.imencode(\".jpg\", frame)[1].tobytes()\n        # yield frame in byte format\n        yield (b\"--frame\\r\\nContent-Type:image/jpeg\\r\\n\\r\\n\" + encodedImage + b\"\\r\\n\")\n        await asyncio.sleep(0)\n\n\nif __name__ == \"__main__\":\n    # Set event loop to client's\n    asyncio.set_event_loop(client.loop)\n\n    # initialize WebGear app without any source\n    web = WebGear(logging=True)\n\n    # add your custom frame producer to config with adequate IP address\n    web.config[\"generator\"] = my_frame_producer\n\n    # run this app on Uvicorn server at address http://localhost:8000/\n    uvicorn.run(web(), host=\"localhost\", port=8000)\n\n    # safely close client\n    client.close()\n\n    # close app safely\n    web.shutdown()\n

On successfully running this code, the output stream will be displayed at address http://localhost:8000/ in your Client's Browser.

"},{"location":"help/netgear_async_ex/#server","title":"Server","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

# import library\nfrom vidgear.gears.asyncio import NetGear_Async\nimport cv2, asyncio\n\n# initialize Server without any source\nserver = NetGear_Async(\n    source=None,\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n)\n\n# Create a async frame generator as custom source\nasync def my_frame_generator():\n\n    # !!! define your own video source here !!!\n    # Open any video stream such as live webcam\n    # video stream on first index(i.e. 0) device\n    stream = cv2.VideoCapture(0)\n\n    # loop over stream until its terminated\n    while True:\n\n        # read frames\n        (grabbed, frame) = stream.read()\n\n        # check if frame empty\n        if not grabbed:\n            break\n\n        # do something with the frame to be sent here\n\n        # yield frame\n        yield frame\n        # sleep for sometime\n        await asyncio.sleep(0)\n\n    # close stream\n    stream.release()\n\n\nif __name__ == \"__main__\":\n    # set event loop\n    asyncio.set_event_loop(server.loop)\n    # Add your custom source generator to Server configuration\n    server.config[\"generator\"] = my_frame_generator()\n    # Launch the Server\n    server.launch()\n    try:\n        # run your main function task until it is complete\n        server.loop.run_until_complete(server.task)\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    finally:\n        # finally close the server\n        server.close()\n

"},{"location":"help/netgear_async_faqs/","title":"FAQs","text":""},{"location":"help/netgear_async_faqs/#netgear_async-faqs","title":"NetGear_Async FAQs","text":""},{"location":"help/netgear_async_faqs/#what-is-netgear_async-api-and-what-does-it-do","title":"What is NetGear_Async API and what does it do?","text":"

Answer: NetGear_Async is an asyncio videoframe messaging framework, built on zmq.asyncio, and powered by high-performance asyncio event loop called uvloop to achieve unmatchable high-speed and lag-free video streaming over the network with minimal resource constraints. Basically, this API is able to transfer thousands of frames in just a few seconds without causing any significant load on your system. For more info. see NetGear_Async doc \u27b6

"},{"location":"help/netgear_async_faqs/#how-to-get-started-with-netgear_async-api","title":"How to get started with NetGear_Async API?","text":"

Answer: Answer: Answer: First, refer to the Switching from OpenCV guide, then go through NetGear_Async documentation \u27b6. If you still have doubts, ask us on Gitter \u27b6 Community channel.

See NetGear_Async doc \u27b6. Still in doubt, then ask us on Gitter \u27b6 Community channel.

"},{"location":"help/netgear_async_faqs/#netgear_async-is-throwing-modulenotfounderror-on-importing-why","title":"\"NetGear_Async is throwing ModuleNotFoundError on importing\", Why?","text":"

Answer: This error means, VidGear is installed WITHOUT asyncio package support on your machine. For this support, see Requirements \u27b6.

"},{"location":"help/netgear_async_faqs/#what-is-the-key-difference-between-netgear_async-and-netgear-apis","title":"What is the key difference between NetGear_Async and NetGear APIs?","text":"

Answer:

Key Difference: NetGear_Async is highly memory efficient, but has less features as compared to NetGear API which is marginally faster too.

"},{"location":"help/netgear_async_faqs/#can-i-use-multi-server-bi-directional-like-modes-in-netgear_async","title":"Can I use Multi-Server, Bi-Directional like modes in NetGear_Async?","text":"

Answer: No, NetGear_Async does NOT provide support for any NetGear's Exclusive modes yet.

"},{"location":"help/netgear_async_faqs/#how-to-use-netgear_async-with-custom-server-source-from-opencv","title":"How to use NetGear_Async with custom Server Source from OpenCV?","text":"

Answer: See this usage example \u27b6.

"},{"location":"help/netgear_async_faqs/#why-netgear_async-is-running-slow","title":"Why NetGear_Async is running slow?","text":"

Answer: Checkout tips suggested in this answer \u27b6

"},{"location":"help/netgear_ex/","title":"Bonus Examples","text":""},{"location":"help/netgear_ex/#netgear-examples","title":"NetGear Examples","text":""},{"location":"help/netgear_ex/#using-netgear-with-webgear","title":"Using NetGear with WebGear","text":"

The complete usage example is as follows:

New in v0.2.2

This example was added in v0.2.2.

"},{"location":"help/netgear_ex/#client-webgear-server","title":"Client + WebGear Server","text":"

Open a terminal on Client System where you want to display the input frames (and setup WebGear server) received from the Server and execute the following python code:

After running this code, Make sure to open Browser immediately otherwise NetGear will soon exit with RuntimeError. You can also try setting max_retries and request_timeout like attributes to a higher value to avoid this.

Make sure you use different port value for NetGear and WebGear API.

High CPU utilization may occur on Client's end. User discretion is advised.

Note down the local IP-address of this system (required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

# import necessary libs\nimport uvicorn, asyncio, cv2\nfrom vidgear.gears import NetGear\nfrom vidgear.gears.asyncio import WebGear\nfrom vidgear.gears.asyncio.helper import reducer\n\n# initialize WebGear app without any source\nweb = WebGear(logging=True)\n\n\n# activate jpeg encoding and specify other related parameters\noptions = {\n    \"jpeg_compression\": True,\n    \"jpeg_compression_quality\": 90,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# create your own custom frame producer\nasync def my_frame_producer():\n    # initialize global params\n    # Define NetGear Client at given IP address and define parameters\n    # !!! change following IP address '192.168.x.xxx' with yours !!!\n    client = NetGear(\n        receive_mode=True,\n        address=\"192.168.x.xxx\",\n        port=\"5454\",\n        protocol=\"tcp\",\n        pattern=1,\n        logging=True,\n        **options,\n    )\n\n    # loop over frames\n    while True:\n        # receive frames from network\n        frame = client.recv()\n\n        # if NoneType\n        if frame is None:\n            break\n\n        # do something with your OpenCV frame here\n\n        # reducer frames size if you want more performance otherwise comment this line\n        frame = await reducer(\n            frame, percentage=30, interpolation=cv2.INTER_AREA\n        )  # reduce frame by 30%\n\n        # handle JPEG encoding\n        encodedImage = cv2.imencode(\".jpg\", frame)[1].tobytes()\n        # yield frame in byte format\n        yield (b\"--frame\\r\\nContent-Type:image/jpeg\\r\\n\\r\\n\" + encodedImage + b\"\\r\\n\")\n        await asyncio.sleep(0)\n    # close stream\n    client.close()\n\n\n# add your custom frame producer to config with adequate IP address\nweb.config[\"generator\"] = my_frame_producer\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

On successfully running this code, the output stream will be displayed at address http://localhost:8000/ in your Client's Browser.

"},{"location":"help/netgear_ex/#server","title":"Server","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate jpeg encoding and specify other related parameters\noptions = {\n    \"jpeg_compression\": True,\n    \"jpeg_compression_quality\": 90,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = VideoGear(source=0).start()\n\n# Define NetGear server at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if None-type\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"help/netgear_ex/#using-netgear-with-webgear_rtc","title":"Using NetGear with WebGear_RTC","text":"

The complete usage example is as follows:

New in v0.2.4

This example was added in v0.2.4.

"},{"location":"help/netgear_ex/#client-webgear_rtc-server","title":"Client + WebGear_RTC Server","text":"

Open a terminal on Client System where you want to display the input frames (and setup WebGear_RTC server) received from the Server and execute the following python code:

After running this code, Make sure to open Browser immediately otherwise NetGear will soon exit with RuntimeError. You can also try setting max_retries and request_timeout like attributes to a higher value to avoid this.

Make sure you use different port value for NetGear and WebGear_RTC API.

High CPU utilization may occur on Client's end. User discretion is advised.

Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

For VideoCapture APIs you also need to implement start() in addition to read() and stop() methods in your Custom Streaming Class as shown in following example, otherwise WebGear_RTC will fail to work!

# import necessary libs\nimport uvicorn, cv2\nfrom vidgear.gears import NetGear\nfrom vidgear.gears.helper import reducer\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# create your own custom streaming class\nclass Custom_Stream_Class:\n    \"\"\"\n    Custom Streaming using NetGear Receiver\n    \"\"\"\n\n    def __init__(\n        self,\n        address=None,\n        port=\"5454\",\n        protocol=\"tcp\",\n        pattern=1,\n        logging=True,\n        **options,\n    ):\n        # initialize global params\n        # Define NetGear Client at given IP address and define parameters\n        self.client = NetGear(\n            receive_mode=True,\n            address=address,\n            port=port,\n            protocol=protocol,\n            pattern=pattern,\n            logging=logging,\n            **options\n        )\n        self.running = False\n\n    def start(self):\n\n        # don't forget this function!!!\n        # This function is specific to VideoCapture APIs only\n\n        if not self.source is None:\n            self.source.start()\n\n    def read(self):\n\n        # don't forget this function!!!\n\n        # check if source was initialized or not\n        if self.source is None:\n            return None\n        # check if we're still running\n        if self.running:\n            # receive frames from network\n            frame = self.client.recv()\n            # check if frame is available\n            if not (frame is None):\n\n                # do something with your OpenCV frame here\n\n                # reducer frames size if you want more performance otherwise comment this line\n                frame = reducer(frame, percentage=20)  # reduce frame by 20%\n\n                # return our gray frame\n                return frame\n            else:\n                # signal we're not running now\n                self.running = False\n        # return None-type\n        return None\n\n    def stop(self):\n\n        # don't forget this function!!!\n\n        # flag that we're not running\n        self.running = False\n        # close stream\n        if not (self.client is None):\n            self.client.close()\n            self.client = None\n\n\n# activate jpeg encoding and specify NetGear related parameters\noptions = {\n    \"jpeg_compression\": True,\n    \"jpeg_compression_quality\": 90,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# assign your Custom Streaming Class with adequate NetGear parameters\n# to `custom_stream` attribute in options parameter of WebGear_RTC.\noptions = {\n    \"custom_stream\": Custom_Stream_Class(\n        address=\"192.168.x.xxx\",\n        port=\"5454\",\n        protocol=\"tcp\",\n        pattern=1,\n        logging=True,\n        **options\n    )\n}\n\n# initialize WebGear_RTC app without any source\nweb = WebGear_RTC(logging=True, **options)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

On successfully running this code, the output stream will be displayed at address http://localhost:8000/ in your Client's Browser.

"},{"location":"help/netgear_ex/#server_1","title":"Server","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate jpeg encoding and specify other related parameters\noptions = {\n    \"jpeg_compression\": True,\n    \"jpeg_compression_quality\": 90,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = VideoGear(source=0).start()\n\n# Define NetGear server at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"help/netgear_faqs/","title":"FAQs","text":""},{"location":"help/netgear_faqs/#netgear-faqs","title":"NetGear FAQs","text":""},{"location":"help/netgear_faqs/#what-is-netgear-api-and-what-does-it-do","title":"What is NetGear API and what does it do?","text":"

Answer: NetGear is exclusively designed to transfer video frames & data synchronously (Pair & Request/Reply) as well as asynchronously (Publish/Subscribe) between various interconnecting systems over the network in real-time. For more info. see NetGear doc \u27b6

"},{"location":"help/netgear_faqs/#how-to-get-started-with-netgear-api","title":"How to get started with NetGear API?","text":"

Answer: First, refer to the Switching from OpenCV guide, then go through NetGear documentation \u27b6. If you still have doubts, ask us on Gitter \u27b6 Community channel.

"},{"location":"help/netgear_faqs/#what-exclusive-modes-are-compatible-with-each-other-in-netgear-api","title":"What Exclusive Modes are compatible with each other in NetGear API?","text":"

Here's the compatibility chart for NetGear's Exclusive Modes:

Exclusive Modes Multi-Servers Multi-Clients Secure Bidirectional SSH Tunneling Multi-Servers - No (throws error) Yes Yes No (throws error) Multi-Clients No (throws error) - Yes Yes No (throws error) Secure Yes Yes - Yes Yes Bidirectional Yes Yes Yes - Yes SSH Tunneling No (throws error) No (throws error) Yes Yes -

"},{"location":"help/netgear_faqs/#why-netgear-is-running-slow","title":"Why NetGear is running slow?","text":"

Answer: Here are few tips to troubleshoot performance on your machine:

"},{"location":"help/netgear_faqs/#how-to-find-local-ip-address-on-different-os-platforms","title":"How to find local IP-address on different OS platforms?","text":"

Answer: For finding local IP-address of your machine:

On Linux OSOn Windows OSOn MAC OS

"},{"location":"help/netgear_faqs/#how-to-send-data-along-with-frames-in-multi-servers-and-multi-clients-modes","title":"How to send data along with frames in Multi-Servers and Multi-Clients Modes?","text":"

Answer: See Multi-Servers usage example \u27b6 and Multi-Clients usage example \u27b6

"},{"location":"help/netgear_faqs/#how-to-use-enable-encryption-and-authentication-in-netgear-api","title":"How to use enable Encryption and Authentication in NetGear API?","text":"

Answer: See its Secure Mode doc \u27b6.

"},{"location":"help/netgear_faqs/#how-to-send-custom-data-along-with-frames-bidirectionally-in-netgear-api","title":"How to send custom data along with frames bidirectionally in NetGear API?","text":"

Answer: See its Bidirectional Mode doc \u27b6.

"},{"location":"help/netgear_faqs/#how-to-access-netgear-api-outside-network-or-remotely","title":"How to access NetGear API outside network or remotely?","text":"

Answer: See its SSH Tunneling Mode doc \u27b6.

"},{"location":"help/netgear_faqs/#are-there-any-side-effect-of-sending-data-with-frames","title":"Are there any side-effect of sending data with frames?","text":"

Answer: Yes, it may lead to additional LATENCY depending upon the size/amount of the data being transferred. User discretion is advised.

"},{"location":"help/netgear_faqs/#why-netgear-api-not-working-correctly","title":"Why NetGear API not working correctly?","text":"

Answer: First, carefully go through NetGear doc \u27b6 that contains detailed information. Also, checkout PyZmq Docs \u27b6 for its various settings/parameters. If still it doesn't work for you, then let us know on Gitter \u27b6

"},{"location":"help/netgear_faqs/#how-to-solve-zmqerrorzmqerror-errors","title":"How to solve zmq.error.ZMQError errors?","text":"

Answer: For those used to the idea that a \"server\" provides their address to a client, then you should recheck your preconceptions! Please read the Netgear instructions carefully, and you will note that it is the client device that defines the IP that is provided to the server config. If you get this the wrong way (using the server IP on the client), then you will get a zmq.error.ZMQError error. Make sure it is the client's IP shared across the two systems.

"},{"location":"help/pigear_ex/","title":"Bonus Examples","text":""},{"location":"help/pigear_ex/#pigear-examples","title":"PiGear Examples","text":""},{"location":"help/pigear_ex/#changing-output-pixel-format-in-pigear-api-with-picamera2-backend","title":"Changing Output Pixel Format in PiGear API with Picamera2 Backend","text":"

With the Picamera2 backend, you can also define a custom format (format of output frame pixels) in PiGear API.

Handling output frames with a custom pixel format correctly

While defining custom format as an optional parameter, it is advised to also define the colorspace parameter in the PiGear API. This is required only under TWO conditions:

Otherwise, output frames might NOT be compatible with OpenCV functions, and you need to handle these frames manually!

Picamera2 library has an unconventional naming convention for its pixel formats.

Please note that, Picamera2 takes its pixel format naming from libcamera, which in turn takes them from certain underlying Linux components. The results are not always the most intuitive. For example, OpenCV users will typically want each pixel to be a (B, G, R) triple for which the RGB888 format should be chosen, and not BGR888. Similarly, OpenCV users wanting an alpha channel should select XRGB8888.

For more information, refer Picamera2 docs \u27b6

YUV420/YVU420YUYV

For reducing the size of frames in memory it is advised to use the YUV420 pixels format.

In this example we will be defining custom YUV420 (or YVU420) pixels format of output frame, and converting it back to BGR to be able to display with OpenCV.

You could also instead define colorspace=\"COLOR_YUV420p2RGB\" parameter in PiGear API for converting it back to BGR similarly.

# import required libraries\nfrom vidgear.gears import PiGear\nimport cv2\n\n# formulate `format` Picamera2 API \n# configurational parameters\noptions = {\n    \"format\": \"YUV420\" # or use `YVU420`\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    yuv420_frame = stream.read()\n\n    # check for frame if Nonetype\n    if yuv420_frame is None:\n        break\n\n    # {do something with the `YUV420` frame here}\n\n    # convert `YUV420` to `BGR`\n    bgr = cv2.cvtColor(yuv420_frame, cv2.COLOR_YUV420p2BGR)\n\n    # {do something with the `BGR` frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", bgr)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

YUYV is a one packed 4:2:2 YUV format that is popularly used by USB cameras.

Make sure YUYV pixel format is supported by your USB camera.

In this example we will be defining custom YUYV pixels format of output frame, and converting it back to BGR to be able to display with OpenCV.

You could also instead define colorspace=\"COLOR_YUV2BGR_YUYV\" parameter in PiGear API for converting it back to BGR similarly.

# import required libraries\nfrom vidgear.gears import PiGear\nimport cv2\n\n# formulate `format` Picamera2 API \n# configurational parameters\noptions = {\n    \"format\": \"YUYV\"\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    yuv420_frame = stream.read()\n\n    # check for frame if Nonetype\n    if yuv420_frame is None:\n        break\n\n    # {do something with the `YUV420` frame here}\n\n    # convert `YUV420` to `BGR`\n    bgr = cv2.cvtColor(yuv420_frame, cv2.COLOR_YUV2BGR_YUYV)\n\n    # {do something with the `BGR` frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", bgr)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"help/pigear_ex/#dynamically-adjusting-raspberry-pi-camera-parameters-at-runtime-in-pigear-api","title":"Dynamically Adjusting Raspberry Pi Camera Parameters at Runtime in PiGear API","text":"New Picamera2 backendLegacy Picamera backend

With the picamera2 backend, using stream global parameter in the PiGear API, you can change all camera controls (except output resolution and format) at runtime after the camera has started.

Accessing all available camera controls

A complete list of all the available camera controls can be found in the picamera2 docs \u27b6, and also by inspecting the camera_controls property of the Picamera2 object available with stream global parameter in PiGear API:

# import required libraries\nfrom vidgear.gears import PiGear\n\n# open any pi video stream\nstream = PiGear()\n\n#display all available camera controls\nprint(stream.stream.camera_controls)\n\n# safely close video stream\nstream.stop()\n

This returns a dictionary with the control names as keys, and each value being a tuple of (min, max, default) values for that control. The default value should be interpreted with some caution as in many cases libcamera's default value will be overwritten by the camera tuning as soon as the camera is started.

In this example, we will set the initial Camera Module's brightness value to -0.5 (dark), and will change it to 0.5 (bright) when the Z key is pressed at runtime:

Delay in setting runtime controls

There will be a delay of several frames before the controls take effect. This is because there is perhaps quite a large number of requests for camera frames already in flight, and for some controls (exposure time and analogue gain specifically), the camera may actually take several frames to apply the updates.

Using with construct for Guaranteed Camera Control Updates at Runtime

While directly modifying using set_controls method might seem convenient, it doesn't guarantee that all camera control settings are applied within the same frame at runtime. The with construct provides a structured approach to managing camera control updates in real-time. Here's how to use it:

# import required libraries\nfrom vidgear.gears import PiGear\n\n# formulate initial configurational parameters\noptions = \"controls\": {\"ExposureTime\": 5000, \"AnalogueGain\": 0.5}\n\n# open pi video stream with these parameters\nstream = PiGear(logging=True, **options).start() \n\n# Enter context manager and set runtime controls\n# Within this block, the controls are guaranteed to be applied atomically\nwith stream.stream.controls as controls:  \n    controls.ExposureTime = 10000  # Set new exposure time\n    controls.AnalogueGain = 1.0     # Set new analogue gain\n\n# ...rest of code goes here...\n\n# safely close video stream\nstream.stop()\n
# import required libraries\nfrom vidgear.gears import PiGear\nimport cv2\n\n# formulate initial configurational parameters\n# set brightness to -0.5 (dark)\noptions = {\"controls\": {\"Brightness\": -0.5}}\n\n# open pi video stream with these parameters\nstream = PiGear(logging=True, **options).start() \n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n    # check for 'z' key if pressed\n    if key == ord(\"z\"):\n        # change brightness to 0.5 (bright)\n        stream.stream.set_controls({\"Brightness\": 0.5})\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

You can also use the stream global parameter in PiGear with thepicamera backend to feed any picamera parameters at runtime after the camera has started.

PiGear API switches to the legacy picamerabackend if the picamera2 library is unavailable.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera optional parameter boolean attribute.

In this example we will set initial Camera Module's brightness value 80 (brighter), and will change it 30 (darker) when Z key is pressed at runtime:

# import required libraries\nfrom vidgear.gears import PiGear\nimport cv2\n\n# formulate initial configurational parameters \n# set brightness to `80` (bright)\noptions = {\"brightness\": 80} \n\n# open pi video stream with these parameters\nstream = PiGear(logging=True, **options).start() \n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n    # check for 'z' key if pressed\n    if key == ord(\"z\"):\n        # change brightness to `30` (darker)\n        stream.stream.brightness = 30\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n
"},{"location":"help/pigear_ex/#accessing-multiple-camera-through-its-index-in-pigear-api","title":"Accessing Multiple Camera through its Index in PiGear API","text":"

With the camera_num parameter in the PiGear API, you can easily select the camera index to be used as the source, allowing you to drive these multiple cameras simultaneously from within a single Python session.

The camera_num value can only be zero or greater, otherwise, PiGear API will throw ValueError for any negative value.

New Picamera2 backendLegacy Picamera backend

With the picamera2 backend, you can use the camera_num parameter in PiGear to select the camera index to be used as the source if you have multiple Raspberry Pi camera modules (such as CM4) and/or USB cameras connected simultaneously to your Raspberry Pi.

Accessing metadata about connected cameras.

You can call the global_camera_info() method of the Picamera2 object available with stream global parameter in PiGear API to find out what cameras are attached. This returns a list containing one dictionary for each camera, ordered according the camera number you would pass to the camera_num parameter in PiGear API to open that device. The dictionary contains:

You should always check this list to discover which camera is which as the order can change when the system boots or USB cameras are re-connected as follows:

# import required libraries\nfrom vidgear.gears import PiGear\n\n# open any pi video stream\nstream = PiGear()\n\n#display all available cameras metadata\nprint(stream.stream.global_camera_info())\n\n# safely close video stream\nstream.stop()\n

The PiGear API can accurately differentiate between USB and Raspberry Pi camera modules by utilizing the camera's metadata.

In this example, we will select the USB Camera connected at index 1 on the Raspberry Pi as the primary source for extracting frames in PiGear API:

Limited support for USB Cameras

This example also works with USB Cameras, However:

This example assumes a USB Camera is connected at index 1, and some other camera connected at index 0 on your Raspberry Pi.

# import required libraries\nfrom vidgear.gears import PiGear\nfrom libcamera import Transform\nimport cv2\n\n# formulate various Picamera2 API \n# configurational parameters for USB camera\noptions = {\n    \"sensor\": {\"output_size\": (480, 320)},  # will override `resolution`\n    \"format\": \"RGB888\" # BGR format for this example\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream at index `1` with defined parameters\nstream = PiGear(camera_num=1, resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

With the Picamera backend, you should not change the camera_num parameter unless you are using the Raspberry Pi 3/3+/4 Compute Module IO Boards or third party Arducam Camarray Multiple Camera Solutions, which supports attaching multiple camera modules to the same Raspberry Pi board using appropriate I/O connections.

You can use the camera_num parameter in PiGear with the picamera backend to select the camera index to be used as the source if you have multiple Raspberry Pi camera modules connected.

PiGear API switches to the legacy picamerabackend if the picamera2 library is unavailable.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera optional parameter boolean attribute.

In this example, we will select the Camera Module connected at index 1 on the Raspberry Pi as the primary source for extracting frames in PiGear API:

This example assumes a Camera Module is connected at index 1 on your Raspberry Pi.

# import required libraries\nfrom vidgear.gears import PiGear\nimport cv2\n\n# formulate various Picamera API \n# configurational parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream at index `1` with defined parameters\nstream = PiGear(camera_num=1, resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"help/pigear_faqs/","title":"FAQs","text":""},{"location":"help/pigear_faqs/#pigear-faqs","title":"PiGear FAQs","text":""},{"location":"help/pigear_faqs/#what-is-pigear-api-and-what-does-it-do","title":"What is PiGear API and what does it do?","text":"

Answer: PiGear is a specialized API similar to the CamGear API but optimized for Raspberry Pi Boards, offering comprehensive support for camera modules (e.g., OmniVision OV5647, Sony IMX219), along with limited compatibility for USB cameras. For more info. see PiGear doc \u27b6

"},{"location":"help/pigear_faqs/#im-only-familiar-with-opencv-how-to-get-started-with-pigear-api","title":"I'm only familiar with OpenCV, how to get started with PiGear API?","text":"

Answer: First, refer to the Switching from OpenCV guide, then go through PiGear documentation. If you still have doubts, ask us on Gitter \u27b6 Community channel.

"},{"location":"help/pigear_faqs/#why-my-camera-module-is-not-detected-by-pigear","title":"Why my camera module is not detected by PiGear?","text":"

Answer: Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using PiGear API. Also, recheck/change your Camera Module's ribbon-cable and Camera Module itself, if it damaged or got broken somehow.

"},{"location":"help/pigear_faqs/#how-to-select-camera-index-on-pi-compute-io-board-with-two-cameras-attached","title":"How to select camera index on Pi Compute IO board with two Cameras attached?","text":"

Answer: Refer this bonus example \u27b6

"},{"location":"help/pigear_faqs/#why-pigear-is-throwing-systemerror","title":"Why PiGear is throwing SystemError?","text":"

Answer: This means your Raspberry Pi CSI ribbon-cable is not connected properly to your Camera Module, or damaged, or even both.

"},{"location":"help/pigear_faqs/#how-to-assign-various-configurational-settings-for-camera-module-with-pigear","title":"How to assign various configurational settings for Camera Module with PiGear?","text":"

Answer: See this usage example \u27b6

"},{"location":"help/pigear_faqs/#video-output-is-too-dark-with-pigear-why","title":"\"Video output is too dark with PiGear\", Why?","text":"

Answer: The camera configuration settings might be incorrect. Check this usage example \u27b6 and try tinkering parameters like sensor_mode, shutter_speed, and exposure_mode. Additionally, if your framerate parameter value is too high, try lowering it.

"},{"location":"help/pigear_faqs/#how-to-dynamically-adjust-raspberry-pi-camera-parameters-at-runtime-with-pigear","title":"How to dynamically adjust Raspberry Pi Camera Parameters at runtime with PiGear?","text":"

Answer: See this bonus example \u27b6

"},{"location":"help/pigear_faqs/#is-it-possible-to-change-output-frames-pixel-format-in-pigear-api","title":"Is it possible to change output frames Pixel Format in PiGear API?","text":"

Answer: Yes it is possible with Picamera2 Backend. See this bonus example \u27b6

"},{"location":"help/screengear_ex/","title":"Bonus Examples","text":""},{"location":"help/screengear_ex/#screengear-examples","title":"ScreenGear Examples","text":""},{"location":"help/screengear_ex/#using-screengear-with-netgear-and-writegear","title":"Using ScreenGear with NetGear and WriteGear","text":"

The complete usage example is as follows:

New in v0.2.2

This example was added in v0.2.2.

"},{"location":"help/screengear_ex/#client-writegear","title":"Client + WriteGear","text":"

Open a terminal on Client System (where you want to save the input frames received from the Server) and execute the following python code:

Note down the IP-address of this system(required at Server's end) by executing the command: hostname -I and also replace it in the following code.

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# define various tweak flags\noptions = {\"flag\": 0, \"copy\": True, \"track\": False}\n\n# Define Netgear Client at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# Define writer with default parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\")\n\n# loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n\n# safely close writer\nwriter.close()\n
"},{"location":"help/screengear_ex/#server-screengear","title":"Server + ScreenGear","text":"

Now, Open the terminal on another Server System (with a montior/display attached to it), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import ScreenGear\nfrom vidgear.gears import NetGear\n\n# define dimensions of screen w.r.t to given monitor to be captured\noptions = {\"top\": 40, \"left\": 0, \"width\": 100, \"height\": 100}\n\n# open stream with defined parameters\nstream = ScreenGear(logging=True, **options).start()\n\n# define various netgear tweak flags\noptions = {\"flag\": 0, \"copy\": True, \"track\": False}\n\n# Define Netgear server at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"help/screengear_ex/#using-screengear-with-webgear_rtc","title":"Using ScreenGear with WebGear_RTC","text":"

The complete usage example is as follows:

New in v0.2.4

This example was added in v0.2.4.

Bare-MinimumAdvanced
# import necessary libs\nimport uvicorn, cv2\nfrom vidgear.gears import ScreenGear\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# assign your ScreenGear class with adequate parameters \n# to `custom_stream` attribute in options parameter\noptions = {\"custom_stream\": ScreenGear(logging=True)}\n\n# initialize WebGear_RTC app without any source\nweb = WebGear_RTC(logging=True, **options)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

For VideoCapture APIs you also need to implement start() in addition to read() and stop() methods in your Custom Streaming Class as shown in following example, otherwise WebGear_RTC will fail to work!

# import necessary libs\nimport uvicorn, cv2\nfrom vidgear.gears import ScreenGear\nfrom vidgear.gears.helper import reducer\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# create your own custom streaming class\nclass Custom_Stream_Class:\n    \"\"\"\n    Custom Streaming using ScreenGear\n    \"\"\"\n\n    def __init__(self, backend=\"mss\", logging=False):\n\n        # !!! define your own video source here !!!\n        self.source = ScreenGear(backend=backend, logging=logging)\n\n        # define running flag\n        self.running = True\n\n    def start(self):\n\n        # don't forget this function!!!\n        # This function is specific to VideoCapture APIs only\n\n        if not self.source is None:\n            self.source.start()\n\n    def read(self):\n\n        # don't forget this function!!!\n\n        # check if source was initialized or not\n        if self.source is None:\n            return None\n        # check if we're still running\n        if self.running:\n            # read frame from provided source\n            frame = self.source.read()\n            # check if frame is available\n            if not(frame is None):\n\n                # do something with your OpenCV frame here\n\n                # reducer frames size if you want more performance otherwise comment this line\n                frame = reducer(frame, percentage=20)  # reduce frame by 20%\n\n                # return our gray frame\n                return frame\n            else:\n                # signal we're not running now\n                self.running = False\n        # return None-type\n        return None\n\n    def stop(self):\n\n        # don't forget this function!!!\n\n        # flag that we're not running\n        self.running = False\n        # close stream\n        if not self.source is None:\n            self.source.stop()\n\n\n# assign your Custom Streaming Class with adequate ScreenGear parameters\n# to `custom_stream` attribute in options parameter\noptions = {\"custom_stream\": Custom_Stream_Class(backend=\"pil\", logging=True)}\n\n# initialize WebGear_RTC app without any source\nweb = WebGear_RTC(logging=True, **options)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

"},{"location":"help/screengear_faqs/","title":"FAQs","text":""},{"location":"help/screengear_faqs/#screengear-faqs","title":"ScreenGear FAQs","text":""},{"location":"help/screengear_faqs/#what-is-screengear-api-and-what-does-it-do","title":"What is ScreenGear API and what does it do?","text":"

Answer: ScreenGear is designed exclusively for targeting rapid Screencasting Capabilities, which means it can grab frames from your monitor in real-time, either by defining an area on the computer screen or full-screen, at the expense of inconsiderable latency. ScreenGear also seamlessly support frame capturing from multiple monitors as well as supports multiple backends. For more info. see ScreenGear doc \u27b6

"},{"location":"help/screengear_faqs/#im-only-familiar-with-opencv-how-to-get-started-with-screengear-api","title":"I'm only familiar with OpenCV, how to get started with ScreenGear API?","text":"

Answer: First, refer to the Switching from OpenCV guide, then go through ScreenGear documentation. If you still have doubts, ask us on Gitter \u27b6 Community channel.

"},{"location":"help/screengear_faqs/#screengear-is-slow","title":"ScreenGear is Slow?","text":"

Answer: This maybe due to selected backend for ScreenGear API is not compatible with your machine. See this usage example to change backend \u27b6. Try different backends, and select which works the best for your machine.

"},{"location":"help/screengear_faqs/#how-to-define-area-on-screen-to-record-with-screengear","title":"How to define area on screen to record with ScreenGear?","text":"

Answer: See this usage example \u27b6

"},{"location":"help/screengear_faqs/#how-to-record-video-from-all-connected-screens","title":"How to record video from all connected screens?","text":"

Answer: With mss backend, see ScreenGear's monitor parameter that sets the index of the monitor to grab a frame from. If its value is -1, it will record from all monitors. More information can be found here \u27b6

"},{"location":"help/screengear_faqs/#im-getting-attributeerror-dxcamera-object-has-no-attribute-is_capturing-error","title":"I'm getting \"AttributeError: 'DXCamera' object has no attribute 'is_capturing'\" Error?","text":"

Answer: This is a well-known error in backend dxcam library which occurs when you've multiple GPUs on your Windows machine. To workaround this, you need select Internal GPU in settings as follows:

On Windows 11On Windows 10

In Settings, go to System > Display > Graphics and add your Python.exe as \"Desktop App\", then select \"Power saving\" as follows:

And finally press Save button.

In Settings, go to Graphics Settings and add your Python.exe as \"Desktop App\", then select \"Power saving\" as follows:

And finally press Save button.

"},{"location":"help/stabilizer_ex/","title":"Bonus Examples","text":""},{"location":"help/stabilizer_ex/#stabilizer-class-examples","title":"Stabilizer Class Examples","text":""},{"location":"help/stabilizer_ex/#saving-stabilizer-class-output-with-live-audio-input","title":"Saving Stabilizer Class output with Live Audio Input","text":"

In this example code, we will merging the audio from a Audio Device (for e.g. Webcam inbuilt mic input) with Stabilized frames incoming from the Stabilizer Class (which is also using same Webcam video input through OpenCV), and save the final output as a compressed video file, all in real time:

New in v0.2.2

This example was added in v0.2.2.

Example Assumptions

Identifying and Specifying sound card on different OS platforms Windows Linux MacOS

Windows OS users can use the dshow (DirectShow) to list audio input device which is the preferred option for Windows users. You can refer following steps to identify and specify your sound card:

If audio still doesn't work then checkout this troubleshooting guide \u27b6 or reach us out on Gitter \u27b6 Community channel

Linux OS users can use the alsa to list input device to capture live audio input such as from a webcam. You can refer following steps to identify and specify your sound card:

If audio still doesn't work then reach us out on Gitter \u27b6 Community channel

MAC OS users can use the avfoundation to list input devices for grabbing audio from integrated iSight cameras as well as cameras connected via USB or FireWire. You can refer following steps to identify and specify your sound card on MacOS/OSX machines:

If audio still doesn't work then reach us out on Gitter \u27b6 Community channel

Make sure this -i audio-source it compatible with provided video-source, otherwise you could encounter multiple errors or no output at all.

You MUST use -input_framerate attribute to set exact value of input framerate when using external audio in Real-time Frames mode, otherwise audio delay will occur in output streams.

# import required libraries\nfrom vidgear.gears import WriteGear\nfrom vidgear.gears.stabilizer import Stabilizer\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = cv2.VideoCapture(0)\n\n# initiate stabilizer object with defined parameters\nstab = Stabilizer(smoothing_radius=30, crop_n_zoom=True, border_size=5, logging=True)\n\n# change with your webcam soundcard, plus add additional required FFmpeg parameters for your writer\noutput_params = {\n    \"-input_framerate\": stream.get(cv2.CAP_PROP_FPS),\n    \"-thread_queue_size\": \"512\",\n    \"-ac\": \"2\",\n    \"-ar\": \"48000\",\n    \"-f\": \"alsa\", # (1)\n    \"-i\": \"hw:1\",\n}\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4\nwriter = WriteGear(output=\"Output.mp4\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n        break\n\n    # send current frame to stabilizer for processing\n    stabilized_frame = stab.stabilize(frame)\n\n    # wait for stabilizer which still be initializing\n    if stabilized_frame is None:\n        continue\n\n    # {do something with the stabilized frame here}\n\n    # write stabilized frame to writer\n    writer.write(stabilized_frame)\n\n\n# clear stabilizer resources\nstab.clean()\n\n# safely close video stream\nstream.release()\n\n# safely close writer\nwriter.close()\n
  1. Always keep this line above -i parameter!

"},{"location":"help/stabilizer_ex/#saving-stabilizer-class-output-with-file-audio-input","title":"Saving Stabilizer Class output with File Audio Input","text":"

In this example code, we will be directly merging the audio from a Video-File (to be stabilized) with its processed stabilized frames into a compressed video output in real time:

New in v0.2.4

This example was added in v0.2.4.

Make sure this input video-file (to be stabilized) contains valid audio source, otherwise you could encounter multiple errors or no output at all.

You MUST use -input_framerate attribute to set exact value of input framerate when using external audio in Real-time Frames mode, otherwise audio delay will occur in output streams.

Use -disable_force_termination flag when video duration is too short(<60sec), otherwise WriteGear will not produce any valid output.

# import required libraries\nfrom vidgear.gears import WriteGear\nfrom vidgear.gears.stabilizer import Stabilizer\nimport cv2\n\n# Give suitable video file path to be stabilized\nunstabilized_videofile = \"test.mp4\"\n\n# open stream on given path\nstream = cv2.VideoCapture(unstabilized_videofile)\n\n# initiate stabilizer object with defined parameters\nstab = Stabilizer(smoothing_radius=30, crop_n_zoom=True, border_size=5, logging=True)\n\n# define required FFmpeg optimizing parameters for your writer\noutput_params = {\n    \"-i\": unstabilized_videofile,\n    \"-c:a\": \"aac\",\n    \"-input_framerate\": stream.get(cv2.CAP_PROP_FPS),\n    \"-clones\": [\"-shortest\"],\n    # !!! Uncomment following line if video duration is too short(<60sec). !!!\n    #\"-disable_force_termination\": True,\n}\n\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4\nwriter = WriteGear(output=\"Output.mp4\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n        break\n\n    # send current frame to stabilizer for processing\n    stabilized_frame = stab.stabilize(frame)\n\n    # wait for stabilizer which still be initializing\n    if stabilized_frame is None:\n        continue\n\n    # {do something with the stabilized frame here}\n\n    # write stabilized frame to writer\n    writer.write(stabilized_frame)\n\n\n# clear stabilizer resources\nstab.clean()\n\n# safely close video stream\nstream.release()\n\n# safely close writer\nwriter.close()\n

"},{"location":"help/stabilizer_faqs/","title":"FAQs","text":""},{"location":"help/stabilizer_faqs/#stabilizer-class-faqs","title":"Stabilizer Class FAQs","text":""},{"location":"help/stabilizer_faqs/#what-is-stabilizer-class-and-what-does-it-do","title":"What is Stabilizer Class and what does it do?","text":"

Answer: Stabilizer Class is an auxiliary class that enables Video Stabilization for vidgear with minimalistic latency, and at the expense of little to no additional computational requirements. For more info. see Stabilizer Class doc \u27b6

"},{"location":"help/stabilizer_faqs/#how-much-latency-you-would-typically-expect-with-stabilizer-class","title":"How much latency you would typically expect with Stabilizer Class?","text":"

Answer: The stabilizer will be Slower for High-Quality videos-frames. Try reducing frames size (Use reducer() method) before feeding them for reducing latency. Also, see smoothing_radius parameter of Stabilizer class that handles the quality of stabilization at the expense of latency and sudden panning. The larger its value, the less will be panning, more will be latency, and vice-versa.

"},{"location":"help/stabilizer_faqs/#how-to-remove-black-borders-in-output-video-after-stabilizing-it","title":"How to remove black borders in output video after stabilizing it?","text":"

Answer: See crop_n_zoom parameter of Stabilizer class, that enables the feature, where it crops and zooms frames(to original size) to reduce the black borders from stabilization being too noticeable (similar to the feature available in Adobe AfterEffects). It works in conjunction with the border_size parameter, i.e. when this parameter is enabled border_size will be used for cropping border instead of making them. Its default value is False.

"},{"location":"help/stabilizer_faqs/#can-i-use-stabilizer-directly-with-opencv","title":"Can I use Stabilizer directly with OpenCV?","text":"

Answer: Yes, see this usage example \u27b6.

"},{"location":"help/stabilizer_faqs/#why-stabilization-is-not-working-properly-for-my-video","title":"Why stabilization is not working properly for my video?","text":"

Answer: The Stabilizer may not perform well against High-frequency jitter in video. But,you can check if increasing smoothing_radius parameter value helps but it will add latency too.

"},{"location":"help/streamgear_ex/","title":"Bonus Examples","text":""},{"location":"help/streamgear_ex/#streamgear-examples","title":"StreamGear Examples","text":""},{"location":"help/streamgear_ex/#streamgear-live-streaming-usage-with-pigear","title":"StreamGear Live-Streaming Usage with PiGear","text":"

In this example, we will be Live-Streaming video-frames from Raspberry Pi (with Camera Module connected) using PiGear API and StreamGear API's Real-time Frames Mode:

Use -window_size & -extra_window_size FFmpeg parameters for controlling number of frames to be kept in Chunks. Less these value, less will be latency.

After every few chunks (equal to the sum of -window_size & -extra_window_size values), all chunks will be overwritten in Live-Streaming. Thereby, since newer chunks in manifest/playlist will contain NO information of any older ones, and therefore resultant DASH/HLS stream will play only the most recent frames.

In this mode, StreamGear DOES NOT automatically maps video-source audio to generated streams. You need to manually assign separate audio-source through -audio attribute of stream_params dictionary parameter.

PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using the PiGear API, otherwise nothing will work.

DASHHLS New Picamera2 backendLegacy Picamera backend
# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import StreamGear\nfrom libcamera import Transform\nimport cv2\n\n# formulate various Picamera2 API \n# configurational parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# enable livestreaming and retrieve framerate from CamGear Stream and\n# pass it as `-input_framerate` parameter for controlled framerate\nstream_params = {\"-input_framerate\": stream.framerate, \"-livestream\": True}\n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n
Under the hood, PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, PiGear API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# formulate various Picamera API \n# configurational parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# enable livestreaming and retrieve framerate from CamGear Stream and\n# pass it as `-input_framerate` parameter for controlled framerate\nstream_params = {\"-input_framerate\": stream.framerate, \"-livestream\": True}\n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n
New Picamera2 backendLegacy Picamera backend
# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import StreamGear\nfrom libcamera import Transform\nimport cv2\n\n# formulate various Picamera2 API \n# configurational parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# enable livestreaming and retrieve framerate from CamGear Stream and\n# pass it as `-input_framerate` parameter for controlled framerate\nstream_params = {\"-input_framerate\": stream.framerate, \"-livestream\": True}\n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n
Under the hood, PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, PiGear API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# formulate various Picamera API \n# configurational parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# enable livestreaming and retrieve framerate from CamGear Stream and\n# pass it as `-input_framerate` parameter for controlled framerate\nstream_params = {\"-input_framerate\": stream.framerate, \"-livestream\": True}\n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n

"},{"location":"help/streamgear_faqs/","title":"FAQs","text":""},{"location":"help/streamgear_faqs/#streamgear-faqs","title":"StreamGear FAQs","text":""},{"location":"help/streamgear_faqs/#what-is-streamgear-api-and-what-does-it-do","title":"What is StreamGear API and what does it do?","text":"

Answer: StreamGear automates transcoding workflow for generating Ultra-Low Latency, High-Quality, Dynamic & Adaptive Streaming Formats (such as MPEG-DASH) in just few lines of python code. For more info. see StreamGear doc \u27b6

"},{"location":"help/streamgear_faqs/#how-to-get-started-with-streamgear-api","title":"How to get started with StreamGear API?","text":"

Answer: First, refer to the Switching from OpenCV guide, then go through StreamGear documentation. If you still have doubts, ask us on Gitter \u27b6 Community channel.

"},{"location":"help/streamgear_faqs/#what-is-mpd-file-created-with-streamgear","title":"What is .mpd file created with StreamGear?","text":"

Answer: SteamGear also creates a Manifest file (such as MPD in-case of DASH) besides segments that describe these segment information (timing, URL, media characteristics like video resolution and bit rates) and is provided to the client before the streaming session.

"},{"location":"help/streamgear_faqs/#how-to-play-streaming-assets-created-with-streamgear-api","title":"How to play Streaming Assets created with StreamGear API?","text":"

Answer: You can easily feed Manifest file(.mpd) to DASH Supported Players Input but sure encoded chunks are present along with it. See this list of recommended players \u27b6

"},{"location":"help/streamgear_faqs/#what-adaptive-streaming-formats-are-supported-yet","title":"What Adaptive Streaming Formats are supported yet?","text":"

Answer: SteamGear currently only supports MPEG-DASH (Dynamic Adaptive Streaming over HTTP, ISO/IEC 23009-1) , but other adaptive streaming technologies such as Apple HLS, Microsoft Smooth Streaming, will be added soon.

"},{"location":"help/streamgear_faqs/#is-drm-encryption-supported-in-streamgear-api","title":"Is DRM Encryption supported in StreamGear API?","text":"

Answer: No, DRM Encryption is NOT supported yet.

"},{"location":"help/streamgear_faqs/#how-to-create-additional-streams-in-streamgear-api","title":"How to create additional streams in StreamGear API?","text":"

Answer: See this example \u27b6

"},{"location":"help/streamgear_faqs/#how-to-use-streamgear-api-with-opencv","title":"How to use StreamGear API with OpenCV?","text":"

Answer: See this example \u27b6

"},{"location":"help/streamgear_faqs/#how-to-use-streamgear-api-with-real-time-frames","title":"How to use StreamGear API with real-time frames?","text":"

Answer: See Real-time Frames Mode \u27b6

"},{"location":"help/streamgear_faqs/#how-to-use-hardwaregpu-encoder-for-transcoding-in-streamgear-api","title":"How to use Hardware/GPU encoder for transcoding in StreamGear API?","text":"

Answer: See this example \u27b6

"},{"location":"help/videogear_ex/","title":"Bonus Examples","text":""},{"location":"help/videogear_ex/#videogear-examples","title":"VideoGear Examples","text":""},{"location":"help/videogear_ex/#using-videogear-with-rosrobot-operating-system","title":"Using VideoGear with ROS(Robot Operating System)","text":"

We will be using cv_bridge to convert OpenCV frames to ROS image messages and vice-versa.

In this example, we'll create a node that convert OpenCV frames into ROS image messages, and then publishes them over ROS.

New in v0.2.2

This example was added in v0.2.2.

This example is vidgear implementation of this wiki example.

# import roslib\nimport roslib\n\nroslib.load_manifest(\"my_package\")\n\n# import other required libraries\nimport sys\nimport rospy\nimport cv2\nfrom std_msgs.msg import String\nfrom sensor_msgs.msg import Image\nfrom cv_bridge import CvBridge, CvBridgeError\nfrom vidgear.gears import VideoGear\n\n# custom publisher class\nclass image_publisher:\n    def __init__(self, source=0, logging=False):\n        # create CV bridge\n        self.bridge = CvBridge()\n        # define publisher topic\n        self.image_pub = rospy.Publisher(\"image_topic_pub\", Image)\n        # open stream with given parameters\n        self.stream = VideoGear(source=source, logging=logging).start()\n        # define publisher topic\n        rospy.Subscriber(\"image_topic_sub\", Image, self.callback)\n\n    def callback(self, data):\n\n        # {do something with received ROS node data here}\n\n        # read frames\n        frame = self.stream.read()\n        # check for frame if None-type\n        if not (frame is None):\n\n            # {do something with the frame here}\n\n            # publish our frame\n            try:\n                self.image_pub.publish(self.bridge.cv2_to_imgmsg(frame, \"bgr8\"))\n            except CvBridgeError as e:\n                # catch any errors\n                print(e)\n\n    def close(self):\n        # stop stream\n        self.stream.stop()\n\n\ndef main(args):\n    # !!! define your own video source here !!!\n    # Open any video stream such as live webcam\n    # video stream on first index(i.e. 0) device\n\n    # define publisher\n    ic = image_publisher(source=0, logging=True)\n    # initiate ROS node on publisher\n    rospy.init_node(\"image_publisher\", anonymous=True)\n    try:\n        # run node\n        rospy.spin()\n    except KeyboardInterrupt:\n        print(\"Shutting down\")\n    finally:\n        # close publisher\n        ic.close()\n\n\nif __name__ == \"__main__\":\n    main(sys.argv)\n

"},{"location":"help/videogear_ex/#using-videogear-for-capturing-rtsprtmp-urls","title":"Using VideoGear for capturing RTSP/RTMP URLs","text":"

Here's a high-level wrapper code around VideoGear API to enable auto-reconnection during capturing, plus stabilization is enabled (stabilize=True) in order to stabilize captured frames on-the-go:

New in v0.2.2

This example was added in v0.2.2.

Enforcing UDP stream

You can easily enforce UDP for RTSP streams inplace of default TCP, by putting following lines of code on the top of your existing code:

# import required libraries\nimport os\n\n# enforce UDP\nos.environ[\"OPENCV_FFMPEG_CAPTURE_OPTIONS\"] = \"rtsp_transport;udp\"\n

Finally, use backend parameter value as backend=cv2.CAP_FFMPEG in VideoGear.

from vidgear.gears import VideoGear\nimport cv2\nimport datetime\nimport time\n\n\nclass Reconnecting_VideoGear:\n    def __init__(self, cam_address, stabilize=False, reset_attempts=50, reset_delay=5):\n        self.cam_address = cam_address\n        self.stabilize = stabilize\n        self.reset_attempts = reset_attempts\n        self.reset_delay = reset_delay\n        self.source = VideoGear(\n            source=self.cam_address, stabilize=self.stabilize\n        ).start()\n        self.running = True\n\n    def read(self):\n        if self.source is None:\n            return None\n        if self.running and self.reset_attempts > 0:\n            frame = self.source.read()\n            if frame is None:\n                self.source.stop()\n                self.reset_attempts -= 1\n                print(\n                    \"Re-connection Attempt-{} occured at time:{}\".format(\n                        str(self.reset_attempts),\n                        datetime.datetime.now().strftime(\"%m-%d-%Y %I:%M:%S%p\"),\n                    )\n                )\n                time.sleep(self.reset_delay)\n                self.source = VideoGear(\n                    source=self.cam_address, stabilize=self.stabilize\n                ).start()\n                # return previous frame\n                return self.frame\n            else:\n                self.frame = frame\n                return frame\n        else:\n            return None\n\n    def stop(self):\n        self.running = False\n        self.reset_attempts = 0\n        self.frame = None\n        if not self.source is None:\n            self.source.stop()\n\n\nif __name__ == \"__main__\":\n    # open any valid video stream\n    stream = Reconnecting_VideoGear(\n        cam_address=\"rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov\",\n        reset_attempts=20,\n        reset_delay=5,\n    )\n\n    # loop over\n    while True:\n\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if None-type\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # Show output window\n        cv2.imshow(\"Output\", frame)\n\n        # check for 'q' key if pressed\n        key = cv2.waitKey(1) & 0xFF\n        if key == ord(\"q\"):\n            break\n\n    # close output window\n    cv2.destroyAllWindows()\n\n    # safely close video stream\n    stream.stop()\n

"},{"location":"help/videogear_ex/#using-videogear-for-real-time-stabilization-with-audio-encoding","title":"Using VideoGear for Real-time Stabilization with Audio Encoding","text":"

In this example code, we will be directly merging the audio from a Video-File (to be stabilized) with its processed stabilized frames into a compressed video output in real time:

New in v0.2.4

This example was added in v0.2.4.

Make sure this input video-file (to be stabilized) contains valid audio source, otherwise you could encounter multiple errors or no output at all.

You MUST use -input_framerate attribute to set exact value of input framerate when using external audio in Real-time Frames mode, otherwise audio delay will occur in output streams.

Use -disable_force_termination flag when video duration is too short(<60sec), otherwise WriteGear will not produce any valid output.

# import required libraries\nfrom vidgear.gears import WriteGear\nfrom vidgear.gears import VideoGear\nimport cv2\n\n# Give suitable video file path to be stabilized\nunstabilized_videofile = \"test.mp4\"\n\n# open any valid video path with stabilization enabled(`stabilize = True`)\nstream_stab = VideoGear(source=unstabilized_videofile, stabilize=True, logging=True).start()\n\n# define required FFmpeg optimizing parameters for your writer\noutput_params = {\n    \"-i\": unstabilized_videofile,\n    \"-c:a\": \"aac\",\n    \"-input_framerate\": stream_stab.framerate,\n    \"-clones\": [\"-shortest\"],\n    # !!! Uncomment following line if video duration is too short(<60sec). !!!\n    #\"-disable_force_termination\": True,\n}\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4\nwriter = WriteGear(output=\"Output.mp4\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame_stab = stream_stab.read()\n\n    # check for frame if not grabbed\n    if frame_stab is None:\n        break\n\n    # {do something with the stabilized frame here}\n\n    # write stabilized frame to writer\n    writer.write(frame_stab)\n\n# safely close streams\nstream_stab.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"help/videogear_faqs/","title":"FAQs","text":""},{"location":"help/videogear_faqs/#videogear-faqs","title":"VideoGear FAQs","text":""},{"location":"help/videogear_faqs/#what-is-videogear-api-and-what-does-it-do","title":"What is VideoGear API and what does it do?","text":"

Answer: VideoGear provides a special internal wrapper around VidGear's exclusive Video Stabilizer class. It also act as a Common API, that provided an internal access to both CamGear and PiGear APIs and their parameters, with a special enablePiCamera boolean flag. For more info. see VideoGear doc \u27b6

"},{"location":"help/videogear_faqs/#whats-the-need-of-videogear-api","title":"What's the need of VideoGear API?","text":"

Answer: VideoGear is basically ideal when you need to switch to different video sources without changing your code much. Also, it enables easy stabilization for various video-streams (real-time or not) with minimum efforts and using way fewer lines of code. It also serve as backend for other powerful APIs, such WebGear and NetGear_Async.

"},{"location":"help/videogear_faqs/#which-apis-are-accessible-with-videogear-api","title":"Which APIs are accessible with VideoGear API?","text":"

Answer: VideoGear provided an internal access to both CamGear and PiGear APIs and their parameters, also it contains wrapper around Video Stabilizer class.

"},{"location":"help/videogear_faqs/#can-we-access-writegear-api-or-netgear-api-too-with-videogear","title":"Can we access WriteGear API or NetGear API too with VideoGear?","text":"

Answer: No, only selected VideoCapture APIs (anwsered above) are accessible.

"},{"location":"help/videogear_faqs/#does-using-videogear-instead-of-camgear-api-directly-affects-performance","title":"Does using VideoGear instead of CamGear API directly, affects performance?","text":"

Answer: No, there's no difference, as VideoGear just a high-level wrapper around CamGear API and without any modifications in-between.

"},{"location":"help/webgear_ex/","title":"Bonus Examples","text":""},{"location":"help/webgear_ex/#webgear-examples","title":"WebGear Examples","text":""},{"location":"help/webgear_ex/#using-webgear-with-raspberrypi-camera-module","title":"Using WebGear with RaspberryPi Camera Module","text":"

Because of WebGear API's flexible internal wapper around VideoGear, it can easily access any parameter of CamGear and PiGear videocapture APIs.

Following usage examples are just an idea of what can be done with WebGear API, you can try various VideoGear, CamGear and PiGear parameters directly in WebGear API in the similar manner.

Here's a bare-minimum example of using WebGear API with the Raspberry Pi camera module while tweaking its various properties in few lines of python code:

Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this backend, otherwise nothing will work.

New Picamera2 backendLegacy Picamera backend
# import libs\nimport uvicorn\nfrom libcamera import Transform\nfrom vidgear.gears.asyncio import WebGear\n\n# various WebGear_RTC performance \n# and Picamera2 API tweaks\noptions = {\n    \"frame_size_reduction\": 40,\n    \"jpeg_compression_quality\": 80,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": False,\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# initialize WebGear app\nweb = WebGear(\n    enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options\n)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import libs\nimport uvicorn\nfrom vidgear.gears.asyncio import WebGear\n\n# various webgear performance and Picamera API tweaks\noptions = {\n    \"frame_size_reduction\": 40,\n    \"jpeg_compression_quality\": 80,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": False,\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# initialize WebGear app\nweb = WebGear(\n    enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options\n)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

"},{"location":"help/webgear_ex/#using-webgear-with-real-time-video-stabilization-enabled","title":"Using WebGear with real-time Video Stabilization enabled","text":"

Here's an example of using WebGear API with real-time Video Stabilization enabled:

# import libs\nimport uvicorn\nfrom vidgear.gears.asyncio import WebGear\n\n# various webgear performance tweaks\noptions = {\n    \"frame_size_reduction\": 40,\n    \"jpeg_compression_quality\": 80,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": False,\n}\n\n# initialize WebGear app  with a raw source and enable video stabilization(`stabilize=True`)\nweb = WebGear(source=\"foo.mp4\", stabilize=True, logging=True, **options)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

"},{"location":"help/webgear_ex/#display-two-sources-simultaneously-in-webgear","title":"Display Two Sources Simultaneously in WebGear","text":"

In this example, we'll be displaying two video feeds side-by-side simultaneously on browser using WebGear API by defining two separate frame generators:

New in v0.2.2

This example was added in v0.2.2.

Step-1 (Trigger Auto-Generation Process): Firstly, run this bare-minimum code to trigger the Auto-generation process, this will create .vidgear directory at current location (directory where you'll run this code):

# import required libraries\nimport uvicorn\nfrom vidgear.gears.asyncio import WebGear\n\n# provide current directory to save data files\noptions = {\"custom_data_location\": \"./\"}\n\n# initialize WebGear app\nweb = WebGear(source=0, logging=True, **options)\n\n# close app safely\nweb.shutdown()\n

Step-2 (Replace HTML file): Now, go inside .vidgear webgear templates directory at current location of your machine, and there replace content of index.html file with following:

{% extends \"base.html\" %}\n{% block content %}\n  <h1 class=\"glow\">WebGear Video Feed</h1>\n   <div class=\"rows\">\n     <img src=\"/video\" alt=\"Feed\"/>\n     <img src=\"/video2\" alt=\"Feed\"/>\n   </div>\n{% endblock %}\n

Step-3 (Build your own Frame Producers): Now, create a python script code with OpenCV source, as follows:

# import necessary libs\nimport uvicorn, asyncio, cv2\nfrom vidgear.gears.asyncio import WebGear\nfrom vidgear.gears.asyncio.helper import reducer\nfrom starlette.responses import StreamingResponse\nfrom starlette.routing import Route\n\n# provide current directory to load data files\noptions = {\"custom_data_location\": \"./\"}\n\n# initialize WebGear app without any source\nweb = WebGear(logging=True, **options)\n\n# create your own custom frame producer\nasync def my_frame_producer1():\n\n   # !!! define your first video source here !!!\n   # Open any video stream such as \"foo1.mp4\"\n   stream = cv2.VideoCapture(\"foo1.mp4\")\n   # loop over frames\n   while True:\n       # read frame from provided source\n       (grabbed, frame) = stream.read()\n       # break if NoneType\n       if not grabbed:\n           break\n\n       # do something with your OpenCV frame here\n\n       # reducer frames size if you want more performance otherwise comment this line\n       frame = await reducer(frame, percentage=30)  # reduce frame by 30%\n       # handle JPEG encoding\n       encodedImage = cv2.imencode(\".jpg\", frame)[1].tobytes()\n       # yield frame in byte format\n       yield (b\"--frame\\r\\nContent-Type:video/jpeg2000\\r\\n\\r\\n\" + encodedImage + b\"\\r\\n\")\n       await asyncio.sleep(0.00001)\n   # close stream\n   stream.release()\n\n\n# create your own custom frame producer\nasync def my_frame_producer2():\n\n   # !!! define your second video source here !!!\n   # Open any video stream such as \"foo2.mp4\"\n   stream = cv2.VideoCapture(\"foo2.mp4\")\n   # loop over frames\n   while True:\n       # read frame from provided source\n       (grabbed, frame) = stream.read()\n       # break if NoneType\n       if not grabbed:\n           break\n\n       # do something with your OpenCV frame here\n\n       # reducer frames size if you want more performance otherwise comment this line\n       frame = await reducer(frame, percentage=30)  # reduce frame by 30%\n       # handle JPEG encoding\n       encodedImage = cv2.imencode(\".jpg\", frame)[1].tobytes()\n       # yield frame in byte format\n       yield (b\"--frame\\r\\nContent-Type:video/jpeg2000\\r\\n\\r\\n\" + encodedImage + b\"\\r\\n\")\n       await asyncio.sleep(0.00001)\n   # close stream\n   stream.release()\n\n\nasync def custom_video_response(scope):\n   \"\"\"\n   Return a async video streaming response for `my_frame_producer2` generator\n   \"\"\"\n   assert scope[\"type\"] in [\"http\", \"https\"]\n   await asyncio.sleep(0.00001)\n   return StreamingResponse(\n       my_frame_producer2(),\n       media_type=\"multipart/x-mixed-replace; boundary=frame\",\n   )\n\n\n# add your custom frame producer to config\nweb.config[\"generator\"] = my_frame_producer1\n\n# append new route i.e. new custom route with custom response\nweb.routes.append(\n    Route(\"/video2\", endpoint=custom_video_response)\n    )\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

On successfully running this code, the output stream will be displayed at address http://localhost:8000/ in Browser.

"},{"location":"help/webgear_faqs/","title":"FAQs","text":""},{"location":"help/webgear_faqs/#webgear-faqs","title":"WebGear FAQs","text":""},{"location":"help/webgear_faqs/#what-is-webgear-api-and-what-does-it-do","title":"What is WebGear API and what does it do?","text":"

Answer: WebGear is a powerful ASGI Video-Broadcaster API ideal for transmitting Motion-JPEG-frames from a single source to multiple recipients via the browser. For more info. see WebGear doc \u27b6

"},{"location":"help/webgear_faqs/#how-to-get-started-with-webgear-api","title":"How to get started with WebGear API?","text":"

Answer: First, refer to the Switching from OpenCV guide, then go through WebGear documentation. If you still have doubts, ask us on Gitter \u27b6 Community channel.

"},{"location":"help/webgear_faqs/#webgear-is-throwing-modulenotfounderror-on-importing-why","title":"\"WebGear is throwing ModuleNotFoundError on importing\", Why?","text":"

Answer: This error means, VidGear is installed WITHOUT asyncio package support on your machine. For this support, see Requirements \u27b6.

"},{"location":"help/webgear_faqs/#can-webgear-always-need-active-internet-connection","title":"Can WebGear always need Active Internet Connection?","text":"

Answer: No, it just need internet only once during its Auto-Generation Process \u27b6 to download default data-files and it takes few seconds. You can also download files manually from Github Server, otherwise you can also add your own custom files. For more information see Data-Files Auto-Generation WorkFlow \u27b6

"},{"location":"help/webgear_faqs/#is-it-possible-to-stream-on-a-different-device-on-the-network-with-webgear","title":"Is it possible to stream on a different device on the network with WebGear?","text":"

If you set \"0.0.0.0\" as host value instead of \"localhost\" on Host Machine, then you must still use http://localhost:8000/ to access stream on that same host machine browser.

For accessing WebGear on different Client Devices on the network, use \"0.0.0.0\" as host value instead of \"localhost\" on Host Machine. Then type the IP-address of source machine followed by the defined port value in your desired Client Device's browser (for e.g. http://192.27.0.101:8000) to access the stream.

"},{"location":"help/webgear_faqs/#can-i-manually-place-default-files-for-webgear","title":"Can I manually place default files for WebGear?","text":"

Answer: Yes, you can either download default files from Github Server, and manually place at default location, OR, you can yourself create the require three critical files (i.e index.html, 404.html & 500.html) inside templates folder at the default location, thereby you don't need any internet connection at all. For more information see Data-Files Auto-Generation WorkFlow \u27b6

"},{"location":"help/webgear_faqs/#how-to-send-opencv-frames-directly-to-webgear-server","title":"How to send OpenCV frames directly to Webgear Server?","text":"

Answer: See this usage example \u27b6.

"},{"location":"help/webgear_faqs/#how-can-i-add-my-custom-webpage-to-webgear","title":"How can I add my custom WebPage to WebGear?","text":"

Answer: See this usage example \u27b6.

"},{"location":"help/webgear_faqs/#how-can-to-add-cors-headers-to-webgear","title":"How can to add CORS headers to WebGear?","text":"

Answer: See this usage example \u27b6.

"},{"location":"help/webgear_faqs/#can-i-change-the-default-location","title":"Can I change the default location?","text":"

Answer: Yes, you can use WebGear's custom_data_location attribute of option parameter in WebGear API, to change default location to somewhere else.

"},{"location":"help/webgear_faqs/#can-i-deleterename-the-webgear-default-data","title":"Can I delete/rename the WebGear default data?","text":"

Answer: Yes, but you've to follow these rules \u27b6

"},{"location":"help/webgear_faqs/#what-web-browser-are-supported-by-webgear-api","title":"What Web browser are supported by WebGear API?","text":"

Answer: All modern browser with Javascript support are supported by WebGear. If not, then discuss with us on Gitter \u27b6 Community channel.

"},{"location":"help/webgear_rtc_ex/","title":"Bonus Examples","text":""},{"location":"help/webgear_rtc_ex/#webgear_rtc_rtc-examples","title":"WebGear_RTC_RTC Examples","text":""},{"location":"help/webgear_rtc_ex/#using-webgear_rtc-with-raspberrypi-camera-module","title":"Using WebGear_RTC with RaspberryPi Camera Module","text":"

Because of WebGear_RTC API's flexible internal wapper around VideoGear, it can easily access any parameter of CamGear and PiGear videocapture APIs.

Following usage examples are just an idea of what can be done with WebGear_RTC API, you can try various VideoGear, CamGear and PiGear parameters directly in WebGear_RTC API in the similar manner.

Here's a bare-minimum example of using WebGear_RTC API with the Raspberry Pi camera module while tweaking its various properties in just one-liner:

Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this backend, otherwise nothing will work.

New Picamera2 backendLegacy Picamera backend
# import libs\nimport uvicorn\nfrom libcamera import Transform\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# various WebGear_RTC performance \n# and Picamera2 API tweaks\noptions = {\n    \"frame_size_reduction\": 25,\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# initialize WebGear app\nweb = WebGear_RTC(\n    enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options\n)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import libs\nimport uvicorn\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# various WebGear_RTC performance and Picamera API tweaks\noptions = {\n    \"frame_size_reduction\": 25,\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# initialize WebGear app\nweb = WebGear_RTC(\n    enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options\n)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

"},{"location":"help/webgear_rtc_ex/#using-webgear_rtc-with-real-time-video-stabilization-enabled","title":"Using WebGear_RTC with real-time Video Stabilization enabled","text":"

Here's an example of using WebGear_RTC API with real-time Video Stabilization enabled:

# import libs\nimport uvicorn\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# various webgear_rtc performance tweaks\noptions = {\n    \"frame_size_reduction\": 25,\n}\n\n# initialize WebGear_RTC app  with a raw source and enable video stabilization(`stabilize=True`)\nweb = WebGear_RTC(source=\"foo.mp4\", stabilize=True, logging=True, **options)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

"},{"location":"help/webgear_rtc_ex/#display-two-sources-simultaneously-in-webgear_rtc","title":"Display Two Sources Simultaneously in WebGear_RTC","text":"

In this example, we'll be displaying two video feeds side-by-side simultaneously on browser using WebGear_RTC API by simply concatenating frames in real-time:

New in v0.2.4

This example was added in v0.2.4.

# import necessary libs\nimport uvicorn, cv2\nimport numpy as np\nfrom vidgear.gears.helper import reducer\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# initialize WebGear_RTC app without any source\nweb = WebGear_RTC(logging=True)\n\n# frame concatenator\ndef get_conc_frame(frame1, frame2):\n    h1, w1 = frame1.shape[:2]\n    h2, w2 = frame2.shape[:2]\n\n    # create empty matrix\n    vis = np.zeros((max(h1, h2), w1 + w2, 3), np.uint8)\n\n    # combine 2 frames\n    vis[:h1, :w1, :3] = frame1\n    vis[:h2, w1 : w1 + w2, :3] = frame2\n\n    return vis\n\n\n# create your own custom streaming class\nclass Custom_Stream_Class:\n    \"\"\"\n    Custom Streaming using two OpenCV sources\n    \"\"\"\n\n    def __init__(self, source1=None, source2=None):\n\n        # !!! define your own video source here !!!\n        # check is source are provided\n        if source1 is None or source2 is None:\n            raise ValueError(\"Provide both source\")\n\n        # initialize global params\n        # define both source here\n        self.stream1 = cv2.VideoCapture(source1)\n        self.stream2 = cv2.VideoCapture(source2)\n\n        # define running flag\n        self.running = True\n\n    def read(self):\n\n        # don't forget this function!!!\n\n        # check if sources were initialized or not\n        if self.stream1 is None or self.stream2 is None:\n            return None\n\n        # check if we're still running\n        if self.running:\n            # read video frame\n            (grabbed1, frame1) = self.stream1.read()\n            (grabbed2, frame2) = self.stream2.read()\n\n            # if NoneType\n            if not grabbed1 or not grabbed2:\n\n                # do something with your OpenCV frame here\n\n                # concatenate frame\n                frame = get_conc_frame(frame1, frame2)\n\n                # reducer frames size if you want more performance otherwise comment this line\n                # frame = await reducer(frame, percentage=30)  # reduce frame by 30%\n\n                # return our gray frame\n                return frame\n            else:\n                # signal we're not running now\n                self.running = False\n        # return None-type\n        return None\n\n    def stop(self):\n\n        # don't forget this function!!!\n\n        # flag that we're not running\n        self.running = False\n        # close stream\n        if not (self.stream1 is None):\n            self.stream1.release()\n            self.stream1 = None\n\n        if not (self.stream2 is None):\n            self.stream2.release()\n            self.stream2 = None\n\n\n# assign your Custom Streaming Class with adequate two sources\n# to `custom_stream` attribute in options parameter\noptions = {\n    \"custom_stream\": Custom_Stream_Class(\n        source1=\"foo1.mp4\", source2=\"foo2.mp4\"\n    )\n}\n\n# initialize WebGear_RTC app without any source\nweb = WebGear_RTC(logging=True, **options)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

On successfully running this code, the output stream will be displayed at address http://localhost:8000/ in Browser.

"},{"location":"help/webgear_rtc_faqs/","title":"FAQs","text":""},{"location":"help/webgear_rtc_faqs/#webgear_rtc-faqs","title":"WebGear_RTC FAQs","text":""},{"location":"help/webgear_rtc_faqs/#what-is-webgear_rtc-api-and-what-does-it-do","title":"What is WebGear_RTC API and what does it do?","text":"

Answer: WebGear_RTC utilizes WebRTC technology under the hood, which makes it suitable for building powerful video-streaming solutions for all modern browsers as well as native clients available on all major platforms. For more info. see WebGear_RTC doc \u27b6

"},{"location":"help/webgear_rtc_faqs/#how-to-get-started-with-webgear_rtc-api","title":"How to get started with WebGear_RTC API?","text":"

Answer: First, refer to the Switching from OpenCV guide, then go through WebGear_RTC documentation. If you still have doubts, ask us on Gitter \u27b6 Community channel.

"},{"location":"help/webgear_rtc_faqs/#how-webgear_rtc-is-different-to-webgear-api-which-should-i-choose","title":"How WebGear_RTC is different to WebGear API, which should I choose?","text":"

Answer: WebGear_RTC is similar to WeGear API in many aspects but utilizes WebRTC technology under the hood instead of Motion JPEG. You can choose any API according to your application, but the quality would be better on WebGear API, on-the-other-hand latency would be better on WebGear_RTC API. Also, WebRTC protocol accepts a wide range of devices, whereas WebGear is limited only to modern browsers.

"},{"location":"help/webgear_rtc_faqs/#webgear_rtc-is-throwing-modulenotfounderror-on-importing-why","title":"\"WebGear_RTC is throwing ModuleNotFoundError on importing\", Why?","text":"

Answer: This error means, VidGear is installed WITHOUT asyncio package support on your machine. For this support, see Requirements \u27b6.

"},{"location":"help/webgear_rtc_faqs/#can-webgear_rtc-always-need-active-internet-connection","title":"Can WebGear_RTC always need Active Internet Connection?","text":"

Answer: No, it just need internet only once during its Auto-Generation Process \u27b6 to download default data-files and it takes few seconds. You can also download files manually from Github Server, otherwise you can also add your own custom files. For more information see Data-Files Auto-Generation WorkFlow \u27b6

"},{"location":"help/webgear_rtc_faqs/#is-it-possible-to-stream-on-a-different-device-on-the-network-with-webgear_rtc","title":"Is it possible to stream on a different device on the network with WebGear_RTC?","text":"

If you set \"0.0.0.0\" as host value instead of \"localhost\" on Host Machine, then you must still use http://localhost:8000/ to access stream on your host machine browser.

For accessing WebGear_RTC on different Client Devices on the network, use \"0.0.0.0\" as host value instead of \"localhost\" on Host Machine. Then type the IP-address of source machine followed by the defined port value in your desired Client Device's browser (for e.g. http://192.27.0.101:8000) to access the stream.

"},{"location":"help/webgear_rtc_faqs/#can-i-manually-place-default-files-for-webgear_rtc","title":"Can I manually place default files for WebGear_RTC?","text":"

Answer: Yes, you can either download default files from Github Server, and manually place at default location, OR, you can yourself create the require three critical files (i.e index.html, 404.html & 500.html) inside templates folder at the default location, thereby you don't need any internet connection at all. For more information see Data-Files Auto-Generation WorkFlow \u27b6

"},{"location":"help/webgear_rtc_faqs/#how-to-stream-webgear_rtc-server-output-to-multiple-clients","title":"How to stream Webgear_RTC Server output to multiple clients?","text":"

Answer: See this usage example \u27b6.

"},{"location":"help/webgear_rtc_faqs/#how-to-send-opencv-frames-directly-to-webgear_rtc-server","title":"How to send OpenCV frames directly to Webgear_RTC Server?","text":"

Answer: See this usage example \u27b6.

"},{"location":"help/webgear_rtc_faqs/#how-can-i-add-my-custom-webpage-to-webgear_rtc","title":"How can I add my custom WebPage to WebGear_RTC?","text":"

Answer: See this usage example \u27b6.

"},{"location":"help/webgear_rtc_faqs/#how-can-to-add-cors-headers-to-webgear_rtc","title":"How can to add CORS headers to WebGear_RTC?","text":"

Answer: See this usage example \u27b6.

"},{"location":"help/webgear_rtc_faqs/#can-i-change-the-default-location","title":"Can I change the default location?","text":"

Answer: Yes, you can use WebGear_RTC's custom_data_location attribute of option parameter in WebGear_RTC API, to change default location to somewhere else.

"},{"location":"help/webgear_rtc_faqs/#can-i-deleterename-the-webgear_rtc-default-data","title":"Can I delete/rename the WebGear_RTC default data?","text":"

Answer: Yes, but you've to follow these rules \u27b6

"},{"location":"help/writegear_ex/","title":"Bonus Examples","text":""},{"location":"help/writegear_ex/#writegear-examples","title":"WriteGear Examples","text":""},{"location":"help/writegear_ex/#using-writegears-compression-mode-for-rtsprtp-live-streaming","title":"Using WriteGear's Compression Mode for RTSP/RTP Live-Streaming","text":"

In Compression Mode, you can use WriteGear for livestreaming with traditional protocols such as RTSP/RTP. The example to achieve that is as follows:

New in v0.2.6

This example was added in v0.2.6.

Creating your own RTSP Server locally

If you want to create your RTSP Server locally, then checkout MediaMTX (formerly rtsp-simple-server) - ready-to-use and zero-dependency real-time media server and media proxy that allows to publish, read, proxy, record and playback video and audio streams.

This example assume you already have a RTSP Server running at specified RTSP address with format rtsp://[RTSP_ADDRESS]:[RTSP_PORT]/[RTSP_PATH] for publishing video frames.

Make sure to change RTSP address rtsp://localhost:8554/mystream with yours in following code before running!

# import required libraries\nimport cv2\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\n\n# open any valid video stream(for e.g `foo.mp4` file)\nstream = CamGear(source=\"foo.mp4\").start()\n\n# define required FFmpeg parameters for your writer\noutput_params = {\"-f\": \"rtsp\", \"-rtsp_transport\": \"tcp\"}\n\n# Define writer with defined parameters and RTSP address\n# [WARNING] Change your RTSP address `rtsp://localhost:8554/mystream` with yours!\nwriter = WriteGear(\n    output=\"rtsp://localhost:8554/mystream\", logging=True, **output_params\n)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"help/writegear_ex/#using-writegears-compression-mode-for-youtube-live-streaming","title":"Using WriteGear's Compression Mode for YouTube-Live Streaming","text":"

In Compression Mode, you can also use WriteGear for Youtube-Livestreaming. The example is as follows:

New in v0.2.1

This example was added in v0.2.1.

This example assume you already have a YouTube Account with Live-Streaming enabled for publishing video.

Make sure to change YouTube-Live Stream Key with yours in following code before running!

Without AudioWith Audio
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# define and open video source\nstream = CamGear(source=\"/home/foo/foo.mp4\", logging=True).start()\n\n# define required FFmpeg parameters for your writer\noutput_params = {\n    \"-clones\": [\"-f\", \"lavfi\", \"-i\", \"anullsrc\"],\n    \"-vcodec\": \"libx264\",\n    \"-preset\": \"medium\",\n    \"-b:v\": \"4500k\",\n    \"-bufsize\": \"512k\",\n    \"-pix_fmt\": \"yuv420p\",\n    \"-f\": \"flv\",\n}\n\n# [WARNING] Change your YouTube-Live Stream Key here:\nYOUTUBE_STREAM_KEY = \"xxxx-xxxx-xxxx-xxxx-xxxx\"\n\n# Define writer with defined parameters\nwriter = WriteGear(\n    output=\"rtmp://a.rtmp.youtube.com/live2/{}\".format(YOUTUBE_STREAM_KEY),\n    logging=True,\n    **output_params\n)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

This code assume given input video source contains valid audio stream.

# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# define video source(with audio) here\nVIDEO_SOURCE = \"/home/foo/foo.mp4\"\n\n# Open stream\nstream = CamGear(source=VIDEO_SOURCE, logging=True).start()\n\n# define required FFmpeg parameters for your writer\n# [NOTE]: Added VIDEO_SOURCE as audio-source\noutput_params = {\n    \"-i\": VIDEO_SOURCE,\n    \"-acodec\": \"aac\",\n    \"-ar\": 44100,\n    \"-b:a\": 712000,\n    \"-vcodec\": \"libx264\",\n    \"-preset\": \"medium\",\n    \"-b:v\": \"4500k\",\n    \"-bufsize\": \"512k\",\n    \"-pix_fmt\": \"yuv420p\",\n    \"-f\": \"flv\",\n}\n\n# [WARNING] Change your YouTube-Live Stream Key here:\nYOUTUBE_STREAM_KEY = \"xxxx-xxxx-xxxx-xxxx-xxxx\"\n\n# Define writer with defined parameters\nwriter = WriteGear(\n    output=\"rtmp://a.rtmp.youtube.com/live2/{}\".format(YOUTUBE_STREAM_KEY),\n    logging=True,\n    **output_params\n)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"help/writegear_ex/#using-writegears-compression-mode-with-v4l2loopback-virtual-cameras","title":"Using WriteGear's Compression Mode with v4l2loopback Virtual Cameras","text":"

With WriteGear's Compression Mode, you can directly feed video-frames to v4l2loopback generated Virtual Camera devices on Linux Machines. The complete usage example is as follows:

New in v0.3.0

This example was added in v0.3.0.

Example Assumptions Creating your own Virtual Camera device with v4l2loopback module.

To install and create a v4l2loopback virtual camera device on Linux Mint OS/Ubuntu (may slightly differ for other distros), run following two terminal commands:

$ sudo apt-get install v4l2loopback-dkms v4l2loopback-utils linux-modules-extra-$(uname -r)\n\n$ sudo modprobe v4l2loopback devices=1 video_nr=0 exclusive_caps=1 card_label='VCamera'\n

For further information on parameters used, checkout v4l2loopback docs

Finally, You can check the loopback device you just created by listing contents of /sys/devices/virtual/video4linux directory with terminal command:

$ sudo ls -1 /sys/devices/virtual/video4linux\n\nvideo0 \n

Now you can use /dev/video0 Virtual Camera device path in WriteGear API.

v4l2: open /dev/videoX: Permission denied

If you got this error, then you must add your username to the video group by running following commands:

$ sudo adduser $(whoami) video\n$ sudo usermod -a -G video $(whoami)\n
Afterwards, restart your computer to finialize these changes.

Note: If the problem still persists, then try to run your python script as superuser with sudo command.

Default libx264 encoder is incompatible with v4l2loopback module.

Kindly use other encoders such as libxvid, mpeg4 etc.

# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# open any valid video stream(for e.g `foo.mp4` file)\nstream = CamGear(source=\"foo.mp4\").start()\n\n# define required FFmpeg parameters for your writer\n# also retrieve framerate from CamGear Stream and pass it as `-input_framerate` parameter\noutput_params = {\n    \"-input_framerate\": stream.framerate,\n    \"-vcodec\": \"libxvid\",\n    \"-f\": \"v4l2\",\n    \"-pix_fmt\": \"yuv420p\",\n}\n\n# Define writer with \"/dev/video0\" as source and user-defined parameters \nwriter = WriteGear(output=\"/dev/video0\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if None-type\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

The data sent to the v4l2loopback device /dev/video0 in this example with WriteGear API, can then be read by any v4l2-capable application (such as OpenCV, VLC, ffplay etc.)

"},{"location":"help/writegear_ex/#using-writegears-compression-mode-for-creating-mp4-segments","title":"Using WriteGear's Compression Mode for creating MP4 segments","text":"

In Compression Mode, you can also use WriteGear for creating MP4 segments from almost any video source. The example is as follows:

New in v0.2.1

This example was added in v0.2.1.

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# Open any video source `foo.mp4`\nstream = VideoGear(\n    source=\"foo.mp4\", logging=True\n).start()\n\n# define required FFmpeg optimizing parameters for your writer\noutput_params = {\n    \"-c:v\": \"libx264\",\n    \"-crf\": 22,\n    \"-map\": 0,\n    \"-segment_time\": 9,\n    \"-g\": 9,\n    \"-sc_threshold\": 0,\n    \"-force_key_frames\": \"expr:gte(t,n_forced*9)\",\n    \"-clones\": [\"-f\", \"segment\"],\n}\n\n# Define writer with defined parameters\nwriter = WriteGear(output=\"output%03d.mp4\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"help/writegear_ex/#using-writegears-compression-mode-to-add-external-audio-file-input-to-video-frames","title":"Using WriteGear's Compression Mode to add external audio file input to video frames","text":"

You can also use WriteGear for merging external audio with live video-source:

New in v0.2.1

This example was added in v0.2.1.

Make sure this -i audio-source it compatible with provided video-source, otherwise you could encounter multiple errors or no output at all.

# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# open any valid video stream(for e.g `foo_video.mp4` file)\nstream = CamGear(source=\"foo_video.mp4\").start()\n\n# add various parameters, along with custom audio\nstream_params = {\n    \"-input_framerate\": stream.framerate,  # controlled framerate for audio-video sync !!! don't forget this line !!!\n    \"-i\": \"foo_audio.aac\",  # assigns input audio-source: \"foo_audio.aac\"\n}\n\n# Define writer with defined parameters\nwriter = WriteGear(output=\"Output.mp4\", logging=True, **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"help/writegear_ex/#using-writegears-compression-mode-for-generating-timely-accurate-video","title":"Using WriteGear's Compression Mode for generating Timely Accurate Video","text":"

If you need timely accurate video with exactly same speed as real-time input, then you need to use FFmpeg directly through its execute_ffmpeg_cmd method:

New in v0.2.4

This example was added in v0.2.4.

In this example we are capturing video from desktop screen in a Timely Accurate manner.

Windows Linux MacOS
# import required libraries\nfrom vidgear.gears import WriteGear\n\n# Define writer with defined parameters and with some dummy name\nwriter = WriteGear(output=\"Output.mp4\", logging=True)\n\n# format FFmpeg command to generate time accurate video\nffmpeg_command = [\n    \"-y\",\n    \"-f\",\n    \"gdigrab\",\n    \"-framerate\",\n    \"30\",\n    \"-i\",\n    \"desktop\",\n    \"Output.mkv\",\n]  # `-y` parameter is to overwrite outputfile if exists\n\n# execute FFmpeg command\nwriter.execute_ffmpeg_cmd(ffmpeg_command)\n\n# safely close writer\nwriter.close()\n
# import required libraries\nfrom vidgear.gears import WriteGear\n\n# Define writer with defined parameters and with some dummy name\nwriter = WriteGear(output=\"Output.mp4\", logging=True)\n\n# format FFmpeg command to generate time accurate video\nffmpeg_command = [\n    \"-y\",\n    \"-f\",\n    \"x11grab\",\n    \"-framerate\",\n    \"30\",\n    \"-i\",\n    \"default\",\n    \"Output.mkv\",\n]  # `-y` parameter is to overwrite outputfile if exists\n\n# execute FFmpeg command\nwriter.execute_ffmpeg_cmd(ffmpeg_command)\n\n# safely close writer\nwriter.close()\n
# import required libraries\nfrom vidgear.gears import WriteGear\n\n# Define writer with defined parameters and with some dummy name\nwriter = WriteGear(output=\"Output.mp4\", logging=True)\n\n# format FFmpeg command to generate time accurate video\nffmpeg_command = [\n    \"-y\",\n    \"-f\",\n    \"avfoundation\",\n    \"-framerate\",\n    \"30\",\n    \"-i\",\n    \"default\",\n    \"Output.mkv\",\n]  # `-y` parameter is to overwrite outputfile if exists\n\n# execute FFmpeg command\nwriter.execute_ffmpeg_cmd(ffmpeg_command)\n\n# safely close writer\nwriter.close()\n

"},{"location":"help/writegear_ex/#using-writegear-with-rosrobot-operating-system","title":"Using WriteGear with ROS(Robot Operating System)","text":"

We will be using cv_bridge to convert OpenCV frames to ROS image messages and vice-versa.

In this example, we'll create a node that listens to a ROS image message topic, converts the received images messages into OpenCV frames, draws a circle on it, and then process these frames into a lossless compressed file format in real-time.

New in v0.2.2

This example was added in v0.2.2.

This example is vidgear implementation of this wiki example.

# import roslib\nimport roslib\n\nroslib.load_manifest(\"my_package\")\n\n# import other required libraries\nimport sys\nimport rospy\nimport cv2\nfrom std_msgs.msg import String\nfrom sensor_msgs.msg import Image\nfrom cv_bridge import CvBridge, CvBridgeError\nfrom vidgear.gears import WriteGear\n\n# custom publisher class\nclass image_subscriber:\n    def __init__(self, output=\"Output.mp4\"):\n        # create CV bridge\n        self.bridge = CvBridge()\n        # define publisher topic\n        self.image_pub = rospy.Subscriber(\"image_topic_sub\", Image, self.callback)\n        # Define writer with default parameters\n        self.writer = WriteGear(output=output)\n\n    def callback(self, data):\n        # convert received data to frame\n        try:\n            cv_image = self.bridge.imgmsg_to_cv2(data, \"bgr8\")\n        except CvBridgeError as e:\n            print(e)\n\n        # check if frame is valid\n        if cv_image:\n\n            # {do something with the frame here}\n            # let's add a circle\n            (rows, cols, channels) = cv_image.shape\n            if cols > 60 and rows > 60:\n                cv2.circle(cv_image, (50, 50), 10, 255)\n\n            # write frame to writer\n            self.writer.write(cv_image)\n\n        def close(self):\n            # safely close video stream\n            self.writer.close()\n\n\ndef main(args):\n    # define publisher with suitable output filename\n    # such as `Output.mp4` for saving output\n    ic = image_subscriber(output=\"Output.mp4\")\n    # initiate ROS node on publisher\n    rospy.init_node(\"image_subscriber\", anonymous=True)\n    try:\n        # run node\n        rospy.spin()\n    except KeyboardInterrupt:\n        print(\"Shutting down\")\n    finally:\n        # close publisher\n        ic.close()\n\n\nif __name__ == \"__main__\":\n    main(sys.argv)\n

"},{"location":"help/writegear_faqs/","title":"FAQs","text":""},{"location":"help/writegear_faqs/#writegear-faqs","title":"WriteGear FAQs","text":""},{"location":"help/writegear_faqs/#what-is-writegear-api-and-what-does-it-do","title":"What is WriteGear API and what does it do?","text":"

Answer: WriteGear handles various powerful Writer Tools that provide us the freedom to do almost anything imagine with multimedia files. For more info. see WriteGear doc \u27b6

"},{"location":"help/writegear_faqs/#im-only-familiar-with-opencv-how-to-get-started-with-writegear-api","title":"I'm only familiar with OpenCV, how to get started with WriteGear API?","text":"

Answer: Answer: First, refer to the Switching from OpenCV guide, then go through WriteGear documentation. If you still have doubts, ask us on Gitter \u27b6 Community channel.

"},{"location":"help/writegear_faqs/#why-writegear-is-throwing-valueerror","title":"Why WriteGear is throwing ValueError?","text":"

Answer: WriteGear will exit with ValueError if you feed frames of different dimensions or channels.

"},{"location":"help/writegear_faqs/#how-to-install-and-configure-ffmpeg-correctly-for-writegear-on-my-machine","title":"How to install and configure FFmpeg correctly for WriteGear on my machine?","text":"

Answer: Follow these Installation Instructions \u27b6 for its installation.

"},{"location":"help/writegear_faqs/#can-i-use-writegear-directly-with-opencv","title":"Can I use WriteGear directly with OpenCV?","text":"

Answer: Yes,

"},{"location":"help/writegear_faqs/#what-ffmpegs-encoders-and-parameters-are-supported-by-writegear-in-compression-mode","title":"What FFmpeg's encoders and parameters are supported by WriteGear in compression mode?","text":"

Answer: See Supported Parameters \u27b6 and Supported encoders \u27b6

"},{"location":"help/writegear_faqs/#what-opencvs-fourcc-and-parameters-are-supported-by-writegear-in-non-compression-mode","title":"What OpenCV's FOURCC and parameters are supported by WriteGear in non-compression mode?","text":"

Answer: See Supported Parameters \u27b6 and Supported FOURCC \u27b6.

"},{"location":"help/writegear_faqs/#why-this-fourcc-is-not-working-for-me","title":"Why this FOURCC is not working for me?","text":"

Answer: Remember not all the FOURCC and Video extensions are compatible and supported by OpenCV VideoWriter Class. You\u2019ll need to try different combinations of FourCC and file extensions. Furthermore, OpenCV does not return any helpful error messages regarding this problem, so it\u2019s pretty much based on trial and error.

"},{"location":"help/writegear_faqs/#can-i-pass-my-custom-ffmpeg-commands-directly-in-writegear-api","title":"Can I pass my custom FFmpeg commands directly in WriteGear API?","text":"

Answer: Yes, See Custom FFmpeg Commands in WriteGear API \u27b6.

"},{"location":"help/writegear_faqs/#how-to-use-specific-hardware-encoder-in-writegear","title":"How to use specific Hardware Encoder in WriteGear?","text":"

Answer: See this usage example \u27b6

"},{"location":"help/writegear_faqs/#how-to-add-live-audio-to-writegear","title":"How to add live audio to WriteGear?","text":"

Answer: See this doc \u27b6

"},{"location":"help/writegear_faqs/#how-to-separate-and-merge-audio-fromto-video","title":"How to separate and merge audio from/to video?","text":"

Answer: See these usage examples \u27b6

"},{"location":"help/writegear_faqs/#can-i-live-stream-to-twitch-with-writegear-api","title":"Can I live stream to Twitch with WriteGear API?","text":"

Answer: Yes, See this usage example \u27b6

"},{"location":"help/writegear_faqs/#is-youtube-live-streaming-possible-with-writegear","title":"Is YouTube-Live Streaming possible with WriteGear?","text":"

Answer: Yes, See this bonus example \u27b6.

"},{"location":"help/writegear_faqs/#how-to-live-streaming-using-rtsprtp-protocol-with-writegear","title":"How to Live-Streaming using RTSP/RTP protocol with WriteGear?","text":"

Answer: See this bonus example \u27b6.

"},{"location":"help/writegear_faqs/#how-to-create-mp4-segments-from-a-video-stream-with-writegear","title":"How to create MP4 segments from a video stream with WriteGear?","text":"

Answer: See this bonus example \u27b6.

"},{"location":"help/writegear_faqs/#how-add-external-audio-file-input-to-video-frames","title":"How add external audio file input to video frames?","text":"

Answer: See this bonus example \u27b6.

"},{"location":"help/writegear_faqs/#why-this-ffmpeg-parameter-is-not-working-for-me-in-compression-mode","title":"Why this FFmpeg parameter is not working for me in compression mode?","text":"

Answer: If some FFmpeg parameter doesn't work for you, then tell us on Gitter \u27b6, and if that doesn't help, then finally report an issue \u27b6

"},{"location":"help/writegear_faqs/#why-writegear-is-switching-to-non-compression-mode-even-if-it-is-not-enable","title":"Why WriteGear is switching to Non-compression Mode, even if it is not enable?","text":"

Answer: In case WriteGear API fails to detect valid FFmpeg executables on your system (even if Compression Mode is enabled), it will automatically fallback to Non-Compression Mode. Follow Installation Instructions \u27b6 for FFmpeg installation.

"},{"location":"installation/pip_install/","title":"Install using pip","text":""},{"location":"installation/pip_install/#install-using-pip","title":"Install using pip","text":"

Best option for easily getting stable VidGear installed.

"},{"location":"installation/pip_install/#prerequisites","title":"Prerequisites","text":"

When installing VidGear with pip, you need to manually install following prerequisites:

Upgrade your pip

It strongly advised to upgrade to latest pip before installing vidgear to avoid any undesired installation error(s).

There are two mechanisms to upgrade pip:

pipensurepip

You can use existing pip to upgrade itself:

Install pip if not present Linux / MacOS Windows
python get-pip.py\n
py get-pip.py\n

More details about this script can be found in pypa/get-pip\u2019s README.

Linux / MacOS Windows
python -m pip install pip --upgrade\n
py -m pip install pip --upgrade\n

Python also comes with an ensurepip module1, which can easily upgrade/install pip in any Python environment.

Linux / MacOS Windows
python -m ensurepip --upgrade\n
py -m ensurepip --upgrade\n

"},{"location":"installation/pip_install/#critical-prerequisites","title":"Critical Prerequisites","text":""},{"location":"installation/pip_install/#opencv","title":"OpenCV","text":"

Must require OpenCV(3.0+) python binaries installed for all core functions. You easily install it directly via pip:

OpenCV installation from source

You can also follow online tutorials for building & installing OpenCV on Windows, Linux, MacOS and Raspberry Pi machines manually from its source.

Make sure not to install both pip and source version together. Otherwise installation will fail to work!

Other OpenCV binaries

OpenCV maintainers also provide additional binaries via pip that contains both main modules and contrib/extra modules opencv-contrib-python, and for server (headless) environments like opencv-python-headless and opencv-contrib-python-headless. You can also install any one of them in similar manner. More information can be found here.

pip install opencv-python       \n
"},{"location":"installation/pip_install/#api-specific-prerequisites","title":"API Specific Prerequisites","text":""},{"location":"installation/pip_install/#ffmpeg","title":"FFmpeg","text":"

Require only for the video compression and encoding compatibility within StreamGear API and WriteGear API's Compression Mode.

FFmpeg Installation

"},{"location":"installation/pip_install/#picamera2","title":"Picamera2","text":"

Required only if you're using Raspberry Pi Camera Modules (or USB webcams) with the PiGear API. Here's how to install Picamera2 python library:

Using Legacy picamera library with PiGear (v0.3.3 and above)

PiGear API (version 0.3.3 onwards) prioritizes the newer Picamera2 library under the hood for Raspberry Pi camera modules. However, if your operating system doesn't support Picamera2, you can still use the legacy picamera library. Here's how to easily install it using pip:

pip install picamera\n

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

Picamera2 is only supported on Raspberry Pi OS Bullseye (or later) images, both 32 and 64-bit.

Picamera2 is NOT supported on:

Installation using apt (Recommended)Installation using pip As of September 2022, Picamera2 is pre-installed on images downloaded from Raspberry Pi. So you don't have to install it manually.

If Picamera2 is not already installed, then your image is presumably older and you should start with system upgrade:

sudo apt update && upgrade\n

If you have installed Picamera2 previously using pip, then you should also uninstall this (pip3 uninstall picamera2).

Thereafter, you can install Picamera2 with all the GUI (Qt and OpenGL) dependencies using:

sudo apt install -y python3-picamera2\n

Or, If you DON'T want the GUI dependencies, use:

sudo apt install -y python3-picamera2 --no-install-recommends\n

This is NOT the recommended way to install Picamera2.

However, if you wish to install Picamera2 with all the GUI (Qt and OpenGL) dependencies with pip, use:

sudo apt install -y python3-libcamera python3-kms++\nsudo apt install -y python3-pyqt5 python3-prctl \nsudo apt install -y libatlas-base-dev ffmpeg python3-pip\npip3 install numpy --upgrade\npip3 install picamera2[gui]\n

Or, If you DON'T want the GUI dependencies, use:

sudo apt install -y python3-libcamera python3-kms++\nsudo apt install -y python3-prctl libatlas-base-dev\nsudo apt install -y ffmpeg libopenjp2-7 python3-pip\npip3 install numpy --upgrade\npip3 install picamera2\n
"},{"location":"installation/pip_install/#uvloop","title":"Uvloop","text":"

Required only if you're using the NetGear_Async API on UNIX machines for maximum performance. You can easily install it via pip:

uvloop is NOT yet supported on Windows Machines.

pip install uvloop\n
"},{"location":"installation/pip_install/#dxcam","title":"DXcam","text":"

Required only if you're using the ScreenGear API on Windows machines for better FPS performance. You can easily install it via pip:

FYI, DXcam is ONLY supported on Windows Machines.

pip install dxcam\n
"},{"location":"installation/pip_install/#installation","title":"Installation","text":"Installation command with pip has been changed in v0.2.4

The legacy pip install vidgear command now installs critical bare-minimum dependencies only. Therefore in order to automatically install all the API specific dependencies as previous versions, use pip install vidgear[core] command instead.

v0.2.4 and newerOlder
# Install latest stable release with all Core dependencies\npip install -U vidgear[core]\n

[core] keyword isn't available in versions older than v0.2.4

# Install older stable release with all Core dependencies\npip install vidgear<0.2.4\n

Similarly in your python project files like setup.py or requirements.txt or setup.cfg, use vidgear dependency as vidgear[core]>=0.2.4 instead.

This change does not affects pip install vidgear[asyncio] command.

Installation is as simple as:

Installing vidgear with only selective dependencies

Starting with version v0.2.2, you can now run any VidGear API by installing only just specific dependencies required by the API in use(except for some Core dependencies).

This is useful when you want to manually review, select and install minimal API-specific dependencies on bare-minimum vidgear from scratch on your system:

Windows Installation

If you are using Windows, some of the commands given below, may not work out-of-the-box.

A quick solution may be to preface every Python command with python -m like this:

# Install latest stable release with all Core dependencies\npython -m pip install -U vidgear[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npython -m pip install -U vidgear[asyncio]\n

And, If you don't have the privileges to the directory you're installing package. Then use --user flag, that makes pip install packages in your home directory instead:

# Install latest stable release with all Core dependencies\npython -m pip install --upgrade --user vidgear[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npython -m pip install --upgrade --user vidgear[asyncio]\n

Or, If you're using py as alias for installed python, then:

# Install latest stable release with all Core dependencies\npy -m pip install --upgrade --user vidgear[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npy -m pip install --upgrade --user vidgear[asyncio]\n
# Install latest stable release with all Core dependencies\npip install -U vidgear[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npip install -U vidgear[asyncio]\n

And if you prefer to install VidGear directly from the repository:

# Install latest stable release with all Core dependencies\npip install git+git://github.com/abhiTronix/vidgear@master#egg=vidgear[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npip install git+git://github.com/abhiTronix/vidgear@master#egg=vidgear[asyncio]\n

Or you can also download its wheel (.whl) package from our repository's releases section, and thereby can be installed as follows:

# Install latest stable release with all Core dependencies\npip install vidgear-0.3.3-py3-none-any.whl[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npip install vidgear-0.3.3-py3-none-any.whl[asyncio]\n

  1. The ensurepip module is missing/disabled on Ubuntu. Use pip method only.\u00a0\u21a9

"},{"location":"installation/source_install/","title":"Install from source","text":""},{"location":"installation/source_install/#install-from-source","title":"Install from source","text":"

Best option for trying latest patches(maybe experimental), forking for Pull Requests, or automatically installing all prerequisites(with a few exceptions).

"},{"location":"installation/source_install/#prerequisites","title":"Prerequisites","text":"

When installing VidGear from source, following are some API specific prerequisites you may need to install manually:

What about rest of the prerequisites?

Any other python prerequisites (Critical/API specific) will be automatically installed based on your OS/System specifications.

Upgrade your pip

It strongly advised to upgrade to latest pip before installing vidgear to avoid any undesired installation error(s).

There are two mechanisms to upgrade pip:

pipensurepip

You can use existing pip to upgrade itself:

Install pip if not present Linux/MacOSWindows
python get-pip.py\n
py get-pip.py\n

More details about this script can be found in pypa/get-pip\u2019s README.

Linux/MacOSWindows
python -m pip install pip --upgrade\n
py -m pip install pip --upgrade\n

Python also comes with an ensurepip module1, which can easily upgrade/install pip in any Python environment.

Linux/MacOSWindows
python -m ensurepip --upgrade\n
py -m ensurepip --upgrade\n
"},{"location":"installation/source_install/#api-specific-prerequisites","title":"API Specific Prerequisites","text":""},{"location":"installation/source_install/#ffmpeg","title":"FFmpeg","text":"

Require only for the video compression and encoding compatibility within StreamGear API and WriteGear API's Compression Mode.

FFmpeg Installation

"},{"location":"installation/source_install/#picamera2","title":"Picamera2","text":"

Required only if you're using Raspberry Pi Camera Modules (or USB webcams) with the PiGear API. Here's how to install Picamera2 python library:

Using Legacy picamera library with PiGear (v0.3.3 and above)

PiGear API (version 0.3.3 onwards) prioritizes the newer Picamera2 library under the hood for Raspberry Pi camera modules. However, if your operating system doesn't support Picamera2, you can still use the legacy picamera library. Here's how to easily install it using pip:

pip install picamera\n

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

Picamera2 is only supported on Raspberry Pi OS Bullseye (or later) images, both 32 and 64-bit.

Picamera2 is NOT supported on:

Installation using apt (Recommended)Installation using pip As of September 2022, Picamera2 is pre-installed on images downloaded from Raspberry Pi. So you don't have to install it manually.

If Picamera2 is not already installed, then your image is presumably older and you should start with system upgrade:

sudo apt update && upgrade\n

If you have installed Picamera2 previously using pip, then you should also uninstall this (pip3 uninstall picamera2).

Thereafter, you can install Picamera2 with all the GUI (Qt and OpenGL) dependencies using:

sudo apt install -y python3-picamera2\n

Or, If you DON'T want the GUI dependencies, use:

sudo apt install -y python3-picamera2 --no-install-recommends\n

This is NOT the recommended way to install Picamera2.

However, if you wish to install Picamera2 with all the GUI (Qt and OpenGL) dependencies with pip, use:

sudo apt install -y python3-libcamera python3-kms++\nsudo apt install -y python3-pyqt5 python3-prctl \nsudo apt install -y libatlas-base-dev ffmpeg python3-pip\npip3 install numpy --upgrade\npip3 install picamera2[gui]\n

Or, If you DON'T want the GUI dependencies, use:

sudo apt install -y python3-libcamera python3-kms++\nsudo apt install -y python3-prctl libatlas-base-dev\nsudo apt install -y ffmpeg libopenjp2-7 python3-pip\npip3 install numpy --upgrade\npip3 install picamera2\n
"},{"location":"installation/source_install/#installation","title":"Installation","text":"

If you want to checkout the latest beta testing branch , you can do so with the following commands:

This can be useful if you want to provide feedback for a new feature or bug fix in the testing branch.

DO NOT clone or install any other branch other than testing unless advised, as it is not tested with CI environments and possibly very unstable or unusable.

Installing vidgear with only selective dependencies

Starting with version v0.2.2, you can now run any VidGear API by installing only just specific dependencies required by the API in use(except for some Core dependencies).

This is useful when you want to manually review, select and install minimal API-specific dependencies on bare-minimum vidgear from scratch on your system:

Windows Installation

If you are using Windows, some of the commands given below, may not work out-of-the-box.

A quick solution may be to preface every Python command with python -m like this:

# Install latest stable release with all Core dependencies\npython -m pip install -U .[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npython -m pip install -U .[asyncio]\n

And, If you don't have the privileges to the directory you're installing package. Then use --user flag, that makes pip install packages in your home directory instead:

# Install latest stable release with all Core dependencies\npython -m pip install --upgrade --user .[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npython -m pip install --upgrade --user .[asyncio]\n

Or, If you're using py as alias for installed python, then:

# Install latest stable release with all Core dependencies\npy -m pip install --upgrade --user .[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npy -m pip install --upgrade --user .[asyncio]\n
# clone the repository and get inside\ngit clone https://github.com/abhiTronix/vidgear.git && cd vidgear\n\n# checkout the latest testing branch\ngit checkout testing\n\n# Install latest stable release with all Core dependencies\npip install -U .[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npip install -U .[asyncio]\n

  1. The ensurepip module is missing/disabled on Ubuntu. Use pip method only.\u00a0\u21a9

"}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Overview","text":""},{"location":"#introduction","title":"Introduction","text":"

VidGear is a cross-platform High-Performance Video-Processing Framework for building complex real-time media applications in python

VidGear provides an easy-to-use, highly extensible, Multi-Threaded + Asyncio API Framework on top of many state-of-the-art specialized libraries like OpenCV, FFmpeg, ZeroMQ, picamera2, starlette, yt_dlp, pyscreenshot, dxcam, aiortc and python-mss at its backend, and enable us to flexibly exploit their internal parameters and methods, while silently delivering robust error-handling and real-time performance \u26a1\ufe0f.

\"Write Less and Accomplish More\" \u2014 VidGear's Motto

VidGear focuses on simplicity, and thereby lets programmers and software developers to easily integrate and perform Complex Video Processing Tasks without going through hefty documentation and in just a few lines of code.

"},{"location":"#getting-started","title":"Getting Started","text":"

In case you're run into any problems, consult the Help section.

If you're just getting started with OpenCV-Python programming, then refer this FAQ \u27b6

"},{"location":"#gears","title":"Gears","text":"

VidGear is built with multiple Gears each with some unique functionality.

Each Gear is designed exclusively to handle/control/process different data-specific & device-specific video streams, network streams, and media encoders/decoders.

These Gears can be classified as follows:

"},{"location":"#videocapture-gears","title":"VideoCapture Gears","text":""},{"location":"#videowriter-gears","title":"VideoWriter Gears","text":""},{"location":"#streaming-gears","title":"Streaming Gears","text":""},{"location":"#network-gears","title":"Network Gears","text":"

"},{"location":"#contributions","title":"Contributions","text":"

Contributions are welcome, and greatly appreciated!

Please see our Contribution Guidelines for more details.

"},{"location":"#community-channel","title":"Community Channel","text":"

If you've come up with some new idea, or looking for the fastest way troubleshoot your problems. Please checkout our Gitter community channel \u27b6

"},{"location":"#become-a-stargazer","title":"Become a Stargazer","text":"

You can be a Stargazer by starring us on Github, it helps us a lot and you're making it easier for others to find & trust this library. Thanks!

"},{"location":"#donations","title":"Donations","text":"

VidGear is free and open source and will always remain so.

It is something I am doing with my own free time. But so much more needs to be done, and I need your help to do this. For just the price of a cup of coffee, you can make a difference

"},{"location":"#citation","title":"Citation","text":"

Here is a Bibtex entry you can use to cite this project in a publication:

@software{vidgear,\n  author       = {Abhishek Thakur and\n                  Zoe Papakipos and\n                  Christian Clauss and\n                  Christian Hollinger and\n                  Ian Max Andolina and\n                  Vincent Boivin and\n                  Kyle Ahn and\n                  freol35241 and\n                  Benjamin Lowe and\n                  Micka\u00ebl Schoentgen and\n                  Renaud Bouckenooghe and\n                  Ibtsam Ahmad},\n  title        = {abhiTronix/vidgear: VidGear Stable v0.3.2},\n  month        = sep,\n  year         = 2023,\n  publisher    = {Zenodo},\n  version      = {vidgear-0.3.2},\n  doi          = {10.5281/zenodo.8332548},\n  url          = {https://doi.org/10.5281/zenodo.8332548}\n}\n

"},{"location":"changelog/","title":"Release Notes","text":""},{"location":"changelog/#release-notes","title":"Release Notes","text":""},{"location":"changelog/#v033-2024-06-22","title":"v0.3.3 (2024-06-22)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v032-2023-09-10","title":"v0.3.2 (2023-09-10)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v031-2023-07-22","title":"v0.3.1 (2023-07-22)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests "},{"location":"changelog/#v030-2023-01-26","title":"v0.3.0 (2023-01-26)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v026-2022-07-05","title":"v0.2.6 (2022-07-05)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v025-2021-02-11","title":"v0.2.5 (2021-02-11)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests "},{"location":"changelog/#v024-2021-12-05","title":"v0.2.4 (2021-12-05)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v023-2021-10-27","title":"v0.2.3 (2021-10-27)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v022-2021-09-02","title":"v0.2.2 (2021-09-02)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v021-2021-04-25","title":"v0.2.1 (2021-04-25)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v020-2021-01-01","title":"v0.2.0 (2021-01-01)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests "},{"location":"changelog/#v019-2020-08-31","title":"v0.1.9 (2020-08-31)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests "},{"location":"changelog/#v018-2020-06-12","title":"v0.1.8 (2020-06-12)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests "},{"location":"changelog/#v017-2020-04-29","title":"v0.1.7 (2020-04-29)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v016-2020-01-01","title":"v0.1.6 (2020-01-01)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v015-2019-07-24","title":"v0.1.5 (2019-07-24)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests "},{"location":"changelog/#v014-2019-05-11","title":"v0.1.4 (2019-05-11)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests "},{"location":"changelog/#v013-2019-04-07","title":"v0.1.3 (2019-04-07)","text":"Bug-fixes Pull Requests "},{"location":"changelog/#v012-2019-03-27","title":"v0.1.2 (2019-03-27)","text":"New Features Updates/Improvements Bug-fixes Pull Requests "},{"location":"changelog/#v011-2019-03-24","title":"v0.1.1 (2019-03-24)","text":"New Features Bug-fixes "},{"location":"changelog/#v010-2019-03-17","title":"v0.1.0 (2019-03-17)","text":"New Features "},{"location":"contribution/","title":"Contribution Overview","text":""},{"location":"contribution/#contribution-overview","title":"Contribution Overview","text":"

Contributions are welcome, We'd love your contribution to VidGear in order to fix bugs or to implement new features!

Contribution Opportunities

If you're looking for something to work on, check for the PR WELCOMED labeled issues on our GitHub Repository.

"},{"location":"contribution/#submission-guidelines","title":"Submission Guidelines","text":""},{"location":"contribution/#submission-contexts","title":"Submission Contexts","text":""},{"location":"contribution/#got-a-question-or-problem","title":"Got a question or problem?","text":"

For quick questions, please refrain from opening an issue, instead read our FAQ & Troubleshooting section or you can reach us on Gitter community channel.

"},{"location":"contribution/#found-a-typo","title":"Found a typo?","text":"

There's no need to contribute for some typos. Just reach us on Gitter \u27b6 community channel, We will correct them in (less than) no time.

"},{"location":"contribution/#found-a-bug","title":"Found a bug?","text":"

If you encountered a bug, you can help us by submitting an issue in our GitHub repository. Even better, you can submit a Pull Request(PR) with a fix, but make sure to read the guidelines \u27b6.

"},{"location":"contribution/#request-for-a-featureimprovement","title":"Request for a feature/improvement?","text":"Subscribe to Github Repository

You can subscribe our GitHub Repository to receive notifications through email for new pull requests, commits and issues that are created in VidGear. Learn more about it here \u27b6

You can request our GitHub Repository for a new feature/improvement based on the type of request:

Please submit an issue with a proposal template for your request to explain how it benefits everyone in the community.

"},{"location":"gears/","title":"Introduction","text":""},{"location":"gears/#introduction","title":"Introduction","text":"Gears: generalized workflow"},{"location":"gears/#gears-what-are-these","title":"Gears , What are these?","text":"

VidGear is built on Standalone APIs - also known as Gears , each with some unique functionality. Each Gears is designed exclusively to handle/control/process different data-specific & device-specific video streams, network streams, and media encoders/decoders.

Gears allows users to work with an inherently optimized, easy-to-use, extensible, and exposed API Framework on top of many state-of-the-art libraries, while silently delivering robust error handling and unmatched real-time performance.

"},{"location":"gears/#gears-classification","title":"Gears Classification","text":"

These Gears can be classified as follows:

"},{"location":"gears/#a-videocapture-gears","title":"A. VideoCapture Gears","text":"

Basic Function: Retrieves numpy.ndarray frames from various sources.

"},{"location":"gears/#b-videowriter-gears","title":"B. VideoWriter Gears","text":"

Basic Function: Writes numpy.ndarray frames to a video file or network stream.

"},{"location":"gears/#c-streaming-gears","title":"C. Streaming Gears","text":"

Basic Function: Transcodes/Broadcasts files and numpy.ndarray frames for streaming.

You can also use WriteGear for streaming with traditional protocols such as RTMP, RTSP/RTP.

"},{"location":"gears/#d-network-gears","title":"D. Network Gears","text":"

Basic Function: Sends/Receives data and numpy.ndarray frames over connected networks.

"},{"location":"help/","title":"Helping VidGear","text":""},{"location":"help/#helping-vidgear","title":"Helping VidGear","text":"

Liked VidGear? Would you like to help VidGear, other users, and the author?

There are many simple ways to help us:

"},{"location":"help/#star-vidgear-on-github","title":"Star VidGear on GitHub","text":"

You can star VidGear on GitHub:

It helps us a lot by making it easier for others to find & trust this library. Thanks!

"},{"location":"help/#help-others-with-issues-on-github","title":"Help others with issues on GitHub","text":"

You can see through any opened or pinned existing issues on our GitHub repository, and try helping others, wherever possible:

"},{"location":"help/#watch-the-github-repository","title":"Watch the GitHub repository","text":"

You can watch \ud83d\udc40 VidGear Activities on GitHub:

When you watch a repository, you will be notified of all conversations for that repository, including when someone creates a new issue, or pushes a new pull request.

You can try helping solving those issues, or give valuable feedback/review on new Pull Requests.

"},{"location":"help/#helping-author","title":"Helping Author","text":"

Donations help keep VidGear's development alive and motivate me (as author).

It is something I am doing with my own free time. But so much more needs to be done, and I need your help to do this. For just the price of a cup of coffee, you can make a difference

Thanks a million!

"},{"location":"help/#connect-with-author","title":"Connect with Author","text":"

You can connect with me, the author \ud83d\udc4b:

"},{"location":"installation/","title":"Installation Overview","text":""},{"location":"installation/#installation-overview","title":"Installation Overview","text":""},{"location":"installation/#supported-systems","title":"Supported Systems","text":"

VidGear is well-tested and supported on the following systems(but not limited to), with python 3.8+ and pip installed:

"},{"location":"installation/#supported-python-legacies","title":"Supported Python legacies","text":"

Depreciation Notice

Python-3.7 legacies support has been dropped from Vidgear.

Python 3.8+ are only supported legacies for installing Vidgear v0.3.1 and above.

"},{"location":"installation/#installation-methods","title":"Installation methods","text":""},{"location":"license/","title":"License","text":""},{"location":"license/#license","title":"License","text":"

This library is released under the Apache 2.0 License.

"},{"location":"license/#copyright-notice","title":"Copyright Notice","text":"
Copyright (c) 2019 Abhishek Thakur(@abhiTronix) <abhi.una12@gmail.com>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n
"},{"location":"switch_from_cv/","title":"Switching from OpenCV","text":""},{"location":"switch_from_cv/#switching-from-opencv-library","title":"Switching from OpenCV Library","text":"

Switching OpenCV with VidGear APIs is fairly painless process, and will just require changing a few lines in your python script.

This document is intended to software developers who want to migrate their python code from OpenCV Library to VidGear APIs.

Prior knowledge of Python or OpenCV won't be covered in this guide. Proficiency with OpenCV-Python (Python API for OpenCV) is a must in order understand this document.

If you're just getting started with OpenCV-Python programming, then refer this FAQ \u27b6

"},{"location":"switch_from_cv/#why-vidgear-is-better-than-opencv","title":"Why VidGear is better than OpenCV?","text":"

Learn more about OpenCV here \u27b6

VidGear employs OpenCV at its backend and enhances its existing capabilities even further by introducing many new state-of-the-art functionalities such as:

Vidgear offers all this at once while maintaining the same standard OpenCV-Python (Python API for OpenCV) coding syntax for all of its APIs, thereby making it even easier to implement complex real-time OpenCV applications in python code without changing things much.

"},{"location":"switch_from_cv/#switching-the-videocapture-apis","title":"Switching the VideoCapture APIs","text":"

Let's compare a bare-minimum python code for extracting frames out of any Webcam/USB-camera (connected at index 0), between OpenCV's VideoCapture Class and VidGear's CamGear VideoCapture API side-by-side:

CamGear API share the same syntax as other VideoCapture APIs, thereby you can easily switch to any of those APIs in a similar manner.

OpenCV VideoCapture ClassVidGear's CamGear API
# import required libraries\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = cv2.VideoCapture(0) \n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n      break\n\n\n    # {do something with the frame here}\n\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.release()\n
# import required libraries\nfrom vidgear.gears import CamGear\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = CamGear(source=0).start() \n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

and both syntax almost looks the same, easy, isn't it?

"},{"location":"switch_from_cv/#differences","title":"Differences","text":"

Let's breakdown a few noteworthy difference in both syntaxes:

Task OpenCV VideoCapture Class VidGear's CamGear API Initiating stream = cv2.VideoCapture(0) stream = CamGear(source=0).start() Reading frames (grabbed, frame) = stream.read() frame = stream.read() Checking empty frame if not grabbed: if frame is None: Terminating stream.release() stream.stop()

Now checkout other VideoCapture Gears \u27b6

"},{"location":"switch_from_cv/#switching-the-videowriter-api","title":"Switching the VideoWriter API","text":"

Let's extend previous bare-minimum python code and save those extracted frames to disk as a valid file, with OpenCV's VideoWriter Class and VidGear's WriteGear (with FFmpeg backend), compared side-to-side:

WriteGear API also provides backend for OpenCV's VideoWriter Class. More information here \u27b6

OpenCV VideoWriter ClassVidGear's WriteGear API
# import required libraries\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = cv2.VideoCapture(0) \n\n# Define the codec and create VideoWriter object with suitable output \n# filename for e.g. `Output.avi`\nfourcc = cv2.VideoWriter_fourcc(*'XVID') \nwriter = cv2.VideoWriter('output.avi', fourcc, 20.0, (640, 480)) \n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n      break\n\n\n    # {do something with the frame here}\n\n\n    # write frame to writer\n    writer.write(frame)\n\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.release()\n\n# safely close writer\nwriter.release() \n
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = CamGear(source=0).start() \n\n# Define WriteGear Object with suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output = 'Output.mp4') \n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if None-type\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

Noticed WriteGear's coding syntax looks similar but less complex?

"},{"location":"switch_from_cv/#differences_1","title":"Differences","text":"

Let's breakdown a few noteworthy difference in both syntaxes:

Task OpenCV VideoWriter Class VidGear's WriteGear API Initiating writer = cv2.VideoWriter('output.avi', cv2.VideoWriter_fourcc(*'XVID'), 20.0, (640, 480)) writer = WriteGear(output='Output.mp4') Writing frames writer.write(frame) writer.write(frame) Terminating writer.release() writer.close()

Now checkout more about WriteGear API here \u27b6

"},{"location":"bonus/TQM/","title":"Threaded Queue Mode","text":""},{"location":"bonus/TQM/#threaded-queue-mode","title":"Threaded Queue Mode","text":""},{"location":"bonus/TQM/#overview","title":"Overview","text":"Threaded-Queue-Mode: generalized timing diagram

Threaded Queue Mode is designed exclusively for VidGear's Videocapture Gears (namely CamGear, VideoGear) and few Network Gears (such as NetGear(Client's end)) for achieving high-performance, asynchronous, error-free video-frames handling.

Threaded-Queue-Mode is enabled by default, but can be disabled, only if extremely necessary.

Threaded-Queue-Mode is NOT required and thereby automatically disabled for Live feed such as Camera Devices/Modules, since .

"},{"location":"bonus/TQM/#what-does-threaded-queue-mode-exactly-do","title":"What does Threaded-Queue-Mode exactly do?","text":"

Threaded-Queue-Mode helps VidGear do the Threaded Video-Processing tasks in highly optimized, well-organized, and most competent way possible:

"},{"location":"bonus/TQM/#a-enables-multi-threading","title":"A. Enables Multi-Threading","text":"

In case you don't already know, OpenCV's' read() is a Blocking I/O function for reading and decoding the next video-frame, and consumes much of the I/O bound memory depending upon our video source properties & system hardware. This essentially means, the corresponding thread that reads data from it, is continuously blocked from retrieving the next frame. As a result, our python program appears slow and sluggish even without any type of computationally expensive image processing operations. This problem is far more severe on low memory SBCs like Raspberry Pis.

In Threaded-Queue-Mode, VidGear creates several Python Threads within one process to offload the frame-decoding task to a different thread. Thereby, VidGear is able to execute different Video I/O-bounded operations at the same time by overlapping there waiting times. Moreover, threads are managed by operating system itself and is capable of distributing them between available CPU cores efficiently. In this way, Threaded-Queue-Mode keeps on processing frames faster in the background without affecting by sluggishness in our main python program thread.

"},{"location":"bonus/TQM/#b-utilizes-fixed-size-queues","title":"B. Utilizes Fixed-Size Queues","text":"

Although Multi-threading is fast, easy, and efficient, it can lead to some serious undesired effects like frame-skipping, Global Interpreter Lock, race conditions, etc. This is because there is no isolation whatsoever in python threads, and in case there is any crash it will cause the whole process to crash. That's not all, the memory of the process is shared by different threads and that may result in random process crashes due to unwanted race conditions.

These problems are avoided in Threaded-Queue-Mode by utilizing Thread-Safe, Memory-Efficient, and Fixed-Size Queues (with approximately same O(1) performance in both directions), that isolates the frame-decoding thread from other parallel threads and provide synchronized access to incoming frames without any obstruction.

"},{"location":"bonus/TQM/#c-accelerates-frame-processing","title":"C. Accelerates Frame Processing","text":"

With queues, VidGear always maintains a fixed-length frames buffer in the memory and blocks the thread temporarily if the queue is full to avoid possible frame drops or otherwise pops out the frames synchronously without any obstructions. This significantly accelerates frame processing rate (and therefore our overall video processing pipeline) comes from dramatically reducing latency \u2014 since we don\u2019t have to wait for the read() method to finish reading and decoding a frame; instead, there is always a pre-decoded frame ready for us to process.

"},{"location":"bonus/TQM/#what-are-the-advantages-of-threaded-queue-mode","title":"What are the advantages of Threaded-Queue-Mode?","text":"

"},{"location":"bonus/TQM/#manually-disabling-threaded-queue-mode","title":"Manually disabling Threaded-Queue-Mode","text":"

To manually disable Threaded-Queue-Mode, VidGear provides THREADED_QUEUE_MODE boolean attribute for options dictionary parameter in respective VideoCapture APIs:

Important Warnings

Disabling Threaded-Queue-Mode may lead to Random Intermittent Bugs that can be quite difficult to discover. More insight can be found here \u27b6

THREADED_QUEUE_MODE (boolean): This attribute can be used to override Threaded-Queue-Mode mode to manually disable it:

options = {'THREADED_QUEUE_MODE': False} # to disable Threaded Queue Mode. \n

and you can pass it to options dictionary parameter of the respective API.

"},{"location":"bonus/colorspace_manipulation/","title":"Colorspace Manipulation","text":""},{"location":"bonus/colorspace_manipulation/#colorspace-manipulation-for-videocapture-gears","title":"Colorspace Manipulation for VideoCapture Gears","text":""},{"location":"bonus/colorspace_manipulation/#source-colorspace-manipulation","title":"Source ColorSpace manipulation","text":"

All VidGear's Videocapture Gears (namely CamGear, ScreenGear, VideoGear) and some Streaming Gears (namely WebGear, WebGear_RTC) and Network Gears (Client's end) - provides exclusive internal support for Source Color Space manipulation.

There are two ways to alter source colorspace:

"},{"location":"bonus/colorspace_manipulation/#using-colorspace-parameter","title":"Using colorspace parameter","text":"

Primarily, the safest way is by colorspace (string) parameter of the respective VideoCapture API, that can be used to easily alter the colorspace of the input source, during initialization. But on the downside, colorspace parameter value CANNOT be changed/altered at runtime.

All possible values for this parameter are discussed below \u27b6

"},{"location":"bonus/colorspace_manipulation/#using-color_space-global-variable","title":"Using color_space global variable","text":"

Alternatively, a more direct approach is by using color_space (integer) global variable the respective VideoCapture API, can be used for directly changing the source colorspace at runtime. It can be used in conjunction with colorspace parameter easily.

Supported Colorspace Conversions

Any conversion from default Source colorspace (i.e. BGR in case of OpenCV), to any other colorspace and vice-versa (use None to revert), is supported.

Important Information

Tip

It is advised to enable logging(logging = True) on the first run for easily identifying any runtime errors.

"},{"location":"bonus/colorspace_manipulation/#supported-colorspace-parameter-values","title":"Supported colorspace parameter values","text":"

All supported string values for colorspace parameter are as follows:

You can check all OpenCV Colorspace Conversion Codes here \u27b6.

Supported Conversion Values Description COLOR_BGR2BGRA BGR to BGRA COLOR_BGR2RGBA BGR to RGBA COLOR_BGR2RGB BGR to RGB backward conversions to RGB/BGR COLOR_BGR2GRAY BGR to GRAY COLOR_BGR2BGR565 BGR to BGR565 COLOR_BGR2BGR555 BGR to BGR555 COLOR_BGR2XYZ BGR to CIE XYZ COLOR_BGR2YCrCb BGR to luma-chroma (aka YCC) COLOR_BGR2HSV BGR to HSV (hue saturation value) COLOR_BGR2Lab BGR to CIE Lab COLOR_BGR2Luv BGR to CIE Luv COLOR_BGR2HLS BGR to HLS (hue lightness saturation) COLOR_BGR2HSV_FULL BGR to HSV_FULL COLOR_BGR2HLS_FULL BGR to HLS_FULL COLOR_BGR2YUV BGR to YUV COLOR_BGR2YUV_I420 BGR to YUV 4:2:0 family COLOR_BGR2YUV_IYUV BGR to IYUV COLOR_BGR2YUV_YV12 BGR to YUV_YV12 None Back to default colorspace (i.e. BGR)

"},{"location":"bonus/colorspace_manipulation/#usage-examples","title":"Usage examples","text":""},{"location":"bonus/colorspace_manipulation/#using-camgear-with-direct-colorspace-manipulation","title":"Using CamGear with Direct Colorspace Manipulation","text":"

The complete usage example can be found here \u27b6

"},{"location":"bonus/colorspace_manipulation/#using-pigear-with-direct-colorspace-manipulation","title":"Using PiGear with Direct Colorspace Manipulation","text":"

The complete usage example can be found here \u27b6

"},{"location":"bonus/colorspace_manipulation/#using-videogear-with-colorspace-manipulation","title":"Using VideoGear with Colorspace Manipulation","text":"

The complete usage example can be found here \u27b6

"},{"location":"bonus/colorspace_manipulation/#using-screengear-with-direct-colorspace-manipulation","title":"Using ScreenGear with Direct Colorspace Manipulation","text":"

The complete usage example can be found here \u27b6

"},{"location":"bonus/reference/camgear/","title":"CamGear API References","text":"

CamGear API usage examples can be found here \u27b6

CamGear API parameters are explained here \u27b6

CamGear supports a diverse range of video streams which can handle/control video stream almost any IP/USB Cameras, multimedia video file format (upto 4k tested), any network stream URL such as http(s), rtp, rtsp, rtmp, mms, etc. It also supports Gstreamer's RAW pipelines.

CamGear API provides a flexible, high-level multi-threaded wrapper around OpenCV's VideoCapture API with direct access to almost all of its available parameters. It relies on Threaded Queue mode for threaded, error-free and synchronized frame handling.

CamGear internally implements yt_dlp backend class for seamlessly pipelining live video-frames and metadata from various streaming services like YouTube, Dailymotion, Twitch, and many more \u27b6

Source code in vidgear/gears/camgear.py
class CamGear:\n    \"\"\"\n    CamGear supports a diverse range of video streams which can handle/control video stream almost any IP/USB Cameras, multimedia video file format (upto 4k tested),\n    any network stream URL such as http(s), rtp, rtsp, rtmp, mms, etc. It also supports Gstreamer's RAW pipelines.\n\n    CamGear API provides a flexible, high-level multi-threaded wrapper around OpenCV's VideoCapture API with direct access to almost all of its available parameters.\n    It relies on Threaded Queue mode for threaded, error-free and synchronized frame handling.\n\n    CamGear internally implements `yt_dlp` backend class for seamlessly pipelining live video-frames and metadata from various streaming services like YouTube, Dailymotion,\n    Twitch, and [many more \u27b6](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md#supported-sites)\n    \"\"\"\n\n    def __init__(\n        self,\n        source=0,\n        stream_mode=False,\n        backend=0,\n        colorspace=None,\n        logging=False,\n        time_delay=0,\n        **options\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the CamGear class.\n\n        Parameters:\n            source (based on input): defines the source for the input stream.\n            stream_mode (bool): controls the exclusive **Stream Mode** for handling streaming URLs.\n            backend (int): selects the backend for OpenCV's VideoCapture class.\n            colorspace (str): selects the colorspace of the input stream.\n            logging (bool): enables/disables logging.\n            time_delay (int): time delay (in sec) before start reading the frames.\n            options (dict): provides ability to alter Source Tweak Parameters.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # initialize global\n        self.ytv_metadata = {}\n\n        # check if Stream-Mode is ON (True)\n        if stream_mode:\n            # TODO: check GStreamer backend support\n            # gst_support = check_gstreamer_support(logging=self.__logging)\n            # handle special Stream Mode parameters\n            stream_resolution = get_supported_resolution(\n                options.pop(\"STREAM_RESOLUTION\", \"best\"), logging=self.__logging\n            )\n            # handle Stream-Mode\n            if not (yt_dlp is None):\n                # extract user-defined params\n                yt_stream_params = options.pop(\"STREAM_PARAMS\", {})\n                if isinstance(yt_stream_params, dict):\n                    yt_stream_params = {\n                        str(k).strip(): v for k, v in yt_stream_params.items()\n                    }\n                else:\n                    yt_stream_params = {}\n                try:\n                    # Validate source for Yt_dlp backend\n                    logger.info(\n                        \"Verifying Streaming URL using yt-dlp backend. Please wait...\"\n                    )\n                    # initialize YT_backend\n                    ytbackend = YT_backend(\n                        source_url=source, logging=self.__logging, **yt_stream_params\n                    )\n                    if ytbackend:\n                        # save video metadata\n                        self.ytv_metadata = ytbackend.meta_data\n                        # handle live-streams\n                        # Throw warning for livestreams\n                        ytbackend.is_livestream and logger.warning(\n                            \"Livestream URL detected. It is strongly recommended to use the GStreamer backend (`backend=cv2.CAP_GSTREAMER`) with these URLs.\"\n                        )\n                        # check whether stream-resolution was specified and available\n                        if not (stream_resolution in ytbackend.streams.keys()):\n                            logger.warning(\n                                \"Specified stream-resolution `{}` is not available. Reverting to `best`!\".format(\n                                    stream_resolution\n                                )\n                            )\n                            # revert to best\n                            stream_resolution = \"best\"\n                        else:\n                            self.__logging and logger.debug(\n                                \"Using `{}` resolution for streaming.\".format(\n                                    stream_resolution\n                                )\n                            )\n                        # extract stream URL as source using stream-resolution\n                        source = ytbackend.streams[stream_resolution]\n                        # log progress\n                        self.__logging and logger.debug(\n                            \"YouTube source ID: `{}`, Title: `{}`, Quality: `{}`\".format(\n                                self.ytv_metadata[\"id\"],\n                                self.ytv_metadata[\"title\"],\n                                stream_resolution,\n                            )\n                        )\n                except Exception as e:\n                    # raise error if something went wrong\n                    raise ValueError(\n                        \"[CamGear:ERROR] :: Stream Mode is enabled but Input URL is invalid!\"\n                    )\n            else:\n                # raise import errors\n                import_dependency_safe(\"yt_dlp\")\n\n        # assigns special parameter to global variable and clear\n        # Threaded Queue Mode\n        self.__threaded_queue_mode = options.pop(\"THREADED_QUEUE_MODE\", True)\n        if not isinstance(self.__threaded_queue_mode, bool):\n            # reset improper values\n            self.__threaded_queue_mode = True\n        # Thread Timeout\n        self.__thread_timeout = options.pop(\"THREAD_TIMEOUT\", None)\n        if self.__thread_timeout and isinstance(self.__thread_timeout, (int, float)):\n            # set values\n            self.__thread_timeout = float(self.__thread_timeout)\n        else:\n            # defaults to 5mins timeout\n            self.__thread_timeout = None\n\n        self.__queue = None\n        # initialize queue for video files only\n        if self.__threaded_queue_mode and isinstance(source, str):\n            # define queue and assign it to global var\n            self.__queue = queue.Queue(maxsize=96)  # max bufferlen 96 to check overflow\n            # log it\n            self.__logging and logger.debug(\n                \"Enabling Threaded Queue Mode for the current video source!\"\n            )\n        else:\n            # otherwise disable it\n            self.__threaded_queue_mode = False\n            # log it\n            self.__logging and logger.warning(\n                \"Threaded Queue Mode is disabled for the current video source!\"\n            )\n\n        self.__thread_timeout and logger.info(\n            \"Setting Video-Thread Timeout to {}s.\".format(self.__thread_timeout)\n        )\n\n        # stream variable initialization\n        self.stream = None\n\n        if backend and isinstance(backend, int):\n            # add backend if specified and initialize the camera stream\n            if check_CV_version() == 3:\n                # Different OpenCV 3.4.x statement\n                self.stream = cv2.VideoCapture(source + backend)\n            else:\n                # Two parameters are available since OpenCV 4+ (master branch)\n                self.stream = cv2.VideoCapture(source, backend)\n            logger.info(\"Setting backend `{}` for this source.\".format(backend))\n        else:\n            # initialize the camera stream\n            self.stream = cv2.VideoCapture(source)\n\n        # initializing colorspace variable\n        self.color_space = None\n\n        # apply attributes to source if specified\n        options = {str(k).strip(): v for k, v in options.items()}\n        for key, value in options.items():\n            property = capPropId(key)\n            not (property is None) and self.stream.set(property, value)\n\n        # handle colorspace value\n        if not (colorspace is None):\n            self.color_space = capPropId(colorspace.strip())\n            self.__logging and not (self.color_space is None) and logger.debug(\n                \"Enabling `{}` colorspace for this video stream!\".format(\n                    colorspace.strip()\n                )\n            )\n\n        # initialize and assign frame-rate variable\n        self.framerate = 0.0\n        _fps = self.stream.get(cv2.CAP_PROP_FPS)\n        if _fps > 1.0:\n            self.framerate = _fps\n\n        # applying time delay to warm-up webcam only if specified\n        time_delay and isinstance(time_delay, (int, float)) and time.sleep(time_delay)\n\n        # frame variable initialization\n        (grabbed, self.frame) = self.stream.read()\n\n        # check if valid stream\n        if grabbed:\n            # render colorspace if defined\n            if not (self.color_space is None):\n                self.frame = cv2.cvtColor(self.frame, self.color_space)\n\n            # initialize and append to queue\n            self.__threaded_queue_mode and self.__queue.put(self.frame)\n        else:\n            raise RuntimeError(\n                \"[CamGear:ERROR] :: Source is invalid, CamGear failed to initialize stream on this source!\"\n            )\n\n        # thread initialization\n        self.__thread = None\n\n        # initialize termination flag event\n        self.__terminate = Event()\n\n        # initialize stream read flag event\n        self.__stream_read = Event()\n\n    def start(self):\n        \"\"\"\n        Launches the internal *Threaded Frames Extractor* daemon.\n\n        **Returns:** A reference to the CamGear class object.\n        \"\"\"\n\n        self.__thread = Thread(target=self.__update, name=\"CamGear\", args=())\n        self.__thread.daemon = True\n        self.__thread.start()\n        return self\n\n    def __update(self):\n        \"\"\"\n        A **Threaded Frames Extractor**, that keep iterating frames from OpenCV's VideoCapture API to a internal monitored queue,\n        until the thread is terminated, or frames runs out.\n        \"\"\"\n\n        # keep iterating infinitely\n        # until the thread is terminated\n        # or frames runs out\n        # if the thread indicator variable is set, stop the thread\n        while not self.__terminate.is_set():\n            # stream not read yet\n            self.__stream_read.clear()\n\n            # otherwise, read the next frame from the stream\n            (grabbed, frame) = self.stream.read()\n\n            # stream read completed\n            self.__stream_read.set()\n\n            # check for valid frame if received\n            if not grabbed:\n                # no frames received, then safely exit\n                if self.__threaded_queue_mode:\n                    if self.__queue.empty():\n                        break\n                    else:\n                        continue\n                else:\n                    break\n\n            # apply colorspace to frames if valid\n            if not (self.color_space is None):\n                # apply colorspace to frames\n                color_frame = None\n                try:\n                    color_frame = cv2.cvtColor(frame, self.color_space)\n                except Exception as e:\n                    # Catch if any error occurred\n                    color_frame = None\n                    self.color_space = None\n                    self.__logging and logger.exception(str(e))\n                    logger.warning(\"Assigned colorspace value is invalid. Discarding!\")\n                self.frame = color_frame if not (color_frame is None) else frame\n            else:\n                self.frame = frame\n\n            # append to queue\n            self.__threaded_queue_mode and self.__queue.put(self.frame)\n\n        # signal queue we're done\n        self.__threaded_queue_mode and self.__queue.put(None)\n        self.__threaded_queue_mode = False\n\n        # indicate immediate termination\n        self.__terminate.set()\n        self.__stream_read.set()\n\n        # release resources\n        self.stream.release()\n\n    def read(self):\n        \"\"\"\n        Extracts frames synchronously from monitored queue, while maintaining a fixed-length frame buffer in the memory,\n        and blocks the thread if the queue is full.\n\n        **Returns:** A n-dimensional numpy array.\n        \"\"\"\n        while self.__threaded_queue_mode and not self.__terminate.is_set():\n            return self.__queue.get(timeout=self.__thread_timeout)\n        # return current frame\n        # only after stream is read\n        return (\n            self.frame\n            if not self.__terminate.is_set()  # check if already terminated\n            and self.__stream_read.wait(timeout=self.__thread_timeout)  # wait for it\n            else None\n        )\n\n    def stop(self):\n        \"\"\"\n        Safely terminates the thread, and release the multi-threaded resources.\n        \"\"\"\n        self.__logging and logger.debug(\"Terminating processes.\")\n        # terminate Threaded queue mode separately\n        self.__threaded_queue_mode = False\n\n        # indicate that the thread\n        # should be terminated immediately\n        self.__stream_read.set()\n        self.__terminate.set()\n\n        # wait until stream resources are released (producer thread might be still grabbing frame)\n        if self.__thread is not None:\n            if not (self.__queue is None):\n                while not self.__queue.empty():\n                    try:\n                        self.__queue.get_nowait()\n                    except queue.Empty:\n                        continue\n                    self.__queue.task_done()\n            self.__thread.join()\n

"},{"location":"bonus/reference/camgear/#vidgear.gears.camgear.CamGear.__init__","title":"__init__(self, source=0, stream_mode=False, backend=0, colorspace=None, logging=False, time_delay=0, **options) special","text":"

This constructor method initializes the object state and attributes of the CamGear class.

Parameters:

Name Type Description Default source based on input

defines the source for the input stream.

0 stream_mode bool

controls the exclusive Stream Mode for handling streaming URLs.

False backend int

selects the backend for OpenCV's VideoCapture class.

0 colorspace str

selects the colorspace of the input stream.

None logging bool

enables/disables logging.

False time_delay int

time delay (in sec) before start reading the frames.

0 options dict

provides ability to alter Source Tweak Parameters.

{} Source code in vidgear/gears/camgear.py
def __init__(\n    self,\n    source=0,\n    stream_mode=False,\n    backend=0,\n    colorspace=None,\n    logging=False,\n    time_delay=0,\n    **options\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the CamGear class.\n\n    Parameters:\n        source (based on input): defines the source for the input stream.\n        stream_mode (bool): controls the exclusive **Stream Mode** for handling streaming URLs.\n        backend (int): selects the backend for OpenCV's VideoCapture class.\n        colorspace (str): selects the colorspace of the input stream.\n        logging (bool): enables/disables logging.\n        time_delay (int): time delay (in sec) before start reading the frames.\n        options (dict): provides ability to alter Source Tweak Parameters.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # initialize global\n    self.ytv_metadata = {}\n\n    # check if Stream-Mode is ON (True)\n    if stream_mode:\n        # TODO: check GStreamer backend support\n        # gst_support = check_gstreamer_support(logging=self.__logging)\n        # handle special Stream Mode parameters\n        stream_resolution = get_supported_resolution(\n            options.pop(\"STREAM_RESOLUTION\", \"best\"), logging=self.__logging\n        )\n        # handle Stream-Mode\n        if not (yt_dlp is None):\n            # extract user-defined params\n            yt_stream_params = options.pop(\"STREAM_PARAMS\", {})\n            if isinstance(yt_stream_params, dict):\n                yt_stream_params = {\n                    str(k).strip(): v for k, v in yt_stream_params.items()\n                }\n            else:\n                yt_stream_params = {}\n            try:\n                # Validate source for Yt_dlp backend\n                logger.info(\n                    \"Verifying Streaming URL using yt-dlp backend. Please wait...\"\n                )\n                # initialize YT_backend\n                ytbackend = YT_backend(\n                    source_url=source, logging=self.__logging, **yt_stream_params\n                )\n                if ytbackend:\n                    # save video metadata\n                    self.ytv_metadata = ytbackend.meta_data\n                    # handle live-streams\n                    # Throw warning for livestreams\n                    ytbackend.is_livestream and logger.warning(\n                        \"Livestream URL detected. It is strongly recommended to use the GStreamer backend (`backend=cv2.CAP_GSTREAMER`) with these URLs.\"\n                    )\n                    # check whether stream-resolution was specified and available\n                    if not (stream_resolution in ytbackend.streams.keys()):\n                        logger.warning(\n                            \"Specified stream-resolution `{}` is not available. Reverting to `best`!\".format(\n                                stream_resolution\n                            )\n                        )\n                        # revert to best\n                        stream_resolution = \"best\"\n                    else:\n                        self.__logging and logger.debug(\n                            \"Using `{}` resolution for streaming.\".format(\n                                stream_resolution\n                            )\n                        )\n                    # extract stream URL as source using stream-resolution\n                    source = ytbackend.streams[stream_resolution]\n                    # log progress\n                    self.__logging and logger.debug(\n                        \"YouTube source ID: `{}`, Title: `{}`, Quality: `{}`\".format(\n                            self.ytv_metadata[\"id\"],\n                            self.ytv_metadata[\"title\"],\n                            stream_resolution,\n                        )\n                    )\n            except Exception as e:\n                # raise error if something went wrong\n                raise ValueError(\n                    \"[CamGear:ERROR] :: Stream Mode is enabled but Input URL is invalid!\"\n                )\n        else:\n            # raise import errors\n            import_dependency_safe(\"yt_dlp\")\n\n    # assigns special parameter to global variable and clear\n    # Threaded Queue Mode\n    self.__threaded_queue_mode = options.pop(\"THREADED_QUEUE_MODE\", True)\n    if not isinstance(self.__threaded_queue_mode, bool):\n        # reset improper values\n        self.__threaded_queue_mode = True\n    # Thread Timeout\n    self.__thread_timeout = options.pop(\"THREAD_TIMEOUT\", None)\n    if self.__thread_timeout and isinstance(self.__thread_timeout, (int, float)):\n        # set values\n        self.__thread_timeout = float(self.__thread_timeout)\n    else:\n        # defaults to 5mins timeout\n        self.__thread_timeout = None\n\n    self.__queue = None\n    # initialize queue for video files only\n    if self.__threaded_queue_mode and isinstance(source, str):\n        # define queue and assign it to global var\n        self.__queue = queue.Queue(maxsize=96)  # max bufferlen 96 to check overflow\n        # log it\n        self.__logging and logger.debug(\n            \"Enabling Threaded Queue Mode for the current video source!\"\n        )\n    else:\n        # otherwise disable it\n        self.__threaded_queue_mode = False\n        # log it\n        self.__logging and logger.warning(\n            \"Threaded Queue Mode is disabled for the current video source!\"\n        )\n\n    self.__thread_timeout and logger.info(\n        \"Setting Video-Thread Timeout to {}s.\".format(self.__thread_timeout)\n    )\n\n    # stream variable initialization\n    self.stream = None\n\n    if backend and isinstance(backend, int):\n        # add backend if specified and initialize the camera stream\n        if check_CV_version() == 3:\n            # Different OpenCV 3.4.x statement\n            self.stream = cv2.VideoCapture(source + backend)\n        else:\n            # Two parameters are available since OpenCV 4+ (master branch)\n            self.stream = cv2.VideoCapture(source, backend)\n        logger.info(\"Setting backend `{}` for this source.\".format(backend))\n    else:\n        # initialize the camera stream\n        self.stream = cv2.VideoCapture(source)\n\n    # initializing colorspace variable\n    self.color_space = None\n\n    # apply attributes to source if specified\n    options = {str(k).strip(): v for k, v in options.items()}\n    for key, value in options.items():\n        property = capPropId(key)\n        not (property is None) and self.stream.set(property, value)\n\n    # handle colorspace value\n    if not (colorspace is None):\n        self.color_space = capPropId(colorspace.strip())\n        self.__logging and not (self.color_space is None) and logger.debug(\n            \"Enabling `{}` colorspace for this video stream!\".format(\n                colorspace.strip()\n            )\n        )\n\n    # initialize and assign frame-rate variable\n    self.framerate = 0.0\n    _fps = self.stream.get(cv2.CAP_PROP_FPS)\n    if _fps > 1.0:\n        self.framerate = _fps\n\n    # applying time delay to warm-up webcam only if specified\n    time_delay and isinstance(time_delay, (int, float)) and time.sleep(time_delay)\n\n    # frame variable initialization\n    (grabbed, self.frame) = self.stream.read()\n\n    # check if valid stream\n    if grabbed:\n        # render colorspace if defined\n        if not (self.color_space is None):\n            self.frame = cv2.cvtColor(self.frame, self.color_space)\n\n        # initialize and append to queue\n        self.__threaded_queue_mode and self.__queue.put(self.frame)\n    else:\n        raise RuntimeError(\n            \"[CamGear:ERROR] :: Source is invalid, CamGear failed to initialize stream on this source!\"\n        )\n\n    # thread initialization\n    self.__thread = None\n\n    # initialize termination flag event\n    self.__terminate = Event()\n\n    # initialize stream read flag event\n    self.__stream_read = Event()\n
"},{"location":"bonus/reference/camgear/#vidgear.gears.camgear.CamGear.read","title":"read(self)","text":"

Extracts frames synchronously from monitored queue, while maintaining a fixed-length frame buffer in the memory, and blocks the thread if the queue is full.

Returns: A n-dimensional numpy array.

Source code in vidgear/gears/camgear.py
def read(self):\n    \"\"\"\n    Extracts frames synchronously from monitored queue, while maintaining a fixed-length frame buffer in the memory,\n    and blocks the thread if the queue is full.\n\n    **Returns:** A n-dimensional numpy array.\n    \"\"\"\n    while self.__threaded_queue_mode and not self.__terminate.is_set():\n        return self.__queue.get(timeout=self.__thread_timeout)\n    # return current frame\n    # only after stream is read\n    return (\n        self.frame\n        if not self.__terminate.is_set()  # check if already terminated\n        and self.__stream_read.wait(timeout=self.__thread_timeout)  # wait for it\n        else None\n    )\n
"},{"location":"bonus/reference/camgear/#vidgear.gears.camgear.CamGear.start","title":"start(self)","text":"

Launches the internal Threaded Frames Extractor daemon.

Returns: A reference to the CamGear class object.

Source code in vidgear/gears/camgear.py
def start(self):\n    \"\"\"\n    Launches the internal *Threaded Frames Extractor* daemon.\n\n    **Returns:** A reference to the CamGear class object.\n    \"\"\"\n\n    self.__thread = Thread(target=self.__update, name=\"CamGear\", args=())\n    self.__thread.daemon = True\n    self.__thread.start()\n    return self\n
"},{"location":"bonus/reference/camgear/#vidgear.gears.camgear.CamGear.stop","title":"stop(self)","text":"

Safely terminates the thread, and release the multi-threaded resources.

Source code in vidgear/gears/camgear.py
def stop(self):\n    \"\"\"\n    Safely terminates the thread, and release the multi-threaded resources.\n    \"\"\"\n    self.__logging and logger.debug(\"Terminating processes.\")\n    # terminate Threaded queue mode separately\n    self.__threaded_queue_mode = False\n\n    # indicate that the thread\n    # should be terminated immediately\n    self.__stream_read.set()\n    self.__terminate.set()\n\n    # wait until stream resources are released (producer thread might be still grabbing frame)\n    if self.__thread is not None:\n        if not (self.__queue is None):\n            while not self.__queue.empty():\n                try:\n                    self.__queue.get_nowait()\n                except queue.Empty:\n                    continue\n                self.__queue.task_done()\n        self.__thread.join()\n
"},{"location":"bonus/reference/helper/","title":"Helper Methods","text":""},{"location":"bonus/reference/helper/#vidgear.gears.helper.logger_handler--logger_handler","title":"logger_handler","text":"

Returns the logger handler

Returns: A logger handler

Source code in vidgear/gears/helper.py
def logger_handler():\n    \"\"\"\n    ## logger_handler\n\n    Returns the logger handler\n\n    **Returns:** A logger handler\n    \"\"\"\n    # logging formatter\n    formatter = ColoredFormatter(\n        \"{green}{asctime}{reset} :: {bold_purple}{name:^13}{reset} :: {log_color}{levelname:^8}{reset} :: {bold_white}{message}\",\n        datefmt=\"%H:%M:%S\",\n        reset=True,\n        log_colors={\n            \"INFO\": \"bold_cyan\",\n            \"DEBUG\": \"bold_yellow\",\n            \"WARNING\": \"bold_red,fg_thin_yellow\",\n            \"ERROR\": \"bold_red\",\n            \"CRITICAL\": \"bold_red,bg_white\",\n        },\n        style=\"{\",\n    )\n    # check if VIDGEAR_LOGFILE defined\n    file_mode = os.environ.get(\"VIDGEAR_LOGFILE\", False)\n    # define handler\n    handler = log.StreamHandler()\n    if file_mode and isinstance(file_mode, str):\n        file_path = os.path.abspath(file_mode)\n        if (os.name == \"nt\" or os.access in os.supports_effective_ids) and os.access(\n            os.path.dirname(file_path), os.W_OK\n        ):\n            file_path = (\n                os.path.join(file_path, \"vidgear.log\")\n                if os.path.isdir(file_path)\n                else file_path\n            )\n            handler = log.FileHandler(file_path, mode=\"a\")\n            formatter = log.Formatter(\n                \"{asctime} :: {name} :: {levelname} :: {message}\",\n                datefmt=\"%H:%M:%S\",\n                style=\"{\",\n            )\n\n    handler.setFormatter(formatter)\n    return handler\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.check_CV_version--check_cv_version","title":"check_CV_version","text":"

Returns: OpenCV's version first bit

Source code in vidgear/gears/helper.py
def check_CV_version():\n    \"\"\"\n    ## check_CV_version\n\n    **Returns:** OpenCV's version first bit\n    \"\"\"\n    if parse_version(cv2.__version__) >= parse_version(\"4\"):\n        return 4\n    else:\n        return 3\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.check_gstreamer_support--check_gstreamer_support","title":"check_gstreamer_support","text":"

Checks whether OpenCV is compiled with Gstreamer(>=1.0.0) support.

Parameters:

Name Type Description Default logging bool

enables logging for its operations

False

Returns: A Boolean value

Source code in vidgear/gears/helper.py
def check_gstreamer_support(logging=False):\n    \"\"\"\n    ## check_gstreamer_support\n\n    Checks whether OpenCV is compiled with Gstreamer(`>=1.0.0`) support.\n\n    Parameters:\n        logging (bool): enables logging for its operations\n\n    **Returns:** A Boolean value\n    \"\"\"\n    raw = cv2.getBuildInformation()\n    gst = [\n        x.strip()\n        for x in raw.split(\"\\n\")\n        if x and re.search(r\"GStreamer[,-:]+\\s*(?:YES|NO)\", x)\n    ]\n    if gst and \"YES\" in gst[0]:\n        version = re.search(r\"(\\d+\\.)?(\\d+\\.)?(\\*|\\d+)\", gst[0])\n        logging and logger.debug(\"Found GStreamer version:{}\".format(version[0]))\n        return version[0] >= \"1.0.0\"\n    else:\n        logger.warning(\"GStreamer not found!\")\n        return False\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.get_supported_resolution--get_supported_resolution","title":"get_supported_resolution","text":"

Parameters:

Name Type Description Default value string

value to be validated

required logging bool

enables logging for its operations

False

Returns: Valid stream resolution

Source code in vidgear/gears/helper.py
def get_supported_resolution(value, logging=False):\n    \"\"\"\n    ## get_supported_resolution\n\n    Parameters:\n        value (string): value to be validated\n        logging (bool): enables logging for its operations\n\n    **Returns:** Valid stream resolution\n    \"\"\"\n    # default to best\n    stream_resolution = \"best\"\n    supported_stream_qualities = [\n        \"144p\",\n        \"240p\",\n        \"360p\",\n        \"480p\",\n        \"720p\",\n        \"1080p\",\n        \"1440p\",\n        \"2160p\",\n        \"4320p\",\n        \"worst\",\n        \"best\",\n    ]\n    if isinstance(value, str):\n        if value.strip().lower() in supported_stream_qualities:\n            stream_resolution = value.strip().lower()\n            logging and logger.debug(\n                \"Selecting `{}` resolution for streams.\".format(stream_resolution)\n            )\n        else:\n            logger.warning(\n                \"Specified stream-resolution `{}` is not supported. Reverting to `best`!\".format(\n                    value\n                )\n            )\n    else:\n        logger.warning(\n            \"Specified stream-resolution `{}` is Invalid. Reverting to `best`!\".format(\n                value\n            )\n        )\n    return stream_resolution\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.dimensions_to_resolutions--dimensions_to_resolutions","title":"dimensions_to_resolutions","text":"

Parameters:

Name Type Description Default value list

list of dimensions (e.g. 640x360)

required

Returns: list of resolutions (e.g. 360p)

Source code in vidgear/gears/helper.py
def dimensions_to_resolutions(value):\n    \"\"\"\n    ## dimensions_to_resolutions\n\n    Parameters:\n        value (list): list of dimensions (e.g. `640x360`)\n\n    **Returns:** list of resolutions (e.g. `360p`)\n    \"\"\"\n    supported_resolutions = {\n        \"256x144\": \"144p\",\n        \"426x240\": \"240p\",\n        \"640x360\": \"360p\",\n        \"854x480\": \"480p\",\n        \"1280x720\": \"720p\",\n        \"1920x1080\": \"1080p\",\n        \"2560x1440\": \"1440p\",\n        \"3840x2160\": \"2160p\",\n        \"7680x4320\": \"4320p\",\n    }\n    return (\n        list(map(supported_resolutions.get, value, value))\n        if isinstance(value, list)\n        else []\n    )\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.mkdir_safe--mkdir_safe","title":"mkdir_safe","text":"

Safely creates directory at given path.

Parameters:

Name Type Description Default dir_path string

path to the directory

required logging bool

enables logging for its operations

False Source code in vidgear/gears/helper.py
def mkdir_safe(dir_path, logging=False):\n    \"\"\"\n    ## mkdir_safe\n\n    Safely creates directory at given path.\n\n    Parameters:\n        dir_path (string): path to the directory\n        logging (bool): enables logging for its operations\n\n    \"\"\"\n    try:\n        os.makedirs(dir_path)\n        logging and logger.debug(\"Created directory at `{}`\".format(dir_path))\n    except (OSError, IOError) as e:\n        if e.errno != errno.EACCES and e.errno != errno.EEXIST:\n            raise\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.delete_ext_safe--delete_ext_safe","title":"delete_ext_safe","text":"

Safely deletes files with given extensions at given path.

Parameters:

Name Type Description Default dir_path string

path to the directory

required extensions list

list of extensions to be deleted

[] logging bool

enables logging for its operations

False Source code in vidgear/gears/helper.py
def delete_ext_safe(dir_path, extensions=[], logging=False):\n    \"\"\"\n    ## delete_ext_safe\n\n    Safely deletes files with given extensions at given path.\n\n    Parameters:\n        dir_path (string): path to the directory\n        extensions (list): list of extensions to be deleted\n        logging (bool): enables logging for its operations\n\n    \"\"\"\n    if not extensions or not os.path.exists(dir_path):\n        logger.warning(\"Invalid input provided for deleting!\")\n        return\n\n    logger.critical(\"Clearing Assets at `{}`!\".format(dir_path))\n\n    for ext in extensions:\n        if len(ext) == 2:\n            files_ext = [\n                os.path.join(dir_path, f)\n                for f in os.listdir(dir_path)\n                if f.startswith(ext[0]) and f.endswith(ext[1])\n            ]\n        else:\n            files_ext = [\n                os.path.join(dir_path, f)\n                for f in os.listdir(dir_path)\n                if f.endswith(ext)\n            ]\n        for file in files_ext:\n            delete_file_safe(file)\n            logging and logger.debug(\"Deleted file: `{}`\".format(file))\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.capPropId--cappropid","title":"capPropId","text":"

Retrieves the OpenCV property's Integer(Actual) value from string.

Parameters:

Name Type Description Default property string

inputs OpenCV property as string.

required logging bool

enables logging for its operations

True

Returns: Resultant integer value.

Source code in vidgear/gears/helper.py
def capPropId(property, logging=True):\n    \"\"\"\n    ## capPropId\n\n    Retrieves the OpenCV property's Integer(Actual) value from string.\n\n    Parameters:\n        property (string): inputs OpenCV property as string.\n        logging (bool): enables logging for its operations\n\n    **Returns:** Resultant integer value.\n    \"\"\"\n    integer_value = 0\n    try:\n        integer_value = getattr(cv2, property)\n    except Exception as e:\n        logging and logger.exception(str(e))\n        logger.critical(\"`{}` is not a valid OpenCV property!\".format(property))\n        return None\n    return integer_value\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.reducer--reducer","title":"reducer","text":"

Reduces frame size by given percentage

Parameters:

Name Type Description Default frame numpy.ndarray

inputs numpy array(frame).

None percentage int/float

inputs size-reduction percentage.

0 interpolation int

Change resize interpolation.

4

Returns: A reduced numpy ndarray array.

Source code in vidgear/gears/helper.py
def reducer(frame=None, percentage=0, interpolation=cv2.INTER_LANCZOS4):\n    \"\"\"\n    ## reducer\n\n    Reduces frame size by given percentage\n\n    Parameters:\n        frame (numpy.ndarray): inputs numpy array(frame).\n        percentage (int/float): inputs size-reduction percentage.\n        interpolation (int): Change resize interpolation.\n\n    **Returns:**  A reduced numpy ndarray array.\n    \"\"\"\n    # check if frame is valid\n    if frame is None:\n        raise ValueError(\"[Helper:ERROR] :: Input frame cannot be NoneType!\")\n\n    # check if valid reduction percentage is given\n    if not (percentage > 0 and percentage < 90):\n        raise ValueError(\n            \"[Helper:ERROR] :: Given frame-size reduction percentage is invalid, Kindly refer docs.\"\n        )\n\n    if not (isinstance(interpolation, int)):\n        raise ValueError(\n            \"[Helper:ERROR] :: Given interpolation is invalid, Kindly refer docs.\"\n        )\n\n    # grab the frame size\n    (height, width) = frame.shape[:2]\n\n    # calculate the ratio of the width from percentage\n    reduction = ((100 - percentage) / 100) * width\n    ratio = reduction / float(width)\n    # construct the dimensions\n    dimensions = (int(reduction), int(height * ratio))\n\n    # return the resized frame\n    return cv2.resize(frame, dimensions, interpolation=interpolation)\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.create_blank_frame--create_blank_frame","title":"create_blank_frame","text":"

Create blank frames of given frame size with text

Parameters:

Name Type Description Default frame numpy.ndarray

inputs numpy array(frame).

None text str

Text to be written on frame.

''

Returns: A reduced numpy ndarray array.

Source code in vidgear/gears/helper.py
def create_blank_frame(frame=None, text=\"\", logging=False):\n    \"\"\"\n    ## create_blank_frame\n\n    Create blank frames of given frame size with text\n\n    Parameters:\n        frame (numpy.ndarray): inputs numpy array(frame).\n        text (str): Text to be written on frame.\n    **Returns:**  A reduced numpy ndarray array.\n    \"\"\"\n    # check if frame is valid\n    if frame is None or not (isinstance(frame, np.ndarray)):\n        raise ValueError(\"[Helper:ERROR] :: Input frame is invalid!\")\n    # grab the frame size\n    (height, width) = frame.shape[:2]\n    # create blank frame\n    blank_frame = np.zeros(frame.shape, frame.dtype)\n    # setup text\n    if text and isinstance(text, str):\n        logging and logger.debug(\"Adding text: {}\".format(text))\n        # setup font\n        font = cv2.FONT_HERSHEY_SCRIPT_COMPLEX\n        # get boundary of this text\n        fontScale = min(height, width) / (25 / 0.25)\n        textsize = cv2.getTextSize(text, font, fontScale, 5)[0]\n        # get coords based on boundary\n        textX = (width - textsize[0]) // 2\n        textY = (height + textsize[1]) // 2\n        # put text\n        cv2.putText(\n            blank_frame, text, (textX, textY), font, fontScale, (125, 125, 125), 6\n        )\n\n    # return frame\n    return blank_frame\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.dict2Args--dict2args","title":"dict2Args","text":"

Converts dictionary attributes to list(args)

Parameters:

Name Type Description Default param_dict dict

Parameters dictionary

required

Returns: Arguments list

Source code in vidgear/gears/helper.py
def dict2Args(param_dict):\n    \"\"\"\n    ## dict2Args\n\n    Converts dictionary attributes to list(args)\n\n    Parameters:\n        param_dict (dict): Parameters dictionary\n\n    **Returns:** Arguments list\n    \"\"\"\n    args = []\n    for key in param_dict.keys():\n        if key in [\"-clones\"] or key.startswith(\"-core\"):\n            if isinstance(param_dict[key], list):\n                args.extend(param_dict[key])\n            else:\n                logger.warning(\n                    \"{} with invalid datatype:`{}`, Skipped!\".format(\n                        \"Core parameter\" if key.startswith(\"-core\") else \"Clone\",\n                        param_dict[key],\n                    )\n                )\n        else:\n            args.append(key)\n            args.append(str(param_dict[key]))\n    return args\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.get_valid_ffmpeg_path--get_valid_ffmpeg_path","title":"get_valid_ffmpeg_path","text":"

Validate the given FFmpeg path/binaries, and returns a valid FFmpeg executable path.

Parameters:

Name Type Description Default custom_ffmpeg string

path to custom FFmpeg executables

'' is_windows boolean

is running on Windows OS?

False ffmpeg_download_path string

FFmpeg static binaries download location (Windows only)

'' logging bool

enables logging for its operations

False

Returns: A valid FFmpeg executable path string.

Source code in vidgear/gears/helper.py
def get_valid_ffmpeg_path(\n    custom_ffmpeg=\"\", is_windows=False, ffmpeg_download_path=\"\", logging=False\n):\n    \"\"\"\n    ## get_valid_ffmpeg_path\n\n    Validate the given FFmpeg path/binaries, and returns a valid FFmpeg executable path.\n\n    Parameters:\n        custom_ffmpeg (string): path to custom FFmpeg executables\n        is_windows (boolean): is running on Windows OS?\n        ffmpeg_download_path (string): FFmpeg static binaries download location _(Windows only)_\n        logging (bool): enables logging for its operations\n\n    **Returns:** A valid FFmpeg executable path string.\n    \"\"\"\n    final_path = \"\"\n    if is_windows:\n        # checks if current os is windows\n        if custom_ffmpeg:\n            # if custom FFmpeg path is given assign to local variable\n            final_path += custom_ffmpeg\n        else:\n            # otherwise auto-download them\n            try:\n                if not (ffmpeg_download_path):\n                    # otherwise save to Temp Directory\n                    import tempfile\n\n                    ffmpeg_download_path = tempfile.gettempdir()\n\n                logging and logger.debug(\n                    \"FFmpeg Windows Download Path: {}\".format(ffmpeg_download_path)\n                )\n\n                # download Binaries\n                os_bit = (\n                    (\"win64\" if platform.machine().endswith(\"64\") else \"win32\")\n                    if is_windows\n                    else \"\"\n                )\n                _path = download_ffmpeg_binaries(\n                    path=ffmpeg_download_path, os_windows=is_windows, os_bit=os_bit\n                )\n                # assign to local variable\n                final_path += _path\n\n            except Exception as e:\n                # log if any error occurred\n                logger.exception(str(e))\n                logger.error(\n                    \"Error in downloading FFmpeg binaries, Check your network and Try again!\"\n                )\n                return False\n\n        if os.path.isfile(final_path):\n            # check if valid FFmpeg file exist\n            pass\n        elif os.path.isfile(os.path.join(final_path, \"ffmpeg.exe\")):\n            # check if FFmpeg directory exists, if does, then check for valid file\n            final_path = os.path.join(final_path, \"ffmpeg.exe\")\n        else:\n            # else return False\n            logging and logger.debug(\n                \"No valid FFmpeg executables found at Custom FFmpeg path!\"\n            )\n            return False\n    else:\n        # otherwise perform test for Unix\n        if custom_ffmpeg:\n            # if custom FFmpeg path is given assign to local variable\n            if os.path.isfile(custom_ffmpeg):\n                # check if valid FFmpeg file exist\n                final_path += custom_ffmpeg\n            elif os.path.isfile(os.path.join(custom_ffmpeg, \"ffmpeg\")):\n                # check if FFmpeg directory exists, if does, then check for valid file\n                final_path = os.path.join(custom_ffmpeg, \"ffmpeg\")\n            else:\n                # else return False\n                logging and logger.debug(\n                    \"No valid FFmpeg executables found at Custom FFmpeg path!\"\n                )\n                return False\n        else:\n            # otherwise assign ffmpeg binaries from system\n            final_path += \"ffmpeg\"\n\n    logging and logger.debug(\"Final FFmpeg Path: {}\".format(final_path))\n\n    # Final Auto-Validation for FFmeg Binaries. returns final path if test is passed\n    return final_path if validate_ffmpeg(final_path, logging=logging) else False\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.download_ffmpeg_binaries--download_ffmpeg_binaries","title":"download_ffmpeg_binaries","text":"

Generates FFmpeg Static Binaries for windows(if not available)

Parameters:

Name Type Description Default path string

path for downloading custom FFmpeg executables

required os_windows boolean

is running on Windows OS?

False os_bit string

32-bit or 64-bit OS?

''

Returns: A valid FFmpeg executable path string.

Source code in vidgear/gears/helper.py
def download_ffmpeg_binaries(path, os_windows=False, os_bit=\"\"):\n    \"\"\"\n    ## download_ffmpeg_binaries\n\n    Generates FFmpeg Static Binaries for windows(if not available)\n\n    Parameters:\n        path (string): path for downloading custom FFmpeg executables\n        os_windows (boolean): is running on Windows OS?\n        os_bit (string): 32-bit or 64-bit OS?\n\n    **Returns:** A valid FFmpeg executable path string.\n    \"\"\"\n    final_path = \"\"\n    if os_windows and os_bit:\n        # initialize with available FFmpeg Static Binaries GitHub Server\n        file_url = \"https://github.com/abhiTronix/FFmpeg-Builds/releases/latest/download/ffmpeg-static-{}-gpl.zip\".format(\n            os_bit\n        )\n\n        file_name = os.path.join(\n            os.path.abspath(path), \"ffmpeg-static-{}-gpl.zip\".format(os_bit)\n        )\n        file_path = os.path.join(\n            os.path.abspath(path),\n            \"ffmpeg-static-{}-gpl/bin/ffmpeg.exe\".format(os_bit),\n        )\n        base_path, _ = os.path.split(file_name)  # extract file base path\n        # check if file already exists\n        if os.path.isfile(file_path):\n            final_path += file_path  # skip download if does\n        else:\n            # import libs\n            import zipfile\n\n            # check if given path has write access\n            assert os.access(path, os.W_OK), (\n                \"[Helper:ERROR] :: Permission Denied, Cannot write binaries to directory = \"\n                + path\n            )\n            # remove leftovers if exists\n            os.path.isfile(file_name) and delete_file_safe(file_name)\n            # download and write file to the given path\n            with open(file_name, \"wb\") as f:\n                logger.debug(\n                    \"No Custom FFmpeg path provided. Auto-Installing FFmpeg static binaries from GitHub Mirror now. Please wait...\"\n                )\n                # create session\n                with requests.Session() as http:\n                    # setup retry strategy\n                    retries = Retry(\n                        total=3,\n                        backoff_factor=1,\n                        status_forcelist=[429, 500, 502, 503, 504],\n                    )\n                    # Mount it for https usage\n                    adapter = TimeoutHTTPAdapter(timeout=2.0, max_retries=retries)\n                    http.mount(\"https://\", adapter)\n                    response = http.get(file_url, stream=True)\n                    response.raise_for_status()\n                    total_length = (\n                        response.headers.get(\"content-length\")\n                        if \"content-length\" in response.headers\n                        else len(response.content)\n                    )\n                    assert not (\n                        total_length is None\n                    ), \"[Helper:ERROR] :: Failed to retrieve files, check your Internet connectivity!\"\n                    bar = tqdm(total=int(total_length), unit=\"B\", unit_scale=True)\n                    for data in response.iter_content(chunk_size=4096):\n                        f.write(data)\n                        len(data) > 0 and bar.update(len(data))\n                    bar.close()\n            logger.debug(\"Extracting executables.\")\n            with zipfile.ZipFile(file_name, \"r\") as zip_ref:\n                zip_fname, _ = os.path.split(zip_ref.infolist()[0].filename)\n                zip_ref.extractall(base_path)\n            # perform cleaning\n            delete_file_safe(file_name)\n            logger.debug(\"FFmpeg binaries for Windows configured successfully!\")\n            final_path += file_path\n    # return final path\n    return final_path\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.validate_ffmpeg--validate_ffmpeg","title":"validate_ffmpeg","text":"

Validate FFmeg Binaries. returns True if tests are passed.

Parameters:

Name Type Description Default path string

absolute path of FFmpeg binaries

required logging bool

enables logging for its operations

False

Returns: A boolean value, confirming whether tests passed, or not?.

Source code in vidgear/gears/helper.py
def validate_ffmpeg(path, logging=False):\n    \"\"\"\n    ## validate_ffmpeg\n\n    Validate FFmeg Binaries. returns `True` if tests are passed.\n\n    Parameters:\n        path (string): absolute path of FFmpeg binaries\n        logging (bool): enables logging for its operations\n\n    **Returns:** A boolean value, confirming whether tests passed, or not?.\n    \"\"\"\n    try:\n        # get the FFmpeg version\n        version = check_output([path, \"-version\"])\n        firstline = version.split(b\"\\n\")[0]\n        version = firstline.split(b\" \")[2].strip()\n        # log if test are passed\n        logging and logger.info(\"FFmpeg validity Test Passed!\")\n        logging and logger.debug(\n            \"Found valid FFmpeg Version: `{}` installed on this system\".format(version)\n        )\n    except Exception as e:\n        # log if test are failed\n        logging and logger.exception(str(e))\n        logger.error(\"FFmpeg validity Test Failed!\")\n        return False\n    return True\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.check_output--check_output","title":"check_output","text":"

Returns stdin output from subprocess module

Source code in vidgear/gears/helper.py
def check_output(*args, **kwargs):\n    \"\"\"\n    ## check_output\n\n    Returns stdin output from subprocess module\n    \"\"\"\n    # import libs\n    import subprocess as sp\n\n    # workaround for python bug: https://bugs.python.org/issue37380\n    if platform.system() == \"Windows\":\n        # see comment https://bugs.python.org/msg370334\n        sp._cleanup = lambda: None\n\n    # handle additional params\n    retrieve_stderr = kwargs.pop(\"force_retrieve_stderr\", False)\n\n    # execute command in subprocess\n    process = sp.Popen(\n        stdout=sp.PIPE,\n        stderr=sp.DEVNULL if not (retrieve_stderr) else sp.PIPE,\n        *args,\n        **kwargs,\n    )\n    output, stderr = process.communicate()\n    retcode = process.poll()\n\n    # handle return code\n    if retcode and not (retrieve_stderr):\n        cmd = kwargs.get(\"args\")\n        if cmd is None:\n            cmd = args[0]\n        error = sp.CalledProcessError(retcode, cmd)\n        error.output = output\n        raise error\n\n    return output if not (retrieve_stderr) else stderr\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.generate_auth_certificates--generate_auth_certificates","title":"generate_auth_certificates","text":"

Auto-Generates, and Auto-validates CURVE ZMQ key-pairs for NetGear API's Secure Mode.

Parameters:

Name Type Description Default path string

path for generating CURVE key-pairs

required overwrite boolean

overwrite existing key-pairs or not?

False logging bool

enables logging for its operations

False

Returns: A valid CURVE key-pairs path as string.

Source code in vidgear/gears/helper.py
def generate_auth_certificates(path, overwrite=False, logging=False):\n    \"\"\"\n    ## generate_auth_certificates\n\n    Auto-Generates, and Auto-validates CURVE ZMQ key-pairs for NetGear API's Secure Mode.\n\n    Parameters:\n        path (string): path for generating CURVE key-pairs\n        overwrite (boolean): overwrite existing key-pairs or not?\n        logging (bool): enables logging for its operations\n\n    **Returns:** A valid CURVE key-pairs path as string.\n    \"\"\"\n    # import necessary lib\n    import zmq.auth\n\n    # check if path corresponds to vidgear only\n    if os.path.basename(path) != \".vidgear\":\n        path = os.path.join(path, \".vidgear\")\n\n    # generate keys dir\n    keys_dir = os.path.join(path, \"keys\")\n    mkdir_safe(keys_dir, logging=logging)\n\n    # generate separate public and private key dirs\n    public_keys_dir = os.path.join(keys_dir, \"public_keys\")\n    secret_keys_dir = os.path.join(keys_dir, \"private_keys\")\n\n    # check if overwriting is allowed\n    if overwrite:\n        # delete previous certificates\n        for dirs in [public_keys_dir, secret_keys_dir]:\n            if os.path.exists(dirs):\n                shutil.rmtree(dirs)\n            mkdir_safe(dirs, logging=logging)\n\n        # generate new keys\n        server_public_file, server_secret_file = zmq.auth.create_certificates(\n            keys_dir, \"server\"\n        )\n        client_public_file, client_secret_file = zmq.auth.create_certificates(\n            keys_dir, \"client\"\n        )\n\n        # move keys to their appropriate directory respectively\n        for key_file in os.listdir(keys_dir):\n            if key_file.endswith(\".key\"):\n                shutil.move(os.path.join(keys_dir, key_file), public_keys_dir)\n            elif key_file.endswith(\".key_secret\"):\n                shutil.move(os.path.join(keys_dir, key_file), secret_keys_dir)\n            else:\n                # clean redundant keys if present\n                redundant_key = os.path.join(keys_dir, key_file)\n                if os.path.isfile(redundant_key):\n                    delete_file_safe(redundant_key)\n    else:\n        # otherwise validate available keys\n        status_public_keys = validate_auth_keys(public_keys_dir, \".key\")\n        status_private_keys = validate_auth_keys(secret_keys_dir, \".key_secret\")\n\n        # check if all valid keys are found\n        if status_private_keys and status_public_keys:\n            return (keys_dir, secret_keys_dir, public_keys_dir)\n\n        # check if valid public keys are found\n        if not (status_public_keys):\n            mkdir_safe(public_keys_dir, logging=logging)\n\n        # check if valid private keys are found\n        if not (status_private_keys):\n            mkdir_safe(secret_keys_dir, logging=logging)\n\n        # generate new keys\n        server_public_file, server_secret_file = zmq.auth.create_certificates(\n            keys_dir, \"server\"\n        )\n        client_public_file, client_secret_file = zmq.auth.create_certificates(\n            keys_dir, \"client\"\n        )\n\n        # move keys to their appropriate directory respectively\n        for key_file in os.listdir(keys_dir):\n            if key_file.endswith(\".key\") and not (status_public_keys):\n                shutil.move(\n                    os.path.join(keys_dir, key_file), os.path.join(public_keys_dir, \".\")\n                )\n            elif key_file.endswith(\".key_secret\") and not (status_private_keys):\n                shutil.move(\n                    os.path.join(keys_dir, key_file), os.path.join(secret_keys_dir, \".\")\n                )\n            else:\n                # clean redundant keys if present\n                redundant_key = os.path.join(keys_dir, key_file)\n                if os.path.isfile(redundant_key):\n                    delete_file_safe(redundant_key)\n\n    # validate newly generated keys\n    status_public_keys = validate_auth_keys(public_keys_dir, \".key\")\n    status_private_keys = validate_auth_keys(secret_keys_dir, \".key_secret\")\n\n    # raise error is validation test fails\n    if not (status_private_keys) or not (status_public_keys):\n        raise RuntimeError(\n            \"[Helper:ERROR] :: Unable to generate valid ZMQ authentication certificates at `{}`!\".format(\n                keys_dir\n            )\n        )\n\n    # finally return valid key paths\n    return (keys_dir, secret_keys_dir, public_keys_dir)\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.validate_audio--validate_audio","title":"validate_audio","text":"

Validates audio by retrieving audio-bitrate from file.

Parameters:

Name Type Description Default path string

absolute path of FFmpeg binaries

required source string/list

source to be validated.

None

Returns: A string value, confirming whether audio is present, or not?.

Source code in vidgear/gears/helper.py
def validate_audio(path, source=None):\n    \"\"\"\n    ## validate_audio\n\n    Validates audio by retrieving audio-bitrate from file.\n\n    Parameters:\n        path (string): absolute path of FFmpeg binaries\n        source (string/list): source to be validated.\n\n    **Returns:** A string value, confirming whether audio is present, or not?.\n    \"\"\"\n    if source is None or not (source):\n        logger.warning(\"Audio input source is empty!\")\n        return \"\"\n\n    # create ffmpeg command\n    cmd = [path, \"-hide_banner\"] + (\n        source if isinstance(source, list) else [\"-i\", source]\n    )\n    # extract metadata\n    metadata = check_output(cmd, force_retrieve_stderr=True)\n    # extract bitrate\n    audio_bitrate_meta = [\n        line.strip()\n        for line in metadata.decode(\"utf-8\").split(\"\\n\")\n        if \"Audio:\" in line\n    ]\n    audio_bitrate = (\n        re.findall(r\"([0-9]+)\\s(kb|mb|gb)\\/s\", audio_bitrate_meta[0])[-1]\n        if audio_bitrate_meta\n        else \"\"\n    )\n    # extract samplerate\n    audio_samplerate_metadata = [\n        line.strip()\n        for line in metadata.decode(\"utf-8\").split(\"\\n\")\n        if all(x in line for x in [\"Audio:\", \"Hz\"])\n    ]\n    audio_samplerate = (\n        re.findall(r\"[0-9]+\\sHz\", audio_samplerate_metadata[0])[0]\n        if audio_samplerate_metadata\n        else \"\"\n    )\n    # format into actual readable bitrate value\n    if audio_bitrate:\n        # return bitrate directly\n        return \"{}{}\".format(int(audio_bitrate[0].strip()), audio_bitrate[1].strip()[0])\n    elif audio_samplerate:\n        # convert samplerate to bitrate first\n        sample_rate_value = int(audio_samplerate.split(\" \")[0])\n        channels_value = 1 if \"mono\" in audio_samplerate_metadata[0] else 2\n        bit_depth_value = re.findall(\n            r\"(u|s|f)([0-9]+)(le|be)\", audio_samplerate_metadata[0]\n        )[0][1]\n        return (\n            (\n                str(\n                    get_audio_bitrate(\n                        sample_rate_value, channels_value, int(bit_depth_value)\n                    )\n                )\n                + \"k\"\n            )\n            if bit_depth_value\n            else \"\"\n        )\n    else:\n        return \"\"\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.extract_time--extract_time","title":"extract_time","text":"

Extract time from give string value.

Parameters:

Name Type Description Default value string

string value.

required

Returns: Time (in seconds) as integer.

Source code in vidgear/gears/helper.py
def extract_time(value):\n    \"\"\"\n    ## extract_time\n\n    Extract time from give string value.\n\n    Parameters:\n        value (string): string value.\n\n    **Returns:** Time _(in seconds)_ as integer.\n    \"\"\"\n    if not (value):\n        logger.warning(\"Value is empty!\")\n        return 0\n    else:\n        stripped_data = value.strip()\n        t_duration = re.findall(r\"\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{2})?\", stripped_data)\n        return (\n            sum(\n                float(x) * 60**i\n                for i, x in enumerate(reversed(t_duration[0].split(\":\")))\n            )\n            if t_duration\n            else 0\n        )\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.validate_video--validate_video","title":"validate_video","text":"

Validates video by retrieving resolution/size and framerate from file.

Parameters:

Name Type Description Default path string

absolute path of FFmpeg binaries

required video_path string

absolute path to Video.

None

Returns: A dictionary of retieved Video resolution (as tuple(width, height)) and framerate (as float).

Source code in vidgear/gears/helper.py
def validate_video(path, video_path=None, logging=False):\n    \"\"\"\n    ## validate_video\n\n    Validates video by retrieving resolution/size and framerate from file.\n\n    Parameters:\n        path (string): absolute path of FFmpeg binaries\n        video_path (string): absolute path to Video.\n\n    **Returns:** A dictionary of retieved Video resolution _(as tuple(width, height))_ and framerate _(as float)_.\n    \"\"\"\n    if video_path is None or not (video_path):\n        logger.warning(\"Video path is empty!\")\n        return None\n\n    # extract metadata\n    metadata = check_output(\n        [path, \"-hide_banner\", \"-i\", video_path], force_retrieve_stderr=True\n    )\n    # clean and search\n    stripped_data = [x.decode(\"utf-8\").strip() for x in metadata.split(b\"\\n\")]\n    logging and logger.debug(stripped_data)\n    result = {}\n    for data in stripped_data:\n        output_a = re.findall(r\"([1-9]\\d+)x([1-9]\\d+)\", data)\n        output_b = re.findall(r\"\\d+(?:\\.\\d+)?\\sfps\", data)\n        if len(result) == 2:\n            break\n        if output_b and not \"framerate\" in result:\n            result[\"framerate\"] = re.findall(r\"[\\d\\.\\d]+\", output_b[0])[0]\n        if output_a and not \"resolution\" in result:\n            result[\"resolution\"] = output_a[-1]\n\n    # return values\n    return result if (len(result) == 2) else None\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.is_valid_url--is_valid_url","title":"is_valid_url","text":"

Checks URL validity by testing its scheme against FFmpeg's supported protocols

Parameters:

Name Type Description Default path string

absolute path of FFmpeg binaries

required url string

URL to be validated

None logging bool

enables logging for its operations

False

Returns: A boolean value, confirming whether tests passed, or not?.

Source code in vidgear/gears/helper.py
def is_valid_url(path, url=None, logging=False):\n    \"\"\"\n    ## is_valid_url\n\n    Checks URL validity by testing its scheme against\n    FFmpeg's supported protocols\n\n    Parameters:\n        path (string): absolute path of FFmpeg binaries\n        url (string): URL to be validated\n        logging (bool): enables logging for its operations\n\n    **Returns:** A boolean value, confirming whether tests passed, or not?.\n    \"\"\"\n    if url is None or not (url):\n        logger.warning(\"URL is empty!\")\n        return False\n    # extract URL scheme\n    extracted_scheme_url = url.split(\"://\", 1)[0]\n    # extract all FFmpeg supported protocols\n    protocols = check_output([path, \"-hide_banner\", \"-protocols\"])\n    splitted = [x.decode(\"utf-8\").strip() for x in protocols.split(b\"\\n\")]\n    supported_protocols = splitted[splitted.index(\"Output:\") + 1 : len(splitted) - 1]\n    # RTSP is a demuxer somehow\n    # support both RTSP and RTSPS(over SSL)\n    supported_protocols += (\n        [\"rtsp\", \"rtsps\"] if \"rtsp\" in get_supported_demuxers(path) else []\n    )\n    # Test and return result whether scheme is supported\n    if extracted_scheme_url and extracted_scheme_url in supported_protocols:\n        logging and logger.debug(\n            \"URL scheme `{}` is supported by FFmpeg.\".format(extracted_scheme_url)\n        )\n        return True\n    else:\n        logger.warning(\n            \"URL scheme `{}` isn't supported by FFmpeg!\".format(extracted_scheme_url)\n        )\n        return False\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.import_dependency_safe--import_dependency_safe","title":"import_dependency_safe","text":"

Imports specified dependency safely. By default(error = raise), if a dependency is missing, an ImportError with a meaningful message will be raised. Otherwise if error = log a warning will be logged and on error = silent everything will be quit. But If a dependency is present, but older than specified, an error is raised if specified.

Parameters:

Name Type Description Default name string

name of dependency to be imported.

required error string

raise or Log or silence ImportError. Possible values are \"raise\", \"log\" and silent. Default is \"raise\".

'raise' pkg_name string

(Optional) package name of dependency(if different pip name). Otherwise name will be used.

None min_version string

(Optional) required minimum version of the dependency to be imported.

None custom_message string

(Optional) custom Import error message to be raised or logged.

None

Returns: The imported module, when found and the version is correct(if specified). Otherwise None.

Source code in vidgear/gears/helper.py
def import_dependency_safe(\n    name,\n    error=\"raise\",\n    pkg_name=None,\n    min_version=None,\n    custom_message=None,\n):\n    \"\"\"\n    ## import_dependency_safe\n\n    Imports specified dependency safely. By default(`error = raise`), if a dependency is missing,\n    an ImportError with a meaningful message will be raised. Otherwise if `error = log` a warning\n    will be logged and on `error = silent` everything will be quit. But If a dependency is present,\n    but older than specified, an error is raised if specified.\n\n    Parameters:\n        name (string): name of dependency to be imported.\n        error (string): raise or Log or silence ImportError. Possible values are `\"raise\"`, `\"log\"` and `silent`. Default is `\"raise\"`.\n        pkg_name (string): (Optional) package name of dependency(if different `pip` name). Otherwise `name` will be used.\n        min_version (string): (Optional) required minimum version of the dependency to be imported.\n        custom_message (string): (Optional) custom Import error message to be raised or logged.\n\n    **Returns:** The imported module, when found and the version is correct(if specified). Otherwise `None`.\n    \"\"\"\n    # check specified parameters\n    sub_class = \"\"\n    if not name or not isinstance(name, str):\n        return None\n    else:\n        # extract name in case of relative import\n        name = name.strip()\n        if name.startswith(\"from\"):\n            name = name.split(\" \")\n            name, sub_class = (name[1].strip(), name[-1].strip())\n\n    assert error in [\n        \"raise\",\n        \"log\",\n        \"silent\",\n    ], \"[Vidgear:ERROR] :: Invalid value at `error` parameter.\"\n\n    # specify package name of dependency(if defined). Otherwise use name\n    install_name = pkg_name if not (pkg_name is None) else name\n\n    # create message\n    msg = (\n        custom_message\n        if not (custom_message is None)\n        else \"Failed to find required dependency '{}'. Install it with  `pip install {}` command.\".format(\n            name, install_name\n        )\n    )\n    # try importing dependency\n    try:\n        module = importlib.import_module(name)\n        module = getattr(module, sub_class) if sub_class else module\n    except Exception as e:\n        if error == \"raise\":\n            if isinstance(e, ModuleNotFoundError):\n                # raise message\n                raise ModuleNotFoundError(msg) from None\n            else:\n                # raise error+message\n                raise ImportError(msg) from e\n        elif error == \"log\":\n            logger.error(msg, exc_info=sys.exc_info())\n            return None\n        else:\n            return None\n\n    # check if minimum required version\n    if not (min_version) is None:\n        # Handle submodules\n        parent_module = name.split(\".\")[0]\n        if parent_module != name:\n            # grab parent module\n            module_to_get = sys.modules[parent_module]\n        else:\n            module_to_get = module\n        # extract version\n        version = get_module_version(module_to_get)\n        # verify\n        if parse_version(version) < parse_version(min_version):\n            # create message\n            msg = \"\"\"Unsupported version '{}' found. Vidgear requires '{}' dependency installed with version '{}' or greater. \n            Update it with  `pip install -U {}` command.\"\"\".format(\n                parent_module, min_version, version, install_name\n            )\n            # handle errors.\n            if error == \"silent\":\n                return None\n            else:\n                # raise\n                raise ImportError(msg)\n\n    return module\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.get_video_bitrate--get_video_bitrate","title":"get_video_bitrate","text":"

Calculate optimum Bitrate from resolution, framerate, bits-per-pixels values

Parameters:

Name Type Description Default width int

video-width

required height int

video-height

required fps float

video-framerate

required bpp float

bit-per-pixels value

required

Returns: Video bitrate (in Kbps) as integer.

Source code in vidgear/gears/helper.py
def get_video_bitrate(width, height, fps, bpp):\n    \"\"\"\n    ## get_video_bitrate\n\n    Calculate optimum Bitrate from resolution, framerate, bits-per-pixels values\n\n    Parameters:\n        width (int): video-width\n        height (int): video-height\n        fps (float): video-framerate\n        bpp (float): bit-per-pixels value\n\n    **Returns:** Video bitrate _(in Kbps)_ as integer.\n    \"\"\"\n    return round((width * height * bpp * fps) / 1000)\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.check_WriteAccess--check_writeaccess","title":"check_WriteAccess","text":"

Checks whether given path directory has Write-Access.

Parameters:

Name Type Description Default path string

absolute path of directory

required is_windows boolean

is running on Windows OS?

False logging bool

enables logging for its operations

False

Returns: A boolean value, confirming whether Write-Access available, or not?.

Source code in vidgear/gears/helper.py
def check_WriteAccess(path, is_windows=False, logging=False):\n    \"\"\"\n    ## check_WriteAccess\n\n    Checks whether given path directory has Write-Access.\n\n    Parameters:\n        path (string): absolute path of directory\n        is_windows (boolean): is running on Windows OS?\n        logging (bool): enables logging for its operations\n\n    **Returns:** A boolean value, confirming whether Write-Access available, or not?.\n    \"\"\"\n    # check if path exists\n    dirpath = Path(path)\n    try:\n        if not (dirpath.exists() and dirpath.is_dir()):\n            logger.warning(\n                \"Specified directory `{}` doesn't exists or valid.\".format(path)\n            )\n            return False\n        else:\n            path = dirpath.resolve()\n    except:\n        return False\n    # check filepath on *nix systems\n    if not is_windows:\n        uid = os.geteuid()\n        gid = os.getegid()\n        s = os.stat(path)\n        mode = s[stat.ST_MODE]\n        return (\n            ((s[stat.ST_UID] == uid) and (mode & stat.S_IWUSR))\n            or ((s[stat.ST_GID] == gid) and (mode & stat.S_IWGRP))\n            or (mode & stat.S_IWOTH)\n        )\n    # otherwise, check filepath on windows\n    else:\n        write_accessible = False\n        temp_fname = os.path.join(path, \"temp.tmp\")\n        try:\n            fd = os.open(temp_fname, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)\n            os.close(fd)\n            write_accessible = True\n        except Exception as e:\n            if isinstance(e, PermissionError):\n                logger.error(\n                    \"You don't have adequate access rights to use `{}` directory!\".format(\n                        path\n                    )\n                )\n            logging and logger.exception(str(e))\n        finally:\n            delete_file_safe(temp_fname)\n        return write_accessible\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.check_open_port--check_open_port","title":"check_open_port","text":"

Checks whether specified port open at given IP address.

Parameters:

Name Type Description Default address string

given IP address.

required port int

check if port is open at given address.

22

Returns: A boolean value, confirming whether given port is open at given IP address.

Source code in vidgear/gears/helper.py
def check_open_port(address, port=22):\n    \"\"\"\n    ## check_open_port\n\n    Checks whether specified port open at given IP address.\n\n    Parameters:\n        address (string): given IP address.\n        port (int): check if port is open at given address.\n\n    **Returns:** A boolean value, confirming whether given port is open at given IP address.\n    \"\"\"\n    if not address:\n        return False\n    with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:\n        if sock.connect_ex((address, port)) == 0:\n            return True\n        else:\n            return False\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.delete_file_safe--delete_ext_safe","title":"delete_ext_safe","text":"

Safely deletes files at given path.

Parameters:

Name Type Description Default file_path string

path to the file

required Source code in vidgear/gears/helper.py
def delete_file_safe(file_path):\n    \"\"\"\n    ## delete_ext_safe\n\n    Safely deletes files at given path.\n\n    Parameters:\n        file_path (string): path to the file\n    \"\"\"\n    try:\n        dfile = Path(file_path)\n        dfile.unlink(missing_ok=True)\n    except Exception as e:\n        logger.exception(str(e))\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.get_supported_demuxers--get_supported_demuxers","title":"get_supported_demuxers","text":"

Find and returns FFmpeg's supported demuxers

Parameters:

Name Type Description Default path string

absolute path of FFmpeg binaries

required

Returns: List of supported demuxers.

Source code in vidgear/gears/helper.py
def get_supported_demuxers(path):\n    \"\"\"\n    ## get_supported_demuxers\n\n    Find and returns FFmpeg's supported demuxers\n\n    Parameters:\n        path (string): absolute path of FFmpeg binaries\n\n    **Returns:** List of supported demuxers.\n    \"\"\"\n    demuxers = check_output([path, \"-hide_banner\", \"-demuxers\"])\n    splitted = [x.decode(\"utf-8\").strip() for x in demuxers.split(b\"\\n\")]\n    split_index = [idx for idx, s in enumerate(splitted) if \"--\" in s][0]\n    supported_demuxers = splitted[split_index + 1 : len(splitted) - 1]\n    # compile regex\n    finder = re.compile(r\"\\s\\s[a-z0-9_,-]+\\s+\")\n    # find all outputs\n    outputs = finder.findall(\"\\n\".join(supported_demuxers))\n    # return output findings\n    return [o.strip() for o in outputs]\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.get_supported_vencoders--get_supported_vencoders","title":"get_supported_vencoders","text":"

Find and returns FFmpeg's supported video encoders

Parameters:

Name Type Description Default path string

absolute path of FFmpeg binaries

required

Returns: List of supported encoders.

Source code in vidgear/gears/helper.py
def get_supported_vencoders(path):\n    \"\"\"\n    ## get_supported_vencoders\n\n    Find and returns FFmpeg's supported video encoders\n\n    Parameters:\n        path (string): absolute path of FFmpeg binaries\n\n    **Returns:** List of supported encoders.\n    \"\"\"\n    encoders = check_output([path, \"-hide_banner\", \"-encoders\"])\n    splitted = encoders.split(b\"\\n\")\n    # extract video encoders\n    supported_vencoders = [\n        x.decode(\"utf-8\").strip()\n        for x in splitted[2 : len(splitted) - 1]\n        if x.decode(\"utf-8\").strip().startswith(\"V\")\n    ]\n    # compile regex\n    finder = re.compile(r\"[A-Z]*[\\.]+[A-Z]*\\s[a-z0-9_-]*\")\n    # find all outputs\n    outputs = finder.findall(\"\\n\".join(supported_vencoders))\n    # return output findings\n    return [[s for s in o.split(\" \")][-1] for o in outputs]\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.validate_auth_keys--validate_auth_keys","title":"validate_auth_keys","text":"

Validates, and also maintains generated ZMQ CURVE Key-pairs.

Parameters:

Name Type Description Default path string

path of generated CURVE key-pairs

required extension string

type of key-pair to be validated

required

Returns: A boolean value, confirming whether tests passed, or not?.

Source code in vidgear/gears/helper.py
def validate_auth_keys(path, extension):\n    \"\"\"\n    ## validate_auth_keys\n\n    Validates, and also maintains generated ZMQ CURVE Key-pairs.\n\n    Parameters:\n        path (string): path of generated CURVE key-pairs\n        extension (string): type of key-pair to be validated\n\n    **Returns:** A boolean value, confirming whether tests passed, or not?.\n    \"\"\"\n    # check for valid path\n    if not (os.path.exists(path)):\n        return False\n\n    # check if directory empty\n    if not (os.listdir(path)):\n        return False\n\n    keys_buffer = []  # stores auth-keys\n\n    # loop over auth-keys\n    for key_file in os.listdir(path):\n        key = os.path.splitext(key_file)\n        # check if valid key is generated\n        if key and (key[0] in [\"server\", \"client\"]) and (key[1] == extension):\n            keys_buffer.append(key_file)  # store it\n\n    # remove invalid keys if found\n    len(keys_buffer) == 1 and delete_file_safe(os.path.join(path, keys_buffer[0]))\n\n    # return results\n    return True if (len(keys_buffer) == 2) else False\n
"},{"location":"bonus/reference/helper_async/","title":"Helper Methods","text":""},{"location":"bonus/reference/helper_async/#vidgear.gears.asyncio.helper.reducer--reducer","title":"reducer","text":"

Asynchronous method that reduces frame size by given percentage.

Parameters:

Name Type Description Default frame numpy.ndarray

inputs numpy array(frame).

None percentage int/float

inputs size-reduction percentage.

0 interpolation int

Change resize interpolation.

4

Returns: A reduced numpy ndarray array.

Source code in vidgear/gears/asyncio/helper.py
async def reducer(frame=None, percentage=0, interpolation=cv2.INTER_LANCZOS4):\n    \"\"\"\n    ## reducer\n\n    Asynchronous method that reduces frame size by given percentage.\n\n    Parameters:\n        frame (numpy.ndarray): inputs numpy array(frame).\n        percentage (int/float): inputs size-reduction percentage.\n        interpolation (int): Change resize interpolation.\n\n    **Returns:**  A reduced numpy ndarray array.\n    \"\"\"\n    # check if frame is valid\n    if frame is None:\n        raise ValueError(\"[Helper:ERROR] :: Input frame cannot be NoneType!\")\n\n    # check if valid reduction percentage is given\n    if not (percentage > 0 and percentage < 90):\n        raise ValueError(\n            \"[Helper:ERROR] :: Given frame-size reduction percentage is invalid, Kindly refer docs.\"\n        )\n\n    if not (isinstance(interpolation, int)):\n        raise ValueError(\n            \"[Helper:ERROR] :: Given interpolation is invalid, Kindly refer docs.\"\n        )\n\n    # grab the frame size\n    (height, width) = frame.shape[:2]\n\n    # calculate the ratio of the width from percentage\n    reduction = ((100 - percentage) / 100) * width\n    ratio = reduction / float(width)\n    # construct the dimensions\n    dimensions = (int(reduction), int(height * ratio))\n\n    # return the resized frame\n    return cv2.resize(frame, dimensions, interpolation=interpolation)\n
"},{"location":"bonus/reference/helper_async/#vidgear.gears.asyncio.helper.create_blank_frame--create_blank_frame","title":"create_blank_frame","text":"

Create blank frames of given frame size with text

Parameters:

Name Type Description Default frame numpy.ndarray

inputs numpy array(frame).

None text str

Text to be written on frame.

''

Returns: A reduced numpy ndarray array.

Source code in vidgear/gears/asyncio/helper.py
def create_blank_frame(frame=None, text=\"\", logging=False):\n    \"\"\"\n    ## create_blank_frame\n\n    Create blank frames of given frame size with text\n\n    Parameters:\n        frame (numpy.ndarray): inputs numpy array(frame).\n        text (str): Text to be written on frame.\n    **Returns:**  A reduced numpy ndarray array.\n    \"\"\"\n    # check if frame is valid\n    if frame is None or not (isinstance(frame, np.ndarray)):\n        raise ValueError(\"[Helper:ERROR] :: Input frame is invalid!\")\n    # grab the frame size\n    (height, width) = frame.shape[:2]\n    # create blank frame\n    blank_frame = np.zeros(frame.shape, frame.dtype)\n    # setup text\n    if text and isinstance(text, str):\n        if logging:\n            logger.debug(\"Adding text: {}\".format(text))\n        # setup font\n        font = cv2.FONT_HERSHEY_SCRIPT_COMPLEX\n        # get boundary of this text\n        fontScale = min(height, width) / (25 / 0.25)\n        textsize = cv2.getTextSize(text, font, fontScale, 5)[0]\n        # get coords based on boundary\n        textX = (width - textsize[0]) // 2\n        textY = (height + textsize[1]) // 2\n        # put text\n        cv2.putText(\n            blank_frame, text, (textX, textY), font, fontScale, (125, 125, 125), 6\n        )\n\n    # return frame\n    return blank_frame\n
"},{"location":"bonus/reference/helper_async/#vidgear.gears.asyncio.helper.generate_webdata--generate_webdata","title":"generate_webdata","text":"

Auto-Generates, and Auto-validates default data for WebGear and WebGear_RTC APIs.

Parameters:

Name Type Description Default path string

path for generating data

required c_name string

class name that is generating files

'webgear' overwrite_default boolean

overwrite existing data or not?

False logging bool

enables logging for its operations

False

Returns: A valid data path as string.

Source code in vidgear/gears/asyncio/helper.py
def generate_webdata(path, c_name=\"webgear\", overwrite_default=False, logging=False):\n    \"\"\"\n    ## generate_webdata\n\n    Auto-Generates, and Auto-validates default data for WebGear and WebGear_RTC APIs.\n\n    Parameters:\n        path (string): path for generating data\n        c_name (string): class name that is generating files\n        overwrite_default (boolean): overwrite existing data or not?\n        logging (bool): enables logging for its operations\n\n    **Returns:** A valid data path as string.\n    \"\"\"\n    # check if path corresponds to vidgear only\n    if os.path.basename(path) != \".vidgear\":\n        path = os.path.join(path, \".vidgear\")\n\n    # generate parent directory\n    path = os.path.join(path, c_name)\n    mkdir_safe(path, logging=logging)\n\n    # self-generate dirs\n    template_dir = os.path.join(path, \"templates\")  # generates HTML templates dir\n    static_dir = os.path.join(path, \"static\")  # generates static dir\n    # generate js & css static and favicon img subdirs\n    js_static_dir = os.path.join(static_dir, \"js\")\n    css_static_dir = os.path.join(static_dir, \"css\")\n    favicon_dir = os.path.join(static_dir, \"img\")\n\n    mkdir_safe(static_dir, logging=logging)\n    mkdir_safe(template_dir, logging=logging)\n    mkdir_safe(js_static_dir, logging=logging)\n    mkdir_safe(css_static_dir, logging=logging)\n    mkdir_safe(favicon_dir, logging=logging)\n\n    # check if overwriting is enabled\n    if overwrite_default or not validate_webdata(\n        template_dir, [\"index.html\", \"404.html\", \"500.html\"]\n    ):\n        logger.critical(\n            \"Overwriting existing {} data-files with default data-files from the server!\".format(\n                c_name.capitalize()\n            )\n            if overwrite_default\n            else \"Failed to detect critical {} data-files: index.html, 404.html & 500.html!\".format(\n                c_name.capitalize()\n            )\n        )\n        # download default files\n        logging and logger.info(\n            \"Downloading default data-files from the Gitlab Server: {}\".format(\n                \"https://gitlab.com/abhiTronix/vidgear-vitals\"\n            )\n        )\n        download_webdata(\n            template_dir,\n            c_name=c_name,\n            files=[\"index.html\", \"404.html\", \"500.html\", \"base.html\"],\n            logging=logging,\n        )\n        download_webdata(\n            css_static_dir, c_name=c_name, files=[\"custom.css\"], logging=logging\n        )\n        download_webdata(\n            js_static_dir,\n            c_name=c_name,\n            files=[\"custom.js\"],\n            logging=logging,\n        )\n        download_webdata(\n            favicon_dir, c_name=c_name, files=[\"favicon-32x32.png\"], logging=logging\n        )\n    else:\n        # validate important data-files\n        if logging:\n            logger.debug(\"Found valid WebGear data-files successfully.\")\n\n    return path\n
"},{"location":"bonus/reference/helper_async/#vidgear.gears.asyncio.helper.download_webdata--download_webdata","title":"download_webdata","text":"

Downloads given list of files for WebGear and WebGear_RTC APIs(if not available) from GitHub/Gitlab Servers, and also Validates them.

Parameters:

Name Type Description Default path string

path for downloading data

required c_name string

class name that is generating files

'webgear' files list

list of files to be downloaded

[] logging bool

enables logging for its operations

False

Returns: A valid path as string.

Source code in vidgear/gears/asyncio/helper.py
def download_webdata(path, c_name=\"webgear\", files=[], logging=False):\n    \"\"\"\n    ## download_webdata\n\n    Downloads given list of files for WebGear and WebGear_RTC APIs(if not available) from GitHub/Gitlab Servers,\n    and also Validates them.\n\n    Parameters:\n        path (string): path for downloading data\n        c_name (string): class name that is generating files\n        files (list): list of files to be downloaded\n        logging (bool): enables logging for its operations\n\n    **Returns:** A valid path as string.\n    \"\"\"\n    basename = os.path.basename(path)\n    if logging:\n        logger.debug(\"Downloading {} data-files at `{}`\".format(basename, path))\n\n    # list all registered urls\n    reg_urls = [\n        \"https://gitlab.com/abhiTronix/vidgear-vitals/-/raw/main\",\n        \"https://raw.githubusercontent.com/abhiTronix/vidgear-vitals/main\",\n    ]\n\n    # create session\n    with requests.Session() as http:\n        for url in reg_urls:\n            try:\n                for file in files:\n                    # get filename\n                    file_name = os.path.join(path, file)\n                    # get URL\n                    file_url = \"{}/{}{}/{}/{}\".format(\n                        url,\n                        c_name,\n                        \"/static\" if basename != \"templates\" else \"\",\n                        basename,\n                        file,\n                    )\n                    # download and write file to the given path\n                    logging and logger.debug(\n                        \"Downloading {} data-file: {}.\".format(basename, file)\n                    )\n\n                    with open(file_name, \"wb\") as f:\n                        # setup retry strategy\n                        retries = Retry(\n                            total=3,\n                            backoff_factor=1,\n                            status_forcelist=[429, 500, 502, 503, 504],\n                        )\n                        # Mount it for https usage\n                        adapter = TimeoutHTTPAdapter(timeout=2.0, max_retries=retries)\n                        http.mount(\"https://\", adapter)\n                        response = http.get(file_url, stream=True)\n                        response.raise_for_status()\n                        total_length = (\n                            response.headers.get(\"content-length\")\n                            if \"content-length\" in response.headers\n                            else len(response.content)\n                        )\n                        assert not (\n                            total_length is None\n                        ), \"[Helper:ERROR] :: Failed to retrieve files, check your Internet connectivity!\"\n                        bar = tqdm(total=int(total_length), unit=\"B\", unit_scale=True)\n                        for data in response.iter_content(chunk_size=256):\n                            f.write(data)\n                            if len(data) > 0:\n                                bar.update(len(data))\n                        bar.close()\n            except AssertionError as e:\n                # raise if connection error\n                raise e\n            except Exception as e:\n                # log error\n                logger.exception(str(e))\n                # log event if necessary\n                url != reg_urls[1] and logger.error(\n                    \"Download failed for Gitlab Server! Retrying from GitHub Server: {}\".format(\n                        url, \"https://github.com/abhiTronix/vidgear-vitals\"\n                    )\n                )\n            else:\n                # break otherwise\n                break\n\n    if logging:\n        logger.debug(\"Verifying downloaded data:\")\n    if validate_webdata(path, files=files, logging=logging):\n        if logging:\n            logger.info(\"Successful!\")\n        return path\n    else:\n        raise RuntimeError(\n            \"[Helper:ERROR] :: Failed to download required {} data-files at: {}, Check your Internet connectivity!\".format(\n                basename, path\n            )\n        )\n
"},{"location":"bonus/reference/helper_async/#vidgear.gears.asyncio.helper.validate_webdata--validate_auth_keys","title":"validate_auth_keys","text":"

Validates, and also maintains downloaded list of files.

Parameters:

Name Type Description Default path string

path of downloaded files

required files list

list of files to be validated

[] logging bool

enables logging for its operations

False

Returns: A boolean value, confirming whether tests passed, or not?.

Source code in vidgear/gears/asyncio/helper.py
def validate_webdata(path, files=[], logging=False):\n    \"\"\"\n    ## validate_auth_keys\n\n    Validates, and also maintains downloaded list of files.\n\n    Parameters:\n        path (string): path of downloaded files\n        files (list): list of files to be validated\n        logging (bool): enables logging for its operations\n\n    **Returns:** A  boolean value, confirming whether tests passed, or not?.\n    \"\"\"\n    # check if valid path or directory empty\n    if not (os.path.exists(path)) or not (os.listdir(path)):\n        return False\n\n    files_buffer = []\n    # loop over files\n    for file in os.listdir(path):\n        if file in files:\n            files_buffer.append(file)  # store them\n\n    # return results\n    if len(files_buffer) < len(files):\n        if logging:\n            logger.warning(\n                \"`{}` file(s) missing from data-files!\".format(\n                    \" ,\".join(list(set(files_buffer) ^ set(files)))\n                )\n            )\n        return False\n    else:\n        return True\n
"},{"location":"bonus/reference/netgear/","title":"NetGear API References","text":"

NetGear API usage examples can be found here \u27b6

NetGear API parameters are explained here \u27b6

NetGear is exclusively designed to transfer video frames synchronously and asynchronously between interconnecting systems over the network in real-time.

NetGear implements a high-level wrapper around PyZmQ python library that contains python bindings for ZeroMQ - a high-performance asynchronous distributed messaging library that provides a message queue, but unlike message-oriented middleware, its system can run without a dedicated message broker.

NetGear also supports real-time Frame Compression capabilities for optimizing performance while sending the frames directly over the network, by encoding the frame before sending it and decoding it on the client's end automatically in real-time.

Info

NetGear API now internally implements robust Lazy Pirate pattern (auto-reconnection) for its synchronous messaging patterns (i.e. zmq.PAIR & zmq.REQ/zmq.REP) at both Server and Client ends, where its API instead of doing a blocking receive, will:

NetGear as of now seamlessly supports three ZeroMQ messaging patterns:

whereas the supported protocol are: tcp and ipc.

Modes of Operation Source code in vidgear/gears/netgear.py
class NetGear:\n    \"\"\"\n    NetGear is exclusively designed to transfer video frames synchronously and asynchronously between interconnecting systems over the network in real-time.\n\n    NetGear implements a high-level wrapper around PyZmQ python library that contains python bindings for ZeroMQ - a high-performance asynchronous distributed messaging library\n    that provides a message queue, but unlike message-oriented middleware, its system can run without a dedicated message broker.\n\n    NetGear also supports real-time Frame Compression capabilities for optimizing performance while sending the frames directly over the network, by encoding the frame before sending\n    it and decoding it on the client's end automatically in real-time.\n\n    !!! info\n        NetGear API now internally implements robust *Lazy Pirate pattern* (auto-reconnection) for its synchronous messaging patterns _(i.e. `zmq.PAIR` & `zmq.REQ/zmq.REP`)_\n        at both Server and Client ends, where its API instead of doing a blocking receive, will:\n\n        * Poll the socket and receive from it only when it's sure a reply has arrived.\n        * Attempt to reconnect, if no reply has arrived within a timeout period.\n        * Abandon the connection if there is still no reply after several requests.\n\n    NetGear as of now seamlessly supports three ZeroMQ messaging patterns:\n\n    - `zmq.PAIR` _(ZMQ Pair Pattern)_\n    - `zmq.REQ/zmq.REP` _(ZMQ Request/Reply Pattern)_\n    - `zmq.PUB/zmq.SUB` _(ZMQ Publish/Subscribe Pattern)_\n\n    _whereas the supported protocol are: `tcp` and `ipc`_.\n\n    ??? tip \"Modes of Operation\"\n\n        * **Primary Modes**\n\n            NetGear API primarily has two modes of operations:\n\n            * **Send Mode:** _which employs `send()` function to send video frames over the network in real-time._\n\n            * **Receive Mode:** _which employs `recv()` function to receive frames, sent over the network with *Send Mode* in real-time. The mode sends back confirmation when the\n            frame is received successfully in few patterns._\n\n        * **Exclusive Modes**\n\n            In addition to these primary modes, NetGear API offers applications-specific Exclusive Modes:\n\n            * **Multi-Servers Mode:** _In this exclusive mode, NetGear API robustly **handles multiple servers at once**, thereby providing seamless access to frames and unidirectional\n            data transfer from multiple Servers/Publishers across the network in real-time._\n\n            * **Multi-Clients Mode:** _In this exclusive mode, NetGear API robustly **handles multiple clients at once**, thereby providing seamless access to frames and unidirectional\n            data transfer to multiple Client/Consumers across the network in real-time._\n\n            * **Bidirectional Mode:** _This exclusive mode **provides seamless support for bidirectional data transmission between between Server and Client along with video frames**._\n\n            * **Secure Mode:** _In this exclusive mode, NetGear API **provides easy access to powerful, smart & secure ZeroMQ's Security Layers** that enables strong encryption on\n            data, and unbreakable authentication between the Server and Client with the help of custom certificates/keys that brings cheap, standardized privacy and authentication\n            for distributed systems over the network._\n    \"\"\"\n\n    def __init__(\n        self,\n        address=None,\n        port=None,\n        protocol=None,\n        pattern=0,\n        receive_mode=False,\n        logging=False,\n        **options\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the NetGear class.\n\n        Parameters:\n            address (str): sets the valid network address of the Server/Client.\n            port (str): sets the valid Network Port of the Server/Client.\n            protocol (str): sets the valid messaging protocol between Server/Client.\n            pattern (int): sets the supported messaging pattern(flow of communication) between Server/Client\n            receive_mode (bool): select the Netgear's Mode of operation.\n            logging (bool): enables/disables logging.\n            options (dict): provides the flexibility to alter various NetGear internal properties.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # raise error(s) for critical Class imports\n        import_dependency_safe(\n            \"zmq\" if zmq is None else \"\", min_version=\"4.0\", pkg_name=\"pyzmq\"\n        )\n        import_dependency_safe(\n            \"simplejpeg\" if simplejpeg is None else \"\", error=\"log\", min_version=\"1.6.1\"\n        )\n\n        # define valid messaging patterns => `0`: zmq.PAIR, `1`:(zmq.REQ,zmq.REP), and `1`:(zmq.SUB,zmq.PUB)\n        valid_messaging_patterns = {\n            0: (zmq.PAIR, zmq.PAIR),\n            1: (zmq.REQ, zmq.REP),\n            2: (zmq.PUB, zmq.SUB),\n        }\n\n        # Handle messaging pattern\n        msg_pattern = None\n        # check whether user-defined messaging pattern is valid\n        if isinstance(pattern, int) and pattern in valid_messaging_patterns.keys():\n            # assign value\n            msg_pattern = valid_messaging_patterns[pattern]\n        else:\n            # otherwise default to 0:`zmq.PAIR`\n            pattern = 0\n            msg_pattern = valid_messaging_patterns[pattern]\n            self.__logging and logger.warning(\n                \"Wrong pattern value, Defaulting to `zmq.PAIR`! Kindly refer Docs for more Information.\"\n            )\n        # assign pattern to global parameter for further use\n        self.__pattern = pattern\n\n        # Handle messaging protocol\n        if protocol is None or not (protocol in [\"tcp\", \"ipc\"]):\n            # else default to `tcp` protocol\n            protocol = \"tcp\"\n            # log it\n            self.__logging and logger.warning(\n                \"Protocol is not supported or not provided. Defaulting to `tcp` protocol!\"\n            )\n\n        # Handle connection params\n\n        self.__msg_flag = 0  # handles connection flags\n        self.__msg_copy = False  # handles whether to copy data\n        self.__msg_track = False  # handles whether to track packets\n\n        # Handle NetGear's internal exclusive modes and params\n\n        # define Secure Mode\n        self.__z_auth = None\n\n        # define SSH Tunneling Mode\n        self.__ssh_tunnel_mode = None  # handles ssh_tunneling mode state\n        self.__ssh_tunnel_pwd = None\n        self.__ssh_tunnel_keyfile = None\n        self.__paramiko_present = False if paramiko is None else True\n\n        # define Multi-Server mode\n        self.__multiserver_mode = False  # handles multi-server mode state\n\n        # define Multi-Client mode\n        self.__multiclient_mode = False  # handles multi-client mode state\n\n        # define Bidirectional mode\n        self.__bi_mode = False  # handles Bidirectional mode state\n\n        # define Secure mode\n        valid_security_mech = {0: \"Grasslands\", 1: \"StoneHouse\", 2: \"IronHouse\"}\n        self.__secure_mode = 0  # handles ZMQ security layer status\n        auth_cert_dir = \"\"  # handles valid ZMQ certificates dir\n        self.__auth_publickeys_dir = \"\"  # handles valid ZMQ public certificates dir\n        self.__auth_secretkeys_dir = \"\"  # handles valid ZMQ private certificates dir\n        overwrite_cert = False  # checks if certificates overwriting allowed\n        custom_cert_location = \"\"  # handles custom ZMQ certificates path\n\n        # define frame-compression handler\n        self.__jpeg_compression = (\n            True if not (simplejpeg is None) else False\n        )  # enabled by default for all connections if simplejpeg is installed\n        self.__jpeg_compression_quality = 90  # 90% quality\n        self.__jpeg_compression_fastdct = True  # fastest DCT on by default\n        self.__jpeg_compression_fastupsample = False  # fastupsample off by default\n        self.__jpeg_compression_colorspace = \"BGR\"  # use BGR colorspace by default\n\n        # defines frame compression on return data\n        self.__ex_compression_params = None\n\n        # define receiver return data handler\n        self.__return_data = None\n\n        # generate 8-digit random system id\n        self.__id = \"\".join(\n            secrets.choice(string.ascii_uppercase + string.digits) for i in range(8)\n        )\n\n        # define termination flag\n        self.__terminate = False\n\n        # additional settings for reliability\n        if pattern < 2:\n            # define zmq poller for reliable transmission\n            self.__poll = zmq.Poller()\n            # define max retries\n            self.__max_retries = 3\n            # request timeout\n            self.__request_timeout = 4000  # 4 secs\n        else:\n            # subscriber timeout\n            self.__subscriber_timeout = None\n\n        # Handle user-defined options dictionary values\n        # reformat dictionary\n        options = {str(k).strip(): v for k, v in options.items()}\n\n        # loop over dictionary key & values and assign to global variables if valid\n        for key, value in options.items():\n            # handle multi-server mode\n            if key == \"multiserver_mode\" and isinstance(value, bool):\n                # check if valid pattern assigned\n                if pattern > 0:\n                    # activate Multi-server mode\n                    self.__multiserver_mode = value\n                else:\n                    # otherwise disable it and raise error\n                    self.__multiserver_mode = False\n                    logger.critical(\"Multi-Server Mode is disabled!\")\n                    raise ValueError(\n                        \"[NetGear:ERROR] :: `{}` pattern is not valid when Multi-Server Mode is enabled. Kindly refer Docs for more Information.\".format(\n                            pattern\n                        )\n                    )\n\n            # handle multi-client mode\n            elif key == \"multiclient_mode\" and isinstance(value, bool):\n                # check if valid pattern assigned\n                if pattern > 0:\n                    # activate Multi-client mode\n                    self.__multiclient_mode = value\n                else:\n                    # otherwise disable it and raise error\n                    self.__multiclient_mode = False\n                    logger.critical(\"Multi-Client Mode is disabled!\")\n                    raise ValueError(\n                        \"[NetGear:ERROR] :: `{}` pattern is not valid when Multi-Client Mode is enabled. Kindly refer Docs for more Information.\".format(\n                            pattern\n                        )\n                    )\n\n            # handle bidirectional mode\n            elif key == \"bidirectional_mode\" and isinstance(value, bool):\n                # check if pattern is valid\n                if pattern < 2:\n                    # activate Bidirectional mode if specified\n                    self.__bi_mode = value\n                else:\n                    # otherwise disable it and raise error\n                    self.__bi_mode = False\n                    logger.warning(\"Bidirectional data transmission is disabled!\")\n                    raise ValueError(\n                        \"[NetGear:ERROR] :: `{}` pattern is not valid when Bidirectional Mode is enabled. Kindly refer Docs for more Information!\".format(\n                            pattern\n                        )\n                    )\n\n            # handle secure mode\n            elif (\n                key == \"secure_mode\"\n                and isinstance(value, int)\n                and (value in valid_security_mech)\n            ):\n                self.__secure_mode = value\n\n            elif key == \"custom_cert_location\" and isinstance(value, str):\n                # verify custom auth certificates path for secure mode\n                custom_cert_location = os.path.abspath(value)\n                assert os.path.isdir(\n                    custom_cert_location\n                ), \"[NetGear:ERROR] :: `custom_cert_location` value must be the path to a valid directory!\"\n                assert check_WriteAccess(\n                    custom_cert_location,\n                    is_windows=True if os.name == \"nt\" else False,\n                    logging=self.__logging,\n                ), \"[NetGear:ERROR] :: Permission Denied!, cannot write ZMQ authentication certificates to '{}' directory!\".format(\n                    value\n                )\n            elif key == \"overwrite_cert\" and isinstance(value, bool):\n                # enable/disable auth certificate overwriting in secure mode\n                overwrite_cert = value\n\n            # handle ssh-tunneling mode\n            elif key == \"ssh_tunnel_mode\" and isinstance(value, str):\n                # enable SSH Tunneling Mode\n                self.__ssh_tunnel_mode = value.strip()\n            elif key == \"ssh_tunnel_pwd\" and isinstance(value, str):\n                # add valid SSH Tunneling password\n                self.__ssh_tunnel_pwd = value\n            elif key == \"ssh_tunnel_keyfile\" and isinstance(value, str):\n                # add valid SSH Tunneling key-file\n                self.__ssh_tunnel_keyfile = value if os.path.isfile(value) else None\n                if self.__ssh_tunnel_keyfile is None:\n                    logger.warning(\n                        \"Discarded invalid or non-existential SSH Tunnel Key-file at {}!\".format(\n                            value\n                        )\n                    )\n\n            # handle jpeg compression\n            elif (\n                key == \"jpeg_compression\"\n                and not (simplejpeg is None)\n                and isinstance(value, (bool, str))\n            ):\n                if isinstance(value, str) and value.strip().upper() in [\n                    \"RGB\",\n                    \"BGR\",\n                    \"RGBX\",\n                    \"BGRX\",\n                    \"XBGR\",\n                    \"XRGB\",\n                    \"GRAY\",\n                    \"RGBA\",\n                    \"BGRA\",\n                    \"ABGR\",\n                    \"ARGB\",\n                    \"CMYK\",\n                ]:\n                    # set encoding colorspace\n                    self.__jpeg_compression_colorspace = value.strip().upper()\n                    # enable frame-compression encoding value\n                    self.__jpeg_compression = True\n                else:\n                    # enable frame-compression encoding value\n                    self.__jpeg_compression = value\n            elif key == \"jpeg_compression_quality\" and isinstance(value, (int, float)):\n                # set valid jpeg quality\n                if value >= 10 and value <= 100:\n                    self.__jpeg_compression_quality = int(value)\n                else:\n                    logger.warning(\"Skipped invalid `jpeg_compression_quality` value!\")\n            elif key == \"jpeg_compression_fastdct\" and isinstance(value, bool):\n                # enable jpeg fastdct\n                self.__jpeg_compression_fastdct = value\n            elif key == \"jpeg_compression_fastupsample\" and isinstance(value, bool):\n                # enable jpeg  fastupsample\n                self.__jpeg_compression_fastupsample = value\n\n            # assign maximum retries in synchronous patterns\n            elif key == \"max_retries\" and isinstance(value, int) and pattern < 2:\n                if value >= 0:\n                    self.__max_retries = value\n                else:\n                    logger.warning(\"Invalid `max_retries` value skipped!\")\n\n            # assign request timeout in synchronous patterns\n            elif key == \"request_timeout\" and isinstance(value, int) and pattern < 2:\n                if value >= 4:\n                    self.__request_timeout = value * 1000  # covert to milliseconds\n                else:\n                    logger.warning(\"Invalid `request_timeout` value skipped!\")\n\n            # assign subscriber timeout\n            elif (\n                key == \"subscriber_timeout\" and isinstance(value, int) and pattern == 2\n            ):\n                if value > 0:\n                    self.__subscriber_timeout = value * 1000  # covert to milliseconds\n                else:\n                    logger.warning(\"Invalid `request_timeout` value skipped!\")\n\n            # handle ZMQ flags\n            elif key == \"flag\" and isinstance(value, int):\n                self.__msg_flag = value\n                self.__msg_flag and logger.warning(\n                    \"The flag optional value is set to `1` (NOBLOCK) for this run. This might cause NetGear to not terminate gracefully.\"\n                )\n            elif key == \"copy\" and isinstance(value, bool):\n                self.__msg_copy = value\n            elif key == \"track\" and isinstance(value, bool):\n                self.__msg_track = value\n                self.__msg_copy and self.__msg_track and logger.info(\n                    \"The `track` optional value will be ignored for this run because `copy=True` is also defined.\"\n                )\n            else:\n                pass\n\n        # Handle ssh tunneling if enabled\n        if not (self.__ssh_tunnel_mode is None):\n            # SSH Tunnel Mode only available for server mode\n            if receive_mode:\n                logger.error(\"SSH Tunneling cannot be enabled for Client-end!\")\n            else:\n                # check if SSH tunneling possible\n                ssh_address = self.__ssh_tunnel_mode\n                ssh_address, ssh_port = (\n                    ssh_address.split(\":\")\n                    if \":\" in ssh_address\n                    else [ssh_address, \"22\"]\n                )  # default to port 22\n                if \"47\" in ssh_port:\n                    self.__ssh_tunnel_mode = self.__ssh_tunnel_mode.replace(\n                        \":47\", \"\"\n                    )  # port-47 is reserved for testing\n                else:\n                    # extract ip for validation\n                    ssh_user, ssh_ip = (\n                        ssh_address.split(\"@\")\n                        if \"@\" in ssh_address\n                        else [\"\", ssh_address]\n                    )\n                    # validate ip specified port\n                    assert check_open_port(\n                        ssh_ip, port=int(ssh_port)\n                    ), \"[NetGear:ERROR] :: Host `{}` is not available for SSH Tunneling at port-{}!\".format(\n                        ssh_address, ssh_port\n                    )\n\n        # Handle multiple exclusive modes if enabled\n        if self.__multiclient_mode and self.__multiserver_mode:\n            raise ValueError(\n                \"[NetGear:ERROR] :: Multi-Client and Multi-Server Mode cannot be enabled simultaneously!\"\n            )\n        elif self.__multiserver_mode or self.__multiclient_mode:\n            # check if Bidirectional Mode also enabled\n            if self.__bi_mode:\n                # log it\n                self.__logging and logger.debug(\n                    \"Bidirectional Data Transmission is also enabled for this connection!\"\n                )\n            # check if SSH Tunneling Mode also enabled\n            if self.__ssh_tunnel_mode:\n                # raise error\n                raise ValueError(\n                    \"[NetGear:ERROR] :: SSH Tunneling and {} Mode cannot be enabled simultaneously. Kindly refer docs!\".format(\n                        \"Multi-Server\" if self.__multiserver_mode else \"Multi-Client\"\n                    )\n                )\n        elif self.__bi_mode:\n            # log Bidirectional mode activation\n            self.__logging and logger.debug(\n                \"Bidirectional Data Transmission is enabled for this connection!\"\n            )\n        elif self.__ssh_tunnel_mode:\n            # log Bidirectional mode activation\n            self.__logging and logger.debug(\n                \"SSH Tunneling is enabled for host:`{}` with `{}` back-end.\".format(\n                    self.__ssh_tunnel_mode,\n                    \"paramiko\" if self.__paramiko_present else \"pexpect\",\n                )\n            )\n\n        # On Windows, NetGear requires the ``WindowsSelectorEventLoop`` but Python 3.8 and above,\n        # defaults to an ``ProactorEventLoop`` loop that is not compatible with it. Thereby,\n        # we had to set it manually.\n        platform.system() == \"Windows\" and asyncio.set_event_loop_policy(\n            asyncio.WindowsSelectorEventLoopPolicy()\n        )\n\n        # define ZMQ messaging context instance\n        self.__msg_context = zmq.Context.instance()\n\n        # initialize and assign receive mode to global variable\n        self.__receive_mode = receive_mode\n\n        # Handle Secure mode\n        if self.__secure_mode > 0:\n            # activate and log if overwriting is enabled\n            if receive_mode:\n                overwrite_cert = False\n                overwrite_cert and logger.warning(\n                    \"Overwriting ZMQ Authentication certificates is disabled for Client's end!\"\n                )\n            else:\n                overwrite_cert and self.__logging and logger.info(\n                    \"Overwriting ZMQ Authentication certificates over previous ones!\"\n                )\n\n            # Validate certificate generation paths\n            # Start threaded authenticator for this context\n            try:\n                # check if custom certificates path is specified\n                if custom_cert_location:\n                    (\n                        auth_cert_dir,\n                        self.__auth_secretkeys_dir,\n                        self.__auth_publickeys_dir,\n                    ) = generate_auth_certificates(\n                        custom_cert_location, overwrite=overwrite_cert, logging=logging\n                    )\n                else:\n                    # otherwise auto-generate suitable path\n                    (\n                        auth_cert_dir,\n                        self.__auth_secretkeys_dir,\n                        self.__auth_publickeys_dir,\n                    ) = generate_auth_certificates(\n                        os.path.join(expanduser(\"~\"), \".vidgear\"),\n                        overwrite=overwrite_cert,\n                        logging=logging,\n                    )\n                # log it\n                self.__logging and logger.debug(\n                    \"`{}` is the default location for storing ZMQ authentication certificates/keys.\".format(\n                        auth_cert_dir\n                    )\n                )\n\n                # start an authenticator for this context\n                self.__z_auth = ThreadAuthenticator(self.__msg_context)\n                self.__z_auth.start()\n                self.__z_auth.allow(str(address))  # allow current address\n\n                # check if `IronHouse` is activated\n                if self.__secure_mode == 2:\n                    # tell authenticator to use the certificate from given valid dir\n                    self.__z_auth.configure_curve(\n                        domain=\"*\", location=self.__auth_publickeys_dir\n                    )\n                else:\n                    # otherwise tell the authenticator how to handle the CURVE requests, if `StoneHouse` is activated\n                    self.__z_auth.configure_curve(\n                        domain=\"*\", location=auth.CURVE_ALLOW_ANY\n                    )\n            except zmq.ZMQError as e:\n                if \"Address in use\" in str(e):\n                    logger.info(\"ZMQ Authenticator already running.\")\n                else:\n                    # catch if any error occurred and disable Secure mode\n                    logger.exception(str(e))\n                    self.__secure_mode = 0\n                    logger.error(\n                        \"ZMQ Security Mechanism is disabled for this connection due to errors!\"\n                    )\n\n        # check whether `receive_mode` is enabled\n        if self.__receive_mode:\n            # define connection address\n            address = \"*\" if address is None else address\n\n            # check if multiserver_mode is enabled\n            if self.__multiserver_mode:\n                # check if unique server port address list/tuple is assigned or not in multiserver_mode\n                if port is None or not isinstance(port, (tuple, list)):\n                    # raise error if not\n                    raise ValueError(\n                        \"[NetGear:ERROR] :: Incorrect port value! Kindly provide a list/tuple of Server ports while Multi-Server mode is enabled. For more information refer VidGear docs.\"\n                    )\n                else:\n                    # otherwise log it\n                    logger.debug(\n                        \"Enabling Multi-Server Mode at PORTS: {}!\".format(port)\n                    )\n                # create port address buffer for keeping track of connected client's port(s)\n                self.__port_buffer = []\n            # check if multiclient_mode is enabled\n            elif self.__multiclient_mode:\n                # check if unique server port address is assigned or not in multiclient_mode\n                if port is None:\n                    # raise error if not\n                    raise ValueError(\n                        \"[NetGear:ERROR] :: Kindly provide a unique & valid port value at Client-end. For more information refer VidGear docs.\"\n                    )\n                else:\n                    # otherwise log it\n                    logger.debug(\n                        \"Enabling Multi-Client Mode at PORT: {} on this device!\".format(\n                            port\n                        )\n                    )\n                # assign value to global variable\n                self.__port = port\n            else:\n                # otherwise assign local port address if None\n                port = \"5555\" if port is None else port\n\n            try:\n                # define thread-safe messaging socket\n                self.__msg_socket = self.__msg_context.socket(msg_pattern[1])\n\n                # define pub-sub flag\n                self.__pattern == 2 and self.__msg_socket.set_hwm(1)\n\n                # enable specified secure mode for the socket\n                if self.__secure_mode > 0:\n                    # load server key\n                    server_secret_file = os.path.join(\n                        self.__auth_secretkeys_dir, \"server.key_secret\"\n                    )\n                    server_public, server_secret = auth.load_certificate(\n                        server_secret_file\n                    )\n                    # load  all CURVE keys\n                    self.__msg_socket.curve_secretkey = server_secret\n                    self.__msg_socket.curve_publickey = server_public\n                    # enable CURVE connection for this socket\n                    self.__msg_socket.curve_server = True\n\n                # define exclusive socket options for `patterns=2`\n                if self.__pattern == 2:\n                    self.__msg_socket.setsockopt_string(zmq.SUBSCRIBE, \"\")\n                    self.__subscriber_timeout and self.__msg_socket.setsockopt(\n                        zmq.RCVTIMEO, self.__subscriber_timeout\n                    )\n                    self.__subscriber_timeout and self.__msg_socket.setsockopt(\n                        zmq.LINGER, 0\n                    )\n\n                # if multiserver_mode is enabled, then assign port addresses to zmq socket\n                if self.__multiserver_mode:\n                    # bind socket to given server protocol, address and ports\n                    for pt in port:\n                        self.__msg_socket.bind(\n                            protocol + \"://\" + str(address) + \":\" + str(pt)\n                        )\n                else:\n                    # bind socket to given protocol, address and port normally\n                    self.__msg_socket.bind(\n                        protocol + \"://\" + str(address) + \":\" + str(port)\n                    )\n\n                # additional settings\n                if pattern < 2:\n                    if self.__multiserver_mode:\n                        self.__connection_address = []\n                        for pt in port:\n                            self.__connection_address.append(\n                                protocol + \"://\" + str(address) + \":\" + str(pt)\n                            )\n                    else:\n                        self.__connection_address = (\n                            protocol + \"://\" + str(address) + \":\" + str(port)\n                        )\n                    self.__msg_pattern = msg_pattern[1]\n                    self.__poll.register(self.__msg_socket, zmq.POLLIN)\n                    self.__logging and logger.debug(\n                        \"Reliable transmission is enabled for this pattern with max-retries: {} and timeout: {} secs.\".format(\n                            self.__max_retries, self.__request_timeout / 1000\n                        )\n                    )\n                else:\n                    self.__logging and self.__subscriber_timeout and logger.debug(\n                        \"Timeout: {} secs is enabled for this system.\".format(\n                            self.__subscriber_timeout / 1000\n                        )\n                    )\n\n            except Exception as e:\n                # otherwise log and raise error\n                logger.exception(str(e))\n                # Handle Secure Mode\n                self.__secure_mode and logger.critical(\n                    \"Failed to activate Secure Mode: `{}` for this connection!\".format(\n                        valid_security_mech[self.__secure_mode]\n                    )\n                )\n                # raise errors for exclusive modes\n                if self.__multiserver_mode or self.__multiclient_mode:\n                    raise RuntimeError(\n                        \"[NetGear:ERROR] :: Receive Mode failed to activate {} Mode at address: {} with pattern: {}! Kindly recheck all parameters.\".format(\n                            (\n                                \"Multi-Server\"\n                                if self.__multiserver_mode\n                                else \"Multi-Client\"\n                            ),\n                            (protocol + \"://\" + str(address) + \":\" + str(port)),\n                            pattern,\n                        )\n                    )\n                else:\n                    self.__bi_mode and logger.critical(\n                        \"Failed to activate Bidirectional Mode for this connection!\"\n                    )\n                    raise RuntimeError(\n                        \"[NetGear:ERROR] :: Receive Mode failed to bind address: {} and pattern: {}! Kindly recheck all parameters.\".format(\n                            (protocol + \"://\" + str(address) + \":\" + str(port)), pattern\n                        )\n                    )\n\n            # Handle threaded queue mode\n            self.__logging and logger.debug(\n                \"Threaded Queue Mode is enabled by default for this connection.\"\n            )\n\n            # define deque and assign it to global var\n            self.__queue = deque(maxlen=96)  # max len 96 to check overflow\n\n            # initialize and start threaded recv_handler\n            self.__thread = Thread(target=self.__recv_handler, name=\"NetGear\", args=())\n            self.__thread.daemon = True\n            self.__thread.start()\n\n            if self.__logging:\n                # finally log progress\n                logger.debug(\n                    \"Successfully Binded to address: {} with pattern: {}.\".format(\n                        (protocol + \"://\" + str(address) + \":\" + str(port)), pattern\n                    )\n                )\n                self.__jpeg_compression and logger.debug(\n                    \"JPEG Frame-Compression is activated for this connection with Colorspace:`{}`, Quality:`{}`%, Fastdct:`{}`, and Fastupsample:`{}`.\".format(\n                        self.__jpeg_compression_colorspace,\n                        self.__jpeg_compression_quality,\n                        (\"enabled\" if self.__jpeg_compression_fastdct else \"disabled\"),\n                        (\n                            \"enabled\"\n                            if self.__jpeg_compression_fastupsample\n                            else \"disabled\"\n                        ),\n                    )\n                )\n                self.__secure_mode and logger.debug(\n                    \"Successfully enabled ZMQ Security Mechanism: `{}` for this connection.\".format(\n                        valid_security_mech[self.__secure_mode]\n                    )\n                )\n                logger.debug(\"Multi-threaded Receive Mode is successfully enabled.\")\n                logger.debug(\"Unique System ID is {}.\".format(self.__id))\n                logger.debug(\"Receive Mode is now activated.\")\n\n        else:\n            # otherwise default to `Send Mode`\n            # define connection address\n            address = \"localhost\" if address is None else address\n\n            # check if multiserver_mode is enabled\n            if self.__multiserver_mode:\n                # check if unique server port address is assigned or not in multiserver_mode\n                if port is None:\n                    # raise error if not\n                    raise ValueError(\n                        \"[NetGear:ERROR] :: Kindly provide a unique & valid port value at Server-end. For more information refer VidGear docs.\"\n                    )\n                else:\n                    # otherwise log it\n                    logger.debug(\n                        \"Enabling Multi-Server Mode at PORT: {} on this device!\".format(\n                            port\n                        )\n                    )\n                # assign value to global variable\n                self.__port = port\n            # check if multiclient_mode is enabled\n            elif self.__multiclient_mode:\n                # check if unique client port address list/tuple is assigned or not in multiclient_mode\n                if port is None or not isinstance(port, (tuple, list)):\n                    # raise error if not\n                    raise ValueError(\n                        \"[NetGear:ERROR] :: Incorrect port value! Kindly provide a list/tuple of Client ports while Multi-Client mode is enabled. For more information refer VidGear docs.\"\n                    )\n                else:\n                    # otherwise log it\n                    logger.debug(\n                        \"Enabling Multi-Client Mode at PORTS: {}!\".format(port)\n                    )\n                # create port address buffer for keeping track of connected client ports\n                self.__port_buffer = []\n            else:\n                # otherwise assign local port address if None\n                port = \"5555\" if port is None else port\n\n            try:\n                # define thread-safe messaging socket\n                self.__msg_socket = self.__msg_context.socket(msg_pattern[0])\n\n                # if req/rep pattern, define additional flags\n                if self.__pattern == 1:\n                    self.__msg_socket.REQ_RELAXED = True\n                    self.__msg_socket.REQ_CORRELATE = True\n\n                # if pub/sub pattern, define additional optimizer\n                if self.__pattern == 2:\n                    self.__msg_socket.set_hwm(1)\n\n                # enable specified secure mode for the socket\n                if self.__secure_mode > 0:\n                    # load client key\n                    client_secret_file = os.path.join(\n                        self.__auth_secretkeys_dir, \"client.key_secret\"\n                    )\n                    client_public, client_secret = auth.load_certificate(\n                        client_secret_file\n                    )\n                    # load  all CURVE keys\n                    self.__msg_socket.curve_secretkey = client_secret\n                    self.__msg_socket.curve_publickey = client_public\n                    # load server key\n                    server_public_file = os.path.join(\n                        self.__auth_publickeys_dir, \"server.key\"\n                    )\n                    server_public, _ = auth.load_certificate(server_public_file)\n                    # inject public key to make a CURVE connection.\n                    self.__msg_socket.curve_serverkey = server_public\n\n                # check if multi-client_mode is enabled\n                if self.__multiclient_mode:\n                    # bind socket to given server protocol, address and ports\n                    for pt in port:\n                        self.__msg_socket.connect(\n                            protocol + \"://\" + str(address) + \":\" + str(pt)\n                        )\n                else:\n                    # handle SSH tunneling if enabled\n                    if self.__ssh_tunnel_mode:\n                        # establish tunnel connection\n                        ssh.tunnel_connection(\n                            self.__msg_socket,\n                            protocol + \"://\" + str(address) + \":\" + str(port),\n                            self.__ssh_tunnel_mode,\n                            keyfile=self.__ssh_tunnel_keyfile,\n                            password=self.__ssh_tunnel_pwd,\n                            paramiko=self.__paramiko_present,\n                        )\n                    else:\n                        # connect socket to given protocol, address and port\n                        self.__msg_socket.connect(\n                            protocol + \"://\" + str(address) + \":\" + str(port)\n                        )\n\n                # additional settings\n                if pattern < 2:\n                    if self.__multiclient_mode:\n                        self.__connection_address = []\n                        for pt in port:\n                            self.__connection_address.append(\n                                protocol + \"://\" + str(address) + \":\" + str(pt)\n                            )\n                    else:\n                        self.__connection_address = (\n                            protocol + \"://\" + str(address) + \":\" + str(port)\n                        )\n                    self.__msg_pattern = msg_pattern[0]\n                    self.__poll.register(self.__msg_socket, zmq.POLLIN)\n\n                    self.__logging and logger.debug(\n                        \"Reliable transmission is enabled for this pattern with max-retries: {} and timeout: {} secs.\".format(\n                            self.__max_retries, self.__request_timeout / 1000\n                        )\n                    )\n\n            except Exception as e:\n                # otherwise log and raise error\n                logger.exception(str(e))\n                # Handle Secure Mode\n                self.__secure_mode and logger.critical(\n                    \"Failed to activate Secure Mode: `{}` for this connection!\".format(\n                        valid_security_mech[self.__secure_mode]\n                    )\n                )\n                # raise errors for exclusive modes\n                if self.__multiserver_mode or self.__multiclient_mode:\n                    raise RuntimeError(\n                        \"[NetGear:ERROR] :: Send Mode failed to activate {} Mode at address: {} with pattern: {}! Kindly recheck all parameters.\".format(\n                            (\n                                \"Multi-Server\"\n                                if self.__multiserver_mode\n                                else \"Multi-Client\"\n                            ),\n                            (protocol + \"://\" + str(address) + \":\" + str(port)),\n                            pattern,\n                        )\n                    )\n                else:\n                    self.__bi_mode and logger.critical(\n                        \"Failed to activate Bidirectional Mode for this connection!\"\n                    )\n                    self.__ssh_tunnel_mode and logger.critical(\n                        \"Failed to initiate SSH Tunneling Mode for this server with `{}` back-end!\".format(\n                            \"paramiko\" if self.__paramiko_present else \"pexpect\"\n                        )\n                    )\n                    raise RuntimeError(\n                        \"[NetGear:ERROR] :: Send Mode failed to connect address: {} and pattern: {}! Kindly recheck all parameters.\".format(\n                            (protocol + \"://\" + str(address) + \":\" + str(port)), pattern\n                        )\n                    )\n\n            if self.__logging:\n                # finally log progress\n                logger.debug(\n                    \"Successfully connected to address: {} with pattern: {}.\".format(\n                        (protocol + \"://\" + str(address) + \":\" + str(port)), pattern\n                    )\n                )\n                self.__jpeg_compression and logger.debug(\n                    \"JPEG Frame-Compression is activated for this connection with Colorspace:`{}`, Quality:`{}`%, Fastdct:`{}`, and Fastupsample:`{}`.\".format(\n                        self.__jpeg_compression_colorspace,\n                        self.__jpeg_compression_quality,\n                        (\"enabled\" if self.__jpeg_compression_fastdct else \"disabled\"),\n                        (\n                            \"enabled\"\n                            if self.__jpeg_compression_fastupsample\n                            else \"disabled\"\n                        ),\n                    )\n                )\n                self.__secure_mode and logger.debug(\n                    \"Enabled ZMQ Security Mechanism: `{}` for this connection.\".format(\n                        valid_security_mech[self.__secure_mode]\n                    )\n                )\n                logger.debug(\"Unique System ID is {}.\".format(self.__id))\n                logger.debug(\n                    \"Send Mode is successfully activated and ready to send data.\"\n                )\n\n    def __recv_handler(self):\n        \"\"\"\n        A threaded receiver handler, that keep iterating data from ZMQ socket to a internally monitored deque,\n        until the thread is terminated, or socket disconnects.\n        \"\"\"\n        # initialize variables\n        frame = None\n        msg_json = None\n\n        # keep looping infinitely until the thread is terminated\n        while not self.__terminate:\n            # check queue buffer for overflow\n            if len(self.__queue) >= 96:\n                # stop iterating if overflowing occurs\n                time.sleep(0.000001)\n                continue\n\n            if self.__pattern < 2:\n                socks = dict(self.__poll.poll(self.__request_timeout * 3))\n                if socks.get(self.__msg_socket) == zmq.POLLIN:\n                    msg_json = self.__msg_socket.recv_json(\n                        flags=self.__msg_flag | zmq.DONTWAIT\n                    )\n                else:\n                    logger.critical(\"No response from Server(s), Reconnecting again...\")\n                    self.__msg_socket.close(linger=0)\n                    self.__poll.unregister(self.__msg_socket)\n                    self.__max_retries -= 1\n\n                    if not (self.__max_retries):\n                        if self.__multiserver_mode:\n                            logger.error(\"All Servers seems to be offline, Abandoning!\")\n                        else:\n                            logger.error(\"Server seems to be offline, Abandoning!\")\n                        self.__terminate = True\n                        continue\n\n                    # Create new connection\n                    try:\n                        self.__msg_socket = self.__msg_context.socket(\n                            self.__msg_pattern\n                        )\n                        if isinstance(self.__connection_address, list):\n                            for _connection in self.__connection_address:\n                                self.__msg_socket.bind(_connection)\n                        else:\n                            self.__msg_socket.bind(self.__connection_address)\n                    except Exception as e:\n                        logger.exception(str(e))\n                        self.__terminate = True\n                        raise RuntimeError(\"API failed to restart the Client-end!\")\n                    self.__poll.register(self.__msg_socket, zmq.POLLIN)\n\n                    continue\n            else:\n                try:\n                    msg_json = self.__msg_socket.recv_json(flags=self.__msg_flag)\n                except zmq.ZMQError as e:\n                    if e.errno == zmq.EAGAIN:\n                        logger.critical(\"Connection Timeout. Exiting!\")\n                        self.__terminate = True\n                        self.__queue.append(None)\n                        break\n\n            # check if terminate_flag` received\n            if msg_json and msg_json[\"terminate_flag\"]:\n                # if multiserver_mode is enabled\n                if self.__multiserver_mode:\n                    # check and remove from which ports signal is received\n                    if msg_json[\"port\"] in self.__port_buffer:\n                        # if pattern is 1, then send back server the info about termination\n                        if self.__pattern == 1:\n                            self.__msg_socket.send_string(\n                                \"Termination signal successfully received at client!\"\n                            )\n                        self.__port_buffer.remove(msg_json[\"port\"])\n                        self.__logging and logger.warning(\n                            \"Termination signal received from Server at port: {}!\".format(\n                                msg_json[\"port\"]\n                            )\n                        )\n                    # if termination signal received from all servers then exit client.\n                    if not self.__port_buffer:\n                        logger.critical(\n                            \"Termination signal received from all Servers!!!\"\n                        )\n                        self.__terminate = True  # termination\n                else:\n                    # if pattern is 1, then send back server the info about termination\n                    if self.__pattern == 1:\n                        self.__msg_socket.send_string(\n                            \"Termination signal successfully received at Client's end!\"\n                        )\n                    # termination\n                    self.__terminate = True\n                    # notify client\n                    self.__logging and logger.critical(\n                        \"Termination signal received from server!\"\n                    )\n                continue\n\n            try:\n                msg_data = self.__msg_socket.recv(\n                    flags=self.__msg_flag | zmq.DONTWAIT,\n                    copy=self.__msg_copy,\n                    track=self.__msg_track,\n                )\n            except zmq.ZMQError as e:\n                logger.critical(\"Socket Session Expired. Exiting!\")\n                self.__terminate = True\n                self.__queue.append(None)\n                break\n\n            # handle data transfer in synchronous modes.\n            if self.__pattern < 2:\n                if self.__bi_mode or self.__multiclient_mode:\n                    # check if we are returning `ndarray` frames\n                    if not (self.__return_data is None) and isinstance(\n                        self.__return_data, np.ndarray\n                    ):\n                        # handle return data for compression\n                        return_data = np.copy(self.__return_data)\n\n                        # check whether exit_flag is False\n                        if not (return_data.flags[\"C_CONTIGUOUS\"]):\n                            # check whether the incoming frame is contiguous\n                            return_data = np.ascontiguousarray(\n                                return_data, dtype=return_data.dtype\n                            )\n\n                        # handle jpeg-compression encoding\n                        if self.__jpeg_compression:\n                            if self.__jpeg_compression_colorspace == \"GRAY\":\n                                if return_data.ndim == 2:\n                                    # patch for https://gitlab.com/jfolz/simplejpeg/-/issues/11\n                                    return_data = return_data[:, :, np.newaxis]\n                                return_data = simplejpeg.encode_jpeg(\n                                    return_data,\n                                    quality=self.__jpeg_compression_quality,\n                                    colorspace=self.__jpeg_compression_colorspace,\n                                    fastdct=self.__jpeg_compression_fastdct,\n                                )\n                            else:\n                                return_data = simplejpeg.encode_jpeg(\n                                    return_data,\n                                    quality=self.__jpeg_compression_quality,\n                                    colorspace=self.__jpeg_compression_colorspace,\n                                    colorsubsampling=\"422\",\n                                    fastdct=self.__jpeg_compression_fastdct,\n                                )\n\n                        return_dict = (\n                            dict(port=self.__port)\n                            if self.__multiclient_mode\n                            else dict()\n                        )\n\n                        return_dict.update(\n                            dict(\n                                return_type=(type(self.__return_data).__name__),\n                                compression=(\n                                    {\n                                        \"dct\": self.__jpeg_compression_fastdct,\n                                        \"ups\": self.__jpeg_compression_fastupsample,\n                                        \"colorspace\": self.__jpeg_compression_colorspace,\n                                    }\n                                    if self.__jpeg_compression\n                                    else False\n                                ),\n                                array_dtype=(\n                                    str(self.__return_data.dtype)\n                                    if not (self.__jpeg_compression)\n                                    else \"\"\n                                ),\n                                array_shape=(\n                                    self.__return_data.shape\n                                    if not (self.__jpeg_compression)\n                                    else \"\"\n                                ),\n                                data=None,\n                            )\n                        )\n\n                        # send the json dict\n                        self.__msg_socket.send_json(\n                            return_dict, self.__msg_flag | zmq.SNDMORE\n                        )\n                        # send the array with correct flags\n                        self.__msg_socket.send(\n                            return_data,\n                            flags=self.__msg_flag,\n                            copy=self.__msg_copy,\n                            track=self.__msg_track,\n                        )\n                    else:\n                        return_dict = (\n                            dict(port=self.__port)\n                            if self.__multiclient_mode\n                            else dict()\n                        )\n                        return_dict.update(\n                            dict(\n                                return_type=(type(self.__return_data).__name__),\n                                data=self.__return_data,\n                            )\n                        )\n                        self.__msg_socket.send_json(return_dict, self.__msg_flag)\n                else:\n                    # send confirmation message to server\n                    self.__msg_socket.send_string(\n                        \"Data received on device: {} !\".format(self.__id)\n                    )\n            else:\n                # else raise warning\n                if self.__return_data:\n                    logger.warning(\"`return_data` is disabled for this pattern!\")\n\n            # check if encoding was enabled\n            if msg_json[\"compression\"]:\n                # decode JPEG frame\n                frame = simplejpeg.decode_jpeg(\n                    msg_data,\n                    colorspace=msg_json[\"compression\"][\"colorspace\"],\n                    fastdct=self.__jpeg_compression_fastdct\n                    or msg_json[\"compression\"][\"dct\"],\n                    fastupsample=self.__jpeg_compression_fastupsample\n                    or msg_json[\"compression\"][\"ups\"],\n                )\n                # check if valid frame returned\n                if frame is None:\n                    self.__terminate = True\n                    # otherwise raise error and exit\n                    raise RuntimeError(\n                        \"[NetGear:ERROR] :: Received compressed JPEG frame decoding failed\"\n                    )\n                if msg_json[\"compression\"][\"colorspace\"] == \"GRAY\" and frame.ndim == 3:\n                    # patch for https://gitlab.com/jfolz/simplejpeg/-/issues/11\n                    frame = np.squeeze(frame, axis=2)\n            else:\n                # recover and reshape frame from buffer\n                frame_buffer = np.frombuffer(msg_data, dtype=msg_json[\"dtype\"])\n                frame = frame_buffer.reshape(msg_json[\"shape\"])\n\n            # check if multiserver_mode\n            if self.__multiserver_mode:\n                # save the unique port addresses\n                if not msg_json[\"port\"] in self.__port_buffer:\n                    self.__port_buffer.append(msg_json[\"port\"])\n                # extract if any message from server and display it\n                if msg_json[\"message\"]:\n                    self.__queue.append((msg_json[\"port\"], msg_json[\"message\"], frame))\n                else:\n                    # append recovered unique port and frame to queue\n                    self.__queue.append((msg_json[\"port\"], frame))\n            # extract if any message from server if Bidirectional Mode is enabled\n            elif self.__bi_mode:\n                if msg_json[\"message\"]:\n                    # append grouped frame and data to queue\n                    self.__queue.append((msg_json[\"message\"], frame))\n                else:\n                    self.__queue.append((None, frame))\n            else:\n                # otherwise append recovered frame to queue\n                self.__queue.append(frame)\n\n    def recv(self, return_data=None):\n        \"\"\"\n        A Receiver end method, that extracts received frames synchronously from monitored deque, while maintaining a\n        fixed-length frame buffer in the memory, and blocks the thread if the deque is full.\n\n        Parameters:\n            return_data (any): inputs return data _(of any datatype)_, for sending back to Server.\n\n        **Returns:** A n-dimensional numpy array.\n        \"\"\"\n        # check whether `receive mode` is activated\n        if not (self.__receive_mode):\n            # raise value error and exit\n            self.__terminate = True\n            raise ValueError(\n                \"[NetGear:ERROR] :: `recv()` function cannot be used while receive_mode is disabled. Kindly refer vidgear docs!\"\n            )\n\n        # handle Bidirectional return data\n        if (self.__bi_mode or self.__multiclient_mode) and not (return_data is None):\n            self.__return_data = return_data\n\n        # check whether or not termination flag is enabled\n        while not self.__terminate:\n            try:\n                # check if queue is empty\n                if len(self.__queue) > 0:\n                    return self.__queue.popleft()\n                else:\n                    time.sleep(0.00001)\n                    continue\n            except KeyboardInterrupt:\n                self.__terminate = True\n                break\n        # otherwise return NoneType\n        return None\n\n    def send(self, frame, message=None):\n        \"\"\"\n        A Server end method, that sends the data and frames over the network to Client(s).\n\n        Parameters:\n            frame (numpy.ndarray): inputs numpy array(frame).\n            message (any): input for sending additional data _(of any datatype except `numpy.ndarray`)_ to Client(s).\n\n        **Returns:** Data _(of any datatype)_ in selected exclusive modes, otherwise None-type.\n\n        \"\"\"\n        # check whether `receive_mode` is disabled\n        if self.__receive_mode:\n            # raise value error and exit\n            self.__terminate = True\n            raise ValueError(\n                \"[NetGear:ERROR] :: `send()` function cannot be used while receive_mode is enabled. Kindly refer vidgear docs!\"\n            )\n\n        if not (message is None) and isinstance(message, np.ndarray):\n            logger.warning(\n                \"Skipped unsupported `message` of datatype: {}!\".format(\n                    type(message).__name__\n                )\n            )\n            message = None\n\n        # define exit_flag and assign value\n        exit_flag = True if (frame is None or self.__terminate) else False\n\n        # check whether exit_flag is False\n        if not (exit_flag) and not (frame.flags[\"C_CONTIGUOUS\"]):\n            # check whether the incoming frame is contiguous\n            frame = np.ascontiguousarray(frame, dtype=frame.dtype)\n\n        # handle JPEG compression encoding\n        if self.__jpeg_compression:\n            if self.__jpeg_compression_colorspace == \"GRAY\":\n                if frame.ndim == 2:\n                    # patch for https://gitlab.com/jfolz/simplejpeg/-/issues/11\n                    frame = np.expand_dims(frame, axis=2)\n                frame = simplejpeg.encode_jpeg(\n                    frame,\n                    quality=self.__jpeg_compression_quality,\n                    colorspace=self.__jpeg_compression_colorspace,\n                    fastdct=self.__jpeg_compression_fastdct,\n                )\n            else:\n                frame = simplejpeg.encode_jpeg(\n                    frame,\n                    quality=self.__jpeg_compression_quality,\n                    colorspace=self.__jpeg_compression_colorspace,\n                    colorsubsampling=\"422\",\n                    fastdct=self.__jpeg_compression_fastdct,\n                )\n\n        # check if multiserver_mode is activated and assign values with unique port\n        msg_dict = dict(port=self.__port) if self.__multiserver_mode else dict()\n\n        # prepare the exclusive json dict\n        msg_dict.update(\n            dict(\n                terminate_flag=exit_flag,\n                compression=(\n                    {\n                        \"dct\": self.__jpeg_compression_fastdct,\n                        \"ups\": self.__jpeg_compression_fastupsample,\n                        \"colorspace\": self.__jpeg_compression_colorspace,\n                    }\n                    if self.__jpeg_compression\n                    else False\n                ),\n                message=message,\n                pattern=str(self.__pattern),\n                dtype=str(frame.dtype) if not (self.__jpeg_compression) else \"\",\n                shape=frame.shape if not (self.__jpeg_compression) else \"\",\n            )\n        )\n\n        # send the json dict\n        self.__msg_socket.send_json(msg_dict, self.__msg_flag | zmq.SNDMORE)\n        # send the frame array with correct flags\n        self.__msg_socket.send(\n            frame, flags=self.__msg_flag, copy=self.__msg_copy, track=self.__msg_track\n        )\n\n        # check if synchronous patterns, then wait for confirmation\n        if self.__pattern < 2:\n            # check if Bidirectional data transmission is enabled\n            if self.__bi_mode or self.__multiclient_mode:\n                # handles return data\n                recvd_data = None\n\n                socks = dict(self.__poll.poll(self.__request_timeout))\n                if socks.get(self.__msg_socket) == zmq.POLLIN:\n                    # handle return data\n                    recv_json = self.__msg_socket.recv_json(flags=self.__msg_flag)\n                else:\n                    logger.critical(\"No response from Client, Reconnecting again...\")\n                    # Socket is confused. Close and remove it.\n                    self.__msg_socket.setsockopt(zmq.LINGER, 0)\n                    self.__msg_socket.close()\n                    self.__poll.unregister(self.__msg_socket)\n                    self.__max_retries -= 1\n\n                    if not (self.__max_retries):\n                        if self.__multiclient_mode:\n                            logger.error(\n                                \"All Clients failed to respond on multiple attempts.\"\n                            )\n                        else:\n                            logger.error(\n                                \"Client failed to respond on multiple attempts.\"\n                            )\n                        self.__terminate = True\n                        raise RuntimeError(\n                            \"[NetGear:ERROR] :: Client(s) seems to be offline, Abandoning.\"\n                        )\n\n                    # Create new connection\n                    self.__msg_socket = self.__msg_context.socket(self.__msg_pattern)\n                    if isinstance(self.__connection_address, list):\n                        for _connection in self.__connection_address:\n                            self.__msg_socket.connect(_connection)\n                    else:\n                        # handle SSH tunneling if enabled\n                        if self.__ssh_tunnel_mode:\n                            # establish tunnel connection\n                            ssh.tunnel_connection(\n                                self.__msg_socket,\n                                self.__connection_address,\n                                self.__ssh_tunnel_mode,\n                                keyfile=self.__ssh_tunnel_keyfile,\n                                password=self.__ssh_tunnel_pwd,\n                                paramiko=self.__paramiko_present,\n                            )\n                        else:\n                            # connect normally\n                            self.__msg_socket.connect(self.__connection_address)\n                    self.__poll.register(self.__msg_socket, zmq.POLLIN)\n                    # return None for mean-time\n                    return None\n\n                # save the unique port addresses\n                if (\n                    self.__multiclient_mode\n                    and not recv_json[\"port\"] in self.__port_buffer\n                ):\n                    self.__port_buffer.append(recv_json[\"port\"])\n\n                if recv_json[\"return_type\"] == \"ndarray\":\n                    recv_array = self.__msg_socket.recv(\n                        flags=self.__msg_flag,\n                        copy=self.__msg_copy,\n                        track=self.__msg_track,\n                    )\n                    # check if encoding was enabled\n                    if recv_json[\"compression\"]:\n                        # decode JPEG frame\n                        recvd_data = simplejpeg.decode_jpeg(\n                            recv_array,\n                            colorspace=recv_json[\"compression\"][\"colorspace\"],\n                            fastdct=self.__jpeg_compression_fastdct\n                            or recv_json[\"compression\"][\"dct\"],\n                            fastupsample=self.__jpeg_compression_fastupsample\n                            or recv_json[\"compression\"][\"ups\"],\n                        )\n                        # check if valid frame returned\n                        if recvd_data is None:\n                            self.__terminate = True\n                            # otherwise raise error and exit\n                            raise RuntimeError(\n                                \"[NetGear:ERROR] :: Received compressed frame `{}` decoding failed with flag: {}.\".format(\n                                    recv_json[\"compression\"],\n                                    self.__ex_compression_params,\n                                )\n                            )\n\n                        if (\n                            recv_json[\"compression\"][\"colorspace\"] == \"GRAY\"\n                            and recvd_data.ndim == 3\n                        ):\n                            # patch for https://gitlab.com/jfolz/simplejpeg/-/issues/11\n                            recvd_data = np.squeeze(recvd_data, axis=2)\n                    else:\n                        recvd_data = np.frombuffer(\n                            recv_array, dtype=recv_json[\"array_dtype\"]\n                        ).reshape(recv_json[\"array_shape\"])\n                else:\n                    recvd_data = recv_json[\"data\"]\n\n                return (\n                    (recv_json[\"port\"], recvd_data)\n                    if self.__multiclient_mode\n                    else recvd_data\n                )\n            else:\n                # otherwise log normally\n                socks = dict(self.__poll.poll(self.__request_timeout))\n                if socks.get(self.__msg_socket) == zmq.POLLIN:\n                    recv_confirmation = self.__msg_socket.recv()\n                else:\n                    logger.critical(\"No response from Client, Reconnecting again...\")\n                    # Socket is confused. Close and remove it.\n                    self.__msg_socket.setsockopt(zmq.LINGER, 0)\n                    self.__msg_socket.close()\n                    self.__poll.unregister(self.__msg_socket)\n                    self.__max_retries -= 1\n\n                    if not (self.__max_retries):\n                        logger.error(\"Client failed to respond on repeated attempts.\")\n                        self.__terminate = True\n                        raise RuntimeError(\n                            \"[NetGear:ERROR] :: Client seems to be offline, Abandoning!\"\n                        )\n\n                    # Create new connection\n                    self.__msg_socket = self.__msg_context.socket(self.__msg_pattern)\n                    # handle SSH tunneling if enabled\n                    if self.__ssh_tunnel_mode:\n                        # establish tunnel connection\n                        ssh.tunnel_connection(\n                            self.__msg_socket,\n                            self.__connection_address,\n                            self.__ssh_tunnel_mode,\n                            keyfile=self.__ssh_tunnel_keyfile,\n                            password=self.__ssh_tunnel_pwd,\n                            paramiko=self.__paramiko_present,\n                        )\n                    else:\n                        # connect normally\n                        self.__msg_socket.connect(self.__connection_address)\n                    self.__poll.register(self.__msg_socket, zmq.POLLIN)\n                    return None\n\n                # log confirmation\n                self.__logging and logger.debug(recv_confirmation)\n\n    def close(self, kill=False):\n        \"\"\"\n        Safely terminates the threads, and NetGear resources.\n\n        Parameters:\n            kill (bool): Kills ZMQ context instead of graceful exiting in receive mode.\n        \"\"\"\n        # log it\n        self.__logging and logger.debug(\n            \"Terminating various {} Processes.\".format(\n                \"Receive Mode\" if self.__receive_mode else \"Send Mode\"\n            )\n        )\n        #  whether `receive_mode` is enabled or not\n        if self.__receive_mode:\n            # check whether queue mode is empty\n            if not (self.__queue is None) and self.__queue:\n                self.__queue.clear()\n            # call immediate termination\n            self.__terminate = True\n            # properly close the socket\n            self.__logging and logger.debug(\"Terminating. Please wait...\")\n            # Handle Secure Mode Thread\n            if self.__z_auth:\n                self.__logging and logger.debug(\"Terminating Authenticator Thread.\")\n                self.__z_auth.stop()\n                while self.__z_auth.is_alive():\n                    pass\n            # wait until stream resources are released\n            # (producer thread might be still grabbing frame)\n            if self.__thread is not None:\n                self.__logging and logger.debug(\"Terminating Main Thread.\")\n                # properly handle thread exit\n                if self.__thread.is_alive() and kill:\n                    # force close if still alive\n                    logger.warning(\"Thread still running...Killing it forcefully!\")\n                    self.__msg_context.destroy()\n                    self.__thread.join()\n                else:\n                    self.__msg_socket.close(linger=0)\n                    self.__thread.join()\n                self.__thread = None\n            self.__logging and logger.debug(\"Terminated Successfully!\")\n        else:\n            # indicate that process should be terminated\n            self.__terminate = True\n            # log if kill enabled\n            kill and logger.warning(\n                \"`kill` parmeter is only available in the receive mode.\"\n            )\n            # Handle Secure Mode Thread\n            if self.__z_auth:\n                self.__logging and logger.debug(\"Terminating Authenticator Thread.\")\n                self.__z_auth.stop()\n                while self.__z_auth.is_alive():\n                    pass\n            # check if all attempts of reconnecting failed, then skip to closure\n            if (self.__pattern < 2 and not self.__max_retries) or (\n                self.__multiclient_mode and not self.__port_buffer\n            ):\n                try:\n                    # properly close the socket\n                    self.__msg_socket.setsockopt(zmq.LINGER, 0)\n                    self.__msg_socket.close()\n                except ZMQError:\n                    pass\n                finally:\n                    # exit\n                    return\n\n            if self.__multiserver_mode:\n                # check if multiserver_mode\n                # send termination flag to client with its unique port\n                term_dict = dict(terminate_flag=True, port=self.__port)\n            else:\n                # otherwise send termination flag to client\n                term_dict = dict(terminate_flag=True)\n\n            try:\n                if self.__multiclient_mode:\n                    for _ in self.__port_buffer:\n                        self.__msg_socket.send_json(term_dict)\n                else:\n                    self.__msg_socket.send_json(term_dict)\n\n                # check for confirmation if available within 1/5 timeout\n                if self.__pattern < 2:\n                    self.__logging and logger.debug(\"Terminating. Please wait...\")\n                    if self.__msg_socket.poll(self.__request_timeout // 5, zmq.POLLIN):\n                        self.__msg_socket.recv()\n            except Exception as e:\n                if not isinstance(e, ZMQError):\n                    logger.exception(str(e))\n            finally:\n                # properly close the socket\n                self.__msg_socket.setsockopt(zmq.LINGER, 0)\n                self.__msg_socket.close()\n                self.__logging and logger.debug(\"Terminated Successfully!\")\n

"},{"location":"bonus/reference/netgear/#vidgear.gears.netgear.NetGear.__init__","title":"__init__(self, address=None, port=None, protocol=None, pattern=0, receive_mode=False, logging=False, **options) special","text":"

This constructor method initializes the object state and attributes of the NetGear class.

Parameters:

Name Type Description Default address str

sets the valid network address of the Server/Client.

None port str

sets the valid Network Port of the Server/Client.

None protocol str

sets the valid messaging protocol between Server/Client.

None pattern int

sets the supported messaging pattern(flow of communication) between Server/Client

0 receive_mode bool

select the Netgear's Mode of operation.

False logging bool

enables/disables logging.

False options dict

provides the flexibility to alter various NetGear internal properties.

{} Source code in vidgear/gears/netgear.py
def __init__(\n    self,\n    address=None,\n    port=None,\n    protocol=None,\n    pattern=0,\n    receive_mode=False,\n    logging=False,\n    **options\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the NetGear class.\n\n    Parameters:\n        address (str): sets the valid network address of the Server/Client.\n        port (str): sets the valid Network Port of the Server/Client.\n        protocol (str): sets the valid messaging protocol between Server/Client.\n        pattern (int): sets the supported messaging pattern(flow of communication) between Server/Client\n        receive_mode (bool): select the Netgear's Mode of operation.\n        logging (bool): enables/disables logging.\n        options (dict): provides the flexibility to alter various NetGear internal properties.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # raise error(s) for critical Class imports\n    import_dependency_safe(\n        \"zmq\" if zmq is None else \"\", min_version=\"4.0\", pkg_name=\"pyzmq\"\n    )\n    import_dependency_safe(\n        \"simplejpeg\" if simplejpeg is None else \"\", error=\"log\", min_version=\"1.6.1\"\n    )\n\n    # define valid messaging patterns => `0`: zmq.PAIR, `1`:(zmq.REQ,zmq.REP), and `1`:(zmq.SUB,zmq.PUB)\n    valid_messaging_patterns = {\n        0: (zmq.PAIR, zmq.PAIR),\n        1: (zmq.REQ, zmq.REP),\n        2: (zmq.PUB, zmq.SUB),\n    }\n\n    # Handle messaging pattern\n    msg_pattern = None\n    # check whether user-defined messaging pattern is valid\n    if isinstance(pattern, int) and pattern in valid_messaging_patterns.keys():\n        # assign value\n        msg_pattern = valid_messaging_patterns[pattern]\n    else:\n        # otherwise default to 0:`zmq.PAIR`\n        pattern = 0\n        msg_pattern = valid_messaging_patterns[pattern]\n        self.__logging and logger.warning(\n            \"Wrong pattern value, Defaulting to `zmq.PAIR`! Kindly refer Docs for more Information.\"\n        )\n    # assign pattern to global parameter for further use\n    self.__pattern = pattern\n\n    # Handle messaging protocol\n    if protocol is None or not (protocol in [\"tcp\", \"ipc\"]):\n        # else default to `tcp` protocol\n        protocol = \"tcp\"\n        # log it\n        self.__logging and logger.warning(\n            \"Protocol is not supported or not provided. Defaulting to `tcp` protocol!\"\n        )\n\n    # Handle connection params\n\n    self.__msg_flag = 0  # handles connection flags\n    self.__msg_copy = False  # handles whether to copy data\n    self.__msg_track = False  # handles whether to track packets\n\n    # Handle NetGear's internal exclusive modes and params\n\n    # define Secure Mode\n    self.__z_auth = None\n\n    # define SSH Tunneling Mode\n    self.__ssh_tunnel_mode = None  # handles ssh_tunneling mode state\n    self.__ssh_tunnel_pwd = None\n    self.__ssh_tunnel_keyfile = None\n    self.__paramiko_present = False if paramiko is None else True\n\n    # define Multi-Server mode\n    self.__multiserver_mode = False  # handles multi-server mode state\n\n    # define Multi-Client mode\n    self.__multiclient_mode = False  # handles multi-client mode state\n\n    # define Bidirectional mode\n    self.__bi_mode = False  # handles Bidirectional mode state\n\n    # define Secure mode\n    valid_security_mech = {0: \"Grasslands\", 1: \"StoneHouse\", 2: \"IronHouse\"}\n    self.__secure_mode = 0  # handles ZMQ security layer status\n    auth_cert_dir = \"\"  # handles valid ZMQ certificates dir\n    self.__auth_publickeys_dir = \"\"  # handles valid ZMQ public certificates dir\n    self.__auth_secretkeys_dir = \"\"  # handles valid ZMQ private certificates dir\n    overwrite_cert = False  # checks if certificates overwriting allowed\n    custom_cert_location = \"\"  # handles custom ZMQ certificates path\n\n    # define frame-compression handler\n    self.__jpeg_compression = (\n        True if not (simplejpeg is None) else False\n    )  # enabled by default for all connections if simplejpeg is installed\n    self.__jpeg_compression_quality = 90  # 90% quality\n    self.__jpeg_compression_fastdct = True  # fastest DCT on by default\n    self.__jpeg_compression_fastupsample = False  # fastupsample off by default\n    self.__jpeg_compression_colorspace = \"BGR\"  # use BGR colorspace by default\n\n    # defines frame compression on return data\n    self.__ex_compression_params = None\n\n    # define receiver return data handler\n    self.__return_data = None\n\n    # generate 8-digit random system id\n    self.__id = \"\".join(\n        secrets.choice(string.ascii_uppercase + string.digits) for i in range(8)\n    )\n\n    # define termination flag\n    self.__terminate = False\n\n    # additional settings for reliability\n    if pattern < 2:\n        # define zmq poller for reliable transmission\n        self.__poll = zmq.Poller()\n        # define max retries\n        self.__max_retries = 3\n        # request timeout\n        self.__request_timeout = 4000  # 4 secs\n    else:\n        # subscriber timeout\n        self.__subscriber_timeout = None\n\n    # Handle user-defined options dictionary values\n    # reformat dictionary\n    options = {str(k).strip(): v for k, v in options.items()}\n\n    # loop over dictionary key & values and assign to global variables if valid\n    for key, value in options.items():\n        # handle multi-server mode\n        if key == \"multiserver_mode\" and isinstance(value, bool):\n            # check if valid pattern assigned\n            if pattern > 0:\n                # activate Multi-server mode\n                self.__multiserver_mode = value\n            else:\n                # otherwise disable it and raise error\n                self.__multiserver_mode = False\n                logger.critical(\"Multi-Server Mode is disabled!\")\n                raise ValueError(\n                    \"[NetGear:ERROR] :: `{}` pattern is not valid when Multi-Server Mode is enabled. Kindly refer Docs for more Information.\".format(\n                        pattern\n                    )\n                )\n\n        # handle multi-client mode\n        elif key == \"multiclient_mode\" and isinstance(value, bool):\n            # check if valid pattern assigned\n            if pattern > 0:\n                # activate Multi-client mode\n                self.__multiclient_mode = value\n            else:\n                # otherwise disable it and raise error\n                self.__multiclient_mode = False\n                logger.critical(\"Multi-Client Mode is disabled!\")\n                raise ValueError(\n                    \"[NetGear:ERROR] :: `{}` pattern is not valid when Multi-Client Mode is enabled. Kindly refer Docs for more Information.\".format(\n                        pattern\n                    )\n                )\n\n        # handle bidirectional mode\n        elif key == \"bidirectional_mode\" and isinstance(value, bool):\n            # check if pattern is valid\n            if pattern < 2:\n                # activate Bidirectional mode if specified\n                self.__bi_mode = value\n            else:\n                # otherwise disable it and raise error\n                self.__bi_mode = False\n                logger.warning(\"Bidirectional data transmission is disabled!\")\n                raise ValueError(\n                    \"[NetGear:ERROR] :: `{}` pattern is not valid when Bidirectional Mode is enabled. Kindly refer Docs for more Information!\".format(\n                        pattern\n                    )\n                )\n\n        # handle secure mode\n        elif (\n            key == \"secure_mode\"\n            and isinstance(value, int)\n            and (value in valid_security_mech)\n        ):\n            self.__secure_mode = value\n\n        elif key == \"custom_cert_location\" and isinstance(value, str):\n            # verify custom auth certificates path for secure mode\n            custom_cert_location = os.path.abspath(value)\n            assert os.path.isdir(\n                custom_cert_location\n            ), \"[NetGear:ERROR] :: `custom_cert_location` value must be the path to a valid directory!\"\n            assert check_WriteAccess(\n                custom_cert_location,\n                is_windows=True if os.name == \"nt\" else False,\n                logging=self.__logging,\n            ), \"[NetGear:ERROR] :: Permission Denied!, cannot write ZMQ authentication certificates to '{}' directory!\".format(\n                value\n            )\n        elif key == \"overwrite_cert\" and isinstance(value, bool):\n            # enable/disable auth certificate overwriting in secure mode\n            overwrite_cert = value\n\n        # handle ssh-tunneling mode\n        elif key == \"ssh_tunnel_mode\" and isinstance(value, str):\n            # enable SSH Tunneling Mode\n            self.__ssh_tunnel_mode = value.strip()\n        elif key == \"ssh_tunnel_pwd\" and isinstance(value, str):\n            # add valid SSH Tunneling password\n            self.__ssh_tunnel_pwd = value\n        elif key == \"ssh_tunnel_keyfile\" and isinstance(value, str):\n            # add valid SSH Tunneling key-file\n            self.__ssh_tunnel_keyfile = value if os.path.isfile(value) else None\n            if self.__ssh_tunnel_keyfile is None:\n                logger.warning(\n                    \"Discarded invalid or non-existential SSH Tunnel Key-file at {}!\".format(\n                        value\n                    )\n                )\n\n        # handle jpeg compression\n        elif (\n            key == \"jpeg_compression\"\n            and not (simplejpeg is None)\n            and isinstance(value, (bool, str))\n        ):\n            if isinstance(value, str) and value.strip().upper() in [\n                \"RGB\",\n                \"BGR\",\n                \"RGBX\",\n                \"BGRX\",\n                \"XBGR\",\n                \"XRGB\",\n                \"GRAY\",\n                \"RGBA\",\n                \"BGRA\",\n                \"ABGR\",\n                \"ARGB\",\n                \"CMYK\",\n            ]:\n                # set encoding colorspace\n                self.__jpeg_compression_colorspace = value.strip().upper()\n                # enable frame-compression encoding value\n                self.__jpeg_compression = True\n            else:\n                # enable frame-compression encoding value\n                self.__jpeg_compression = value\n        elif key == \"jpeg_compression_quality\" and isinstance(value, (int, float)):\n            # set valid jpeg quality\n            if value >= 10 and value <= 100:\n                self.__jpeg_compression_quality = int(value)\n            else:\n                logger.warning(\"Skipped invalid `jpeg_compression_quality` value!\")\n        elif key == \"jpeg_compression_fastdct\" and isinstance(value, bool):\n            # enable jpeg fastdct\n            self.__jpeg_compression_fastdct = value\n        elif key == \"jpeg_compression_fastupsample\" and isinstance(value, bool):\n            # enable jpeg  fastupsample\n            self.__jpeg_compression_fastupsample = value\n\n        # assign maximum retries in synchronous patterns\n        elif key == \"max_retries\" and isinstance(value, int) and pattern < 2:\n            if value >= 0:\n                self.__max_retries = value\n            else:\n                logger.warning(\"Invalid `max_retries` value skipped!\")\n\n        # assign request timeout in synchronous patterns\n        elif key == \"request_timeout\" and isinstance(value, int) and pattern < 2:\n            if value >= 4:\n                self.__request_timeout = value * 1000  # covert to milliseconds\n            else:\n                logger.warning(\"Invalid `request_timeout` value skipped!\")\n\n        # assign subscriber timeout\n        elif (\n            key == \"subscriber_timeout\" and isinstance(value, int) and pattern == 2\n        ):\n            if value > 0:\n                self.__subscriber_timeout = value * 1000  # covert to milliseconds\n            else:\n                logger.warning(\"Invalid `request_timeout` value skipped!\")\n\n        # handle ZMQ flags\n        elif key == \"flag\" and isinstance(value, int):\n            self.__msg_flag = value\n            self.__msg_flag and logger.warning(\n                \"The flag optional value is set to `1` (NOBLOCK) for this run. This might cause NetGear to not terminate gracefully.\"\n            )\n        elif key == \"copy\" and isinstance(value, bool):\n            self.__msg_copy = value\n        elif key == \"track\" and isinstance(value, bool):\n            self.__msg_track = value\n            self.__msg_copy and self.__msg_track and logger.info(\n                \"The `track` optional value will be ignored for this run because `copy=True` is also defined.\"\n            )\n        else:\n            pass\n\n    # Handle ssh tunneling if enabled\n    if not (self.__ssh_tunnel_mode is None):\n        # SSH Tunnel Mode only available for server mode\n        if receive_mode:\n            logger.error(\"SSH Tunneling cannot be enabled for Client-end!\")\n        else:\n            # check if SSH tunneling possible\n            ssh_address = self.__ssh_tunnel_mode\n            ssh_address, ssh_port = (\n                ssh_address.split(\":\")\n                if \":\" in ssh_address\n                else [ssh_address, \"22\"]\n            )  # default to port 22\n            if \"47\" in ssh_port:\n                self.__ssh_tunnel_mode = self.__ssh_tunnel_mode.replace(\n                    \":47\", \"\"\n                )  # port-47 is reserved for testing\n            else:\n                # extract ip for validation\n                ssh_user, ssh_ip = (\n                    ssh_address.split(\"@\")\n                    if \"@\" in ssh_address\n                    else [\"\", ssh_address]\n                )\n                # validate ip specified port\n                assert check_open_port(\n                    ssh_ip, port=int(ssh_port)\n                ), \"[NetGear:ERROR] :: Host `{}` is not available for SSH Tunneling at port-{}!\".format(\n                    ssh_address, ssh_port\n                )\n\n    # Handle multiple exclusive modes if enabled\n    if self.__multiclient_mode and self.__multiserver_mode:\n        raise ValueError(\n            \"[NetGear:ERROR] :: Multi-Client and Multi-Server Mode cannot be enabled simultaneously!\"\n        )\n    elif self.__multiserver_mode or self.__multiclient_mode:\n        # check if Bidirectional Mode also enabled\n        if self.__bi_mode:\n            # log it\n            self.__logging and logger.debug(\n                \"Bidirectional Data Transmission is also enabled for this connection!\"\n            )\n        # check if SSH Tunneling Mode also enabled\n        if self.__ssh_tunnel_mode:\n            # raise error\n            raise ValueError(\n                \"[NetGear:ERROR] :: SSH Tunneling and {} Mode cannot be enabled simultaneously. Kindly refer docs!\".format(\n                    \"Multi-Server\" if self.__multiserver_mode else \"Multi-Client\"\n                )\n            )\n    elif self.__bi_mode:\n        # log Bidirectional mode activation\n        self.__logging and logger.debug(\n            \"Bidirectional Data Transmission is enabled for this connection!\"\n        )\n    elif self.__ssh_tunnel_mode:\n        # log Bidirectional mode activation\n        self.__logging and logger.debug(\n            \"SSH Tunneling is enabled for host:`{}` with `{}` back-end.\".format(\n                self.__ssh_tunnel_mode,\n                \"paramiko\" if self.__paramiko_present else \"pexpect\",\n            )\n        )\n\n    # On Windows, NetGear requires the ``WindowsSelectorEventLoop`` but Python 3.8 and above,\n    # defaults to an ``ProactorEventLoop`` loop that is not compatible with it. Thereby,\n    # we had to set it manually.\n    platform.system() == \"Windows\" and asyncio.set_event_loop_policy(\n        asyncio.WindowsSelectorEventLoopPolicy()\n    )\n\n    # define ZMQ messaging context instance\n    self.__msg_context = zmq.Context.instance()\n\n    # initialize and assign receive mode to global variable\n    self.__receive_mode = receive_mode\n\n    # Handle Secure mode\n    if self.__secure_mode > 0:\n        # activate and log if overwriting is enabled\n        if receive_mode:\n            overwrite_cert = False\n            overwrite_cert and logger.warning(\n                \"Overwriting ZMQ Authentication certificates is disabled for Client's end!\"\n            )\n        else:\n            overwrite_cert and self.__logging and logger.info(\n                \"Overwriting ZMQ Authentication certificates over previous ones!\"\n            )\n\n        # Validate certificate generation paths\n        # Start threaded authenticator for this context\n        try:\n            # check if custom certificates path is specified\n            if custom_cert_location:\n                (\n                    auth_cert_dir,\n                    self.__auth_secretkeys_dir,\n                    self.__auth_publickeys_dir,\n                ) = generate_auth_certificates(\n                    custom_cert_location, overwrite=overwrite_cert, logging=logging\n                )\n            else:\n                # otherwise auto-generate suitable path\n                (\n                    auth_cert_dir,\n                    self.__auth_secretkeys_dir,\n                    self.__auth_publickeys_dir,\n                ) = generate_auth_certificates(\n                    os.path.join(expanduser(\"~\"), \".vidgear\"),\n                    overwrite=overwrite_cert,\n                    logging=logging,\n                )\n            # log it\n            self.__logging and logger.debug(\n                \"`{}` is the default location for storing ZMQ authentication certificates/keys.\".format(\n                    auth_cert_dir\n                )\n            )\n\n            # start an authenticator for this context\n            self.__z_auth = ThreadAuthenticator(self.__msg_context)\n            self.__z_auth.start()\n            self.__z_auth.allow(str(address))  # allow current address\n\n            # check if `IronHouse` is activated\n            if self.__secure_mode == 2:\n                # tell authenticator to use the certificate from given valid dir\n                self.__z_auth.configure_curve(\n                    domain=\"*\", location=self.__auth_publickeys_dir\n                )\n            else:\n                # otherwise tell the authenticator how to handle the CURVE requests, if `StoneHouse` is activated\n                self.__z_auth.configure_curve(\n                    domain=\"*\", location=auth.CURVE_ALLOW_ANY\n                )\n        except zmq.ZMQError as e:\n            if \"Address in use\" in str(e):\n                logger.info(\"ZMQ Authenticator already running.\")\n            else:\n                # catch if any error occurred and disable Secure mode\n                logger.exception(str(e))\n                self.__secure_mode = 0\n                logger.error(\n                    \"ZMQ Security Mechanism is disabled for this connection due to errors!\"\n                )\n\n    # check whether `receive_mode` is enabled\n    if self.__receive_mode:\n        # define connection address\n        address = \"*\" if address is None else address\n\n        # check if multiserver_mode is enabled\n        if self.__multiserver_mode:\n            # check if unique server port address list/tuple is assigned or not in multiserver_mode\n            if port is None or not isinstance(port, (tuple, list)):\n                # raise error if not\n                raise ValueError(\n                    \"[NetGear:ERROR] :: Incorrect port value! Kindly provide a list/tuple of Server ports while Multi-Server mode is enabled. For more information refer VidGear docs.\"\n                )\n            else:\n                # otherwise log it\n                logger.debug(\n                    \"Enabling Multi-Server Mode at PORTS: {}!\".format(port)\n                )\n            # create port address buffer for keeping track of connected client's port(s)\n            self.__port_buffer = []\n        # check if multiclient_mode is enabled\n        elif self.__multiclient_mode:\n            # check if unique server port address is assigned or not in multiclient_mode\n            if port is None:\n                # raise error if not\n                raise ValueError(\n                    \"[NetGear:ERROR] :: Kindly provide a unique & valid port value at Client-end. For more information refer VidGear docs.\"\n                )\n            else:\n                # otherwise log it\n                logger.debug(\n                    \"Enabling Multi-Client Mode at PORT: {} on this device!\".format(\n                        port\n                    )\n                )\n            # assign value to global variable\n            self.__port = port\n        else:\n            # otherwise assign local port address if None\n            port = \"5555\" if port is None else port\n\n        try:\n            # define thread-safe messaging socket\n            self.__msg_socket = self.__msg_context.socket(msg_pattern[1])\n\n            # define pub-sub flag\n            self.__pattern == 2 and self.__msg_socket.set_hwm(1)\n\n            # enable specified secure mode for the socket\n            if self.__secure_mode > 0:\n                # load server key\n                server_secret_file = os.path.join(\n                    self.__auth_secretkeys_dir, \"server.key_secret\"\n                )\n                server_public, server_secret = auth.load_certificate(\n                    server_secret_file\n                )\n                # load  all CURVE keys\n                self.__msg_socket.curve_secretkey = server_secret\n                self.__msg_socket.curve_publickey = server_public\n                # enable CURVE connection for this socket\n                self.__msg_socket.curve_server = True\n\n            # define exclusive socket options for `patterns=2`\n            if self.__pattern == 2:\n                self.__msg_socket.setsockopt_string(zmq.SUBSCRIBE, \"\")\n                self.__subscriber_timeout and self.__msg_socket.setsockopt(\n                    zmq.RCVTIMEO, self.__subscriber_timeout\n                )\n                self.__subscriber_timeout and self.__msg_socket.setsockopt(\n                    zmq.LINGER, 0\n                )\n\n            # if multiserver_mode is enabled, then assign port addresses to zmq socket\n            if self.__multiserver_mode:\n                # bind socket to given server protocol, address and ports\n                for pt in port:\n                    self.__msg_socket.bind(\n                        protocol + \"://\" + str(address) + \":\" + str(pt)\n                    )\n            else:\n                # bind socket to given protocol, address and port normally\n                self.__msg_socket.bind(\n                    protocol + \"://\" + str(address) + \":\" + str(port)\n                )\n\n            # additional settings\n            if pattern < 2:\n                if self.__multiserver_mode:\n                    self.__connection_address = []\n                    for pt in port:\n                        self.__connection_address.append(\n                            protocol + \"://\" + str(address) + \":\" + str(pt)\n                        )\n                else:\n                    self.__connection_address = (\n                        protocol + \"://\" + str(address) + \":\" + str(port)\n                    )\n                self.__msg_pattern = msg_pattern[1]\n                self.__poll.register(self.__msg_socket, zmq.POLLIN)\n                self.__logging and logger.debug(\n                    \"Reliable transmission is enabled for this pattern with max-retries: {} and timeout: {} secs.\".format(\n                        self.__max_retries, self.__request_timeout / 1000\n                    )\n                )\n            else:\n                self.__logging and self.__subscriber_timeout and logger.debug(\n                    \"Timeout: {} secs is enabled for this system.\".format(\n                        self.__subscriber_timeout / 1000\n                    )\n                )\n\n        except Exception as e:\n            # otherwise log and raise error\n            logger.exception(str(e))\n            # Handle Secure Mode\n            self.__secure_mode and logger.critical(\n                \"Failed to activate Secure Mode: `{}` for this connection!\".format(\n                    valid_security_mech[self.__secure_mode]\n                )\n            )\n            # raise errors for exclusive modes\n            if self.__multiserver_mode or self.__multiclient_mode:\n                raise RuntimeError(\n                    \"[NetGear:ERROR] :: Receive Mode failed to activate {} Mode at address: {} with pattern: {}! Kindly recheck all parameters.\".format(\n                        (\n                            \"Multi-Server\"\n                            if self.__multiserver_mode\n                            else \"Multi-Client\"\n                        ),\n                        (protocol + \"://\" + str(address) + \":\" + str(port)),\n                        pattern,\n                    )\n                )\n            else:\n                self.__bi_mode and logger.critical(\n                    \"Failed to activate Bidirectional Mode for this connection!\"\n                )\n                raise RuntimeError(\n                    \"[NetGear:ERROR] :: Receive Mode failed to bind address: {} and pattern: {}! Kindly recheck all parameters.\".format(\n                        (protocol + \"://\" + str(address) + \":\" + str(port)), pattern\n                    )\n                )\n\n        # Handle threaded queue mode\n        self.__logging and logger.debug(\n            \"Threaded Queue Mode is enabled by default for this connection.\"\n        )\n\n        # define deque and assign it to global var\n        self.__queue = deque(maxlen=96)  # max len 96 to check overflow\n\n        # initialize and start threaded recv_handler\n        self.__thread = Thread(target=self.__recv_handler, name=\"NetGear\", args=())\n        self.__thread.daemon = True\n        self.__thread.start()\n\n        if self.__logging:\n            # finally log progress\n            logger.debug(\n                \"Successfully Binded to address: {} with pattern: {}.\".format(\n                    (protocol + \"://\" + str(address) + \":\" + str(port)), pattern\n                )\n            )\n            self.__jpeg_compression and logger.debug(\n                \"JPEG Frame-Compression is activated for this connection with Colorspace:`{}`, Quality:`{}`%, Fastdct:`{}`, and Fastupsample:`{}`.\".format(\n                    self.__jpeg_compression_colorspace,\n                    self.__jpeg_compression_quality,\n                    (\"enabled\" if self.__jpeg_compression_fastdct else \"disabled\"),\n                    (\n                        \"enabled\"\n                        if self.__jpeg_compression_fastupsample\n                        else \"disabled\"\n                    ),\n                )\n            )\n            self.__secure_mode and logger.debug(\n                \"Successfully enabled ZMQ Security Mechanism: `{}` for this connection.\".format(\n                    valid_security_mech[self.__secure_mode]\n                )\n            )\n            logger.debug(\"Multi-threaded Receive Mode is successfully enabled.\")\n            logger.debug(\"Unique System ID is {}.\".format(self.__id))\n            logger.debug(\"Receive Mode is now activated.\")\n\n    else:\n        # otherwise default to `Send Mode`\n        # define connection address\n        address = \"localhost\" if address is None else address\n\n        # check if multiserver_mode is enabled\n        if self.__multiserver_mode:\n            # check if unique server port address is assigned or not in multiserver_mode\n            if port is None:\n                # raise error if not\n                raise ValueError(\n                    \"[NetGear:ERROR] :: Kindly provide a unique & valid port value at Server-end. For more information refer VidGear docs.\"\n                )\n            else:\n                # otherwise log it\n                logger.debug(\n                    \"Enabling Multi-Server Mode at PORT: {} on this device!\".format(\n                        port\n                    )\n                )\n            # assign value to global variable\n            self.__port = port\n        # check if multiclient_mode is enabled\n        elif self.__multiclient_mode:\n            # check if unique client port address list/tuple is assigned or not in multiclient_mode\n            if port is None or not isinstance(port, (tuple, list)):\n                # raise error if not\n                raise ValueError(\n                    \"[NetGear:ERROR] :: Incorrect port value! Kindly provide a list/tuple of Client ports while Multi-Client mode is enabled. For more information refer VidGear docs.\"\n                )\n            else:\n                # otherwise log it\n                logger.debug(\n                    \"Enabling Multi-Client Mode at PORTS: {}!\".format(port)\n                )\n            # create port address buffer for keeping track of connected client ports\n            self.__port_buffer = []\n        else:\n            # otherwise assign local port address if None\n            port = \"5555\" if port is None else port\n\n        try:\n            # define thread-safe messaging socket\n            self.__msg_socket = self.__msg_context.socket(msg_pattern[0])\n\n            # if req/rep pattern, define additional flags\n            if self.__pattern == 1:\n                self.__msg_socket.REQ_RELAXED = True\n                self.__msg_socket.REQ_CORRELATE = True\n\n            # if pub/sub pattern, define additional optimizer\n            if self.__pattern == 2:\n                self.__msg_socket.set_hwm(1)\n\n            # enable specified secure mode for the socket\n            if self.__secure_mode > 0:\n                # load client key\n                client_secret_file = os.path.join(\n                    self.__auth_secretkeys_dir, \"client.key_secret\"\n                )\n                client_public, client_secret = auth.load_certificate(\n                    client_secret_file\n                )\n                # load  all CURVE keys\n                self.__msg_socket.curve_secretkey = client_secret\n                self.__msg_socket.curve_publickey = client_public\n                # load server key\n                server_public_file = os.path.join(\n                    self.__auth_publickeys_dir, \"server.key\"\n                )\n                server_public, _ = auth.load_certificate(server_public_file)\n                # inject public key to make a CURVE connection.\n                self.__msg_socket.curve_serverkey = server_public\n\n            # check if multi-client_mode is enabled\n            if self.__multiclient_mode:\n                # bind socket to given server protocol, address and ports\n                for pt in port:\n                    self.__msg_socket.connect(\n                        protocol + \"://\" + str(address) + \":\" + str(pt)\n                    )\n            else:\n                # handle SSH tunneling if enabled\n                if self.__ssh_tunnel_mode:\n                    # establish tunnel connection\n                    ssh.tunnel_connection(\n                        self.__msg_socket,\n                        protocol + \"://\" + str(address) + \":\" + str(port),\n                        self.__ssh_tunnel_mode,\n                        keyfile=self.__ssh_tunnel_keyfile,\n                        password=self.__ssh_tunnel_pwd,\n                        paramiko=self.__paramiko_present,\n                    )\n                else:\n                    # connect socket to given protocol, address and port\n                    self.__msg_socket.connect(\n                        protocol + \"://\" + str(address) + \":\" + str(port)\n                    )\n\n            # additional settings\n            if pattern < 2:\n                if self.__multiclient_mode:\n                    self.__connection_address = []\n                    for pt in port:\n                        self.__connection_address.append(\n                            protocol + \"://\" + str(address) + \":\" + str(pt)\n                        )\n                else:\n                    self.__connection_address = (\n                        protocol + \"://\" + str(address) + \":\" + str(port)\n                    )\n                self.__msg_pattern = msg_pattern[0]\n                self.__poll.register(self.__msg_socket, zmq.POLLIN)\n\n                self.__logging and logger.debug(\n                    \"Reliable transmission is enabled for this pattern with max-retries: {} and timeout: {} secs.\".format(\n                        self.__max_retries, self.__request_timeout / 1000\n                    )\n                )\n\n        except Exception as e:\n            # otherwise log and raise error\n            logger.exception(str(e))\n            # Handle Secure Mode\n            self.__secure_mode and logger.critical(\n                \"Failed to activate Secure Mode: `{}` for this connection!\".format(\n                    valid_security_mech[self.__secure_mode]\n                )\n            )\n            # raise errors for exclusive modes\n            if self.__multiserver_mode or self.__multiclient_mode:\n                raise RuntimeError(\n                    \"[NetGear:ERROR] :: Send Mode failed to activate {} Mode at address: {} with pattern: {}! Kindly recheck all parameters.\".format(\n                        (\n                            \"Multi-Server\"\n                            if self.__multiserver_mode\n                            else \"Multi-Client\"\n                        ),\n                        (protocol + \"://\" + str(address) + \":\" + str(port)),\n                        pattern,\n                    )\n                )\n            else:\n                self.__bi_mode and logger.critical(\n                    \"Failed to activate Bidirectional Mode for this connection!\"\n                )\n                self.__ssh_tunnel_mode and logger.critical(\n                    \"Failed to initiate SSH Tunneling Mode for this server with `{}` back-end!\".format(\n                        \"paramiko\" if self.__paramiko_present else \"pexpect\"\n                    )\n                )\n                raise RuntimeError(\n                    \"[NetGear:ERROR] :: Send Mode failed to connect address: {} and pattern: {}! Kindly recheck all parameters.\".format(\n                        (protocol + \"://\" + str(address) + \":\" + str(port)), pattern\n                    )\n                )\n\n        if self.__logging:\n            # finally log progress\n            logger.debug(\n                \"Successfully connected to address: {} with pattern: {}.\".format(\n                    (protocol + \"://\" + str(address) + \":\" + str(port)), pattern\n                )\n            )\n            self.__jpeg_compression and logger.debug(\n                \"JPEG Frame-Compression is activated for this connection with Colorspace:`{}`, Quality:`{}`%, Fastdct:`{}`, and Fastupsample:`{}`.\".format(\n                    self.__jpeg_compression_colorspace,\n                    self.__jpeg_compression_quality,\n                    (\"enabled\" if self.__jpeg_compression_fastdct else \"disabled\"),\n                    (\n                        \"enabled\"\n                        if self.__jpeg_compression_fastupsample\n                        else \"disabled\"\n                    ),\n                )\n            )\n            self.__secure_mode and logger.debug(\n                \"Enabled ZMQ Security Mechanism: `{}` for this connection.\".format(\n                    valid_security_mech[self.__secure_mode]\n                )\n            )\n            logger.debug(\"Unique System ID is {}.\".format(self.__id))\n            logger.debug(\n                \"Send Mode is successfully activated and ready to send data.\"\n            )\n
"},{"location":"bonus/reference/netgear/#vidgear.gears.netgear.NetGear.close","title":"close(self, kill=False)","text":"

Safely terminates the threads, and NetGear resources.

Parameters:

Name Type Description Default kill bool

Kills ZMQ context instead of graceful exiting in receive mode.

False Source code in vidgear/gears/netgear.py
def close(self, kill=False):\n    \"\"\"\n    Safely terminates the threads, and NetGear resources.\n\n    Parameters:\n        kill (bool): Kills ZMQ context instead of graceful exiting in receive mode.\n    \"\"\"\n    # log it\n    self.__logging and logger.debug(\n        \"Terminating various {} Processes.\".format(\n            \"Receive Mode\" if self.__receive_mode else \"Send Mode\"\n        )\n    )\n    #  whether `receive_mode` is enabled or not\n    if self.__receive_mode:\n        # check whether queue mode is empty\n        if not (self.__queue is None) and self.__queue:\n            self.__queue.clear()\n        # call immediate termination\n        self.__terminate = True\n        # properly close the socket\n        self.__logging and logger.debug(\"Terminating. Please wait...\")\n        # Handle Secure Mode Thread\n        if self.__z_auth:\n            self.__logging and logger.debug(\"Terminating Authenticator Thread.\")\n            self.__z_auth.stop()\n            while self.__z_auth.is_alive():\n                pass\n        # wait until stream resources are released\n        # (producer thread might be still grabbing frame)\n        if self.__thread is not None:\n            self.__logging and logger.debug(\"Terminating Main Thread.\")\n            # properly handle thread exit\n            if self.__thread.is_alive() and kill:\n                # force close if still alive\n                logger.warning(\"Thread still running...Killing it forcefully!\")\n                self.__msg_context.destroy()\n                self.__thread.join()\n            else:\n                self.__msg_socket.close(linger=0)\n                self.__thread.join()\n            self.__thread = None\n        self.__logging and logger.debug(\"Terminated Successfully!\")\n    else:\n        # indicate that process should be terminated\n        self.__terminate = True\n        # log if kill enabled\n        kill and logger.warning(\n            \"`kill` parmeter is only available in the receive mode.\"\n        )\n        # Handle Secure Mode Thread\n        if self.__z_auth:\n            self.__logging and logger.debug(\"Terminating Authenticator Thread.\")\n            self.__z_auth.stop()\n            while self.__z_auth.is_alive():\n                pass\n        # check if all attempts of reconnecting failed, then skip to closure\n        if (self.__pattern < 2 and not self.__max_retries) or (\n            self.__multiclient_mode and not self.__port_buffer\n        ):\n            try:\n                # properly close the socket\n                self.__msg_socket.setsockopt(zmq.LINGER, 0)\n                self.__msg_socket.close()\n            except ZMQError:\n                pass\n            finally:\n                # exit\n                return\n\n        if self.__multiserver_mode:\n            # check if multiserver_mode\n            # send termination flag to client with its unique port\n            term_dict = dict(terminate_flag=True, port=self.__port)\n        else:\n            # otherwise send termination flag to client\n            term_dict = dict(terminate_flag=True)\n\n        try:\n            if self.__multiclient_mode:\n                for _ in self.__port_buffer:\n                    self.__msg_socket.send_json(term_dict)\n            else:\n                self.__msg_socket.send_json(term_dict)\n\n            # check for confirmation if available within 1/5 timeout\n            if self.__pattern < 2:\n                self.__logging and logger.debug(\"Terminating. Please wait...\")\n                if self.__msg_socket.poll(self.__request_timeout // 5, zmq.POLLIN):\n                    self.__msg_socket.recv()\n        except Exception as e:\n            if not isinstance(e, ZMQError):\n                logger.exception(str(e))\n        finally:\n            # properly close the socket\n            self.__msg_socket.setsockopt(zmq.LINGER, 0)\n            self.__msg_socket.close()\n            self.__logging and logger.debug(\"Terminated Successfully!\")\n
"},{"location":"bonus/reference/netgear/#vidgear.gears.netgear.NetGear.recv","title":"recv(self, return_data=None)","text":"

A Receiver end method, that extracts received frames synchronously from monitored deque, while maintaining a fixed-length frame buffer in the memory, and blocks the thread if the deque is full.

Parameters:

Name Type Description Default return_data any

inputs return data (of any datatype), for sending back to Server.

None

Returns: A n-dimensional numpy array.

Source code in vidgear/gears/netgear.py
def recv(self, return_data=None):\n    \"\"\"\n    A Receiver end method, that extracts received frames synchronously from monitored deque, while maintaining a\n    fixed-length frame buffer in the memory, and blocks the thread if the deque is full.\n\n    Parameters:\n        return_data (any): inputs return data _(of any datatype)_, for sending back to Server.\n\n    **Returns:** A n-dimensional numpy array.\n    \"\"\"\n    # check whether `receive mode` is activated\n    if not (self.__receive_mode):\n        # raise value error and exit\n        self.__terminate = True\n        raise ValueError(\n            \"[NetGear:ERROR] :: `recv()` function cannot be used while receive_mode is disabled. Kindly refer vidgear docs!\"\n        )\n\n    # handle Bidirectional return data\n    if (self.__bi_mode or self.__multiclient_mode) and not (return_data is None):\n        self.__return_data = return_data\n\n    # check whether or not termination flag is enabled\n    while not self.__terminate:\n        try:\n            # check if queue is empty\n            if len(self.__queue) > 0:\n                return self.__queue.popleft()\n            else:\n                time.sleep(0.00001)\n                continue\n        except KeyboardInterrupt:\n            self.__terminate = True\n            break\n    # otherwise return NoneType\n    return None\n
"},{"location":"bonus/reference/netgear/#vidgear.gears.netgear.NetGear.send","title":"send(self, frame, message=None)","text":"

A Server end method, that sends the data and frames over the network to Client(s).

Parameters:

Name Type Description Default frame numpy.ndarray

inputs numpy array(frame).

required message any

input for sending additional data (of any datatype except numpy.ndarray) to Client(s).

None

Returns: Data (of any datatype) in selected exclusive modes, otherwise None-type.

Source code in vidgear/gears/netgear.py
def send(self, frame, message=None):\n    \"\"\"\n    A Server end method, that sends the data and frames over the network to Client(s).\n\n    Parameters:\n        frame (numpy.ndarray): inputs numpy array(frame).\n        message (any): input for sending additional data _(of any datatype except `numpy.ndarray`)_ to Client(s).\n\n    **Returns:** Data _(of any datatype)_ in selected exclusive modes, otherwise None-type.\n\n    \"\"\"\n    # check whether `receive_mode` is disabled\n    if self.__receive_mode:\n        # raise value error and exit\n        self.__terminate = True\n        raise ValueError(\n            \"[NetGear:ERROR] :: `send()` function cannot be used while receive_mode is enabled. Kindly refer vidgear docs!\"\n        )\n\n    if not (message is None) and isinstance(message, np.ndarray):\n        logger.warning(\n            \"Skipped unsupported `message` of datatype: {}!\".format(\n                type(message).__name__\n            )\n        )\n        message = None\n\n    # define exit_flag and assign value\n    exit_flag = True if (frame is None or self.__terminate) else False\n\n    # check whether exit_flag is False\n    if not (exit_flag) and not (frame.flags[\"C_CONTIGUOUS\"]):\n        # check whether the incoming frame is contiguous\n        frame = np.ascontiguousarray(frame, dtype=frame.dtype)\n\n    # handle JPEG compression encoding\n    if self.__jpeg_compression:\n        if self.__jpeg_compression_colorspace == \"GRAY\":\n            if frame.ndim == 2:\n                # patch for https://gitlab.com/jfolz/simplejpeg/-/issues/11\n                frame = np.expand_dims(frame, axis=2)\n            frame = simplejpeg.encode_jpeg(\n                frame,\n                quality=self.__jpeg_compression_quality,\n                colorspace=self.__jpeg_compression_colorspace,\n                fastdct=self.__jpeg_compression_fastdct,\n            )\n        else:\n            frame = simplejpeg.encode_jpeg(\n                frame,\n                quality=self.__jpeg_compression_quality,\n                colorspace=self.__jpeg_compression_colorspace,\n                colorsubsampling=\"422\",\n                fastdct=self.__jpeg_compression_fastdct,\n            )\n\n    # check if multiserver_mode is activated and assign values with unique port\n    msg_dict = dict(port=self.__port) if self.__multiserver_mode else dict()\n\n    # prepare the exclusive json dict\n    msg_dict.update(\n        dict(\n            terminate_flag=exit_flag,\n            compression=(\n                {\n                    \"dct\": self.__jpeg_compression_fastdct,\n                    \"ups\": self.__jpeg_compression_fastupsample,\n                    \"colorspace\": self.__jpeg_compression_colorspace,\n                }\n                if self.__jpeg_compression\n                else False\n            ),\n            message=message,\n            pattern=str(self.__pattern),\n            dtype=str(frame.dtype) if not (self.__jpeg_compression) else \"\",\n            shape=frame.shape if not (self.__jpeg_compression) else \"\",\n        )\n    )\n\n    # send the json dict\n    self.__msg_socket.send_json(msg_dict, self.__msg_flag | zmq.SNDMORE)\n    # send the frame array with correct flags\n    self.__msg_socket.send(\n        frame, flags=self.__msg_flag, copy=self.__msg_copy, track=self.__msg_track\n    )\n\n    # check if synchronous patterns, then wait for confirmation\n    if self.__pattern < 2:\n        # check if Bidirectional data transmission is enabled\n        if self.__bi_mode or self.__multiclient_mode:\n            # handles return data\n            recvd_data = None\n\n            socks = dict(self.__poll.poll(self.__request_timeout))\n            if socks.get(self.__msg_socket) == zmq.POLLIN:\n                # handle return data\n                recv_json = self.__msg_socket.recv_json(flags=self.__msg_flag)\n            else:\n                logger.critical(\"No response from Client, Reconnecting again...\")\n                # Socket is confused. Close and remove it.\n                self.__msg_socket.setsockopt(zmq.LINGER, 0)\n                self.__msg_socket.close()\n                self.__poll.unregister(self.__msg_socket)\n                self.__max_retries -= 1\n\n                if not (self.__max_retries):\n                    if self.__multiclient_mode:\n                        logger.error(\n                            \"All Clients failed to respond on multiple attempts.\"\n                        )\n                    else:\n                        logger.error(\n                            \"Client failed to respond on multiple attempts.\"\n                        )\n                    self.__terminate = True\n                    raise RuntimeError(\n                        \"[NetGear:ERROR] :: Client(s) seems to be offline, Abandoning.\"\n                    )\n\n                # Create new connection\n                self.__msg_socket = self.__msg_context.socket(self.__msg_pattern)\n                if isinstance(self.__connection_address, list):\n                    for _connection in self.__connection_address:\n                        self.__msg_socket.connect(_connection)\n                else:\n                    # handle SSH tunneling if enabled\n                    if self.__ssh_tunnel_mode:\n                        # establish tunnel connection\n                        ssh.tunnel_connection(\n                            self.__msg_socket,\n                            self.__connection_address,\n                            self.__ssh_tunnel_mode,\n                            keyfile=self.__ssh_tunnel_keyfile,\n                            password=self.__ssh_tunnel_pwd,\n                            paramiko=self.__paramiko_present,\n                        )\n                    else:\n                        # connect normally\n                        self.__msg_socket.connect(self.__connection_address)\n                self.__poll.register(self.__msg_socket, zmq.POLLIN)\n                # return None for mean-time\n                return None\n\n            # save the unique port addresses\n            if (\n                self.__multiclient_mode\n                and not recv_json[\"port\"] in self.__port_buffer\n            ):\n                self.__port_buffer.append(recv_json[\"port\"])\n\n            if recv_json[\"return_type\"] == \"ndarray\":\n                recv_array = self.__msg_socket.recv(\n                    flags=self.__msg_flag,\n                    copy=self.__msg_copy,\n                    track=self.__msg_track,\n                )\n                # check if encoding was enabled\n                if recv_json[\"compression\"]:\n                    # decode JPEG frame\n                    recvd_data = simplejpeg.decode_jpeg(\n                        recv_array,\n                        colorspace=recv_json[\"compression\"][\"colorspace\"],\n                        fastdct=self.__jpeg_compression_fastdct\n                        or recv_json[\"compression\"][\"dct\"],\n                        fastupsample=self.__jpeg_compression_fastupsample\n                        or recv_json[\"compression\"][\"ups\"],\n                    )\n                    # check if valid frame returned\n                    if recvd_data is None:\n                        self.__terminate = True\n                        # otherwise raise error and exit\n                        raise RuntimeError(\n                            \"[NetGear:ERROR] :: Received compressed frame `{}` decoding failed with flag: {}.\".format(\n                                recv_json[\"compression\"],\n                                self.__ex_compression_params,\n                            )\n                        )\n\n                    if (\n                        recv_json[\"compression\"][\"colorspace\"] == \"GRAY\"\n                        and recvd_data.ndim == 3\n                    ):\n                        # patch for https://gitlab.com/jfolz/simplejpeg/-/issues/11\n                        recvd_data = np.squeeze(recvd_data, axis=2)\n                else:\n                    recvd_data = np.frombuffer(\n                        recv_array, dtype=recv_json[\"array_dtype\"]\n                    ).reshape(recv_json[\"array_shape\"])\n            else:\n                recvd_data = recv_json[\"data\"]\n\n            return (\n                (recv_json[\"port\"], recvd_data)\n                if self.__multiclient_mode\n                else recvd_data\n            )\n        else:\n            # otherwise log normally\n            socks = dict(self.__poll.poll(self.__request_timeout))\n            if socks.get(self.__msg_socket) == zmq.POLLIN:\n                recv_confirmation = self.__msg_socket.recv()\n            else:\n                logger.critical(\"No response from Client, Reconnecting again...\")\n                # Socket is confused. Close and remove it.\n                self.__msg_socket.setsockopt(zmq.LINGER, 0)\n                self.__msg_socket.close()\n                self.__poll.unregister(self.__msg_socket)\n                self.__max_retries -= 1\n\n                if not (self.__max_retries):\n                    logger.error(\"Client failed to respond on repeated attempts.\")\n                    self.__terminate = True\n                    raise RuntimeError(\n                        \"[NetGear:ERROR] :: Client seems to be offline, Abandoning!\"\n                    )\n\n                # Create new connection\n                self.__msg_socket = self.__msg_context.socket(self.__msg_pattern)\n                # handle SSH tunneling if enabled\n                if self.__ssh_tunnel_mode:\n                    # establish tunnel connection\n                    ssh.tunnel_connection(\n                        self.__msg_socket,\n                        self.__connection_address,\n                        self.__ssh_tunnel_mode,\n                        keyfile=self.__ssh_tunnel_keyfile,\n                        password=self.__ssh_tunnel_pwd,\n                        paramiko=self.__paramiko_present,\n                    )\n                else:\n                    # connect normally\n                    self.__msg_socket.connect(self.__connection_address)\n                self.__poll.register(self.__msg_socket, zmq.POLLIN)\n                return None\n\n            # log confirmation\n            self.__logging and logger.debug(recv_confirmation)\n
"},{"location":"bonus/reference/netgear_async/","title":"NetGear_Async API References","text":"

NetGear_Async API usage examples can be found here \u27b6

NetGear_Async API parameters are explained here \u27b6

NetGear_Async can generate the same performance as NetGear API at about one-third the memory consumption, and also provide complete server-client handling with various options to use variable protocols/patterns similar to NetGear, but lacks in term of flexibility as it supports only a few NetGear's Exclusive Modes.

NetGear_Async is built on zmq.asyncio, and powered by a high-performance asyncio event loop called uvloop to achieve unwatchable high-speed and lag-free video streaming over the network with minimal resource constraints. NetGear_Async can transfer thousands of frames in just a few seconds without causing any significant load on your system.

NetGear_Async provides complete server-client handling and options to use variable protocols/patterns similar to NetGear API. Furthermore, NetGear_Async allows us to define our custom Server as source to transform frames easily before sending them across the network.

NetGear_Async now supports additional bidirectional data transmission between receiver(client) and sender(server) while transferring frames. Users can easily build complex applications such as like Real-Time Video Chat in just few lines of code.

In addition to all this, NetGear_Async API also provides internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs, thereby granting it exclusive power for transferring frames incoming from any source to the network.

NetGear_Async as of now supports four ZeroMQ messaging patterns:

Whereas supported protocol are: tcp and ipc.

Source code in vidgear/gears/asyncio/netgear_async.py
class NetGear_Async:\n    \"\"\"\n    NetGear_Async can generate the same performance as NetGear API at about one-third the memory consumption, and also provide complete server-client handling with various\n    options to use variable protocols/patterns similar to NetGear, but lacks in term of flexibility as it supports only a few NetGear's Exclusive Modes.\n\n    NetGear_Async is built on `zmq.asyncio`, and powered by a high-performance asyncio event loop called uvloop to achieve unwatchable high-speed and lag-free video streaming\n    over the network with minimal resource constraints. NetGear_Async can transfer thousands of frames in just a few seconds without causing any significant load on your\n    system.\n\n    NetGear_Async provides complete server-client handling and options to use variable protocols/patterns similar to NetGear API. Furthermore, NetGear_Async allows us to define\n     our custom Server as source to transform frames easily before sending them across the network.\n\n    NetGear_Async now supports additional **bidirectional data transmission** between receiver(client) and sender(server) while transferring frames.\n    Users can easily build complex applications such as like _Real-Time Video Chat_ in just few lines of code.\n\n    In addition to all this, NetGear_Async API also provides internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs, thereby\n    granting it exclusive power for transferring frames incoming from any source to the network.\n\n    NetGear_Async as of now supports four ZeroMQ messaging patterns:\n\n    - `zmq.PAIR` _(ZMQ Pair Pattern)_\n    - `zmq.REQ/zmq.REP` _(ZMQ Request/Reply Pattern)_\n    - `zmq.PUB/zmq.SUB` _(ZMQ Publish/Subscribe Pattern)_\n    - `zmq.PUSH/zmq.PULL` _(ZMQ Push/Pull Pattern)_\n\n    Whereas supported protocol are: `tcp` and `ipc`.\n    \"\"\"\n\n    def __init__(\n        self,\n        # NetGear_Async parameters\n        address=None,\n        port=None,\n        protocol=\"tcp\",\n        pattern=0,\n        receive_mode=False,\n        timeout=0.0,\n        # Videogear parameters\n        enablePiCamera=False,\n        stabilize=False,\n        source=None,\n        camera_num=0,\n        stream_mode=False,\n        backend=0,\n        colorspace=None,\n        resolution=(640, 480),\n        framerate=25,\n        time_delay=0,\n        # common parameters\n        logging=False,\n        **options\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the NetGear_Async class.\n\n        Parameters:\n            address (str): sets the valid network address of the Server/Client.\n            port (str): sets the valid Network Port of the Server/Client.\n            protocol (str): sets the valid messaging protocol between Server/Client.\n            pattern (int): sets the supported messaging pattern(flow of communication) between Server/Client\n            receive_mode (bool): select the NetGear_Async's Mode of operation.\n            timeout (int/float): controls the maximum waiting time(in sec) after which Client throws `TimeoutError`.\n            enablePiCamera (bool): provide access to PiGear(if True) or CamGear(if False) APIs respectively.\n            stabilize (bool): enable access to Stabilizer Class for stabilizing frames.\n            camera_num (int): selects the camera module index which will be used as Rpi source.\n            resolution (tuple): sets the resolution (i.e. `(width,height)`) of the Rpi source.\n            framerate (int/float): sets the framerate of the Rpi source.\n            source (based on input): defines the source for the input stream.\n            stream_mode (bool): controls the exclusive YouTube Mode.\n            backend (int): selects the backend for OpenCV's VideoCapture class.\n            colorspace (str): selects the colorspace of the input stream.\n            logging (bool): enables/disables logging.\n            time_delay (int): time delay (in sec) before start reading the frames.\n            options (dict): provides ability to alter Tweak Parameters of NetGear_Async, CamGear, PiGear & Stabilizer.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # raise error(s) for critical Class imports\n        import_dependency_safe(\n            \"zmq\" if zmq is None else \"\", min_version=\"4.0\", pkg_name=\"pyzmq\"\n        )\n        import_dependency_safe(\"msgpack\" if msgpack is None else \"\")\n        import_dependency_safe(\"msgpack_numpy\" if m is None else \"\")\n\n        # define valid messaging patterns => `0`: PAIR, `1`:(REQ, REP), `2`:(SUB, PUB), `3`:(PUSH, PULL)\n        valid_messaging_patterns = {\n            0: (zmq.PAIR, zmq.PAIR),\n            1: (zmq.REQ, zmq.REP),\n            2: (zmq.PUB, zmq.SUB),\n            3: (zmq.PUSH, zmq.PULL),\n        }\n\n        # check whether user-defined messaging pattern is valid\n        if isinstance(pattern, int) and pattern in valid_messaging_patterns:\n            # assign value\n            self.__msg_pattern = pattern\n            self.__pattern = valid_messaging_patterns[pattern]\n        else:\n            # otherwise default to 0:`zmq.PAIR`\n            self.__msg_pattern = 0\n            self.__pattern = valid_messaging_patterns[self.__msg_pattern]\n            self.__logging and logger.warning(\n                \"Invalid pattern {pattern}. Defaulting to `zmq.PAIR`!\".format(\n                    pattern=pattern\n                )\n            )\n\n        # check  whether user-defined messaging protocol is valid\n        if isinstance(protocol, str) and protocol in [\"tcp\", \"ipc\"]:\n            # assign value\n            self.__protocol = protocol\n        else:\n            # else default to `tcp` protocol\n            self.__protocol = \"tcp\"\n            self.__logging and logger.warning(\"Invalid protocol. Defaulting to `tcp`!\")\n\n        # initialize Termination flag\n        self.__terminate = False\n        # initialize and assign `Receive Mode`\n        self.__receive_mode = receive_mode\n        # initialize stream handler\n        self.__stream = None\n        # initialize Messaging Socket\n        self.__msg_socket = None\n        # initialize NetGear_Async's configuration dictionary\n        self.config = {}\n        # asyncio queue handler\n        self.__queue = None\n        # define Bidirectional mode\n        self.__bi_mode = False  # handles Bidirectional mode state\n\n        # assign timeout for Receiver end\n        if timeout and isinstance(timeout, (int, float)):\n            self.__timeout = float(timeout)\n        else:\n            self.__timeout = 15.0\n\n        # generate 8-digit random system id\n        self.__id = \"\".join(\n            secrets.choice(string.ascii_uppercase + string.digits) for i in range(8)\n        )\n\n        # Handle user-defined options dictionary values\n        # reformat dictionary\n        options = {str(k).strip(): v for k, v in options.items()}\n        # handle bidirectional mode\n        if \"bidirectional_mode\" in options:\n            value = options[\"bidirectional_mode\"]\n            # also check if pattern and source is valid\n            if isinstance(value, bool) and pattern < 2 and source is None:\n                # activate Bidirectional mode if specified\n                self.__bi_mode = value\n            else:\n                # otherwise disable it\n                self.__bi_mode = False\n                logger.warning(\"Bidirectional data transmission is disabled!\")\n            # handle errors and logging\n            if pattern >= 2:\n                # raise error\n                raise ValueError(\n                    \"[NetGear_Async:ERROR] :: `{}` pattern is not valid when Bidirectional Mode is enabled. Kindly refer Docs for more Information!\".format(\n                        pattern\n                    )\n                )\n            elif not (source is None):\n                raise ValueError(\n                    \"[NetGear_Async:ERROR] :: Custom source must be used when Bidirectional Mode is enabled. Kindly refer Docs for more Information!\".format(\n                        pattern\n                    )\n                )\n            elif isinstance(value, bool) and self.__logging:\n                # log Bidirectional mode activation\n                logger.debug(\n                    \"Bidirectional Data Transmission is {} for this connection!\".format(\n                        \"enabled\" if value else \"disabled\"\n                    )\n                )\n            else:\n                logger.error(\"`bidirectional_mode` value is invalid!\")\n            # clean\n            del options[\"bidirectional_mode\"]\n\n        # Setup and assign event loop policy\n        if platform.system() == \"Windows\":\n            # On Windows, VidGear requires the ``WindowsSelectorEventLoop``, but Python 3.8 and above,\n            # defaults to an ``ProactorEventLoop`` loop that is not compatible with it. Thereby,\n            # we had to set it manually.\n            asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())\n        else:\n            if not (uvloop is None):\n                # Latest uvloop eventloop is only available for UNIX machines.\n                asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())\n            else:\n                # log if not present\n                import_dependency_safe(\"uvloop\", error=\"log\")\n\n        # Retrieve event loop and assign it\n        try:\n            self.loop = asyncio.get_running_loop()\n        except RuntimeError:\n            # otherwise create one\n            logger.critical(\"No running event loop found. Creating a new one.\")\n            self.loop = asyncio.new_event_loop()\n\n        # log eventloop for debugging\n        self.__logging and logger.info(\n            \"Using ``{}`` event loop for this process.\".format(\n                self.loop.__class__.__name__\n            )\n        )\n\n        # define messaging asynchronous Context\n        self.__msg_context = zmq.asyncio.Context()\n\n        # check whether `Receive Mode` is enabled\n        if receive_mode:\n            # assign local IP address if None\n            if address is None:\n                self.__address = \"*\"  # define address\n            else:\n                self.__address = address\n            # assign default port address if None\n            if port is None:\n                self.__port = \"5555\"\n            else:\n                self.__port = port\n        else:\n            # Handle video source\n            if source is None:\n                self.config = {\"generator\": None}\n                self.__logging and logger.warning(\"Given source is of NoneType!\")\n            else:\n                # define stream with necessary params\n                self.__stream = VideoGear(\n                    enablePiCamera=enablePiCamera,\n                    stabilize=stabilize,\n                    source=source,\n                    camera_num=camera_num,\n                    stream_mode=stream_mode,\n                    backend=backend,\n                    colorspace=colorspace,\n                    resolution=resolution,\n                    framerate=framerate,\n                    logging=logging,\n                    time_delay=time_delay,\n                    **options\n                )\n                # define default frame generator in configuration\n                self.config = {\"generator\": self.__frame_generator()}\n            # assign local ip address if None\n            if address is None:\n                self.__address = \"localhost\"\n            else:\n                self.__address = address\n            # assign default port address if None\n            if port is None:\n                self.__port = \"5555\"\n            else:\n                self.__port = port\n            # add server task handler\n            self.task = None\n\n        # create asyncio queue if bidirectional mode activated\n        self.__queue = asyncio.Queue() if self.__bi_mode else None\n\n    def launch(self):\n        \"\"\"\n        Launches an asynchronous generators and loop executors for respective task.\n        \"\"\"\n        # check if receive mode enabled\n        if self.__receive_mode:\n            self.__logging and logger.debug(\n                \"Launching NetGear_Async asynchronous generator!\"\n            )\n            # run loop executor for Receiver asynchronous generator\n            self.loop.run_in_executor(None, self.recv_generator)\n        else:\n            # Otherwise launch Server handler\n            self.__logging and logger.debug(\n                \"Creating NetGear_Async asynchronous server handler!\"\n            )\n            # create task for Server Handler\n            self.task = self.loop.create_task(self.__server_handler())\n        # return instance\n        return self\n\n    async def __server_handler(self):\n        \"\"\"\n        Handles various Server-end processes/tasks.\n        \"\"\"\n        # validate assigned frame generator in NetGear_Async configuration\n        if isinstance(self.config, dict) and \"generator\" in self.config:\n            # check if its  assigned value is a asynchronous generator\n            if self.config[\"generator\"] is None or not inspect.isasyncgen(\n                self.config[\"generator\"]\n            ):\n                # otherwise raise error\n                raise ValueError(\n                    \"[NetGear_Async:ERROR] :: Invalid configuration. Assigned generator must be a asynchronous generator function/method only!\"\n                )\n        else:\n            # raise error if validation fails\n            raise RuntimeError(\n                \"[NetGear_Async:ERROR] :: Assigned NetGear_Async configuration is invalid!\"\n            )\n\n        # define our messaging socket\n        self.__msg_socket = self.__msg_context.socket(self.__pattern[0])\n\n        # if req/rep pattern, define additional flags\n        if self.__msg_pattern == 1:\n            self.__msg_socket.REQ_RELAXED = True\n            self.__msg_socket.REQ_CORRELATE = True\n\n        # if pub/sub pattern, define additional optimizer\n        if self.__msg_pattern == 2:\n            self.__msg_socket.set_hwm(1)\n\n        # try connecting socket to assigned protocol, address and port\n        try:\n            self.__msg_socket.connect(\n                self.__protocol + \"://\" + str(self.__address) + \":\" + str(self.__port)\n            )\n            # finally log if successful\n            self.__logging and logger.debug(\n                \"Successfully connected to address: {} with pattern: {}.\".format(\n                    (\n                        self.__protocol\n                        + \"://\"\n                        + str(self.__address)\n                        + \":\"\n                        + str(self.__port)\n                    ),\n                    self.__msg_pattern,\n                )\n            )\n            logger.critical(\n                \"Send Mode is successfully activated and ready to send data!\"\n            )\n        except Exception as e:\n            # log ad raise error if failed\n            logger.exception(str(e))\n            if self.__bi_mode:\n                logger.error(\n                    \"Failed to activate Bidirectional Mode for this connection!\"\n                )\n            raise ValueError(\n                \"[NetGear_Async:ERROR] :: Failed to connect address: {} and pattern: {}!\".format(\n                    (\n                        self.__protocol\n                        + \"://\"\n                        + str(self.__address)\n                        + \":\"\n                        + str(self.__port)\n                    ),\n                    self.__msg_pattern,\n                )\n            )\n\n        # loop over our Asynchronous frame generator\n        async for dataframe in self.config[\"generator\"]:\n            # extract data if bidirectional mode\n            if self.__bi_mode and len(dataframe) == 2:\n                (data, frame) = dataframe\n                if not (data is None) and isinstance(data, np.ndarray):\n                    logger.warning(\n                        \"Skipped unsupported `data` of datatype: {}!\".format(\n                            type(data).__name__\n                        )\n                    )\n                    data = None\n                assert isinstance(\n                    frame, np.ndarray\n                ), \"[NetGear_Async:ERROR] :: Invalid data received from server end!\"\n            elif self.__bi_mode:\n                # raise error for invalid data\n                raise ValueError(\n                    \"[NetGear_Async:ERROR] :: Send Mode only accepts tuple(data, frame) as input in Bidirectional Mode. \\\n                    Kindly refer vidgear docs!\"\n                )\n            else:\n                # otherwise just make a copy of frame\n                frame = np.copy(dataframe)\n                data = None\n\n            # check if retrieved frame is `CONTIGUOUS`\n            if not (frame.flags[\"C_CONTIGUOUS\"]):\n                # otherwise make it\n                frame = np.ascontiguousarray(frame, dtype=frame.dtype)\n\n            # create data dict\n            data_dict = dict(\n                terminate=False,\n                bi_mode=self.__bi_mode,\n                data=data if not (data is None) else \"\",\n            )\n            # encode it\n            data_enc = msgpack.packb(data_dict)\n            # send the encoded data with correct flags\n            await self.__msg_socket.send(data_enc, flags=zmq.SNDMORE)\n\n            # encode frame\n            frame_enc = msgpack.packb(frame, default=m.encode)\n            # send the encoded frame\n            await self.__msg_socket.send_multipart([frame_enc])\n\n            # check if bidirectional patterns used\n            if self.__msg_pattern < 2:\n                # handle bidirectional data transfer if enabled\n                if self.__bi_mode:\n                    # get receiver encoded message withing timeout limit\n                    recvdmsg_encoded = await asyncio.wait_for(\n                        self.__msg_socket.recv(), timeout=self.__timeout\n                    )\n                    # retrieve receiver data from encoded message\n                    recvd_data = msgpack.unpackb(recvdmsg_encoded, use_list=False)\n                    # check message type\n                    if recvd_data[\"return_type\"] == \"ndarray\":  # numpy.ndarray\n                        # get encoded frame from receiver\n                        recvdframe_encoded = await asyncio.wait_for(\n                            self.__msg_socket.recv_multipart(), timeout=self.__timeout\n                        )\n                        # retrieve frame and put in queue\n                        await self.__queue.put(\n                            msgpack.unpackb(\n                                recvdframe_encoded[0],\n                                use_list=False,\n                                object_hook=m.decode,\n                            )\n                        )\n                    else:\n                        # otherwise put data directly in queue\n                        await self.__queue.put(\n                            recvd_data[\"return_data\"]\n                            if recvd_data[\"return_data\"]\n                            else None\n                        )\n                else:\n                    # otherwise log received confirmation\n                    recv_confirmation = await asyncio.wait_for(\n                        self.__msg_socket.recv(), timeout=self.__timeout\n                    )\n                    self.__logging and logger.debug(recv_confirmation)\n\n    async def recv_generator(self):\n        \"\"\"\n        A default Asynchronous Frame Generator for NetGear_Async's Receiver-end.\n        \"\"\"\n        # check whether `receive mode` is activated\n        if not (self.__receive_mode):\n            # raise Value error and exit\n            self.__terminate = True\n            raise ValueError(\n                \"[NetGear_Async:ERROR] :: `recv_generator()` function cannot be accessed while `receive_mode` is disabled. Kindly refer vidgear docs!\"\n            )\n\n        # initialize and define messaging socket\n        self.__msg_socket = self.__msg_context.socket(self.__pattern[1])\n\n        # define exclusive socket options for patterns\n        if self.__msg_pattern == 2:\n            self.__msg_socket.set_hwm(1)\n            self.__msg_socket.setsockopt(zmq.SUBSCRIBE, b\"\")\n\n        try:\n            # bind socket to the assigned protocol, address and port\n            self.__msg_socket.bind(\n                self.__protocol + \"://\" + str(self.__address) + \":\" + str(self.__port)\n            )\n            # finally log progress\n            self.__logging and logger.debug(\n                \"Successfully binded to address: {} with pattern: {}.\".format(\n                    (\n                        self.__protocol\n                        + \"://\"\n                        + str(self.__address)\n                        + \":\"\n                        + str(self.__port)\n                    ),\n                    self.__msg_pattern,\n                )\n            )\n            logger.critical(\"Receive Mode is activated successfully!\")\n        except Exception as e:\n            logger.exception(str(e))\n            raise RuntimeError(\n                \"[NetGear_Async:ERROR] :: Failed to bind address: {} and pattern: {}{}!\".format(\n                    (\n                        self.__protocol\n                        + \"://\"\n                        + str(self.__address)\n                        + \":\"\n                        + str(self.__port)\n                    ),\n                    self.__msg_pattern,\n                    \" and Bidirectional Mode enabled\" if self.__bi_mode else \"\",\n                )\n            )\n\n        # loop until terminated\n        while not self.__terminate:\n            # get encoded data message from server withing timeout limit\n            datamsg_encoded = await asyncio.wait_for(\n                self.__msg_socket.recv(), timeout=self.__timeout\n            )\n            # retrieve data from message\n            data = msgpack.unpackb(datamsg_encoded, use_list=False)\n            # terminate if exit` flag received from server\n            if data[\"terminate\"]:\n                # send confirmation message to server if bidirectional patterns\n                if self.__msg_pattern < 2:\n                    # create termination confirmation message\n                    return_dict = dict(\n                        terminated=\"Client-`{}` successfully terminated!\".format(\n                            self.__id\n                        ),\n                    )\n                    # encode message\n                    retdata_enc = msgpack.packb(return_dict)\n                    # send message back to server\n                    await self.__msg_socket.send(retdata_enc)\n                self.__logging and logger.info(\n                    \"Termination signal received from server!\"\n                )\n                # break loop and terminate\n                self.__terminate = True\n                break\n            # get encoded frame message from server withing timeout limit\n            framemsg_encoded = await asyncio.wait_for(\n                self.__msg_socket.recv_multipart(), timeout=self.__timeout\n            )\n            # retrieve frame from message\n            frame = msgpack.unpackb(\n                framemsg_encoded[0], use_list=False, object_hook=m.decode\n            )\n\n            # check if bidirectional patterns\n            if self.__msg_pattern < 2:\n                # handle bidirectional data transfer if enabled\n                if self.__bi_mode and data[\"bi_mode\"]:\n                    # handle empty queue\n                    if not self.__queue.empty():\n                        return_data = await self.__queue.get()\n                        self.__queue.task_done()\n                    else:\n                        return_data = None\n                    # check if we are returning `ndarray` frames\n                    if not (return_data is None) and isinstance(\n                        return_data, np.ndarray\n                    ):\n                        # check whether the incoming frame is contiguous\n                        if not (return_data.flags[\"C_CONTIGUOUS\"]):\n                            return_data = np.ascontiguousarray(\n                                return_data, dtype=return_data.dtype\n                            )\n\n                        # create return type dict without data\n                        rettype_dict = dict(\n                            return_type=(type(return_data).__name__),\n                            return_data=None,\n                        )\n                        # encode it\n                        rettype_enc = msgpack.packb(rettype_dict)\n                        # send it to server with correct flags\n                        await self.__msg_socket.send(rettype_enc, flags=zmq.SNDMORE)\n\n                        # encode return ndarray data\n                        retframe_enc = msgpack.packb(return_data, default=m.encode)\n                        # send it over network to server\n                        await self.__msg_socket.send_multipart([retframe_enc])\n                    else:\n                        # otherwise create type and data dict\n                        return_dict = dict(\n                            return_type=(type(return_data).__name__),\n                            return_data=(\n                                return_data if not (return_data is None) else \"\"\n                            ),\n                        )\n                        # encode it\n                        retdata_enc = msgpack.packb(return_dict)\n                        # send it over network to server\n                        await self.__msg_socket.send(retdata_enc)\n                elif self.__bi_mode or data[\"bi_mode\"]:\n                    # raise error if bidirectional mode is disabled at server or client but not both\n                    raise RuntimeError(\n                        \"[NetGear_Async:ERROR] :: Invalid configuration! Bidirectional Mode is not activate on {} end.\".format(\n                            \"client\" if self.__bi_mode else \"server\"\n                        )\n                    )\n                else:\n                    # otherwise just send confirmation message to server\n                    await self.__msg_socket.send(\n                        bytes(\n                            \"Data received on client: {} !\".format(self.__id), \"utf-8\"\n                        )\n                    )\n            # yield received tuple(data-frame) if bidirectional mode or else just frame\n            if self.__bi_mode:\n                yield (data[\"data\"], frame) if data[\"data\"] else (None, frame)\n            else:\n                yield frame\n            # sleep for sometime\n            await asyncio.sleep(0)\n\n    async def __frame_generator(self):\n        \"\"\"\n        Returns a default frame-generator for NetGear_Async's Server Handler.\n        \"\"\"\n        # start stream\n        self.__stream.start()\n        # loop over stream until its terminated\n        while not self.__terminate:\n            # read frames\n            frame = self.__stream.read()\n            # break if NoneType\n            if frame is None:\n                break\n            # yield frame\n            yield frame\n            # sleep for sometime\n            await asyncio.sleep(0)\n\n    async def transceive_data(self, data=None):\n        \"\"\"\n        Bidirectional Mode exclusive method to Transmit data _(in Receive mode)_ and Receive data _(in Send mode)_.\n\n        Parameters:\n            data (any): inputs data _(of any datatype)_ for sending back to Server.\n        \"\"\"\n        recvd_data = None\n        if not self.__terminate:\n            if self.__bi_mode:\n                if self.__receive_mode:\n                    await self.__queue.put(data)\n                else:\n                    if not self.__queue.empty():\n                        recvd_data = await self.__queue.get()\n                        self.__queue.task_done()\n            else:\n                logger.error(\n                    \"`transceive_data()` function cannot be used when Bidirectional Mode is disabled.\"\n                )\n        return recvd_data\n\n    async def __terminate_connection(self, disable_confirmation=False):\n        \"\"\"\n        Internal asyncio method to safely terminate ZMQ connection and queues\n\n        Parameters:\n            disable_confirmation (boolean): Force disable termination confirmation from client in bidirectional patterns.\n        \"\"\"\n        # log termination\n        self.__logging and logger.debug(\n            \"Terminating various {} Processes. Please wait.\".format(\n                \"Receive Mode\" if self.__receive_mode else \"Send Mode\"\n            )\n        )\n\n        # check whether `receive_mode` is enabled or not\n        if self.__receive_mode:\n            # indicate that process should be terminated\n            self.__terminate = True\n        else:\n            # indicate that process should be terminated\n            self.__terminate = True\n            # terminate stream\n            if not (self.__stream is None):\n                self.__stream.stop()\n            # signal `exit` flag for termination!\n            data_dict = dict(terminate=True)\n            data_enc = msgpack.packb(data_dict)\n            await self.__msg_socket.send(data_enc)\n            # check if bidirectional patterns\n            if self.__msg_pattern < 2 and not disable_confirmation:\n                # then receive and log confirmation\n                recv_confirmation = await self.__msg_socket.recv()\n                recvd_conf = msgpack.unpackb(recv_confirmation, use_list=False)\n                self.__logging and \"terminated\" in recvd_conf and logger.debug(\n                    recvd_conf[\"terminated\"]\n                )\n        # close socket\n        self.__msg_socket.setsockopt(zmq.LINGER, 0)\n        self.__msg_socket.close()\n        # handle asyncio queues in bidirectional mode\n        if self.__bi_mode:\n            # empty queue if not\n            while not self.__queue.empty():\n                try:\n                    self.__queue.get_nowait()\n                except asyncio.QueueEmpty:\n                    continue\n                self.__queue.task_done()\n            # join queues\n            await self.__queue.join()\n\n        logger.critical(\n            \"{} successfully terminated!\".format(\n                \"Receive Mode\" if self.__receive_mode else \"Send Mode\"\n            )\n        )\n\n    def close(self, skip_loop=False):\n        \"\"\"\n        Terminates all NetGear_Async Asynchronous processes gracefully.\n\n        Parameters:\n            skip_loop (Boolean): (optional)used only if don't want to close eventloop(required in pytest).\n        \"\"\"\n        # close event loop if specified\n        if not (skip_loop):\n            # close connection gracefully\n            self.loop.run_until_complete(self.__terminate_connection())\n            self.loop.close()\n        else:\n            # otherwise create a task\n            asyncio.ensure_future(\n                self.__terminate_connection(disable_confirmation=True)\n            )\n

"},{"location":"bonus/reference/netgear_async/#vidgear.gears.asyncio.netgear_async.NetGear_Async.__init__","title":"__init__(self, address=None, port=None, protocol='tcp', pattern=0, receive_mode=False, timeout=0.0, enablePiCamera=False, stabilize=False, source=None, camera_num=0, stream_mode=False, backend=0, colorspace=None, resolution=(640, 480), framerate=25, time_delay=0, logging=False, **options) special","text":"

This constructor method initializes the object state and attributes of the NetGear_Async class.

Parameters:

Name Type Description Default address str

sets the valid network address of the Server/Client.

None port str

sets the valid Network Port of the Server/Client.

None protocol str

sets the valid messaging protocol between Server/Client.

'tcp' pattern int

sets the supported messaging pattern(flow of communication) between Server/Client

0 receive_mode bool

select the NetGear_Async's Mode of operation.

False timeout int/float

controls the maximum waiting time(in sec) after which Client throws TimeoutError.

0.0 enablePiCamera bool

provide access to PiGear(if True) or CamGear(if False) APIs respectively.

False stabilize bool

enable access to Stabilizer Class for stabilizing frames.

False camera_num int

selects the camera module index which will be used as Rpi source.

0 resolution tuple

sets the resolution (i.e. (width,height)) of the Rpi source.

(640, 480) framerate int/float

sets the framerate of the Rpi source.

25 source based on input

defines the source for the input stream.

None stream_mode bool

controls the exclusive YouTube Mode.

False backend int

selects the backend for OpenCV's VideoCapture class.

0 colorspace str

selects the colorspace of the input stream.

None logging bool

enables/disables logging.

False time_delay int

time delay (in sec) before start reading the frames.

0 options dict

provides ability to alter Tweak Parameters of NetGear_Async, CamGear, PiGear & Stabilizer.

{} Source code in vidgear/gears/asyncio/netgear_async.py
def __init__(\n    self,\n    # NetGear_Async parameters\n    address=None,\n    port=None,\n    protocol=\"tcp\",\n    pattern=0,\n    receive_mode=False,\n    timeout=0.0,\n    # Videogear parameters\n    enablePiCamera=False,\n    stabilize=False,\n    source=None,\n    camera_num=0,\n    stream_mode=False,\n    backend=0,\n    colorspace=None,\n    resolution=(640, 480),\n    framerate=25,\n    time_delay=0,\n    # common parameters\n    logging=False,\n    **options\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the NetGear_Async class.\n\n    Parameters:\n        address (str): sets the valid network address of the Server/Client.\n        port (str): sets the valid Network Port of the Server/Client.\n        protocol (str): sets the valid messaging protocol between Server/Client.\n        pattern (int): sets the supported messaging pattern(flow of communication) between Server/Client\n        receive_mode (bool): select the NetGear_Async's Mode of operation.\n        timeout (int/float): controls the maximum waiting time(in sec) after which Client throws `TimeoutError`.\n        enablePiCamera (bool): provide access to PiGear(if True) or CamGear(if False) APIs respectively.\n        stabilize (bool): enable access to Stabilizer Class for stabilizing frames.\n        camera_num (int): selects the camera module index which will be used as Rpi source.\n        resolution (tuple): sets the resolution (i.e. `(width,height)`) of the Rpi source.\n        framerate (int/float): sets the framerate of the Rpi source.\n        source (based on input): defines the source for the input stream.\n        stream_mode (bool): controls the exclusive YouTube Mode.\n        backend (int): selects the backend for OpenCV's VideoCapture class.\n        colorspace (str): selects the colorspace of the input stream.\n        logging (bool): enables/disables logging.\n        time_delay (int): time delay (in sec) before start reading the frames.\n        options (dict): provides ability to alter Tweak Parameters of NetGear_Async, CamGear, PiGear & Stabilizer.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # raise error(s) for critical Class imports\n    import_dependency_safe(\n        \"zmq\" if zmq is None else \"\", min_version=\"4.0\", pkg_name=\"pyzmq\"\n    )\n    import_dependency_safe(\"msgpack\" if msgpack is None else \"\")\n    import_dependency_safe(\"msgpack_numpy\" if m is None else \"\")\n\n    # define valid messaging patterns => `0`: PAIR, `1`:(REQ, REP), `2`:(SUB, PUB), `3`:(PUSH, PULL)\n    valid_messaging_patterns = {\n        0: (zmq.PAIR, zmq.PAIR),\n        1: (zmq.REQ, zmq.REP),\n        2: (zmq.PUB, zmq.SUB),\n        3: (zmq.PUSH, zmq.PULL),\n    }\n\n    # check whether user-defined messaging pattern is valid\n    if isinstance(pattern, int) and pattern in valid_messaging_patterns:\n        # assign value\n        self.__msg_pattern = pattern\n        self.__pattern = valid_messaging_patterns[pattern]\n    else:\n        # otherwise default to 0:`zmq.PAIR`\n        self.__msg_pattern = 0\n        self.__pattern = valid_messaging_patterns[self.__msg_pattern]\n        self.__logging and logger.warning(\n            \"Invalid pattern {pattern}. Defaulting to `zmq.PAIR`!\".format(\n                pattern=pattern\n            )\n        )\n\n    # check  whether user-defined messaging protocol is valid\n    if isinstance(protocol, str) and protocol in [\"tcp\", \"ipc\"]:\n        # assign value\n        self.__protocol = protocol\n    else:\n        # else default to `tcp` protocol\n        self.__protocol = \"tcp\"\n        self.__logging and logger.warning(\"Invalid protocol. Defaulting to `tcp`!\")\n\n    # initialize Termination flag\n    self.__terminate = False\n    # initialize and assign `Receive Mode`\n    self.__receive_mode = receive_mode\n    # initialize stream handler\n    self.__stream = None\n    # initialize Messaging Socket\n    self.__msg_socket = None\n    # initialize NetGear_Async's configuration dictionary\n    self.config = {}\n    # asyncio queue handler\n    self.__queue = None\n    # define Bidirectional mode\n    self.__bi_mode = False  # handles Bidirectional mode state\n\n    # assign timeout for Receiver end\n    if timeout and isinstance(timeout, (int, float)):\n        self.__timeout = float(timeout)\n    else:\n        self.__timeout = 15.0\n\n    # generate 8-digit random system id\n    self.__id = \"\".join(\n        secrets.choice(string.ascii_uppercase + string.digits) for i in range(8)\n    )\n\n    # Handle user-defined options dictionary values\n    # reformat dictionary\n    options = {str(k).strip(): v for k, v in options.items()}\n    # handle bidirectional mode\n    if \"bidirectional_mode\" in options:\n        value = options[\"bidirectional_mode\"]\n        # also check if pattern and source is valid\n        if isinstance(value, bool) and pattern < 2 and source is None:\n            # activate Bidirectional mode if specified\n            self.__bi_mode = value\n        else:\n            # otherwise disable it\n            self.__bi_mode = False\n            logger.warning(\"Bidirectional data transmission is disabled!\")\n        # handle errors and logging\n        if pattern >= 2:\n            # raise error\n            raise ValueError(\n                \"[NetGear_Async:ERROR] :: `{}` pattern is not valid when Bidirectional Mode is enabled. Kindly refer Docs for more Information!\".format(\n                    pattern\n                )\n            )\n        elif not (source is None):\n            raise ValueError(\n                \"[NetGear_Async:ERROR] :: Custom source must be used when Bidirectional Mode is enabled. Kindly refer Docs for more Information!\".format(\n                    pattern\n                )\n            )\n        elif isinstance(value, bool) and self.__logging:\n            # log Bidirectional mode activation\n            logger.debug(\n                \"Bidirectional Data Transmission is {} for this connection!\".format(\n                    \"enabled\" if value else \"disabled\"\n                )\n            )\n        else:\n            logger.error(\"`bidirectional_mode` value is invalid!\")\n        # clean\n        del options[\"bidirectional_mode\"]\n\n    # Setup and assign event loop policy\n    if platform.system() == \"Windows\":\n        # On Windows, VidGear requires the ``WindowsSelectorEventLoop``, but Python 3.8 and above,\n        # defaults to an ``ProactorEventLoop`` loop that is not compatible with it. Thereby,\n        # we had to set it manually.\n        asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())\n    else:\n        if not (uvloop is None):\n            # Latest uvloop eventloop is only available for UNIX machines.\n            asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())\n        else:\n            # log if not present\n            import_dependency_safe(\"uvloop\", error=\"log\")\n\n    # Retrieve event loop and assign it\n    try:\n        self.loop = asyncio.get_running_loop()\n    except RuntimeError:\n        # otherwise create one\n        logger.critical(\"No running event loop found. Creating a new one.\")\n        self.loop = asyncio.new_event_loop()\n\n    # log eventloop for debugging\n    self.__logging and logger.info(\n        \"Using ``{}`` event loop for this process.\".format(\n            self.loop.__class__.__name__\n        )\n    )\n\n    # define messaging asynchronous Context\n    self.__msg_context = zmq.asyncio.Context()\n\n    # check whether `Receive Mode` is enabled\n    if receive_mode:\n        # assign local IP address if None\n        if address is None:\n            self.__address = \"*\"  # define address\n        else:\n            self.__address = address\n        # assign default port address if None\n        if port is None:\n            self.__port = \"5555\"\n        else:\n            self.__port = port\n    else:\n        # Handle video source\n        if source is None:\n            self.config = {\"generator\": None}\n            self.__logging and logger.warning(\"Given source is of NoneType!\")\n        else:\n            # define stream with necessary params\n            self.__stream = VideoGear(\n                enablePiCamera=enablePiCamera,\n                stabilize=stabilize,\n                source=source,\n                camera_num=camera_num,\n                stream_mode=stream_mode,\n                backend=backend,\n                colorspace=colorspace,\n                resolution=resolution,\n                framerate=framerate,\n                logging=logging,\n                time_delay=time_delay,\n                **options\n            )\n            # define default frame generator in configuration\n            self.config = {\"generator\": self.__frame_generator()}\n        # assign local ip address if None\n        if address is None:\n            self.__address = \"localhost\"\n        else:\n            self.__address = address\n        # assign default port address if None\n        if port is None:\n            self.__port = \"5555\"\n        else:\n            self.__port = port\n        # add server task handler\n        self.task = None\n\n    # create asyncio queue if bidirectional mode activated\n    self.__queue = asyncio.Queue() if self.__bi_mode else None\n
"},{"location":"bonus/reference/netgear_async/#vidgear.gears.asyncio.netgear_async.NetGear_Async.close","title":"close(self, skip_loop=False)","text":"

Terminates all NetGear_Async Asynchronous processes gracefully.

Parameters:

Name Type Description Default skip_loop Boolean

(optional)used only if don't want to close eventloop(required in pytest).

False Source code in vidgear/gears/asyncio/netgear_async.py
def close(self, skip_loop=False):\n    \"\"\"\n    Terminates all NetGear_Async Asynchronous processes gracefully.\n\n    Parameters:\n        skip_loop (Boolean): (optional)used only if don't want to close eventloop(required in pytest).\n    \"\"\"\n    # close event loop if specified\n    if not (skip_loop):\n        # close connection gracefully\n        self.loop.run_until_complete(self.__terminate_connection())\n        self.loop.close()\n    else:\n        # otherwise create a task\n        asyncio.ensure_future(\n            self.__terminate_connection(disable_confirmation=True)\n        )\n
"},{"location":"bonus/reference/netgear_async/#vidgear.gears.asyncio.netgear_async.NetGear_Async.launch","title":"launch(self)","text":"

Launches an asynchronous generators and loop executors for respective task.

Source code in vidgear/gears/asyncio/netgear_async.py
def launch(self):\n    \"\"\"\n    Launches an asynchronous generators and loop executors for respective task.\n    \"\"\"\n    # check if receive mode enabled\n    if self.__receive_mode:\n        self.__logging and logger.debug(\n            \"Launching NetGear_Async asynchronous generator!\"\n        )\n        # run loop executor for Receiver asynchronous generator\n        self.loop.run_in_executor(None, self.recv_generator)\n    else:\n        # Otherwise launch Server handler\n        self.__logging and logger.debug(\n            \"Creating NetGear_Async asynchronous server handler!\"\n        )\n        # create task for Server Handler\n        self.task = self.loop.create_task(self.__server_handler())\n    # return instance\n    return self\n
"},{"location":"bonus/reference/netgear_async/#vidgear.gears.asyncio.netgear_async.NetGear_Async.recv_generator","title":"recv_generator(self)","text":"

A default Asynchronous Frame Generator for NetGear_Async's Receiver-end.

Source code in vidgear/gears/asyncio/netgear_async.py
async def recv_generator(self):\n    \"\"\"\n    A default Asynchronous Frame Generator for NetGear_Async's Receiver-end.\n    \"\"\"\n    # check whether `receive mode` is activated\n    if not (self.__receive_mode):\n        # raise Value error and exit\n        self.__terminate = True\n        raise ValueError(\n            \"[NetGear_Async:ERROR] :: `recv_generator()` function cannot be accessed while `receive_mode` is disabled. Kindly refer vidgear docs!\"\n        )\n\n    # initialize and define messaging socket\n    self.__msg_socket = self.__msg_context.socket(self.__pattern[1])\n\n    # define exclusive socket options for patterns\n    if self.__msg_pattern == 2:\n        self.__msg_socket.set_hwm(1)\n        self.__msg_socket.setsockopt(zmq.SUBSCRIBE, b\"\")\n\n    try:\n        # bind socket to the assigned protocol, address and port\n        self.__msg_socket.bind(\n            self.__protocol + \"://\" + str(self.__address) + \":\" + str(self.__port)\n        )\n        # finally log progress\n        self.__logging and logger.debug(\n            \"Successfully binded to address: {} with pattern: {}.\".format(\n                (\n                    self.__protocol\n                    + \"://\"\n                    + str(self.__address)\n                    + \":\"\n                    + str(self.__port)\n                ),\n                self.__msg_pattern,\n            )\n        )\n        logger.critical(\"Receive Mode is activated successfully!\")\n    except Exception as e:\n        logger.exception(str(e))\n        raise RuntimeError(\n            \"[NetGear_Async:ERROR] :: Failed to bind address: {} and pattern: {}{}!\".format(\n                (\n                    self.__protocol\n                    + \"://\"\n                    + str(self.__address)\n                    + \":\"\n                    + str(self.__port)\n                ),\n                self.__msg_pattern,\n                \" and Bidirectional Mode enabled\" if self.__bi_mode else \"\",\n            )\n        )\n\n    # loop until terminated\n    while not self.__terminate:\n        # get encoded data message from server withing timeout limit\n        datamsg_encoded = await asyncio.wait_for(\n            self.__msg_socket.recv(), timeout=self.__timeout\n        )\n        # retrieve data from message\n        data = msgpack.unpackb(datamsg_encoded, use_list=False)\n        # terminate if exit` flag received from server\n        if data[\"terminate\"]:\n            # send confirmation message to server if bidirectional patterns\n            if self.__msg_pattern < 2:\n                # create termination confirmation message\n                return_dict = dict(\n                    terminated=\"Client-`{}` successfully terminated!\".format(\n                        self.__id\n                    ),\n                )\n                # encode message\n                retdata_enc = msgpack.packb(return_dict)\n                # send message back to server\n                await self.__msg_socket.send(retdata_enc)\n            self.__logging and logger.info(\n                \"Termination signal received from server!\"\n            )\n            # break loop and terminate\n            self.__terminate = True\n            break\n        # get encoded frame message from server withing timeout limit\n        framemsg_encoded = await asyncio.wait_for(\n            self.__msg_socket.recv_multipart(), timeout=self.__timeout\n        )\n        # retrieve frame from message\n        frame = msgpack.unpackb(\n            framemsg_encoded[0], use_list=False, object_hook=m.decode\n        )\n\n        # check if bidirectional patterns\n        if self.__msg_pattern < 2:\n            # handle bidirectional data transfer if enabled\n            if self.__bi_mode and data[\"bi_mode\"]:\n                # handle empty queue\n                if not self.__queue.empty():\n                    return_data = await self.__queue.get()\n                    self.__queue.task_done()\n                else:\n                    return_data = None\n                # check if we are returning `ndarray` frames\n                if not (return_data is None) and isinstance(\n                    return_data, np.ndarray\n                ):\n                    # check whether the incoming frame is contiguous\n                    if not (return_data.flags[\"C_CONTIGUOUS\"]):\n                        return_data = np.ascontiguousarray(\n                            return_data, dtype=return_data.dtype\n                        )\n\n                    # create return type dict without data\n                    rettype_dict = dict(\n                        return_type=(type(return_data).__name__),\n                        return_data=None,\n                    )\n                    # encode it\n                    rettype_enc = msgpack.packb(rettype_dict)\n                    # send it to server with correct flags\n                    await self.__msg_socket.send(rettype_enc, flags=zmq.SNDMORE)\n\n                    # encode return ndarray data\n                    retframe_enc = msgpack.packb(return_data, default=m.encode)\n                    # send it over network to server\n                    await self.__msg_socket.send_multipart([retframe_enc])\n                else:\n                    # otherwise create type and data dict\n                    return_dict = dict(\n                        return_type=(type(return_data).__name__),\n                        return_data=(\n                            return_data if not (return_data is None) else \"\"\n                        ),\n                    )\n                    # encode it\n                    retdata_enc = msgpack.packb(return_dict)\n                    # send it over network to server\n                    await self.__msg_socket.send(retdata_enc)\n            elif self.__bi_mode or data[\"bi_mode\"]:\n                # raise error if bidirectional mode is disabled at server or client but not both\n                raise RuntimeError(\n                    \"[NetGear_Async:ERROR] :: Invalid configuration! Bidirectional Mode is not activate on {} end.\".format(\n                        \"client\" if self.__bi_mode else \"server\"\n                    )\n                )\n            else:\n                # otherwise just send confirmation message to server\n                await self.__msg_socket.send(\n                    bytes(\n                        \"Data received on client: {} !\".format(self.__id), \"utf-8\"\n                    )\n                )\n        # yield received tuple(data-frame) if bidirectional mode or else just frame\n        if self.__bi_mode:\n            yield (data[\"data\"], frame) if data[\"data\"] else (None, frame)\n        else:\n            yield frame\n        # sleep for sometime\n        await asyncio.sleep(0)\n
"},{"location":"bonus/reference/netgear_async/#vidgear.gears.asyncio.netgear_async.NetGear_Async.transceive_data","title":"transceive_data(self, data=None) async","text":"

Bidirectional Mode exclusive method to Transmit data (in Receive mode) and Receive data (in Send mode).

Parameters:

Name Type Description Default data any

inputs data (of any datatype) for sending back to Server.

None Source code in vidgear/gears/asyncio/netgear_async.py
async def transceive_data(self, data=None):\n    \"\"\"\n    Bidirectional Mode exclusive method to Transmit data _(in Receive mode)_ and Receive data _(in Send mode)_.\n\n    Parameters:\n        data (any): inputs data _(of any datatype)_ for sending back to Server.\n    \"\"\"\n    recvd_data = None\n    if not self.__terminate:\n        if self.__bi_mode:\n            if self.__receive_mode:\n                await self.__queue.put(data)\n            else:\n                if not self.__queue.empty():\n                    recvd_data = await self.__queue.get()\n                    self.__queue.task_done()\n        else:\n            logger.error(\n                \"`transceive_data()` function cannot be used when Bidirectional Mode is disabled.\"\n            )\n    return recvd_data\n
"},{"location":"bonus/reference/pigear/","title":"PiGear API References","text":"

PiGear API usage examples can be found here \u27b6

PiGear API parameters are explained here \u27b6

PiGear implements a seamless and robust wrapper around the picamera2 python library, simplifying integration with minimal code changes and ensuring a smooth transition for developers already familiar with the Picamera2 API. PiGear leverages the libcamera API under the hood with multi-threading, providing high-performance , enhanced control and functionality for Raspberry Pi camera modules.

PiGear handles common configuration parameters and non-standard settings for various camera types, simplifying the integration process. PiGear currently supports picamera2 API parameters such as sensor, controls, transform, and stride, with internal type and sanity checks for robust performance.

While primarily focused on Raspberry Pi camera modules, PiGear also provides basic functionality for USB webcams only with Picamera2 API, along with the ability to accurately differentiate between USB and Raspberry Pi cameras using metadata.

Backward compatibility with picamera library

PiGear seamlessly switches to the legacy picamera library if the picamera2 library is unavailable, ensuring seamless backward compatibility. For this, PiGear also provides a flexible multi-threaded framework around complete picamera API, allowing developers to effortlessly exploit a wide range of parameters, such as brightness, saturation, sensor_mode, iso, exposure, and more.

Furthermore, PiGear supports the use of multiple camera modules, including those found on Raspberry Pi Compute Module IO boards and USB cameras (only with Picamera2 API).

Threaded Internal Timer

PiGear ensures proper resource release during the termination of the API, preventing potential issues or resource leaks. PiGear API internally implements a Threaded Internal Timer that silently keeps active track of any frozen-threads or hardware-failures and exits safely if any do occur. This means that if you're running the PiGear API in your script and someone accidentally pulls the Camera-Module cable out, instead of going into a possible kernel panic, the API will exit safely to save resources.

Make sure to enable Raspberry Pi hardware-specific settings prior using this API, otherwise nothing will work.

Source code in vidgear/gears/pigear.py
class PiGear:\n    \"\"\"\n    PiGear implements a seamless and robust wrapper around the [picamera2](https://github.com/raspberrypi/picamera2) python library, simplifying integration with minimal code changes and ensuring a\n    smooth transition for developers already familiar with the Picamera2 API. PiGear leverages the `libcamera` API under the hood with multi-threading, providing high-performance :fire:, enhanced\n    control and functionality for Raspberry Pi camera modules.\n\n    PiGear handles common configuration parameters and non-standard settings for various camera types, simplifying the integration process. PiGear currently supports picamera2 API parameters such as\n    `sensor`, `controls`, `transform`, and `stride`, with internal type and sanity checks for robust performance.\n\n    While primarily focused on Raspberry Pi camera modules, PiGear also provides basic functionality for USB webcams only with Picamera2 API, along with the ability to accurately differentiate between\n    USB and Raspberry Pi cameras using metadata.\n\n    ???+ info \"Backward compatibility with `picamera` library\"\n        PiGear seamlessly switches to the legacy [picamera](https://picamera.readthedocs.io/en/release-1.13/index.html) library if the `picamera2` library is unavailable, ensuring seamless backward\n        compatibility. For this, PiGear also provides a flexible multi-threaded framework around complete `picamera` API, allowing developers to effortlessly exploit a wide range of parameters, such\n        as `brightness`, `saturation`, `sensor_mode`, `iso`, `exposure`, and more.\n\n    Furthermore, PiGear supports the use of multiple camera modules, including those found on Raspberry Pi Compute Module IO boards and USB cameras _(only with Picamera2 API)_.\n\n    ??? new \"Threaded Internal Timer :material-camera-timer:\"\n        PiGear ensures proper resource release during the termination of the API, preventing potential issues or resource leaks. PiGear API internally implements a\n        ==Threaded Internal Timer== that silently keeps active track of any frozen-threads or hardware-failures and exits safely if any do occur. This means that if\n        you're running the PiGear API in your script and someone accidentally pulls the Camera-Module cable out, instead of going into a possible kernel panic,\n        the API will exit safely to save resources.\n\n    !!! failure \"Make sure to [enable Raspberry Pi hardware-specific settings](https://picamera.readthedocs.io/en/release-1.13/quickstart.html) prior using this API, otherwise nothing will work.\"\n    \"\"\"\n\n    def __init__(\n        self,\n        camera_num=0,\n        resolution=(640, 480),\n        framerate=30,\n        colorspace=None,\n        logging=False,\n        time_delay=0,\n        **options\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the PiGear class.\n\n        Parameters:\n            camera_num (int): selects the camera module index which will be used as source.\n            resolution (tuple): sets the resolution (i.e. `(width,height)`) of the source..\n            framerate (int/float): sets the framerate of the source.\n            colorspace (str): selects the colorspace of the input stream.\n            logging (bool): enables/disables logging.\n            time_delay (int): time delay (in sec) before start reading the frames.\n            options (dict): provides ability to alter Source Tweak Parameters.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # raise error(s) for critical Class imports\n        global picamera, picamera2\n        if picamera2:\n            # log if picamera2\n            self.__logging and logger.info(\"picamera2 API is currently being accessed.\")\n        elif picamera:\n            # switch to picamera otherwise\n            logger.critical(\n                \"picamera2 library not installed on this system. Defaulting to legacy picamera API.\"\n            )\n        else:\n            # raise error if none\n            import_dependency_safe(\"picamera\")\n\n        assert (\n            isinstance(framerate, (int, float)) and framerate > 0.0\n        ), \"[PiGear:ERROR] :: Input framerate value `{}` is a Invalid! Kindly read docs.\".format(\n            framerate\n        )\n        assert (\n            isinstance(resolution, (tuple, list)) and len(resolution) == 2\n        ), \"[PiGear:ERROR] :: Input resolution value `{}` is a Invalid! Kindly read docs.\".format(\n            resolution\n        )\n        if not (isinstance(camera_num, int) and camera_num >= 0):\n            camera_num = 0\n            logger.warning(\n                \"Input camera_num value `{}` is invalid, Defaulting to index 0!\"\n            )\n\n        # reformat dict\n        options = {str(k).strip(): v for k, v in options.items()}\n\n        # check if legacy picamera backend is enforced\n        enforce_legacy_picamera = options.pop(\"enforce_legacy_picamera\", False)\n        if isinstance(enforce_legacy_picamera, bool) and enforce_legacy_picamera:\n            # check if picamera library is available.\n            if picamera:\n                logger.critical(\n                    \"Enforcing legacy picamera API for this run. picamera2 API access will be disabled!\"\n                )\n                # disable picamera2\n                picamera2 = None\n            else:\n                # raise error otherwise\n                logger.error(\n                    \"`picamera` is unavailable or unsupported on this system, `enforce_legacy_picamera` will be discarded!\"\n                )\n                import_dependency_safe(\"picamera\")\n\n        if picamera2:\n            # handle logging\n            not (self.__logging) and not os.getenv(\n                \"LIBCAMERA_LOG_LEVELS\", False\n            ) and logger.info(\n                \"Kindly set `LIBCAMERA_LOG_LEVELS=2` environment variable to disable common libcamera API messages.\"\n            )\n            # collect metadata\n            cameras_metadata = Picamera2.global_camera_info()\n            # initialize the picamera stream at given index\n            self.__camera = Picamera2(camera_num=camera_num)\n            # extract metadata for current camera\n            camera_metadata = [x for x in cameras_metadata if x[\"Num\"] == camera_num][0]\n            # check connected camera is USB or I2C\n            self.__camera_is_usb = True if \"usb\" in camera_metadata[\"Id\"] else False\n            # handle framerate control\n            if not self.__camera_is_usb:\n                self.__camera.set_controls({\"FrameRate\": framerate})\n            else:\n                logger.warning(\n                    \"USB camera detected. Setting input framerate is NOT supported with Picamera2 API!\"\n                )\n            # log\n            self.__logging and logger.debug(\n                \"Activating Picamera2 API for `{}` camera at index: {} with resolution: {} & framerate: {}\".format(\n                    camera_metadata[\"Model\"],\n                    camera_num,\n                    resolution if not self.__camera_is_usb else \"default\",\n                    framerate,\n                )\n            )\n        else:\n            # initialize the picamera stream at given index\n            self.__camera = PiCamera(camera_num=camera_num)\n            self.__camera.resolution = tuple(resolution)\n            self.__camera.framerate = framerate\n            self.__logging and logger.debug(\n                \"Activating Picamera API at index: {} with resolution: {} & framerate: {}\".format(\n                    camera_num, resolution, framerate\n                )\n            )\n\n        # initialize framerate (Read-only) variable\n        self.framerate = framerate\n\n        # initializing colorspace variable\n        self.color_space = None\n\n        # define timeout variable default value(handles hardware failures)\n        self.__failure_timeout = options.pop(\"HWFAILURE_TIMEOUT\", 2.0)\n        if isinstance(self.__failure_timeout, (int, float)):\n            if not (10.0 > self.__failure_timeout > 1.0):\n                raise ValueError(\n                    \"[PiGear:ERROR] :: `HWFAILURE_TIMEOUT` value can only be between 1.0 ~ 10.0\"\n                )\n            self.__logging and logger.debug(\n                \"Setting HW Failure Timeout: {} seconds\".format(self.__failure_timeout)\n            )\n        else:\n            # reset improper values\n            self.__failure_timeout = 2.0\n\n        try:\n            if picamera2:\n                # define common supported picamera2 config parameters\n                valid_config_options = [\n                    \"auto_align_output_size\",  # internal\n                    \"enable_verbose_logs\",  # internal\n                    \"format\",\n                    \"sensor\",\n                ]\n\n                # define non-USB supported picamera2 config parameters\n                non_usb_options = [\n                    \"controls\",  # not-supported on USB\n                    \"transform\",  # not-working on USB\n                    \"buffer_count\",  # not-supported on USB\n                    \"queue\",  # not-supported on USB\n                ]  # Less are supported (will be changed in future)\n\n                # filter parameter supported with non-USB cameras only\n                if self.__camera_is_usb:\n                    unsupported_config_keys = set(list(options.keys())).intersection(\n                        set(non_usb_options)\n                    )\n                    unsupported_config_keys and logger.warning(\n                        \"Setting parameters: `{}` for USB camera is NOT supported with Picamera2 API!\".format(\n                            \"`, `\".join(unsupported_config_keys)\n                        )\n                    )\n                else:\n                    valid_config_options += non_usb_options\n\n                # log all invalid keys\n                invalid_config_keys = set(list(options.keys())) - set(\n                    valid_config_options\n                )\n                invalid_config_keys and logger.warning(\n                    \"Discarding invalid options NOT supported by Picamera2 API for current Camera Sensor: `{}`\".format(\n                        \"`, `\".join(invalid_config_keys)\n                    )\n                )\n                # delete all unsupported options\n                options = {\n                    x: y for x, y in options.items() if x in valid_config_options\n                }\n\n                # setting size, already defined\n                options.update({\"size\": tuple(resolution)})\n\n                # set 24-bit, BGR format by default\n                if not \"format\" in options:\n                    # auto defaults for USB cameras\n                    not self.__camera_is_usb and options.update({\"format\": \"RGB888\"})\n                elif self.__camera_is_usb:\n                    # check the supported formats, if USB camera\n                    avail_formats = [\n                        mode[\"format\"] for mode in self.__camera.sensor_modes\n                    ]\n                    # handle unsupported formats\n                    if not options[\"format\"] in avail_formats:\n                        logger.warning(\n                            \"Discarding `format={}`. `{}` are the only available formats for USB camera in use!\".format(\n                                options[\"format\"], \"`, `\".join(avail_formats)\n                            )\n                        )\n                        del options[\"format\"]\n                    else:\n                        # `colorspace` parameter must define with  `format` optional parameter\n                        # unless format is MPEG (tested)\n                        (\n                            not (colorspace is None) or options[\"format\"] == \"MPEG\"\n                        ) and logger.warning(\n                            \"Custom Output frames `format={}` detected. It is advised to define `colorspace` parameter or handle this format manually in your code!\".format(\n                                options[\"format\"]\n                            )\n                        )\n                else:\n                    # `colorspace` parameter must define with  `format` optional parameter\n                    # unless format is either BGR or BGRA\n                    (\n                        not (colorspace is None)\n                        or options[\"format\"]\n                        in [\n                            \"RGB888\",\n                            \"XRGB8888\",\n                        ]\n                    ) and logger.warning(\n                        \"Custom Output frames `format={}` detected. It is advised to define `colorspace` parameter or handle this format manually in your code!\".format(\n                            options[\"format\"]\n                        )\n                    )\n\n                # enable verbose logging mode (handled by Picamera2 API)\n                verbose = options.pop(\"enable_verbose_logs\", False)\n                if self.__logging and isinstance(verbose, bool) and verbose:\n                    self.__camera.set_logging(Picamera2.DEBUG)\n                else:\n                    # setup logging\n                    self.__camera.set_logging(Picamera2.WARNING)\n\n                # handle transformations, if specified\n                transform = options.pop(\"transform\", Transform())\n                if not isinstance(transform, Transform):\n                    logger.warning(\"`transform` value is of invalid type, Discarding!\")\n                    transform = Transform()\n\n                # handle sensor configurations, if specified\n                sensor = options.pop(\"sensor\", {})\n                if isinstance(sensor, dict):\n                    # extract all valid sensor keys\n                    valid_sensor = [\"output_size\", \"bit_depth\"]\n                    # log all invalid keys\n                    invalid_sensor_keys = set(list(sensor)) - set(valid_sensor)\n                    invalid_sensor_keys and logger.warning(\n                        \"Discarding sensor properties NOT supported by current Camera Sensor: `{}`. Only supported are: (`{}`)\".format(\n                            \"`, `\".join(invalid_sensor_keys),\n                            \"`, `\".join(valid_sensor),\n                        )\n                    )\n                    # delete all unsupported control keys\n                    sensor = {x: y for x, y in sensor.items() if x in valid_sensor}\n                    # remove size if output size is defined\n                    if \"output_size\" in sensor:\n                        del options[\"size\"]\n                        logger.critical(\n                            \"Overriding output frame size with `output_size={}!\".format(\n                                sensor[\"output_size\"]\n                            )\n                        )\n                else:\n                    logger.warning(\"`sensor` value is of invalid type, Discarding!\")\n                    sensor = {}\n\n                # handle controls, if specified\n                controls = options.pop(\"controls\", {})\n                if isinstance(controls, dict):\n                    # extract all valid control keys\n                    valid_controls = self.__camera.camera_controls\n                    # remove any fps controls, assigned already\n                    valid_controls.pop(\"FrameDuration\", None)\n                    valid_controls.pop(\"FrameDurationLimits\", None)\n                    # log all invalid keys\n                    invalid_control_keys = set(list(controls.keys())) - set(\n                        list(valid_controls.keys())\n                    )\n                    invalid_control_keys and logger.warning(\n                        \"Discarding control properties NOT supported by current Camera Sensor: `{}`. Only supported are: (`{}`)\".format(\n                            \"`, `\".join(invalid_control_keys),\n                            \"`, `\".join(list(valid_controls.keys())),\n                        )\n                    )\n                    # delete all unsupported control keys\n                    controls = {\n                        x: y for x, y in controls.items() if x in valid_controls.keys()\n                    }\n                else:\n                    logger.warning(\"`controls` value is of invalid type, Discarding!\")\n                    controls = {}\n\n                # handle buffer_count, if specified\n                buffer_count = options.pop(\"buffer_count\", 4)\n                if (\n                    not isinstance(buffer_count, int) or buffer_count < 1\n                ):  # must be greater than 1\n                    logger.warning(\n                        \"`buffer_count` value is of invalid type, Discarding!\"\n                    )\n                    # `create_preview_configuration` requests 4 sets of buffers\n                    buffer_count = 4\n\n                # handle queue, if specified\n                queue = options.pop(\"queue\", True)\n                if not isinstance(queue, bool):\n                    logger.warning(\"`queue` value is of invalid type, Discarding!\")\n                    queue = True\n\n                # check if auto-align camera configuration is specified\n                auto_align_output_size = options.pop(\"auto_align_output_size\", False)\n\n                # create default configuration for camera\n                config = self.__camera.create_preview_configuration(\n                    main=options,\n                    transform=transform,\n                    sensor=sensor,\n                    controls=controls,\n                    buffer_count=buffer_count,\n                    queue=queue,\n                )\n\n                # auto-align camera configuration, if specified\n                if isinstance(auto_align_output_size, bool) and auto_align_output_size:\n                    self.__logging and logger.debug(\n                        \"Re-aligning Output frames to optimal size supported by current Camera Sensor.\"\n                    )\n                    self.__camera.align_configuration(config)\n\n                # configure camera\n                self.__camera.configure(config)\n                self.__logging and logger.debug(\n                    \"Setting Picamera2 API Parameters: `{}`, controls: `{}`, sensor: `{}`, buffer_count: `{}`, and queue: `{}`.\".format(\n                        self.__camera.camera_configuration()[\"main\"],\n                        controls,\n                        sensor,\n                        buffer_count,\n                        queue,\n                    )\n                )\n            else:\n                # apply attributes to source if specified\n                for key, value in options.items():\n                    self.__logging and logger.debug(\n                        \"Setting {} API Parameter for Picamera: `{}`\".format(key, value)\n                    )\n                    setattr(self.__camera, key, value)\n        except Exception as e:\n            # Catch if any error occurred\n            logger.exception(str(e))\n\n        # separately handle colorspace value to int conversion\n        if not (colorspace is None):\n            self.color_space = capPropId(colorspace.strip())\n            if self.__logging and not (self.color_space is None):\n                logger.debug(\n                    \"Enabling `{}` colorspace for this video stream!\".format(\n                        colorspace.strip()\n                    )\n                )\n\n        # enable rgb capture array thread and capture stream\n        if not picamera2:\n            self.__rawCapture = PiRGBArray(self.__camera, size=resolution)\n            self.stream = self.__camera.capture_continuous(\n                self.__rawCapture, format=\"bgr\", use_video_port=True\n            )\n\n        # initialize frame variable\n        # with captured frame\n        try:\n            if picamera2:\n                # start camera thread\n                self.__camera.start()\n                # capture frame array\n                self.frame = self.__camera.capture_array(\"main\")\n                # assign camera as stream for setting\n                # parameters after starting the camera\n                self.stream = self.__camera\n            else:\n                # capture frame array from stream\n                stream = next(self.stream)\n                self.frame = stream.array\n                self.__rawCapture.seek(0)\n                self.__rawCapture.truncate()\n            # render colorspace if defined\n            if not (self.frame is None) and not (self.color_space is None):\n                self.frame = cv2.cvtColor(self.frame, self.color_space)\n        except Exception as e:\n            logger.exception(str(e))\n            raise RuntimeError(\"[PiGear:ERROR] :: Camera Module failed to initialize!\")\n\n        # applying time delay to warm-up picamera only if specified\n        if time_delay and isinstance(time_delay, (int, float)):\n            time.sleep(time_delay)\n\n        # thread initialization\n        self.__thread = None\n\n        # timer thread initialization(Keeps check on frozen thread)\n        self.__timer = None\n        self.__t_elapsed = 0.0  # records time taken by thread\n\n        # catching thread exceptions\n        self.__exceptions = None\n\n        # initialize termination flag\n        self.__terminate = False\n\n    def start(self):\n        \"\"\"\n        Launches the internal *Threaded Frames Extractor* daemon\n\n        **Returns:** A reference to the PiGear class object.\n        \"\"\"\n        # Start frame producer thread\n        self.__thread = Thread(target=self.__update, name=\"PiGear\", args=())\n        self.__thread.daemon = True\n        self.__thread.start()\n\n        # Start internal timer thread\n        self.__timer = Thread(target=self.__timeit, name=\"PiTimer\", args=())\n        self.__timer.daemon = True\n        self.__timer.start()\n\n        return self\n\n    def __timeit(self):\n        \"\"\"\n        Threaded Internal Timer that keep checks on thread execution timing\n        \"\"\"\n        # assign current time\n        self.__t_elapsed = time.time()\n\n        # loop until terminated\n        while not (self.__terminate):\n            # check for frozen thread\n            if time.time() - self.__t_elapsed > self.__failure_timeout:\n                # log failure\n                self.__logging and logger.critical(\"Camera Module Disconnected!\")\n                # prepare for clean exit\n                self.__exceptions = True\n                self.__terminate = True  # self-terminate\n\n    def __update(self):\n        \"\"\"\n        A **Threaded Frames Extractor**, that keep iterating frames from PiCamera API to a internal monitored deque,\n        until the thread is terminated, or frames runs out.\n        \"\"\"\n        # keep looping infinitely until the thread is terminated\n        while not (self.__terminate):\n            if not picamera2:\n                try:\n                    # Try to iterate next frame from generator\n                    stream = next(self.stream)\n                except Exception:\n                    # catch and save any exceptions\n                    self.__exceptions = sys.exc_info()\n                    break  # exit\n\n            # __update timer\n            self.__t_elapsed = time.time()\n\n            # grab the frame from the stream\n            if picamera2:\n                frame = self.__camera.capture_array(\"main\")\n            else:\n                frame = stream.array\n                # clear the stream in preparation\n                # for the next frame\n                self.__rawCapture.seek(0)\n                self.__rawCapture.truncate()\n\n            # apply colorspace if specified\n            if not (self.color_space is None):\n                # apply colorspace to frames\n                color_frame = None\n                try:\n                    color_frame = cv2.cvtColor(frame, self.color_space)\n                except Exception as e:\n                    # Catch if any error occurred\n                    color_frame = None\n                    self.color_space = None\n                    self.__logging and logger.exception(str(e))\n                    logger.warning(\"Assigned colorspace value is invalid. Discarding!\")\n                self.frame = color_frame if not (color_frame is None) else frame\n            else:\n                self.frame = frame\n\n        # terminate processes\n        if not (self.__terminate):\n            self.__terminate = True\n\n        # release resources\n        if picamera2:\n            self.__camera.stop()\n        else:\n            self.__rawCapture.close()\n            self.__camera.close()\n\n    def read(self):\n        \"\"\"\n        Extracts frames synchronously from monitored deque, while maintaining a fixed-length frame buffer in the memory,\n        and blocks the thread if the deque is full.\n\n        **Returns:** A n-dimensional numpy array.\n        \"\"\"\n        # check if there are any thread exceptions\n        if not (self.__exceptions is None):\n            if isinstance(self.__exceptions, bool):\n                # clear frame\n                self.frame = None\n                # notify user about hardware failure\n                raise SystemError(\n                    \"[PiGear:ERROR] :: Hardware failure occurred, Kindly reconnect Camera Module and restart your Pi!\"\n                )\n            else:\n                # clear frame\n                self.frame = None\n                # re-raise error for debugging\n                error_msg = (\n                    \"[PiGear:ERROR] :: Camera Module API failure occurred: {}\".format(\n                        self.__exceptions[1]\n                    )\n                )\n                raise RuntimeError(error_msg).with_traceback(self.__exceptions[2])\n        # return the frame\n        return self.frame\n\n    def stop(self):\n        \"\"\"\n        Safely terminates the thread, and release the multi-threaded resources.\n        \"\"\"\n        # log termination\n        self.__logging and logger.debug(\"Terminating PiGear Processes.\")\n\n        # make sure that the threads should be terminated\n        self.__terminate = True\n\n        # stop timer thread\n        if not (self.__timer is None):\n            self.__timer.join()\n            self.__timer = None\n\n        # handle camera thread\n        if not (self.__thread is None):\n            # check if hardware failure occurred\n            if not (self.__exceptions is None) and isinstance(self.__exceptions, bool):\n                if picamera2:\n                    # release picamera2 resources\n                    self.__camera.stop()\n                else:\n                    # force release picamera resources\n                    self.__rawCapture.close()\n                    self.__camera.close()\n            # properly handle thread exit\n            # wait if still process is still\n            # processing some information\n            self.__thread.join()\n            # remove any threads\n            self.__thread = None\n

"},{"location":"bonus/reference/pigear/#vidgear.gears.pigear.PiGear.__init__","title":"__init__(self, camera_num=0, resolution=(640, 480), framerate=30, colorspace=None, logging=False, time_delay=0, **options) special","text":"

This constructor method initializes the object state and attributes of the PiGear class.

Parameters:

Name Type Description Default camera_num int

selects the camera module index which will be used as source.

0 resolution tuple

sets the resolution (i.e. (width,height)) of the source..

(640, 480) framerate int/float

sets the framerate of the source.

30 colorspace str

selects the colorspace of the input stream.

None logging bool

enables/disables logging.

False time_delay int

time delay (in sec) before start reading the frames.

0 options dict

provides ability to alter Source Tweak Parameters.

{} Source code in vidgear/gears/pigear.py
def __init__(\n    self,\n    camera_num=0,\n    resolution=(640, 480),\n    framerate=30,\n    colorspace=None,\n    logging=False,\n    time_delay=0,\n    **options\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the PiGear class.\n\n    Parameters:\n        camera_num (int): selects the camera module index which will be used as source.\n        resolution (tuple): sets the resolution (i.e. `(width,height)`) of the source..\n        framerate (int/float): sets the framerate of the source.\n        colorspace (str): selects the colorspace of the input stream.\n        logging (bool): enables/disables logging.\n        time_delay (int): time delay (in sec) before start reading the frames.\n        options (dict): provides ability to alter Source Tweak Parameters.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # raise error(s) for critical Class imports\n    global picamera, picamera2\n    if picamera2:\n        # log if picamera2\n        self.__logging and logger.info(\"picamera2 API is currently being accessed.\")\n    elif picamera:\n        # switch to picamera otherwise\n        logger.critical(\n            \"picamera2 library not installed on this system. Defaulting to legacy picamera API.\"\n        )\n    else:\n        # raise error if none\n        import_dependency_safe(\"picamera\")\n\n    assert (\n        isinstance(framerate, (int, float)) and framerate > 0.0\n    ), \"[PiGear:ERROR] :: Input framerate value `{}` is a Invalid! Kindly read docs.\".format(\n        framerate\n    )\n    assert (\n        isinstance(resolution, (tuple, list)) and len(resolution) == 2\n    ), \"[PiGear:ERROR] :: Input resolution value `{}` is a Invalid! Kindly read docs.\".format(\n        resolution\n    )\n    if not (isinstance(camera_num, int) and camera_num >= 0):\n        camera_num = 0\n        logger.warning(\n            \"Input camera_num value `{}` is invalid, Defaulting to index 0!\"\n        )\n\n    # reformat dict\n    options = {str(k).strip(): v for k, v in options.items()}\n\n    # check if legacy picamera backend is enforced\n    enforce_legacy_picamera = options.pop(\"enforce_legacy_picamera\", False)\n    if isinstance(enforce_legacy_picamera, bool) and enforce_legacy_picamera:\n        # check if picamera library is available.\n        if picamera:\n            logger.critical(\n                \"Enforcing legacy picamera API for this run. picamera2 API access will be disabled!\"\n            )\n            # disable picamera2\n            picamera2 = None\n        else:\n            # raise error otherwise\n            logger.error(\n                \"`picamera` is unavailable or unsupported on this system, `enforce_legacy_picamera` will be discarded!\"\n            )\n            import_dependency_safe(\"picamera\")\n\n    if picamera2:\n        # handle logging\n        not (self.__logging) and not os.getenv(\n            \"LIBCAMERA_LOG_LEVELS\", False\n        ) and logger.info(\n            \"Kindly set `LIBCAMERA_LOG_LEVELS=2` environment variable to disable common libcamera API messages.\"\n        )\n        # collect metadata\n        cameras_metadata = Picamera2.global_camera_info()\n        # initialize the picamera stream at given index\n        self.__camera = Picamera2(camera_num=camera_num)\n        # extract metadata for current camera\n        camera_metadata = [x for x in cameras_metadata if x[\"Num\"] == camera_num][0]\n        # check connected camera is USB or I2C\n        self.__camera_is_usb = True if \"usb\" in camera_metadata[\"Id\"] else False\n        # handle framerate control\n        if not self.__camera_is_usb:\n            self.__camera.set_controls({\"FrameRate\": framerate})\n        else:\n            logger.warning(\n                \"USB camera detected. Setting input framerate is NOT supported with Picamera2 API!\"\n            )\n        # log\n        self.__logging and logger.debug(\n            \"Activating Picamera2 API for `{}` camera at index: {} with resolution: {} & framerate: {}\".format(\n                camera_metadata[\"Model\"],\n                camera_num,\n                resolution if not self.__camera_is_usb else \"default\",\n                framerate,\n            )\n        )\n    else:\n        # initialize the picamera stream at given index\n        self.__camera = PiCamera(camera_num=camera_num)\n        self.__camera.resolution = tuple(resolution)\n        self.__camera.framerate = framerate\n        self.__logging and logger.debug(\n            \"Activating Picamera API at index: {} with resolution: {} & framerate: {}\".format(\n                camera_num, resolution, framerate\n            )\n        )\n\n    # initialize framerate (Read-only) variable\n    self.framerate = framerate\n\n    # initializing colorspace variable\n    self.color_space = None\n\n    # define timeout variable default value(handles hardware failures)\n    self.__failure_timeout = options.pop(\"HWFAILURE_TIMEOUT\", 2.0)\n    if isinstance(self.__failure_timeout, (int, float)):\n        if not (10.0 > self.__failure_timeout > 1.0):\n            raise ValueError(\n                \"[PiGear:ERROR] :: `HWFAILURE_TIMEOUT` value can only be between 1.0 ~ 10.0\"\n            )\n        self.__logging and logger.debug(\n            \"Setting HW Failure Timeout: {} seconds\".format(self.__failure_timeout)\n        )\n    else:\n        # reset improper values\n        self.__failure_timeout = 2.0\n\n    try:\n        if picamera2:\n            # define common supported picamera2 config parameters\n            valid_config_options = [\n                \"auto_align_output_size\",  # internal\n                \"enable_verbose_logs\",  # internal\n                \"format\",\n                \"sensor\",\n            ]\n\n            # define non-USB supported picamera2 config parameters\n            non_usb_options = [\n                \"controls\",  # not-supported on USB\n                \"transform\",  # not-working on USB\n                \"buffer_count\",  # not-supported on USB\n                \"queue\",  # not-supported on USB\n            ]  # Less are supported (will be changed in future)\n\n            # filter parameter supported with non-USB cameras only\n            if self.__camera_is_usb:\n                unsupported_config_keys = set(list(options.keys())).intersection(\n                    set(non_usb_options)\n                )\n                unsupported_config_keys and logger.warning(\n                    \"Setting parameters: `{}` for USB camera is NOT supported with Picamera2 API!\".format(\n                        \"`, `\".join(unsupported_config_keys)\n                    )\n                )\n            else:\n                valid_config_options += non_usb_options\n\n            # log all invalid keys\n            invalid_config_keys = set(list(options.keys())) - set(\n                valid_config_options\n            )\n            invalid_config_keys and logger.warning(\n                \"Discarding invalid options NOT supported by Picamera2 API for current Camera Sensor: `{}`\".format(\n                    \"`, `\".join(invalid_config_keys)\n                )\n            )\n            # delete all unsupported options\n            options = {\n                x: y for x, y in options.items() if x in valid_config_options\n            }\n\n            # setting size, already defined\n            options.update({\"size\": tuple(resolution)})\n\n            # set 24-bit, BGR format by default\n            if not \"format\" in options:\n                # auto defaults for USB cameras\n                not self.__camera_is_usb and options.update({\"format\": \"RGB888\"})\n            elif self.__camera_is_usb:\n                # check the supported formats, if USB camera\n                avail_formats = [\n                    mode[\"format\"] for mode in self.__camera.sensor_modes\n                ]\n                # handle unsupported formats\n                if not options[\"format\"] in avail_formats:\n                    logger.warning(\n                        \"Discarding `format={}`. `{}` are the only available formats for USB camera in use!\".format(\n                            options[\"format\"], \"`, `\".join(avail_formats)\n                        )\n                    )\n                    del options[\"format\"]\n                else:\n                    # `colorspace` parameter must define with  `format` optional parameter\n                    # unless format is MPEG (tested)\n                    (\n                        not (colorspace is None) or options[\"format\"] == \"MPEG\"\n                    ) and logger.warning(\n                        \"Custom Output frames `format={}` detected. It is advised to define `colorspace` parameter or handle this format manually in your code!\".format(\n                            options[\"format\"]\n                        )\n                    )\n            else:\n                # `colorspace` parameter must define with  `format` optional parameter\n                # unless format is either BGR or BGRA\n                (\n                    not (colorspace is None)\n                    or options[\"format\"]\n                    in [\n                        \"RGB888\",\n                        \"XRGB8888\",\n                    ]\n                ) and logger.warning(\n                    \"Custom Output frames `format={}` detected. It is advised to define `colorspace` parameter or handle this format manually in your code!\".format(\n                        options[\"format\"]\n                    )\n                )\n\n            # enable verbose logging mode (handled by Picamera2 API)\n            verbose = options.pop(\"enable_verbose_logs\", False)\n            if self.__logging and isinstance(verbose, bool) and verbose:\n                self.__camera.set_logging(Picamera2.DEBUG)\n            else:\n                # setup logging\n                self.__camera.set_logging(Picamera2.WARNING)\n\n            # handle transformations, if specified\n            transform = options.pop(\"transform\", Transform())\n            if not isinstance(transform, Transform):\n                logger.warning(\"`transform` value is of invalid type, Discarding!\")\n                transform = Transform()\n\n            # handle sensor configurations, if specified\n            sensor = options.pop(\"sensor\", {})\n            if isinstance(sensor, dict):\n                # extract all valid sensor keys\n                valid_sensor = [\"output_size\", \"bit_depth\"]\n                # log all invalid keys\n                invalid_sensor_keys = set(list(sensor)) - set(valid_sensor)\n                invalid_sensor_keys and logger.warning(\n                    \"Discarding sensor properties NOT supported by current Camera Sensor: `{}`. Only supported are: (`{}`)\".format(\n                        \"`, `\".join(invalid_sensor_keys),\n                        \"`, `\".join(valid_sensor),\n                    )\n                )\n                # delete all unsupported control keys\n                sensor = {x: y for x, y in sensor.items() if x in valid_sensor}\n                # remove size if output size is defined\n                if \"output_size\" in sensor:\n                    del options[\"size\"]\n                    logger.critical(\n                        \"Overriding output frame size with `output_size={}!\".format(\n                            sensor[\"output_size\"]\n                        )\n                    )\n            else:\n                logger.warning(\"`sensor` value is of invalid type, Discarding!\")\n                sensor = {}\n\n            # handle controls, if specified\n            controls = options.pop(\"controls\", {})\n            if isinstance(controls, dict):\n                # extract all valid control keys\n                valid_controls = self.__camera.camera_controls\n                # remove any fps controls, assigned already\n                valid_controls.pop(\"FrameDuration\", None)\n                valid_controls.pop(\"FrameDurationLimits\", None)\n                # log all invalid keys\n                invalid_control_keys = set(list(controls.keys())) - set(\n                    list(valid_controls.keys())\n                )\n                invalid_control_keys and logger.warning(\n                    \"Discarding control properties NOT supported by current Camera Sensor: `{}`. Only supported are: (`{}`)\".format(\n                        \"`, `\".join(invalid_control_keys),\n                        \"`, `\".join(list(valid_controls.keys())),\n                    )\n                )\n                # delete all unsupported control keys\n                controls = {\n                    x: y for x, y in controls.items() if x in valid_controls.keys()\n                }\n            else:\n                logger.warning(\"`controls` value is of invalid type, Discarding!\")\n                controls = {}\n\n            # handle buffer_count, if specified\n            buffer_count = options.pop(\"buffer_count\", 4)\n            if (\n                not isinstance(buffer_count, int) or buffer_count < 1\n            ):  # must be greater than 1\n                logger.warning(\n                    \"`buffer_count` value is of invalid type, Discarding!\"\n                )\n                # `create_preview_configuration` requests 4 sets of buffers\n                buffer_count = 4\n\n            # handle queue, if specified\n            queue = options.pop(\"queue\", True)\n            if not isinstance(queue, bool):\n                logger.warning(\"`queue` value is of invalid type, Discarding!\")\n                queue = True\n\n            # check if auto-align camera configuration is specified\n            auto_align_output_size = options.pop(\"auto_align_output_size\", False)\n\n            # create default configuration for camera\n            config = self.__camera.create_preview_configuration(\n                main=options,\n                transform=transform,\n                sensor=sensor,\n                controls=controls,\n                buffer_count=buffer_count,\n                queue=queue,\n            )\n\n            # auto-align camera configuration, if specified\n            if isinstance(auto_align_output_size, bool) and auto_align_output_size:\n                self.__logging and logger.debug(\n                    \"Re-aligning Output frames to optimal size supported by current Camera Sensor.\"\n                )\n                self.__camera.align_configuration(config)\n\n            # configure camera\n            self.__camera.configure(config)\n            self.__logging and logger.debug(\n                \"Setting Picamera2 API Parameters: `{}`, controls: `{}`, sensor: `{}`, buffer_count: `{}`, and queue: `{}`.\".format(\n                    self.__camera.camera_configuration()[\"main\"],\n                    controls,\n                    sensor,\n                    buffer_count,\n                    queue,\n                )\n            )\n        else:\n            # apply attributes to source if specified\n            for key, value in options.items():\n                self.__logging and logger.debug(\n                    \"Setting {} API Parameter for Picamera: `{}`\".format(key, value)\n                )\n                setattr(self.__camera, key, value)\n    except Exception as e:\n        # Catch if any error occurred\n        logger.exception(str(e))\n\n    # separately handle colorspace value to int conversion\n    if not (colorspace is None):\n        self.color_space = capPropId(colorspace.strip())\n        if self.__logging and not (self.color_space is None):\n            logger.debug(\n                \"Enabling `{}` colorspace for this video stream!\".format(\n                    colorspace.strip()\n                )\n            )\n\n    # enable rgb capture array thread and capture stream\n    if not picamera2:\n        self.__rawCapture = PiRGBArray(self.__camera, size=resolution)\n        self.stream = self.__camera.capture_continuous(\n            self.__rawCapture, format=\"bgr\", use_video_port=True\n        )\n\n    # initialize frame variable\n    # with captured frame\n    try:\n        if picamera2:\n            # start camera thread\n            self.__camera.start()\n            # capture frame array\n            self.frame = self.__camera.capture_array(\"main\")\n            # assign camera as stream for setting\n            # parameters after starting the camera\n            self.stream = self.__camera\n        else:\n            # capture frame array from stream\n            stream = next(self.stream)\n            self.frame = stream.array\n            self.__rawCapture.seek(0)\n            self.__rawCapture.truncate()\n        # render colorspace if defined\n        if not (self.frame is None) and not (self.color_space is None):\n            self.frame = cv2.cvtColor(self.frame, self.color_space)\n    except Exception as e:\n        logger.exception(str(e))\n        raise RuntimeError(\"[PiGear:ERROR] :: Camera Module failed to initialize!\")\n\n    # applying time delay to warm-up picamera only if specified\n    if time_delay and isinstance(time_delay, (int, float)):\n        time.sleep(time_delay)\n\n    # thread initialization\n    self.__thread = None\n\n    # timer thread initialization(Keeps check on frozen thread)\n    self.__timer = None\n    self.__t_elapsed = 0.0  # records time taken by thread\n\n    # catching thread exceptions\n    self.__exceptions = None\n\n    # initialize termination flag\n    self.__terminate = False\n
"},{"location":"bonus/reference/pigear/#vidgear.gears.pigear.PiGear.read","title":"read(self)","text":"

Extracts frames synchronously from monitored deque, while maintaining a fixed-length frame buffer in the memory, and blocks the thread if the deque is full.

Returns: A n-dimensional numpy array.

Source code in vidgear/gears/pigear.py
def read(self):\n    \"\"\"\n    Extracts frames synchronously from monitored deque, while maintaining a fixed-length frame buffer in the memory,\n    and blocks the thread if the deque is full.\n\n    **Returns:** A n-dimensional numpy array.\n    \"\"\"\n    # check if there are any thread exceptions\n    if not (self.__exceptions is None):\n        if isinstance(self.__exceptions, bool):\n            # clear frame\n            self.frame = None\n            # notify user about hardware failure\n            raise SystemError(\n                \"[PiGear:ERROR] :: Hardware failure occurred, Kindly reconnect Camera Module and restart your Pi!\"\n            )\n        else:\n            # clear frame\n            self.frame = None\n            # re-raise error for debugging\n            error_msg = (\n                \"[PiGear:ERROR] :: Camera Module API failure occurred: {}\".format(\n                    self.__exceptions[1]\n                )\n            )\n            raise RuntimeError(error_msg).with_traceback(self.__exceptions[2])\n    # return the frame\n    return self.frame\n
"},{"location":"bonus/reference/pigear/#vidgear.gears.pigear.PiGear.start","title":"start(self)","text":"

Launches the internal Threaded Frames Extractor daemon

Returns: A reference to the PiGear class object.

Source code in vidgear/gears/pigear.py
def start(self):\n    \"\"\"\n    Launches the internal *Threaded Frames Extractor* daemon\n\n    **Returns:** A reference to the PiGear class object.\n    \"\"\"\n    # Start frame producer thread\n    self.__thread = Thread(target=self.__update, name=\"PiGear\", args=())\n    self.__thread.daemon = True\n    self.__thread.start()\n\n    # Start internal timer thread\n    self.__timer = Thread(target=self.__timeit, name=\"PiTimer\", args=())\n    self.__timer.daemon = True\n    self.__timer.start()\n\n    return self\n
"},{"location":"bonus/reference/pigear/#vidgear.gears.pigear.PiGear.stop","title":"stop(self)","text":"

Safely terminates the thread, and release the multi-threaded resources.

Source code in vidgear/gears/pigear.py
def stop(self):\n    \"\"\"\n    Safely terminates the thread, and release the multi-threaded resources.\n    \"\"\"\n    # log termination\n    self.__logging and logger.debug(\"Terminating PiGear Processes.\")\n\n    # make sure that the threads should be terminated\n    self.__terminate = True\n\n    # stop timer thread\n    if not (self.__timer is None):\n        self.__timer.join()\n        self.__timer = None\n\n    # handle camera thread\n    if not (self.__thread is None):\n        # check if hardware failure occurred\n        if not (self.__exceptions is None) and isinstance(self.__exceptions, bool):\n            if picamera2:\n                # release picamera2 resources\n                self.__camera.stop()\n            else:\n                # force release picamera resources\n                self.__rawCapture.close()\n                self.__camera.close()\n        # properly handle thread exit\n        # wait if still process is still\n        # processing some information\n        self.__thread.join()\n        # remove any threads\n        self.__thread = None\n
"},{"location":"bonus/reference/screengear/","title":"ScreenGear API References","text":"

ScreenGear API usage examples can be found here \u27b6

ScreenGear API parameters are explained here \u27b6

ScreenGear is designed exclusively for targeting rapid Screencasting Capabilities, which means it can grab frames from your monitor in real-time, either by defining an area on the computer screen or full-screen, at the expense of inconsiderable latency. ScreenGear also seamlessly support frame capturing from multiple monitors as well as supports multiple backends.

ScreenGear API implements a multi-threaded wrapper around dxcam, pyscreenshot, python-mss python library, and also flexibly supports its internal parameter.

Source code in vidgear/gears/screengear.py
class ScreenGear:\n    \"\"\"\n    ScreenGear is designed exclusively for targeting rapid Screencasting Capabilities, which means it can\n    grab frames from your monitor in real-time, either by defining an area on the computer screen or full-screen,\n    at the expense of inconsiderable latency. ScreenGear also seamlessly support frame capturing from multiple\n    monitors as well as supports multiple backends.\n\n    ScreenGear API implements a multi-threaded wrapper around dxcam, pyscreenshot, python-mss python library,\n    and also flexibly supports its internal parameter.\n    \"\"\"\n\n    def __init__(\n        self, monitor=None, backend=None, colorspace=None, logging=False, **options\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the ScreenGear class.\n\n        Parameters:\n            monitor (int): enables `mss` backend and sets the index of the monitor screen.\n            backend (str): select suitable backend for extracting frames.\n            colorspace (str): selects the colorspace of the input stream.\n            logging (bool): enables/disables logging.\n            options (dict): provides the flexibility to easily alter backend library parameters. Such as, manually set the dimensions of capture screen area etc.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # create instances for the user-defined monitor\n        self.__monitor_instance = None\n        self.__backend = None\n\n        # validate monitor instance\n        assert (\n            monitor is None or monitor and isinstance(monitor, (int, tuple))\n        ), \"[ScreenGear:ERROR] :: Invalid `monitor` value detected!\"\n\n        # initialize backend\n        if backend and monitor is None:\n            self.__backend = backend.lower().strip()\n        else:\n            # enforce `dxcam` for Windows machines if undefined (or monitor is defined)\n            self.__backend = (\n                \"dxcam\" if platform.system() == \"Windows\" and dxcam else None\n            )\n\n        # initiate screen dimension handler\n        screen_dims = {}\n        # reformat proper mss dict and assign to screen dimension handler\n        screen_dims = {\n            k.strip(): v\n            for k, v in options.items()\n            if k.strip() in [\"top\", \"left\", \"width\", \"height\"]\n        }\n        # check whether user-defined dimensions are provided\n        if screen_dims and len(screen_dims) == 4:\n            key_order = (\n                (\"top\", \"left\", \"width\", \"height\")\n                if self.__backend != \"dxcam\"\n                else (\"left\", \"top\", \"width\", \"height\")\n            )\n            screen_dims = OrderedDict((k, screen_dims[k]) for k in key_order)\n            self.__logging and logger.debug(\n                \"Setting Capture-Area dimensions: {}\".format(json.dumps(screen_dims))\n            )\n        else:\n            screen_dims.clear()\n\n        # handle backends\n        if self.__backend == \"dxcam\":\n            # get target fps in case of DXcam\n            self.__target_fps = options.pop(\"dxcam_target_fps\", 0)\n            if self.__target_fps and isinstance(self.__target_fps, (int, float)):\n                # set values\n                self.__target_fps = int(self.__target_fps)\n                self.__logging and logger.debug(\n                    \"Setting Target FPS: {}\".format(self.__target_fps)\n                )\n            else:\n                # defaults to 0fps\n                self.__target_fps = 0\n            # check if platform is windows\n            assert (\n                platform.system() == \"Windows\"\n            ), \"`dxcam` backend is only available for Windows Machines.\"\n            # verify monitor values if tuple\n            assert (\n                monitor is None\n                or isinstance(monitor, int)\n                or (\n                    isinstance(monitor, tuple)\n                    and len(monitor) == 2\n                    and all(isinstance(x, int) for x in monitor)\n                )\n            ), \"For dxcam` backend, monitor` tuple value must be format `int` or `(int, int)` only.\"\n            # raise error(s) for critical Class imports\n            import_dependency_safe(\"dxcam\" if dxcam is None else \"\")\n            if monitor is None:\n                self.__capture_object = dxcam.create(\n                    region=tuple(screen_dims.values()) if screen_dims else None\n                )\n            else:\n                self.__capture_object = (\n                    dxcam.create(\n                        device_idx=monitor[0],\n                        output_idx=monitor[1],\n                        region=tuple(screen_dims.values()) if screen_dims else None,\n                    )\n                    if isinstance(monitor, tuple)\n                    else dxcam.create(\n                        device_idx=monitor,\n                        region=tuple(screen_dims.values()) if screen_dims else None,\n                    )\n                )\n        else:\n            if monitor is None:\n                # raise error(s) for critical Class imports\n                import_dependency_safe(\"pyscreenshot\" if pysct is None else \"\")\n                # reset backend if not provided\n                self.__backend = \"pil\" if self.__backend is None else self.__backend\n                # check if valid backend\n                assert (\n                    self.__backend in pysct.backends()\n                ), \"Unsupported backend {} provided!\".format(backend)\n                # create capture object\n                self.__capture_object = pysct\n            else:\n                # monitor value must be integer\n                assert monitor and isinstance(\n                    monitor, int\n                ), \"[ScreenGear:ERROR] :: Invalid `monitor` value must be integer with mss backend.\"\n                # raise error(s) for critical Class imports\n                import_dependency_safe(\n                    \"from mss import mss\" if mss is None else \"\", pkg_name=\"mss\"\n                )\n                # create capture object\n                self.__capture_object = mss()\n                self.__backend and logger.warning(\n                    \"Backends are disabled for Monitor Indexing(monitor>=0)!\"\n                )\n                self.__monitor_instance = self.__capture_object.monitors[monitor]\n\n        # log backend\n        self.__backend and self.__logging and logger.debug(\n            \"Setting Backend: {}\".format(self.__backend.upper())\n        )\n\n        # assigns special parameter to global variable and clear\n        # separately handle colorspace value to int conversion\n        if colorspace:\n            self.color_space = capPropId(colorspace.strip())\n            self.__logging and not (self.color_space is None) and logger.debug(\n                \"Enabling `{}` colorspace for this video stream!\".format(\n                    colorspace.strip()\n                )\n            )\n        else:\n            self.color_space = None\n\n        # initialize mss capture instance\n        self.__mss_capture_instance = None\n        try:\n            if self.__backend == \"dxcam\":\n                # extract global frame from instance\n                self.frame = self.__capture_object.grab()\n            else:\n                if self.__monitor_instance is None:\n                    if screen_dims:\n                        self.__mss_capture_instance = tuple(screen_dims.values())\n                    # extract global frame from instance\n                    self.frame = np.asanyarray(\n                        self.__capture_object.grab(\n                            bbox=self.__mss_capture_instance,\n                            childprocess=False,\n                            backend=self.__backend,\n                        )\n                    )\n                else:\n                    if screen_dims:\n                        self.__mss_capture_instance = {\n                            \"top\": self.__monitor_instance[\"top\"] + screen_dims[\"top\"],\n                            \"left\": self.__monitor_instance[\"left\"]\n                            + screen_dims[\"left\"],\n                            \"width\": screen_dims[\"width\"],\n                            \"height\": screen_dims[\"height\"],\n                            \"mon\": monitor,\n                        }\n                    else:\n                        self.__mss_capture_instance = (\n                            self.__monitor_instance  # otherwise create instance from monitor\n                        )\n                    # extract global frame from instance\n                    self.frame = np.asanyarray(\n                        self.__capture_object.grab(self.__mss_capture_instance)\n                    )\n            # convert to bgr frame if applicable\n            self.frame = (\n                self.frame[:, :, ::-1]\n                if self.__backend == \"dxcam\" or not (pysct is None)\n                else self.frame\n            )\n            # render colorspace if defined\n            if not (self.frame is None) and not (self.color_space is None):\n                self.frame = cv2.cvtColor(self.frame, self.color_space)\n        except Exception as e:\n            if isinstance(e, ScreenShotError):\n                # otherwise catch and log errors\n                self.__logging and logger.exception(\n                    self.__capture_object.get_error_details()\n                )\n                raise ValueError(\n                    \"[ScreenGear:ERROR] :: ScreenShotError caught, Wrong dimensions passed to python-mss, Kindly Refer Docs!\"\n                )\n            else:\n                raise SystemError(\n                    \"[ScreenGear:ERROR] :: Unable to grab any instance on this system, Are you running headless?\"\n                )\n        # thread initialization\n        self.__thread = None\n        # initialize termination flag\n        self.__terminate = Event()\n\n    def start(self):\n        \"\"\"\n        Launches the internal *Threaded Frames Extractor* daemon\n\n        **Returns:** A reference to the ScreenGear class object.\n        \"\"\"\n        self.__thread = Thread(target=self.__update, name=\"ScreenGear\", args=())\n        self.__thread.daemon = True\n        self.__thread.start()\n        if self.__backend == \"dxcam\":\n            self.__capture_object.start(\n                target_fps=self.__target_fps,\n                video_mode=True,\n            )\n            self.__logging and self.__target_fps and logger.debug(\n                \"Targeting FPS: {}\".format(self.__target_fps)\n            )\n        return self\n\n    def __update(self):\n        \"\"\"\n        A **Threaded Frames Extractor**, that keep iterating frames from `mss` API to a internal monitored deque,\n        until the thread is terminated, or frames runs out.\n        \"\"\"\n        # initialize frame variable\n        frame = None\n        # keep looping infinitely until the thread is terminated\n        while not self.__terminate.is_set():\n            try:\n                if self.__backend == \"dxcam\":\n                    # extract global frame from instance\n                    frame = self.__capture_object.get_latest_frame()\n                else:\n                    if self.__monitor_instance:\n                        frame = np.asanyarray(\n                            self.__capture_object.grab(self.__mss_capture_instance)\n                        )\n                    else:\n                        frame = np.asanyarray(\n                            self.__capture_object.grab(\n                                bbox=self.__mss_capture_instance,\n                                childprocess=False,\n                                backend=self.__backend,\n                            )\n                        )\n                # check if valid frame\n                assert not (\n                    frame is None or np.shape(frame) == ()\n                ), \"[ScreenGear:ERROR] :: Failed to retrieve valid frame!\"\n                # convert to bgr frame if applicable\n                frame = (\n                    frame[:, :, ::-1]\n                    if self.__backend == \"dxcam\" or not (pysct is None)\n                    else frame\n                )\n            except Exception as e:\n                if isinstance(e, ScreenShotError):\n                    raise RuntimeError(self.__capture_object.get_error_details())\n                else:\n                    logger.exception(str(e))\n                self.__terminate.set()\n                continue\n\n            if not (self.color_space is None):\n                # apply colorspace to frames\n                color_frame = None\n                try:\n                    color_frame = cv2.cvtColor(frame, self.color_space)\n                except Exception as e:\n                    # Catch if any error occurred\n                    color_frame = None\n                    self.color_space = None\n                    self.__logging and logger.exception(str(e))\n                    logger.warning(\"Assigned colorspace value is invalid. Discarding!\")\n                self.frame = color_frame if not (color_frame is None) else frame\n            else:\n                self.frame = frame\n\n        # indicate immediate termination\n        self.__terminate.set()\n\n        # finally release mss resources\n        if self.__monitor_instance:\n            self.__capture_object.close()\n        if self.__backend == \"dxcam\":\n            self.__capture_object.stop()\n            del self.__capture_object\n\n    def read(self):\n        \"\"\"\n        Extracts frames synchronously from monitored deque, while maintaining a fixed-length frame buffer in the memory,\n        and blocks the thread if the deque is full.\n\n        **Returns:** A n-dimensional numpy array.\n        \"\"\"\n        # return the frame\n        return self.frame\n\n    def stop(self):\n        \"\"\"\n        Safely terminates the thread, and release the resources.\n        \"\"\"\n        self.__logging and logger.debug(\"Terminating ScreenGear Processes.\")\n\n        # indicate that the thread should be terminate\n        self.__terminate.set()\n\n        # wait until stream resources are released (producer thread might be still grabbing frame)\n        not (self.__thread is None) and self.__thread.join()\n

"},{"location":"bonus/reference/screengear/#vidgear.gears.screengear.ScreenGear.__init__","title":"__init__(self, monitor=None, backend=None, colorspace=None, logging=False, **options) special","text":"

This constructor method initializes the object state and attributes of the ScreenGear class.

Parameters:

Name Type Description Default monitor int

enables mss backend and sets the index of the monitor screen.

None backend str

select suitable backend for extracting frames.

None colorspace str

selects the colorspace of the input stream.

None logging bool

enables/disables logging.

False options dict

provides the flexibility to easily alter backend library parameters. Such as, manually set the dimensions of capture screen area etc.

{} Source code in vidgear/gears/screengear.py
def __init__(\n    self, monitor=None, backend=None, colorspace=None, logging=False, **options\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the ScreenGear class.\n\n    Parameters:\n        monitor (int): enables `mss` backend and sets the index of the monitor screen.\n        backend (str): select suitable backend for extracting frames.\n        colorspace (str): selects the colorspace of the input stream.\n        logging (bool): enables/disables logging.\n        options (dict): provides the flexibility to easily alter backend library parameters. Such as, manually set the dimensions of capture screen area etc.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # create instances for the user-defined monitor\n    self.__monitor_instance = None\n    self.__backend = None\n\n    # validate monitor instance\n    assert (\n        monitor is None or monitor and isinstance(monitor, (int, tuple))\n    ), \"[ScreenGear:ERROR] :: Invalid `monitor` value detected!\"\n\n    # initialize backend\n    if backend and monitor is None:\n        self.__backend = backend.lower().strip()\n    else:\n        # enforce `dxcam` for Windows machines if undefined (or monitor is defined)\n        self.__backend = (\n            \"dxcam\" if platform.system() == \"Windows\" and dxcam else None\n        )\n\n    # initiate screen dimension handler\n    screen_dims = {}\n    # reformat proper mss dict and assign to screen dimension handler\n    screen_dims = {\n        k.strip(): v\n        for k, v in options.items()\n        if k.strip() in [\"top\", \"left\", \"width\", \"height\"]\n    }\n    # check whether user-defined dimensions are provided\n    if screen_dims and len(screen_dims) == 4:\n        key_order = (\n            (\"top\", \"left\", \"width\", \"height\")\n            if self.__backend != \"dxcam\"\n            else (\"left\", \"top\", \"width\", \"height\")\n        )\n        screen_dims = OrderedDict((k, screen_dims[k]) for k in key_order)\n        self.__logging and logger.debug(\n            \"Setting Capture-Area dimensions: {}\".format(json.dumps(screen_dims))\n        )\n    else:\n        screen_dims.clear()\n\n    # handle backends\n    if self.__backend == \"dxcam\":\n        # get target fps in case of DXcam\n        self.__target_fps = options.pop(\"dxcam_target_fps\", 0)\n        if self.__target_fps and isinstance(self.__target_fps, (int, float)):\n            # set values\n            self.__target_fps = int(self.__target_fps)\n            self.__logging and logger.debug(\n                \"Setting Target FPS: {}\".format(self.__target_fps)\n            )\n        else:\n            # defaults to 0fps\n            self.__target_fps = 0\n        # check if platform is windows\n        assert (\n            platform.system() == \"Windows\"\n        ), \"`dxcam` backend is only available for Windows Machines.\"\n        # verify monitor values if tuple\n        assert (\n            monitor is None\n            or isinstance(monitor, int)\n            or (\n                isinstance(monitor, tuple)\n                and len(monitor) == 2\n                and all(isinstance(x, int) for x in monitor)\n            )\n        ), \"For dxcam` backend, monitor` tuple value must be format `int` or `(int, int)` only.\"\n        # raise error(s) for critical Class imports\n        import_dependency_safe(\"dxcam\" if dxcam is None else \"\")\n        if monitor is None:\n            self.__capture_object = dxcam.create(\n                region=tuple(screen_dims.values()) if screen_dims else None\n            )\n        else:\n            self.__capture_object = (\n                dxcam.create(\n                    device_idx=monitor[0],\n                    output_idx=monitor[1],\n                    region=tuple(screen_dims.values()) if screen_dims else None,\n                )\n                if isinstance(monitor, tuple)\n                else dxcam.create(\n                    device_idx=monitor,\n                    region=tuple(screen_dims.values()) if screen_dims else None,\n                )\n            )\n    else:\n        if monitor is None:\n            # raise error(s) for critical Class imports\n            import_dependency_safe(\"pyscreenshot\" if pysct is None else \"\")\n            # reset backend if not provided\n            self.__backend = \"pil\" if self.__backend is None else self.__backend\n            # check if valid backend\n            assert (\n                self.__backend in pysct.backends()\n            ), \"Unsupported backend {} provided!\".format(backend)\n            # create capture object\n            self.__capture_object = pysct\n        else:\n            # monitor value must be integer\n            assert monitor and isinstance(\n                monitor, int\n            ), \"[ScreenGear:ERROR] :: Invalid `monitor` value must be integer with mss backend.\"\n            # raise error(s) for critical Class imports\n            import_dependency_safe(\n                \"from mss import mss\" if mss is None else \"\", pkg_name=\"mss\"\n            )\n            # create capture object\n            self.__capture_object = mss()\n            self.__backend and logger.warning(\n                \"Backends are disabled for Monitor Indexing(monitor>=0)!\"\n            )\n            self.__monitor_instance = self.__capture_object.monitors[monitor]\n\n    # log backend\n    self.__backend and self.__logging and logger.debug(\n        \"Setting Backend: {}\".format(self.__backend.upper())\n    )\n\n    # assigns special parameter to global variable and clear\n    # separately handle colorspace value to int conversion\n    if colorspace:\n        self.color_space = capPropId(colorspace.strip())\n        self.__logging and not (self.color_space is None) and logger.debug(\n            \"Enabling `{}` colorspace for this video stream!\".format(\n                colorspace.strip()\n            )\n        )\n    else:\n        self.color_space = None\n\n    # initialize mss capture instance\n    self.__mss_capture_instance = None\n    try:\n        if self.__backend == \"dxcam\":\n            # extract global frame from instance\n            self.frame = self.__capture_object.grab()\n        else:\n            if self.__monitor_instance is None:\n                if screen_dims:\n                    self.__mss_capture_instance = tuple(screen_dims.values())\n                # extract global frame from instance\n                self.frame = np.asanyarray(\n                    self.__capture_object.grab(\n                        bbox=self.__mss_capture_instance,\n                        childprocess=False,\n                        backend=self.__backend,\n                    )\n                )\n            else:\n                if screen_dims:\n                    self.__mss_capture_instance = {\n                        \"top\": self.__monitor_instance[\"top\"] + screen_dims[\"top\"],\n                        \"left\": self.__monitor_instance[\"left\"]\n                        + screen_dims[\"left\"],\n                        \"width\": screen_dims[\"width\"],\n                        \"height\": screen_dims[\"height\"],\n                        \"mon\": monitor,\n                    }\n                else:\n                    self.__mss_capture_instance = (\n                        self.__monitor_instance  # otherwise create instance from monitor\n                    )\n                # extract global frame from instance\n                self.frame = np.asanyarray(\n                    self.__capture_object.grab(self.__mss_capture_instance)\n                )\n        # convert to bgr frame if applicable\n        self.frame = (\n            self.frame[:, :, ::-1]\n            if self.__backend == \"dxcam\" or not (pysct is None)\n            else self.frame\n        )\n        # render colorspace if defined\n        if not (self.frame is None) and not (self.color_space is None):\n            self.frame = cv2.cvtColor(self.frame, self.color_space)\n    except Exception as e:\n        if isinstance(e, ScreenShotError):\n            # otherwise catch and log errors\n            self.__logging and logger.exception(\n                self.__capture_object.get_error_details()\n            )\n            raise ValueError(\n                \"[ScreenGear:ERROR] :: ScreenShotError caught, Wrong dimensions passed to python-mss, Kindly Refer Docs!\"\n            )\n        else:\n            raise SystemError(\n                \"[ScreenGear:ERROR] :: Unable to grab any instance on this system, Are you running headless?\"\n            )\n    # thread initialization\n    self.__thread = None\n    # initialize termination flag\n    self.__terminate = Event()\n
"},{"location":"bonus/reference/screengear/#vidgear.gears.screengear.ScreenGear.read","title":"read(self)","text":"

Extracts frames synchronously from monitored deque, while maintaining a fixed-length frame buffer in the memory, and blocks the thread if the deque is full.

Returns: A n-dimensional numpy array.

Source code in vidgear/gears/screengear.py
def read(self):\n    \"\"\"\n    Extracts frames synchronously from monitored deque, while maintaining a fixed-length frame buffer in the memory,\n    and blocks the thread if the deque is full.\n\n    **Returns:** A n-dimensional numpy array.\n    \"\"\"\n    # return the frame\n    return self.frame\n
"},{"location":"bonus/reference/screengear/#vidgear.gears.screengear.ScreenGear.start","title":"start(self)","text":"

Launches the internal Threaded Frames Extractor daemon

Returns: A reference to the ScreenGear class object.

Source code in vidgear/gears/screengear.py
def start(self):\n    \"\"\"\n    Launches the internal *Threaded Frames Extractor* daemon\n\n    **Returns:** A reference to the ScreenGear class object.\n    \"\"\"\n    self.__thread = Thread(target=self.__update, name=\"ScreenGear\", args=())\n    self.__thread.daemon = True\n    self.__thread.start()\n    if self.__backend == \"dxcam\":\n        self.__capture_object.start(\n            target_fps=self.__target_fps,\n            video_mode=True,\n        )\n        self.__logging and self.__target_fps and logger.debug(\n            \"Targeting FPS: {}\".format(self.__target_fps)\n        )\n    return self\n
"},{"location":"bonus/reference/screengear/#vidgear.gears.screengear.ScreenGear.stop","title":"stop(self)","text":"

Safely terminates the thread, and release the resources.

Source code in vidgear/gears/screengear.py
def stop(self):\n    \"\"\"\n    Safely terminates the thread, and release the resources.\n    \"\"\"\n    self.__logging and logger.debug(\"Terminating ScreenGear Processes.\")\n\n    # indicate that the thread should be terminate\n    self.__terminate.set()\n\n    # wait until stream resources are released (producer thread might be still grabbing frame)\n    not (self.__thread is None) and self.__thread.join()\n
"},{"location":"bonus/reference/stabilizer/","title":"API References","text":"

Stabilizer API usage examples can be found here \u27b6

Stabilizer API parameters are explained here \u27b6

This is an auxiliary class that enables Video Stabilization for vidgear with minimalistic latency, and at the expense of little to no additional computational requirements.

The basic idea behind it is to tracks and save the salient feature array for the given number of frames and then uses these anchor point to cancel out all perturbations relative to it for the incoming frames in the queue. This class relies heavily on Threaded Queue mode for error-free & ultra-fast frame handling.

Source code in vidgear/gears/stabilizer.py
class Stabilizer:\n    \"\"\"\n    This is an auxiliary class that enables Video Stabilization for vidgear with minimalistic latency, and at the expense\n    of little to no additional computational requirements.\n\n    The basic idea behind it is to tracks and save the salient feature array for the given number of frames and then uses\n    these anchor point to cancel out all perturbations relative to it for the incoming frames in the queue. This class relies\n    heavily on **Threaded Queue mode** for error-free & ultra-fast frame handling.\n    \"\"\"\n\n    def __init__(\n        self,\n        smoothing_radius=25,\n        border_type=\"black\",\n        border_size=0,\n        crop_n_zoom=False,\n        logging=False,\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the Stabilizer class.\n\n        Parameters:\n            smoothing_radius (int): alter averaging window size.\n            border_type (str): changes the extended border type.\n            border_size (int): enables and set the value for extended border size to reduce the black borders.\n            crop_n_zoom (bool): enables cropping and zooming of frames(to original size) to reduce the black borders.\n            logging (bool): enables/disables logging.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # initialize deques for handling input frames and its indexes\n        self.__frame_queue = deque(maxlen=smoothing_radius)\n        self.__frame_queue_indexes = deque(maxlen=smoothing_radius)\n\n        # define and create Adaptive histogram equalization (AHE) object for optimizations\n        self.__clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))\n\n        # initialize global vars\n        self.__smoothing_radius = smoothing_radius  # averaging window, handles the quality of stabilization at expense of latency and sudden panning\n        self.__smoothed_path = None  # handles the smoothed path with box filter\n        self.__path = None  # handles path i.e cumulative sum of previous_2_current transformations along a axis\n        self.__transforms = []  # handles previous_2_current transformations [dx,dy,da]\n        self.__frame_transforms_smoothed = None  # handles smoothed array of previous_2_current transformations w.r.t to frames\n        self.__previous_gray = None  # handles previous gray frame\n        self.__previous_keypoints = (\n            None  # handles previous detect_GFTTed keypoints w.r.t previous gray frame\n        )\n        self.__frame_height, self.frame_width = (\n            0,\n            0,\n        )  # handles width and height of input frames\n        self.__crop_n_zoom = 0  # handles cropping and zooms frames to reduce the black borders from stabilization being too noticeable.\n\n        # if check if crop_n_zoom defined\n        if crop_n_zoom and border_size:\n            self.__crop_n_zoom = border_size  # crops and zoom frame to original size\n            self.__border_size = 0  # zero out border size\n            self.__frame_size = None  # handles frame size for zooming\n            self.__logging and logger.debug(\n                \"Setting Cropping margin {} pixels\".format(border_size)\n            )\n        else:\n            # Add output borders to frame\n            self.__border_size = border_size\n            self.__logging and border_size and logger.debug(\n                \"Setting Border size {} pixels\".format(border_size)\n            )\n\n        # define valid border modes\n        border_modes = {\n            \"black\": cv2.BORDER_CONSTANT,\n            \"reflect\": cv2.BORDER_REFLECT,\n            \"reflect_101\": cv2.BORDER_REFLECT_101,\n            \"replicate\": cv2.BORDER_REPLICATE,\n            \"wrap\": cv2.BORDER_WRAP,\n        }\n        # choose valid border_mode from border_type\n        if border_type in [\"black\", \"reflect\", \"reflect_101\", \"replicate\", \"wrap\"]:\n            if not crop_n_zoom:\n                # initialize global border mode variable\n                self.__border_mode = border_modes[border_type]\n                self.__logging and border_type != \"black\" and logger.info(\n                    \"Setting Border type: {}\".format(border_type)\n                )\n            else:\n                # log and reset to default\n                self.__logging and border_type != \"black\" and logger.debug(\n                    \"Setting border type is disabled if cropping is enabled!\"\n                )\n                self.__border_mode = border_modes[\"black\"]\n        else:\n            # otherwise log if not\n            self.__logging and logger.debug(\"Invalid input border type!\")\n            self.__border_mode = border_modes[\"black\"]  # reset to default mode\n\n        # define OpenCV version\n        self.__cv2_version = check_CV_version()\n\n        # retrieve best interpolation\n        self.__interpolation = retrieve_best_interpolation(\n            [\"INTER_LINEAR_EXACT\", \"INTER_LINEAR\", \"INTER_AREA\"]\n        )\n\n        # define normalized box filter\n        self.__box_filter = np.ones(smoothing_radius) / smoothing_radius\n\n    def stabilize(self, frame):\n        \"\"\"\n        This method takes an unstabilized video frame, and returns a stabilized one.\n\n        Parameters:\n            frame (numpy.ndarray): inputs unstabilized video frames.\n        \"\"\"\n        # check if frame is None\n        if frame is None:\n            # return if it does\n            return\n\n        # save frame size for zooming\n        if self.__crop_n_zoom and self.__frame_size == None:\n            self.__frame_size = frame.shape[:2]\n\n        # initiate transformations capturing\n        if not self.__frame_queue:\n            # for first frame\n            previous_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)  # convert to gray\n            previous_gray = self.__clahe.apply(previous_gray)  # optimize gray frame\n            self.__previous_keypoints = cv2.goodFeaturesToTrack(\n                previous_gray,\n                maxCorners=200,\n                qualityLevel=0.05,\n                minDistance=30.0,\n                blockSize=3,\n                mask=None,\n                useHarrisDetector=False,\n                k=0.04,\n            )  # track features using GFTT\n            self.__frame_height, self.frame_width = frame.shape[\n                :2\n            ]  # save input frame height and width\n            self.__frame_queue.append(frame)  # save frame to deque\n            self.__frame_queue_indexes.append(0)  # save frame index to deque\n            self.__previous_gray = previous_gray[\n                :\n            ]  # save gray frame clone for further processing\n\n        elif self.__frame_queue_indexes[-1] < self.__smoothing_radius - 1:\n            # for rest of frames\n            self.__frame_queue.append(frame)  # save frame to deque\n            self.__frame_queue_indexes.append(\n                self.__frame_queue_indexes[-1] + 1\n            )  # save frame index\n            self.__generate_transformations()  # generate transformations\n        else:\n            # start applying transformations\n            self.__frame_queue.append(frame)  # save frame to deque\n            self.__frame_queue_indexes.append(\n                self.__frame_queue_indexes[-1] + 1\n            )  # save frame index\n            self.__generate_transformations()  # generate transformations\n            # calculate smooth path once transformation capturing is completed\n            for i in range(3):\n                # apply normalized box filter to the path\n                self.__smoothed_path[:, i] = self.__box_filter_convolve(\n                    (self.__path[:, i]), window_size=self.__smoothing_radius\n                )\n            # calculate deviation of path from smoothed path\n            deviation = self.__smoothed_path - self.__path\n            # save smoothed transformation\n            self.__frame_transforms_smoothed = self.frame_transform + deviation\n            # return transformation applied stabilized frame\n            return self.__apply_transformations()\n\n    def __generate_transformations(self):\n        \"\"\"\n        An internal method that generate previous-to-current transformations [dx,dy,da].\n        \"\"\"\n        frame_gray = cv2.cvtColor(\n            self.__frame_queue[-1], cv2.COLOR_BGR2GRAY\n        )  # retrieve current frame and convert to gray\n        frame_gray = self.__clahe.apply(frame_gray)  # optimize it\n\n        transformation = None\n        try:\n            # calculate optical flow using Lucas-Kanade differential method\n            curr_kps, status, error = cv2.calcOpticalFlowPyrLK(\n                self.__previous_gray, frame_gray, self.__previous_keypoints, None\n            )\n\n            # select only valid key-points\n            valid_curr_kps = curr_kps[status == 1]  # current\n            valid_previous_keypoints = self.__previous_keypoints[\n                status == 1\n            ]  # previous\n\n            # calculate optimal affine transformation between previous_2_current key-points\n            if self.__cv2_version == 3:\n                # backward compatibility with OpenCV3\n                transformation = cv2.estimateRigidTransform(\n                    valid_previous_keypoints, valid_curr_kps, False\n                )\n            else:\n                transformation = cv2.estimateAffinePartial2D(\n                    valid_previous_keypoints, valid_curr_kps\n                )[0]\n        except cv2.error as e:\n            # catch any OpenCV assertion errors and warn user\n            logger.warning(\"Video-Frame is too dark to generate any transformations!\")\n            transformation = None\n\n        # check if transformation is not None\n        if not (transformation is None):\n            # previous_2_current translation in x direction\n            dx = transformation[0, 2]\n            # previous_2_current translation in y direction\n            dy = transformation[1, 2]\n            # previous_2_current rotation in angle\n            da = np.arctan2(transformation[1, 0], transformation[0, 0])\n        else:\n            # otherwise zero it\n            dx = dy = da = 0\n\n        # save this transformation\n        self.__transforms.append([dx, dy, da])\n\n        # calculate path from cumulative transformations sum\n        self.frame_transform = np.array(self.__transforms, dtype=\"float32\")\n        self.__path = np.cumsum(self.frame_transform, axis=0)\n        # create smoothed path from a copy of path\n        self.__smoothed_path = np.copy(self.__path)\n\n        # re-calculate and save GFTT key-points for current gray frame\n        self.__previous_keypoints = cv2.goodFeaturesToTrack(\n            frame_gray,\n            maxCorners=200,\n            qualityLevel=0.05,\n            minDistance=30.0,\n            blockSize=3,\n            mask=None,\n            useHarrisDetector=False,\n            k=0.04,\n        )\n        # save this gray frame for further processing\n        self.__previous_gray = frame_gray[:]\n\n    def __box_filter_convolve(self, path, window_size):\n        \"\"\"\n        An internal method that applies *normalized linear box filter* to path w.r.t averaging window\n\n        Parameters:\n\n        * path (numpy.ndarray): a cumulative sum of transformations\n        * window_size (int): averaging window size\n        \"\"\"\n        # pad path to size of averaging window\n        path_padded = np.pad(path, (window_size, window_size), \"median\")\n        # apply linear box filter to path\n        path_smoothed = np.convolve(path_padded, self.__box_filter, mode=\"same\")\n        # crop the smoothed path to original path\n        path_smoothed = path_smoothed[window_size:-window_size]\n        # assert if cropping is completed\n        assert path.shape == path_smoothed.shape\n        # return smoothed path\n        return path_smoothed\n\n    def __apply_transformations(self):\n        \"\"\"\n        An internal method that applies affine transformation to the given frame\n        from previously calculated transformations\n        \"\"\"\n        # extract frame and its index from deque\n        queue_frame = self.__frame_queue.popleft()\n        queue_frame_index = self.__frame_queue_indexes.popleft()\n\n        # create border around extracted frame w.r.t border_size\n        bordered_frame = cv2.copyMakeBorder(\n            queue_frame,\n            top=self.__border_size,\n            bottom=self.__border_size,\n            left=self.__border_size,\n            right=self.__border_size,\n            borderType=self.__border_mode,\n            value=[0, 0, 0],\n        )\n        alpha_bordered_frame = cv2.cvtColor(\n            bordered_frame, cv2.COLOR_BGR2BGRA\n        )  # create alpha channel\n        # extract alpha channel\n        alpha_bordered_frame[:, :, 3] = 0\n        alpha_bordered_frame[\n            self.__border_size : self.__border_size + self.__frame_height,\n            self.__border_size : self.__border_size + self.frame_width,\n            3,\n        ] = 255\n\n        # extracting Transformations w.r.t frame index\n        dx = self.__frame_transforms_smoothed[queue_frame_index, 0]  # x-axis\n        dy = self.__frame_transforms_smoothed[queue_frame_index, 1]  # y-axis\n        da = self.__frame_transforms_smoothed[queue_frame_index, 2]  # angle\n\n        # building 2x3 transformation matrix from extracted transformations\n        queue_frame_transform = np.zeros((2, 3), np.float32)\n        queue_frame_transform[0, 0] = np.cos(da)\n        queue_frame_transform[0, 1] = -np.sin(da)\n        queue_frame_transform[1, 0] = np.sin(da)\n        queue_frame_transform[1, 1] = np.cos(da)\n        queue_frame_transform[0, 2] = dx\n        queue_frame_transform[1, 2] = dy\n\n        # Applying an affine transformation to the frame\n        frame_wrapped = cv2.warpAffine(\n            alpha_bordered_frame,\n            queue_frame_transform,\n            alpha_bordered_frame.shape[:2][::-1],\n            borderMode=self.__border_mode,\n        )\n\n        # drop alpha channel\n        frame_stabilized = frame_wrapped[:, :, :3]\n\n        # crop and zoom\n        if self.__crop_n_zoom:\n            # crop stabilized frame\n            frame_cropped = frame_stabilized[\n                self.__crop_n_zoom : -self.__crop_n_zoom,\n                self.__crop_n_zoom : -self.__crop_n_zoom,\n            ]\n            # zoom stabilized frame\n            frame_stabilized = cv2.resize(\n                frame_cropped,\n                self.__frame_size[::-1],\n                interpolation=self.__interpolation,\n            )\n\n        # finally return stabilized frame\n        return frame_stabilized\n\n    def clean(self):\n        \"\"\"\n        Cleans Stabilizer resources\n        \"\"\"\n        # check if deque present\n        if self.__frame_queue:\n            # clear frame deque\n            self.__frame_queue.clear()\n            # clear frame indexes deque\n            self.__frame_queue_indexes.clear()\n

"},{"location":"bonus/reference/stabilizer/#vidgear.gears.stabilizer.Stabilizer.__init__","title":"__init__(self, smoothing_radius=25, border_type='black', border_size=0, crop_n_zoom=False, logging=False) special","text":"

This constructor method initializes the object state and attributes of the Stabilizer class.

Parameters:

Name Type Description Default smoothing_radius int

alter averaging window size.

25 border_type str

changes the extended border type.

'black' border_size int

enables and set the value for extended border size to reduce the black borders.

0 crop_n_zoom bool

enables cropping and zooming of frames(to original size) to reduce the black borders.

False logging bool

enables/disables logging.

False Source code in vidgear/gears/stabilizer.py
def __init__(\n    self,\n    smoothing_radius=25,\n    border_type=\"black\",\n    border_size=0,\n    crop_n_zoom=False,\n    logging=False,\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the Stabilizer class.\n\n    Parameters:\n        smoothing_radius (int): alter averaging window size.\n        border_type (str): changes the extended border type.\n        border_size (int): enables and set the value for extended border size to reduce the black borders.\n        crop_n_zoom (bool): enables cropping and zooming of frames(to original size) to reduce the black borders.\n        logging (bool): enables/disables logging.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # initialize deques for handling input frames and its indexes\n    self.__frame_queue = deque(maxlen=smoothing_radius)\n    self.__frame_queue_indexes = deque(maxlen=smoothing_radius)\n\n    # define and create Adaptive histogram equalization (AHE) object for optimizations\n    self.__clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))\n\n    # initialize global vars\n    self.__smoothing_radius = smoothing_radius  # averaging window, handles the quality of stabilization at expense of latency and sudden panning\n    self.__smoothed_path = None  # handles the smoothed path with box filter\n    self.__path = None  # handles path i.e cumulative sum of previous_2_current transformations along a axis\n    self.__transforms = []  # handles previous_2_current transformations [dx,dy,da]\n    self.__frame_transforms_smoothed = None  # handles smoothed array of previous_2_current transformations w.r.t to frames\n    self.__previous_gray = None  # handles previous gray frame\n    self.__previous_keypoints = (\n        None  # handles previous detect_GFTTed keypoints w.r.t previous gray frame\n    )\n    self.__frame_height, self.frame_width = (\n        0,\n        0,\n    )  # handles width and height of input frames\n    self.__crop_n_zoom = 0  # handles cropping and zooms frames to reduce the black borders from stabilization being too noticeable.\n\n    # if check if crop_n_zoom defined\n    if crop_n_zoom and border_size:\n        self.__crop_n_zoom = border_size  # crops and zoom frame to original size\n        self.__border_size = 0  # zero out border size\n        self.__frame_size = None  # handles frame size for zooming\n        self.__logging and logger.debug(\n            \"Setting Cropping margin {} pixels\".format(border_size)\n        )\n    else:\n        # Add output borders to frame\n        self.__border_size = border_size\n        self.__logging and border_size and logger.debug(\n            \"Setting Border size {} pixels\".format(border_size)\n        )\n\n    # define valid border modes\n    border_modes = {\n        \"black\": cv2.BORDER_CONSTANT,\n        \"reflect\": cv2.BORDER_REFLECT,\n        \"reflect_101\": cv2.BORDER_REFLECT_101,\n        \"replicate\": cv2.BORDER_REPLICATE,\n        \"wrap\": cv2.BORDER_WRAP,\n    }\n    # choose valid border_mode from border_type\n    if border_type in [\"black\", \"reflect\", \"reflect_101\", \"replicate\", \"wrap\"]:\n        if not crop_n_zoom:\n            # initialize global border mode variable\n            self.__border_mode = border_modes[border_type]\n            self.__logging and border_type != \"black\" and logger.info(\n                \"Setting Border type: {}\".format(border_type)\n            )\n        else:\n            # log and reset to default\n            self.__logging and border_type != \"black\" and logger.debug(\n                \"Setting border type is disabled if cropping is enabled!\"\n            )\n            self.__border_mode = border_modes[\"black\"]\n    else:\n        # otherwise log if not\n        self.__logging and logger.debug(\"Invalid input border type!\")\n        self.__border_mode = border_modes[\"black\"]  # reset to default mode\n\n    # define OpenCV version\n    self.__cv2_version = check_CV_version()\n\n    # retrieve best interpolation\n    self.__interpolation = retrieve_best_interpolation(\n        [\"INTER_LINEAR_EXACT\", \"INTER_LINEAR\", \"INTER_AREA\"]\n    )\n\n    # define normalized box filter\n    self.__box_filter = np.ones(smoothing_radius) / smoothing_radius\n
"},{"location":"bonus/reference/stabilizer/#vidgear.gears.stabilizer.Stabilizer.clean","title":"clean(self)","text":"

Cleans Stabilizer resources

Source code in vidgear/gears/stabilizer.py
def clean(self):\n    \"\"\"\n    Cleans Stabilizer resources\n    \"\"\"\n    # check if deque present\n    if self.__frame_queue:\n        # clear frame deque\n        self.__frame_queue.clear()\n        # clear frame indexes deque\n        self.__frame_queue_indexes.clear()\n
"},{"location":"bonus/reference/stabilizer/#vidgear.gears.stabilizer.Stabilizer.stabilize","title":"stabilize(self, frame)","text":"

This method takes an unstabilized video frame, and returns a stabilized one.

Parameters:

Name Type Description Default frame numpy.ndarray

inputs unstabilized video frames.

required Source code in vidgear/gears/stabilizer.py
def stabilize(self, frame):\n    \"\"\"\n    This method takes an unstabilized video frame, and returns a stabilized one.\n\n    Parameters:\n        frame (numpy.ndarray): inputs unstabilized video frames.\n    \"\"\"\n    # check if frame is None\n    if frame is None:\n        # return if it does\n        return\n\n    # save frame size for zooming\n    if self.__crop_n_zoom and self.__frame_size == None:\n        self.__frame_size = frame.shape[:2]\n\n    # initiate transformations capturing\n    if not self.__frame_queue:\n        # for first frame\n        previous_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)  # convert to gray\n        previous_gray = self.__clahe.apply(previous_gray)  # optimize gray frame\n        self.__previous_keypoints = cv2.goodFeaturesToTrack(\n            previous_gray,\n            maxCorners=200,\n            qualityLevel=0.05,\n            minDistance=30.0,\n            blockSize=3,\n            mask=None,\n            useHarrisDetector=False,\n            k=0.04,\n        )  # track features using GFTT\n        self.__frame_height, self.frame_width = frame.shape[\n            :2\n        ]  # save input frame height and width\n        self.__frame_queue.append(frame)  # save frame to deque\n        self.__frame_queue_indexes.append(0)  # save frame index to deque\n        self.__previous_gray = previous_gray[\n            :\n        ]  # save gray frame clone for further processing\n\n    elif self.__frame_queue_indexes[-1] < self.__smoothing_radius - 1:\n        # for rest of frames\n        self.__frame_queue.append(frame)  # save frame to deque\n        self.__frame_queue_indexes.append(\n            self.__frame_queue_indexes[-1] + 1\n        )  # save frame index\n        self.__generate_transformations()  # generate transformations\n    else:\n        # start applying transformations\n        self.__frame_queue.append(frame)  # save frame to deque\n        self.__frame_queue_indexes.append(\n            self.__frame_queue_indexes[-1] + 1\n        )  # save frame index\n        self.__generate_transformations()  # generate transformations\n        # calculate smooth path once transformation capturing is completed\n        for i in range(3):\n            # apply normalized box filter to the path\n            self.__smoothed_path[:, i] = self.__box_filter_convolve(\n                (self.__path[:, i]), window_size=self.__smoothing_radius\n            )\n        # calculate deviation of path from smoothed path\n        deviation = self.__smoothed_path - self.__path\n        # save smoothed transformation\n        self.__frame_transforms_smoothed = self.frame_transform + deviation\n        # return transformation applied stabilized frame\n        return self.__apply_transformations()\n
"},{"location":"bonus/reference/streamgear/","title":"StreamGear API References","text":"

StreamGear API usage examples for: Single-Source Mode \u27b6 and Real-time Frames Mode \u27b6

StreamGear API parameters are explained here \u27b6

StreamGear automates transcoding workflow for generating Ultra-Low Latency, High-Quality, Dynamic & Adaptive Streaming Formats (such as MPEG-DASH and HLS) in just few lines of python code. StreamGear provides a standalone, highly extensible, and flexible wrapper around FFmpeg multimedia framework for generating chunked-encoded media segments of the content.

SteamGear easily transcodes source videos/audio files & real-time video-frames and breaks them into a sequence of multiple smaller chunks/segments of suitable length. These segments make it possible to stream videos at different quality levels (different bitrate or spatial resolutions) and can be switched in the middle of a video from one quality level to another - if bandwidth permits - on a per-segment basis. A user can serve these segments on a web server that makes it easier to download them through HTTP standard-compliant GET requests.

SteamGear also creates a Manifest/Playlist file (such as MPD in-case of DASH and M3U8 in-case of HLS) besides segments that describe these segment information (timing, URL, media characteristics like video resolution and bit rates) and is provided to the client before the streaming session.

SteamGear currently supports MPEG-DASH (Dynamic Adaptive Streaming over HTTP, ISO/IEC 23009-1) and Apple HLS (HTTP live streaming).

Source code in vidgear/gears/streamgear.py
class StreamGear:\n    \"\"\"\n    StreamGear automates transcoding workflow for generating Ultra-Low Latency, High-Quality, Dynamic & Adaptive Streaming Formats (such as MPEG-DASH and HLS) in just few lines of python code.\n    StreamGear provides a standalone, highly extensible, and flexible wrapper around FFmpeg multimedia framework for generating chunked-encoded media segments of the content.\n\n    SteamGear easily transcodes source videos/audio files & real-time video-frames and breaks them into a sequence of multiple smaller chunks/segments of suitable length. These segments make it\n    possible to stream videos at different quality levels _(different bitrate or spatial resolutions)_ and can be switched in the middle of a video from one quality level to another - if bandwidth\n    permits - on a per-segment basis. A user can serve these segments on a web server that makes it easier to download them through HTTP standard-compliant GET requests.\n\n    SteamGear also creates a Manifest/Playlist file (such as MPD in-case of DASH and M3U8 in-case of HLS) besides segments that describe these segment information\n    (timing, URL, media characteristics like video resolution and bit rates) and is provided to the client before the streaming session.\n\n    SteamGear currently supports MPEG-DASH (Dynamic Adaptive Streaming over HTTP, ISO/IEC 23009-1) and Apple HLS (HTTP live streaming).\n    \"\"\"\n\n    def __init__(\n        self, output=\"\", format=\"dash\", custom_ffmpeg=\"\", logging=False, **stream_params\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the StreamGear class.\n\n        Parameters:\n            output (str): sets the valid filename/path for generating the StreamGear assets.\n            format (str): select the adaptive HTTP streaming format(DASH and HLS).\n            custom_ffmpeg (str): assigns the location of custom path/directory for custom FFmpeg executables.\n            logging (bool): enables/disables logging.\n            stream_params (dict): provides the flexibility to control supported internal parameters and FFmpeg properties.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # checks if machine in-use is running windows os or not\n        self.__os_windows = True if os.name == \"nt\" else False\n\n        # initialize various class variables\n        # handles user-defined parameters\n        self.__params = {}\n        # handle input video/frame resolution and channels\n        self.__inputheight = None\n        self.__inputwidth = None\n        self.__inputchannels = None\n        self.__sourceframerate = None\n        # handle process to be frames written\n        self.__process = None\n        # handle valid FFmpeg assets location\n        self.__ffmpeg = \"\"\n        # handle one time process for valid process initialization\n        self.__initiate_stream = True\n\n        # cleans and reformat user-defined parameters\n        self.__params = {\n            str(k).strip(): (v.strip() if isinstance(v, str) else v)\n            for k, v in stream_params.items()\n        }\n\n        # handle where to save the downloaded FFmpeg Static assets on Windows(if specified)\n        __ffmpeg_download_path = self.__params.pop(\"-ffmpeg_download_path\", \"\")\n        if not isinstance(__ffmpeg_download_path, (str)):\n            # reset improper values\n            __ffmpeg_download_path = \"\"\n\n        # validate the FFmpeg assets and return location (also downloads static assets on windows)\n        self.__ffmpeg = get_valid_ffmpeg_path(\n            str(custom_ffmpeg),\n            self.__os_windows,\n            ffmpeg_download_path=__ffmpeg_download_path,\n            logging=self.__logging,\n        )\n\n        # check if valid FFmpeg path returned\n        if self.__ffmpeg:\n            self.__logging and logger.debug(\n                \"Found valid FFmpeg executables: `{}`.\".format(self.__ffmpeg)\n            )\n        else:\n            # else raise error\n            raise RuntimeError(\n                \"[StreamGear:ERROR] :: Failed to find FFmpeg assets on this system. Kindly compile/install FFmpeg or provide a valid custom FFmpeg binary path!\"\n            )\n\n        # handle streaming format\n        supported_formats = [\"dash\", \"hls\"]  # TODO will be extended in future\n        if format and isinstance(format, str):\n            _format = format.strip().lower()\n            if _format in supported_formats:\n                self.__format = _format\n                logger.info(\n                    \"StreamGear will generate asset files for {} streaming format.\".format(\n                        self.__format.upper()\n                    )\n                )\n            elif difflib.get_close_matches(_format, supported_formats):\n                raise ValueError(\n                    \"[StreamGear:ERROR] :: Incorrect `format` parameter value! Did you mean `{}`?\".format(\n                        difflib.get_close_matches(_format, supported_formats)[0]\n                    )\n                )\n            else:\n                raise ValueError(\n                    \"[StreamGear:ERROR] :: The `format` parameter value `{}` not valid/supported!\".format(\n                        format\n                    )\n                )\n        else:\n            raise ValueError(\n                \"[StreamGear:ERROR] :: The `format` parameter value is Missing or Invalid!\"\n            )\n\n        # handle Audio-Input\n        audio = self.__params.pop(\"-audio\", False)\n        if audio and isinstance(audio, str):\n            if os.path.isfile(audio):\n                self.__audio = os.path.abspath(audio)\n            elif is_valid_url(self.__ffmpeg, url=audio, logging=self.__logging):\n                self.__audio = audio\n            else:\n                self.__audio = False\n        elif audio and isinstance(audio, list):\n            self.__audio = audio\n        else:\n            self.__audio = False\n        # log external audio source\n        self.__audio and self.__logging and logger.debug(\n            \"External audio source `{}` detected.\".format(self.__audio)\n        )\n\n        # handle Video-Source input\n        source = self.__params.pop(\"-video_source\", False)\n        # Check if input is valid string\n        if source and isinstance(source, str) and len(source) > 1:\n            # Differentiate input\n            if os.path.isfile(source):\n                self.__video_source = os.path.abspath(source)\n            elif is_valid_url(self.__ffmpeg, url=source, logging=self.__logging):\n                self.__video_source = source\n            else:\n                # discard the value otherwise\n                self.__video_source = False\n\n            # Validate input\n            if self.__video_source:\n                validation_results = validate_video(\n                    self.__ffmpeg, video_path=self.__video_source\n                )\n                assert not (\n                    validation_results is None\n                ), \"[StreamGear:ERROR] :: Given `{}` video_source is Invalid, Check Again!\".format(\n                    self.__video_source\n                )\n                self.__aspect_source = validation_results[\"resolution\"]\n                self.__fps_source = validation_results[\"framerate\"]\n                # log it\n                self.__logging and logger.debug(\n                    \"Given video_source is valid and has {}x{} resolution, and a framerate of {} fps.\".format(\n                        self.__aspect_source[0],\n                        self.__aspect_source[1],\n                        self.__fps_source,\n                    )\n                )\n            else:\n                # log warning\n                logger.warning(\"Discarded invalid `-video_source` value provided.\")\n        else:\n            if source:\n                # log warning if source provided\n                logger.warning(\"Invalid `-video_source` value provided.\")\n            else:\n                # log normally\n                logger.info(\"No `-video_source` value provided.\")\n            # discard the value otherwise\n            self.__video_source = False\n\n        # handle user-defined framerate\n        self.__inputframerate = self.__params.pop(\"-input_framerate\", 0.0)\n        if isinstance(self.__inputframerate, (float, int)):\n            # must be float\n            self.__inputframerate = float(self.__inputframerate)\n        else:\n            # reset improper values\n            self.__inputframerate = 0.0\n\n        # handle old assets\n        clear_assets = self.__params.pop(\"-clear_prev_assets\", False)\n        if isinstance(clear_assets, bool):\n            self.__clear_assets = clear_assets\n            # log if clearing assets is enabled\n            clear_assets and logger.info(\n                \"The `-clear_prev_assets` parameter is enabled successfully. All previous StreamGear API assets for `{}` format will be removed for this run.\".format(\n                    self.__format.upper()\n                )\n            )\n        else:\n            # reset improper values\n            self.__clear_assets = False\n\n        # handle whether to livestream?\n        livestreaming = self.__params.pop(\"-livestream\", False)\n        if isinstance(livestreaming, bool) and livestreaming:\n            # NOTE:  `livestream` is only available with real-time mode.\n            self.__livestreaming = livestreaming if not (self.__video_source) else False\n            if self.__video_source:\n                logger.error(\n                    \"Live-Streaming is only available with Real-time Mode. Refer docs for more information.\"\n                )\n            else:\n                # log if live streaming is enabled\n                livestreaming and logger.info(\n                    \"Live-Streaming is successfully enabled for this run.\"\n                )\n        else:\n            # reset improper values\n            self.__livestreaming = False\n\n        # handle the special-case of forced-termination\n        enable_force_termination = self.__params.pop(\"-enable_force_termination\", False)\n        # check if value is valid\n        if isinstance(enable_force_termination, bool):\n            self.__forced_termination = enable_force_termination\n            # log if forced termination is enabled\n            self.__forced_termination and logger.warning(\n                \"Forced termination is enabled for this run. This may result in corrupted output in certain scenarios!\"\n            )\n        else:\n            # handle improper values\n            self.__forced_termination = False\n\n        # handle streaming format\n        supported_formats = [\"dash\", \"hls\"]  # TODO will be extended in future\n        if format and isinstance(format, str):\n            _format = format.strip().lower()\n            if _format in supported_formats:\n                self.__format = _format\n                logger.info(\n                    \"StreamGear will generate asset files for {} streaming format.\".format(\n                        self.__format.upper()\n                    )\n                )\n            elif difflib.get_close_matches(_format, supported_formats):\n                raise ValueError(\n                    \"[StreamGear:ERROR] :: Incorrect `format` parameter value! Did you mean `{}`?\".format(\n                        difflib.get_close_matches(_format, supported_formats)[0]\n                    )\n                )\n            else:\n                raise ValueError(\n                    \"[StreamGear:ERROR] :: The `format` parameter value `{}` not valid/supported!\".format(\n                        format\n                    )\n                )\n        else:\n            raise ValueError(\n                \"[StreamGear:ERROR] :: The `format` parameter value is Missing or Invalid!\"\n            )\n\n        # handles output asset filenames\n        if output:\n            # validate this class has the access rights to specified directory or not\n            abs_path = os.path.abspath(output)\n            # check if given output is a valid system path\n            if check_WriteAccess(\n                os.path.dirname(abs_path),\n                is_windows=self.__os_windows,\n                logging=self.__logging,\n            ):\n                # get all assets extensions\n                valid_extension = \"mpd\" if self.__format == \"dash\" else \"m3u8\"\n                assets_exts = [\n                    (\"chunk-stream\", \".m4s\"),  # filename prefix, extension\n                    (\"chunk-stream\", \".ts\"),  # filename prefix, extension\n                    \".{}\".format(valid_extension),\n                ]\n                # add source file extension too\n                self.__video_source and assets_exts.append(\n                    (\n                        \"chunk-stream\",\n                        os.path.splitext(self.__video_source)[1],\n                    )  # filename prefix, extension\n                )\n                # handle output\n                # check if path is a directory\n                if os.path.isdir(abs_path):\n                    # clear previous assets if specified\n                    self.__clear_assets and delete_ext_safe(\n                        abs_path, assets_exts, logging=self.__logging\n                    )\n                    # auto-assign valid name and adds it to path\n                    abs_path = os.path.join(\n                        abs_path,\n                        \"{}-{}.{}\".format(\n                            self.__format,\n                            time.strftime(\"%Y%m%d-%H%M%S\"),\n                            valid_extension,\n                        ),\n                    )\n                # or check if path is a file\n                elif os.path.isfile(abs_path) and self.__clear_assets:\n                    # clear previous assets if specified\n                    delete_ext_safe(\n                        os.path.dirname(abs_path),\n                        assets_exts,\n                        logging=self.__logging,\n                    )\n                # check if path has valid file extension\n                assert abs_path.endswith(\n                    valid_extension\n                ), \"Given `{}` path has invalid file-extension w.r.t selected format: `{}`!\".format(\n                    output, self.__format.upper()\n                )\n                self.__logging and logger.debug(\n                    \"Output Path:`{}` is successfully configured for generating streaming assets.\".format(\n                        abs_path\n                    )\n                )\n                # workaround patch for Windows only,\n                # others platforms will not be affected\n                self.__out_file = abs_path.replace(\"\\\\\", \"/\")\n            # check if given output is a valid URL\n            elif is_valid_url(self.__ffmpeg, url=output, logging=self.__logging):\n                self.__logging and logger.debug(\n                    \"URL:`{}` is valid and successfully configured for generating streaming assets.\".format(\n                        output\n                    )\n                )\n                self.__out_file = output\n            # raise ValueError otherwise\n            else:\n                raise ValueError(\n                    \"[StreamGear:ERROR] :: The output parameter value:`{}` is not valid/supported!\".format(\n                        output\n                    )\n                )\n        else:\n            # raise ValueError otherwise\n            raise ValueError(\n                \"[StreamGear:ERROR] :: Kindly provide a valid `output` parameter value. Refer Docs for more information.\"\n            )\n\n        # log Mode of operation\n        self.__video_source and logger.info(\n            \"StreamGear has been successfully configured for {} Mode.\".format(\n                \"Single-Source\" if self.__video_source else \"Real-time Frames\"\n            )\n        )\n\n    @deprecated(\n        parameter=\"rgb_mode\",\n        message=\"The `rgb_mode` parameter is deprecated and will be removed in a future version. Only BGR format frames will be supported going forward.\",\n    )\n    def stream(self, frame, rgb_mode=False):\n        \"\"\"\n        Pipes `ndarray` frames to FFmpeg Pipeline for transcoding them into chunked-encoded media segments of\n        streaming formats such as MPEG-DASH and HLS.\n\n        !!! warning \"[DEPRECATION NOTICE]: The `rgb_mode` parameter is deprecated and will be removed in a future version.\"\n\n        Parameters:\n            frame (ndarray): a valid numpy frame\n            rgb_mode (boolean): enable this flag to activate RGB mode _(i.e. specifies that incoming frames are of RGB format instead of default BGR)_.\n        \"\"\"\n        # check if function is called in correct context\n        if self.__video_source:\n            raise RuntimeError(\n                \"[StreamGear:ERROR] :: The `stream()` method cannot be used when streaming from a `-video_source` input file. Kindly refer vidgear docs!\"\n            )\n        # None-Type frames will be skipped\n        if frame is None:\n            return\n        # extract height, width and number of channels of frame\n        height, width = frame.shape[:2]\n        channels = frame.shape[-1] if frame.ndim == 3 else 1\n        # assign values to class variables on first run\n        if self.__initiate_stream:\n            self.__inputheight = height\n            self.__inputwidth = width\n            self.__inputchannels = channels\n            self.__sourceframerate = (\n                25.0 if not (self.__inputframerate) else self.__inputframerate\n            )\n            self.__logging and logger.debug(\n                \"InputFrame => Height:{} Width:{} Channels:{}\".format(\n                    self.__inputheight, self.__inputwidth, self.__inputchannels\n                )\n            )\n        # validate size of frame\n        if height != self.__inputheight or width != self.__inputwidth:\n            raise ValueError(\"[StreamGear:ERROR] :: All frames must have same size!\")\n        # validate number of channels\n        if channels != self.__inputchannels:\n            raise ValueError(\n                \"[StreamGear:ERROR] :: All frames must have same number of channels!\"\n            )\n        # initiate FFmpeg process on first run\n        if self.__initiate_stream:\n            # launch pre-processing\n            self.__PreProcess(channels=channels, rgb=rgb_mode)\n            # Check status of the process\n            assert self.__process is not None\n\n        # write the frame to pipeline\n        try:\n            self.__process.stdin.write(frame.tobytes())\n        except (OSError, IOError):\n            # log something is wrong!\n            logger.error(\n                \"BrokenPipeError caught, Wrong values passed to FFmpeg Pipe, Kindly Refer Docs!\"\n            )\n            raise ValueError  # for testing purpose only\n\n    def transcode_source(self):\n        \"\"\"\n        Transcodes an entire video file _(with or without audio)_ into chunked-encoded media segments of\n        streaming formats such as MPEG-DASH and HLS.\n        \"\"\"\n        # check if function is called in correct context\n        if not (self.__video_source):\n            raise RuntimeError(\n                \"[StreamGear:ERROR] :: The `transcode_source()` method cannot be used without a valid `-video_source` input. Kindly refer vidgear docs!\"\n            )\n        # assign height, width and framerate\n        self.__inputheight = int(self.__aspect_source[1])\n        self.__inputwidth = int(self.__aspect_source[0])\n        self.__sourceframerate = float(self.__fps_source)\n        # launch pre-processing\n        self.__PreProcess()\n\n    def __PreProcess(self, channels=0, rgb=False):\n        \"\"\"\n        Internal method that pre-processes default FFmpeg parameters before starting pipelining.\n\n        Parameters:\n            channels (int): Number of channels\n            rgb (boolean): activates RGB mode _(if enabled)_.\n        \"\"\"\n        # turn off initiate flag\n        self.__initiate_stream = False\n        # initialize I/O parameters\n        input_parameters = OrderedDict()\n        output_parameters = OrderedDict()\n        # pre-assign default codec parameters (if not assigned by user).\n        default_codec = \"libx264rgb\" if rgb else \"libx264\"\n        output_vcodec = self.__params.pop(\"-vcodec\", default_codec)\n        # enforce default encoder if stream copy specified\n        # in Real-time Frames Mode\n        output_parameters[\"-vcodec\"] = (\n            default_codec\n            if output_vcodec == \"copy\"\n            and (not (self.__video_source) or \"-streams\" in self.__params)\n            else output_vcodec\n        )\n        # enforce compatibility with stream copy\n        if output_parameters[\"-vcodec\"] != \"copy\":\n            # NOTE: these parameters only supported when stream copy not defined\n            output_parameters[\"-vf\"] = self.__params.pop(\"-vf\", \"format=yuv420p\")\n            # Non-essential `-aspect` parameter is removed from the default pipeline.\n        else:\n            # log warnings if stream copy specified in Real-time Frames Mode\n            not (self.__video_source) and logger.error(\n                \"Stream copy is not compatible with Real-time Frames Mode as it require re-encoding of incoming frames. Discarding the `-vcodec copy` parameter!\"\n            )\n            (\"-streams\" in self.__params) and logger.error(\n                \"Stream copying is incompatible with Custom Streams as it require re-encoding for each additional stream. Discarding the `-vcodec copy` parameter!\"\n            )\n            # log warnings for these parameters\n            self.__params.pop(\"-vf\", False) and logger.warning(\n                \"Filtering and stream copy cannot be used together. Discarding specified `-vf` parameter!\"\n            )\n            self.__params.pop(\"-aspect\", False) and logger.warning(\n                \"Overriding aspect ratio with stream copy may produce invalid files. Discarding specified `-aspect` parameter!\"\n            )\n\n        # enable optimizations w.r.t selected codec\n        ### OPTIMIZATION-1 ###\n        if output_parameters[\"-vcodec\"] in [\n            \"libx264\",\n            \"libx264rgb\",\n            \"libx265\",\n            \"libvpx-vp9\",\n        ]:\n            output_parameters[\"-crf\"] = self.__params.pop(\"-crf\", \"20\")\n        ### OPTIMIZATION-2 ###\n        if output_parameters[\"-vcodec\"] == \"libx264\":\n            if not (self.__video_source):\n                output_parameters[\"-profile:v\"] = self.__params.pop(\n                    \"-profile:v\", \"high\"\n                )\n        ### OPTIMIZATION-3 ###\n        if output_parameters[\"-vcodec\"] in [\"libx264\", \"libx264rgb\"]:\n            output_parameters[\"-tune\"] = self.__params.pop(\"-tune\", \"zerolatency\")\n            output_parameters[\"-preset\"] = self.__params.pop(\"-preset\", \"veryfast\")\n        ### OPTIMIZATION-4 ###\n        if output_parameters[\"-vcodec\"] == \"libx265\":\n            output_parameters[\"-x265-params\"] = self.__params.pop(\n                \"-x265-params\", \"lossless=1\"\n            )\n\n        # enable audio (if present)\n        if self.__audio:\n            # validate audio source\n            bitrate = validate_audio(self.__ffmpeg, source=self.__audio)\n            if bitrate:\n                logger.info(\n                    \"Detected External Audio Source is valid, and will be used for generating streams.\"\n                )\n                # assign audio source\n                output_parameters[\n                    \"{}\".format(\n                        \"-core_asource\" if isinstance(self.__audio, list) else \"-i\"\n                    )\n                ] = self.__audio\n                # assign audio codec\n                output_parameters[\"-acodec\"] = self.__params.pop(\"-acodec\", \"aac\")\n                output_parameters[\"a_bitrate\"] = bitrate  # temporary handler\n                output_parameters[\"-core_audio\"] = (\n                    [\"-map\", \"1:a:0\"] if self.__format == \"dash\" else []\n                )\n            else:\n                # discard invalid audio\n                logger.warning(\n                    \"Audio source `{}` is not valid, Skipped!\".format(self.__audio)\n                )\n                self.__audio = False\n        # validate input video's audio source if available\n        elif self.__video_source:\n            bitrate = validate_audio(self.__ffmpeg, source=self.__video_source)\n            if bitrate:\n                logger.info(\"Input video's audio source will be used for this run.\")\n                # assign audio codec\n                output_parameters[\"-acodec\"] = self.__params.pop(\n                    \"-acodec\",\n                    \"aac\" if (\"-streams\" in self.__params) else \"copy\",\n                )\n                if output_parameters[\"-acodec\"] != \"copy\":\n                    output_parameters[\"a_bitrate\"] = bitrate  # temporary handler\n            else:\n                logger.info(\n                    \"No valid audio source available in the input video. Disabling audio while generating streams.\"\n                )\n        else:\n            logger.info(\n                \"No valid audio source provided. Disabling audio while generating streams.\"\n            )\n        # enable audio optimizations based on audio codec\n        if \"-acodec\" in output_parameters and output_parameters[\"-acodec\"] == \"aac\":\n            output_parameters[\"-movflags\"] = \"+faststart\"\n\n        # set input framerate\n        if self.__sourceframerate > 0.0 and not (self.__video_source):\n            # set input framerate\n            self.__logging and logger.debug(\n                \"Setting Input framerate: {}\".format(self.__sourceframerate)\n            )\n            input_parameters[\"-framerate\"] = str(self.__sourceframerate)\n\n        # handle input resolution and pixel format\n        if not (self.__video_source):\n            dimensions = \"{}x{}\".format(self.__inputwidth, self.__inputheight)\n            input_parameters[\"-video_size\"] = str(dimensions)\n            # handles pix_fmt based on channels(HACK)\n            if channels == 1:\n                input_parameters[\"-pix_fmt\"] = \"gray\"\n            elif channels == 2:\n                input_parameters[\"-pix_fmt\"] = \"ya8\"\n            elif channels == 3:\n                input_parameters[\"-pix_fmt\"] = \"rgb24\" if rgb else \"bgr24\"\n            elif channels == 4:\n                input_parameters[\"-pix_fmt\"] = \"rgba\" if rgb else \"bgra\"\n            else:\n                raise ValueError(\n                    \"[StreamGear:ERROR] :: Frames with channels outside range 1-to-4 are not supported!\"\n                )\n        # process assigned format parameters\n        process_params = self.__handle_streams(\n            input_params=input_parameters, output_params=output_parameters\n        )\n        # check if processing completed successfully\n        assert not (\n            process_params is None\n        ), \"[StreamGear:ERROR] :: `{}` stream cannot be initiated properly!\".format(\n            self.__format.upper()\n        )\n        # Finally start FFmpeg pipeline and process everything\n        self.__Build_n_Execute(process_params[0], process_params[1])\n\n    def __handle_streams(self, input_params, output_params):\n        \"\"\"\n        An internal function that parses various streams and its parameters.\n\n        Parameters:\n            input_params (dict): Input FFmpeg parameters\n            output_params (dict): Output FFmpeg parameters\n        \"\"\"\n        # handle bit-per-pixels\n        bpp = self.__params.pop(\"-bpp\", 0.1000)\n        if isinstance(bpp, float) and bpp >= 0.001:\n            bpp = float(bpp)\n        else:\n            # reset to default if invalid\n            bpp = 0.1000\n        # log it\n        bpp and self.__logging and logger.debug(\n            \"Setting bit-per-pixels: {} for this stream.\".format(bpp)\n        )\n\n        # handle gop\n        gop = self.__params.pop(\"-gop\", 2 * int(self.__sourceframerate))\n        if isinstance(gop, (int, float)) and gop >= 0:\n            gop = int(gop)\n        else:\n            # reset to some recommended value\n            gop = 2 * int(self.__sourceframerate)\n        # log it\n        gop and self.__logging and logger.debug(\n            \"Setting GOP: {} for this stream.\".format(gop)\n        )\n\n        # define default stream and its mapping\n        if self.__format == \"hls\":\n            output_params[\"-corev0\"] = [\"-map\", \"0:v\"]\n            if \"-acodec\" in output_params:\n                output_params[\"-corea0\"] = [\n                    \"-map\",\n                    \"{}:a\".format(1 if \"-core_audio\" in output_params else 0),\n                ]\n        else:\n            output_params[\"-map\"] = 0\n\n        # assign default output resolution\n        if \"-s:v:0\" in self.__params:\n            # prevent duplicates\n            del self.__params[\"-s:v:0\"]\n        if output_params[\"-vcodec\"] != \"copy\":\n            output_params[\"-s:v:0\"] = \"{}x{}\".format(\n                self.__inputwidth, self.__inputheight\n            )\n        # assign default output video-bitrate\n        if \"-b:v:0\" in self.__params:\n            # prevent duplicates\n            del self.__params[\"-b:v:0\"]\n        if output_params[\"-vcodec\"] != \"copy\":\n            output_params[\"-b:v:0\"] = (\n                str(\n                    get_video_bitrate(\n                        int(self.__inputwidth),\n                        int(self.__inputheight),\n                        self.__sourceframerate,\n                        bpp,\n                    )\n                )\n                + \"k\"\n            )\n\n        # assign default output audio-bitrate\n        if \"-b:a:0\" in self.__params:\n            # prevent duplicates\n            del self.__params[\"-b:a:0\"]\n        # extract and assign audio-bitrate from temporary handler\n        a_bitrate = output_params.pop(\"a_bitrate\", False)\n        if \"-acodec\" in output_params and a_bitrate:\n            output_params[\"-b:a:0\"] = a_bitrate\n\n        # handle user-defined streams\n        streams = self.__params.pop(\"-streams\", {})\n        output_params = self.__evaluate_streams(streams, output_params, bpp)\n\n        # define additional streams optimization parameters\n        if output_params[\"-vcodec\"] in [\"libx264\", \"libx264rgb\"]:\n            if not \"-bf\" in self.__params:\n                output_params[\"-bf\"] = 1\n            if not \"-sc_threshold\" in self.__params:\n                output_params[\"-sc_threshold\"] = 0\n            if not \"-keyint_min\" in self.__params:\n                output_params[\"-keyint_min\"] = gop\n        if (\n            output_params[\"-vcodec\"] in [\"libx264\", \"libx264rgb\", \"libvpx-vp9\"]\n            and not \"-g\" in self.__params\n        ):\n            output_params[\"-g\"] = gop\n        if output_params[\"-vcodec\"] == \"libx265\":\n            output_params[\"-core_x265\"] = [\n                \"-x265-params\",\n                \"keyint={}:min-keyint={}\".format(gop, gop),\n            ]\n\n        # process given dash/hls stream and return it\n        if self.__format == \"dash\":\n            processed_params = self.__generate_dash_stream(\n                input_params=input_params,\n                output_params=output_params,\n            )\n        else:\n            processed_params = self.__generate_hls_stream(\n                input_params=input_params,\n                output_params=output_params,\n            )\n        return processed_params\n\n    def __evaluate_streams(self, streams, output_params, bpp):\n        \"\"\"\n        Internal function that Extracts, Evaluates & Validates user-defined streams\n\n        Parameters:\n            streams (dict): Individual streams formatted as list of dict.\n            output_params (dict): Output FFmpeg parameters\n        \"\"\"\n        # temporary streams count variable\n        output_params[\"stream_count\"] = 1  # default is 1\n\n        # check if streams are empty\n        if not streams:\n            logger.info(\"No additional `-streams` are provided.\")\n            return output_params\n\n        # check if streams are valid\n        if isinstance(streams, list) and all(isinstance(x, dict) for x in streams):\n            # keep track of streams\n            stream_count = 1\n            # calculate source aspect-ratio\n            source_aspect_ratio = self.__inputwidth / self.__inputheight\n            # log the process\n            self.__logging and logger.debug(\n                \"Processing {} streams.\".format(len(streams))\n            )\n            # iterate over given streams\n            for idx, stream in enumerate(streams):\n                # log stream processing\n                self.__logging and logger.debug(\"Processing Stream: #{}\".format(idx))\n                # make copy\n                stream_copy = stream.copy()\n                # handle intermediate stream data as dictionary\n                intermediate_dict = {}\n                # define and map stream to intermediate dict\n                if self.__format == \"hls\":\n                    intermediate_dict[\"-corev{}\".format(stream_count)] = [\"-map\", \"0:v\"]\n                    if \"-acodec\" in output_params:\n                        intermediate_dict[\"-corea{}\".format(stream_count)] = [\n                            \"-map\",\n                            \"{}:a\".format(1 if \"-core_audio\" in output_params else 0),\n                        ]\n                else:\n                    intermediate_dict[\"-core{}\".format(stream_count)] = [\"-map\", \"0\"]\n\n                # extract resolution & individual dimension of stream\n                resolution = stream.pop(\"-resolution\", \"\")\n                dimensions = (\n                    resolution.lower().split(\"x\")\n                    if (resolution and isinstance(resolution, str))\n                    else []\n                )\n                # validate resolution\n                if (\n                    len(dimensions) == 2\n                    and dimensions[0].isnumeric()\n                    and dimensions[1].isnumeric()\n                ):\n                    # verify resolution is w.r.t source aspect-ratio\n                    expected_width = math.floor(\n                        int(dimensions[1]) * source_aspect_ratio\n                    )\n                    if int(dimensions[0]) != expected_width:\n                        logger.warning(\n                            \"The provided stream resolution '{}' does not align with the source aspect ratio. Output stream may appear distorted!\".format(\n                                resolution\n                            )\n                        )\n                    # assign stream resolution to intermediate dict\n                    intermediate_dict[\"-s:v:{}\".format(stream_count)] = resolution\n                else:\n                    # otherwise log error and skip stream\n                    logger.error(\n                        \"Missing `-resolution` value. Invalid stream `{}` Skipped!\".format(\n                            stream_copy\n                        )\n                    )\n                    continue\n\n                # verify given stream video-bitrate\n                video_bitrate = stream.pop(\"-video_bitrate\", \"\")\n                if (\n                    video_bitrate\n                    and isinstance(video_bitrate, str)\n                    and video_bitrate.endswith((\"k\", \"M\"))\n                ):\n                    # assign it\n                    intermediate_dict[\"-b:v:{}\".format(stream_count)] = video_bitrate\n                else:\n                    # otherwise calculate video-bitrate\n                    fps = stream.pop(\"-framerate\", 0.0)\n                    if dimensions and isinstance(fps, (float, int)) and fps > 0:\n                        intermediate_dict[\"-b:v:{}\".format(stream_count)] = (\n                            \"{}k\".format(\n                                get_video_bitrate(\n                                    int(dimensions[0]), int(dimensions[1]), fps, bpp\n                                )\n                            )\n                        )\n                    else:\n                        # If everything fails, log and skip the stream!\n                        logger.error(\n                            \"Unable to determine Video-Bitrate for the stream `{}`. Skipped!\".format(\n                                stream_copy\n                            )\n                        )\n                        continue\n                # verify given stream audio-bitrate\n                audio_bitrate = stream.pop(\"-audio_bitrate\", \"\")\n                if \"-acodec\" in output_params:\n                    if audio_bitrate and audio_bitrate.endswith((\"k\", \"M\")):\n                        intermediate_dict[\"-b:a:{}\".format(stream_count)] = (\n                            audio_bitrate\n                        )\n                    else:\n                        # otherwise calculate audio-bitrate\n                        if dimensions:\n                            aspect_width = int(dimensions[0])\n                            intermediate_dict[\"-b:a:{}\".format(stream_count)] = (\n                                \"{}k\".format(128 if (aspect_width > 800) else 96)\n                            )\n                # update output parameters\n                output_params.update(intermediate_dict)\n                # clear intermediate dict\n                intermediate_dict.clear()\n                # clear stream copy\n                stream_copy.clear()\n                # increment to next stream\n                stream_count += 1\n                # log stream processing\n                self.__logging and logger.debug(\n                    \"Processed #{} stream successfully.\".format(idx)\n                )\n            # store stream count\n            output_params[\"stream_count\"] = stream_count\n            # log streams processing\n            self.__logging and logger.debug(\"All streams processed successfully!\")\n        else:\n            # skip and log\n            logger.warning(\"Invalid type `-streams` skipped!\")\n\n        return output_params\n\n    def __generate_hls_stream(self, input_params, output_params):\n        \"\"\"\n        An internal function that parses user-defined parameters and generates\n        suitable FFmpeg Terminal Command for transcoding input into HLS Stream.\n\n        Parameters:\n            input_params (dict): Input FFmpeg parameters\n            output_params (dict): Output FFmpeg parameters\n        \"\"\"\n        # validate `hls_segment_type`\n        default_hls_segment_type = self.__params.pop(\"-hls_segment_type\", \"mpegts\")\n        if isinstance(\n            default_hls_segment_type, str\n        ) and default_hls_segment_type.strip() in [\"fmp4\", \"mpegts\"]:\n            output_params[\"-hls_segment_type\"] = default_hls_segment_type.strip()\n        else:\n            # otherwise reset to default\n            logger.warning(\"Invalid `-hls_segment_type` value skipped!\")\n            output_params[\"-hls_segment_type\"] = \"mpegts\"\n        # gather required parameters\n        if self.__livestreaming:\n            # `hls_list_size` must be greater than or equal to 0\n            default_hls_list_size = self.__params.pop(\"-hls_list_size\", 6)\n            if isinstance(default_hls_list_size, int) and default_hls_list_size >= 0:\n                output_params[\"-hls_list_size\"] = default_hls_list_size\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-hls_list_size` value skipped!\")\n                output_params[\"-hls_list_size\"] = 6\n            # `hls_init_time` must be greater than or equal to 0\n            default_hls_init_time = self.__params.pop(\"-hls_init_time\", 4)\n            if isinstance(default_hls_init_time, int) and default_hls_init_time >= 0:\n                output_params[\"-hls_init_time\"] = default_hls_init_time\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-hls_init_time` value skipped!\")\n                output_params[\"-hls_init_time\"] = 4\n            # `hls_time` must be greater than or equal to 0\n            default_hls_time = self.__params.pop(\"-hls_time\", 4)\n            if isinstance(default_hls_time, int) and default_hls_time >= 0:\n                output_params[\"-hls_time\"] = default_hls_time\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-hls_time` value skipped!\")\n                output_params[\"-hls_time\"] = 6\n            # `hls_flags` must be string\n            default_hls_flags = self.__params.pop(\n                \"-hls_flags\", \"delete_segments+discont_start+split_by_time\"\n            )\n            if isinstance(default_hls_flags, str):\n                output_params[\"-hls_flags\"] = default_hls_flags\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-hls_flags` value skipped!\")\n                output_params[\"-hls_flags\"] = (\n                    \"delete_segments+discont_start+split_by_time\"\n                )\n            # clean everything at exit?\n            remove_at_exit = self.__params.pop(\"-remove_at_exit\", 0)\n            if isinstance(remove_at_exit, int) and remove_at_exit in [\n                0,\n                1,\n            ]:\n                output_params[\"-remove_at_exit\"] = remove_at_exit\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-remove_at_exit` value skipped!\")\n                output_params[\"-remove_at_exit\"] = 0\n        else:\n            # enforce \"contain all the segments\"\n            output_params[\"-hls_list_size\"] = 0\n            output_params[\"-hls_playlist_type\"] = \"vod\"\n\n        # handle base URL for absolute paths\n        hls_base_url = self.__params.pop(\"-hls_base_url\", \"\")\n        if isinstance(hls_base_url, str):\n            output_params[\"-hls_base_url\"] = hls_base_url\n        else:\n            # otherwise reset to default\n            logger.warning(\"Invalid `-hls_base_url` value skipped!\")\n            output_params[\"-hls_base_url\"] = \"\"\n\n        # Hardcoded HLS parameters (Refer FFmpeg docs for more info.)\n        output_params[\"-allowed_extensions\"] = \"ALL\"\n        # Handling <hls_segment_filename>\n        # Here filename will be based on `stream_count` dict parameter that\n        # would be used to check whether stream is multi-variant(>1) or single(0-1)\n        segment_template = (\n            \"{}-stream%v-%03d.{}\"\n            if output_params[\"stream_count\"] > 1\n            else \"{}-stream-%03d.{}\"\n        )\n        output_params[\"-hls_segment_filename\"] = segment_template.format(\n            os.path.join(os.path.dirname(self.__out_file), \"chunk\"),\n            \"m4s\" if output_params[\"-hls_segment_type\"] == \"fmp4\" else \"ts\",\n        )\n        # Hardcoded HLS parameters (Refer FFmpeg docs for more info.)\n        output_params[\"-hls_allow_cache\"] = 0\n        # enable hls formatting\n        output_params[\"-f\"] = \"hls\"\n        # return HLS params\n        return (input_params, output_params)\n\n    def __generate_dash_stream(self, input_params, output_params):\n        \"\"\"\n        An internal function that parses user-defined parameters and generates\n        suitable FFmpeg Terminal Command for transcoding input into MPEG-dash Stream.\n\n        Parameters:\n            input_params (dict): Input FFmpeg parameters\n            output_params (dict): Output FFmpeg parameters\n        \"\"\"\n\n        # Check if live-streaming or not?\n        if self.__livestreaming:\n            # `extra_window_size` must be greater than or equal to 0\n            window_size = self.__params.pop(\"-window_size\", 5)\n            if isinstance(window_size, int) and window_size >= 0:\n                output_params[\"-window_size\"] = window_size\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-window_size` value skipped!\")\n                output_params[\"-window_size\"] = 5\n            # `extra_window_size` must be greater than or equal to 0\n            extra_window_size = self.__params.pop(\"-extra_window_size\", 5)\n            if isinstance(extra_window_size, int) and extra_window_size >= 0:\n                output_params[\"-extra_window_size\"] = window_size\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-extra_window_size` value skipped!\")\n                output_params[\"-extra_window_size\"] = 5\n            # clean everything at exit?\n            remove_at_exit = self.__params.pop(\"-remove_at_exit\", 0)\n            if isinstance(remove_at_exit, int) and remove_at_exit in [\n                0,\n                1,\n            ]:\n                output_params[\"-remove_at_exit\"] = remove_at_exit\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-remove_at_exit` value skipped!\")\n                output_params[\"-remove_at_exit\"] = 0\n            # `seg_duration` must be greater than or equal to 0\n            seg_duration = self.__params.pop(\"-seg_duration\", 20)\n            if isinstance(seg_duration, int) and seg_duration >= 0:\n                output_params[\"-seg_duration\"] = seg_duration\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-seg_duration` value skipped!\")\n                output_params[\"-seg_duration\"] = 20\n            # Disable (0) the use of a SegmentTimeline inside a SegmentTemplate.\n            output_params[\"-use_timeline\"] = 0\n        else:\n            # `seg_duration` must be greater than or equal to 0\n            seg_duration = self.__params.pop(\"-seg_duration\", 5)\n            if isinstance(seg_duration, int) and seg_duration >= 0:\n                output_params[\"-seg_duration\"] = seg_duration\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-seg_duration` value skipped!\")\n                output_params[\"-seg_duration\"] = 5\n            # Enable (1) the use of a SegmentTimeline inside a SegmentTemplate.\n            output_params[\"-use_timeline\"] = 1\n\n        # Finally, some hardcoded DASH parameters (Refer FFmpeg docs for more info.)\n        output_params[\"-use_template\"] = 1\n        output_params[\"-adaptation_sets\"] = \"id=0,streams=v {}\".format(\n            \"id=1,streams=a\" if (\"-acodec\" in output_params) else \"\"\n        )\n        # enable dash formatting\n        output_params[\"-f\"] = \"dash\"\n        # return DASH params\n        return (input_params, output_params)\n\n    def __Build_n_Execute(self, input_params, output_params):\n        \"\"\"\n        An Internal function that launches FFmpeg subprocess and pipelines commands.\n\n        Parameters:\n            input_params (dict): Input FFmpeg parameters\n            output_params (dict): Output FFmpeg parameters\n        \"\"\"\n        # handle audio source if present\n        \"-core_asource\" in output_params and output_params.move_to_end(\n            \"-core_asource\", last=False\n        )\n        # handle `-i` parameter\n        \"-i\" in output_params and output_params.move_to_end(\"-i\", last=False)\n        # copy streams count\n        stream_count = output_params.pop(\"stream_count\", 1)\n\n        # convert input parameters to list\n        input_commands = dict2Args(input_params)\n        # convert output parameters to list\n        output_commands = dict2Args(output_params)\n        # convert any additional parameters to list\n        stream_commands = dict2Args(self.__params)\n\n        # create exclusive HLS params\n        hls_commands = []\n        # handle HLS multi-variant streams\n        if self.__format == \"hls\" and stream_count > 1:\n            stream_map = \"\"\n            for count in range(0, stream_count):\n                stream_map += \"v:{}{} \".format(\n                    count, \",a:{}\".format(count) if \"-acodec\" in output_params else \",\"\n                )\n            hls_commands += [\n                \"-master_pl_name\",\n                os.path.basename(self.__out_file),\n                \"-var_stream_map\",\n                stream_map.strip(),\n                os.path.join(os.path.dirname(self.__out_file), \"stream_%v.m3u8\"),\n            ]\n\n        # log it if enabled\n        self.__logging and logger.debug(\n            \"User-Defined Output parameters: `{}`\".format(\n                \" \".join(output_commands) if output_commands else None\n            )\n        )\n        self.__logging and logger.debug(\n            \"Additional parameters: `{}`\".format(\n                \" \".join(stream_commands) if stream_commands else None\n            )\n        )\n        # build FFmpeg command from parameters\n        ffmpeg_cmd = None\n        # ensuring less cluttering if silent mode\n        hide_banner = [] if self.__logging else [\"-hide_banner\"]\n        # format commands\n        if self.__video_source:\n            ffmpeg_cmd = (\n                [self.__ffmpeg, \"-y\"]\n                + ([\"-re\"] if self.__livestreaming else [])  # pseudo live-streaming\n                + hide_banner\n                + [\"-i\", self.__video_source]\n                + input_commands\n                + output_commands\n                + stream_commands\n            )\n        else:\n            ffmpeg_cmd = (\n                [self.__ffmpeg, \"-y\"]\n                + hide_banner\n                + [\"-f\", \"rawvideo\", \"-vcodec\", \"rawvideo\"]\n                + input_commands\n                + [\"-i\", \"-\"]\n                + output_commands\n                + stream_commands\n            )\n        # format outputs\n        ffmpeg_cmd.extend([self.__out_file] if not (hls_commands) else hls_commands)\n        # Launch the FFmpeg pipeline with built command\n        logger.critical(\"Transcoding streaming chunks. Please wait...\")  # log it\n        self.__process = sp.Popen(\n            ffmpeg_cmd,\n            stdin=sp.PIPE,\n            stdout=(\n                sp.DEVNULL\n                if (not self.__video_source and not self.__logging)\n                else sp.PIPE\n            ),\n            stderr=None if self.__logging else sp.STDOUT,\n        )\n        # post handle progress bar and runtime errors in case of video_source\n        if self.__video_source:\n            return_code = 0\n            pbar = None\n            sec_prev = 0\n            if self.__logging:\n                self.__process.communicate()\n                return_code = self.__process.returncode\n            else:\n                # iterate until stdout runs out\n                while True:\n                    # read and process data\n                    data = self.__process.stdout.readline()\n                    if data:\n                        data = data.decode(\"utf-8\")\n                        # extract duration and time-left\n                        if pbar is None and \"Duration:\" in data:\n                            # extract time in seconds\n                            sec_duration = extract_time(data)\n                            # initiate progress bar\n                            pbar = tqdm(\n                                total=sec_duration,\n                                desc=\"Processing Frames\",\n                                unit=\"frame\",\n                            )\n                        elif \"time=\" in data:\n                            # extract time in seconds\n                            sec_current = extract_time(data)\n                            # update progress bar\n                            if sec_current:\n                                pbar.update(sec_current - sec_prev)\n                                sec_prev = sec_current\n                    else:\n                        # poll if no data\n                        if self.__process.poll() is not None:\n                            break\n                return_code = self.__process.poll()\n            # close progress bar\n            not (pbar is None) and pbar.close()\n            # handle return_code\n            if return_code != 0:\n                # log and raise error if return_code is `1`\n                logger.error(\n                    \"StreamGear failed to initiate stream for this video source!\"\n                )\n                raise sp.CalledProcessError(return_code, ffmpeg_cmd)\n            else:\n                # log if successful\n                logger.critical(\n                    \"Transcoding Ended. {} Streaming assets are successfully generated at specified path.\".format(\n                        self.__format.upper()\n                    )\n                )\n\n    def __enter__(self):\n        \"\"\"\n        Handles entry with the `with` statement. See [PEP343 -- The 'with' statement'](https://peps.python.org/pep-0343/).\n\n        **Returns:** Returns a reference to the StreamGear Class\n        \"\"\"\n        return self\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        \"\"\"\n        Handles exit with the `with` statement. See [PEP343 -- The 'with' statement'](https://peps.python.org/pep-0343/).\n        \"\"\"\n        self.close()\n\n    @deprecated(\n        message=\"The `terminate()` method will be removed in the next release. Kindly use `close()` method instead.\"\n    )\n    def terminate(self):\n        \"\"\"\n        !!! warning \"[DEPRECATION NOTICE]: This method is now deprecated and will be removed in a future release.\"\n\n        This function ensures backward compatibility for the `terminate()` method to maintain the API on existing systems.\n        It achieves this by calling the new `close()` method to terminate various\n        StreamGear processes.\n        \"\"\"\n\n        self.close()\n\n    def close(self):\n        \"\"\"\n        Safely terminates various StreamGear process.\n        \"\"\"\n        # log termination\n        self.__logging and logger.debug(\"Terminating StreamGear Processes.\")\n\n        # return if no process was initiated at first place\n        if self.__process is None or not (self.__process.poll() is None):\n            return\n        # close `stdin` output\n        self.__process.stdin and self.__process.stdin.close()\n        # close `stdout` output\n        self.__process.stdout and self.__process.stdout.close()\n        # forced termination if specified.\n        if self.__forced_termination:\n            self.__process.terminate()\n        # handle device audio streams\n        elif self.__audio and isinstance(self.__audio, list):\n            # send `CTRL_BREAK_EVENT` signal if Windows else `SIGINT`\n            self.__process.send_signal(\n                signal.CTRL_BREAK_EVENT if self.__os_windows else signal.SIGINT\n            )\n        # wait if process is still processing\n        self.__process.wait()\n        # discard process\n        self.__process = None\n

"},{"location":"bonus/reference/streamgear/#vidgear.gears.streamgear.StreamGear.__enter__","title":"__enter__(self) special","text":"

Handles entry with the with statement. See PEP343 -- The 'with' statement'.

Returns: Returns a reference to the StreamGear Class

Source code in vidgear/gears/streamgear.py
def __enter__(self):\n    \"\"\"\n    Handles entry with the `with` statement. See [PEP343 -- The 'with' statement'](https://peps.python.org/pep-0343/).\n\n    **Returns:** Returns a reference to the StreamGear Class\n    \"\"\"\n    return self\n
"},{"location":"bonus/reference/streamgear/#vidgear.gears.streamgear.StreamGear.__exit__","title":"__exit__(self, exc_type, exc_val, exc_tb) special","text":"

Handles exit with the with statement. See PEP343 -- The 'with' statement'.

Source code in vidgear/gears/streamgear.py
def __exit__(self, exc_type, exc_val, exc_tb):\n    \"\"\"\n    Handles exit with the `with` statement. See [PEP343 -- The 'with' statement'](https://peps.python.org/pep-0343/).\n    \"\"\"\n    self.close()\n
"},{"location":"bonus/reference/streamgear/#vidgear.gears.streamgear.StreamGear.__init__","title":"__init__(self, output='', format='dash', custom_ffmpeg='', logging=False, **stream_params) special","text":"

This constructor method initializes the object state and attributes of the StreamGear class.

Parameters:

Name Type Description Default output str

sets the valid filename/path for generating the StreamGear assets.

'' format str

select the adaptive HTTP streaming format(DASH and HLS).

'dash' custom_ffmpeg str

assigns the location of custom path/directory for custom FFmpeg executables.

'' logging bool

enables/disables logging.

False stream_params dict

provides the flexibility to control supported internal parameters and FFmpeg properties.

{} Source code in vidgear/gears/streamgear.py
def __init__(\n    self, output=\"\", format=\"dash\", custom_ffmpeg=\"\", logging=False, **stream_params\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the StreamGear class.\n\n    Parameters:\n        output (str): sets the valid filename/path for generating the StreamGear assets.\n        format (str): select the adaptive HTTP streaming format(DASH and HLS).\n        custom_ffmpeg (str): assigns the location of custom path/directory for custom FFmpeg executables.\n        logging (bool): enables/disables logging.\n        stream_params (dict): provides the flexibility to control supported internal parameters and FFmpeg properties.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # checks if machine in-use is running windows os or not\n    self.__os_windows = True if os.name == \"nt\" else False\n\n    # initialize various class variables\n    # handles user-defined parameters\n    self.__params = {}\n    # handle input video/frame resolution and channels\n    self.__inputheight = None\n    self.__inputwidth = None\n    self.__inputchannels = None\n    self.__sourceframerate = None\n    # handle process to be frames written\n    self.__process = None\n    # handle valid FFmpeg assets location\n    self.__ffmpeg = \"\"\n    # handle one time process for valid process initialization\n    self.__initiate_stream = True\n\n    # cleans and reformat user-defined parameters\n    self.__params = {\n        str(k).strip(): (v.strip() if isinstance(v, str) else v)\n        for k, v in stream_params.items()\n    }\n\n    # handle where to save the downloaded FFmpeg Static assets on Windows(if specified)\n    __ffmpeg_download_path = self.__params.pop(\"-ffmpeg_download_path\", \"\")\n    if not isinstance(__ffmpeg_download_path, (str)):\n        # reset improper values\n        __ffmpeg_download_path = \"\"\n\n    # validate the FFmpeg assets and return location (also downloads static assets on windows)\n    self.__ffmpeg = get_valid_ffmpeg_path(\n        str(custom_ffmpeg),\n        self.__os_windows,\n        ffmpeg_download_path=__ffmpeg_download_path,\n        logging=self.__logging,\n    )\n\n    # check if valid FFmpeg path returned\n    if self.__ffmpeg:\n        self.__logging and logger.debug(\n            \"Found valid FFmpeg executables: `{}`.\".format(self.__ffmpeg)\n        )\n    else:\n        # else raise error\n        raise RuntimeError(\n            \"[StreamGear:ERROR] :: Failed to find FFmpeg assets on this system. Kindly compile/install FFmpeg or provide a valid custom FFmpeg binary path!\"\n        )\n\n    # handle streaming format\n    supported_formats = [\"dash\", \"hls\"]  # TODO will be extended in future\n    if format and isinstance(format, str):\n        _format = format.strip().lower()\n        if _format in supported_formats:\n            self.__format = _format\n            logger.info(\n                \"StreamGear will generate asset files for {} streaming format.\".format(\n                    self.__format.upper()\n                )\n            )\n        elif difflib.get_close_matches(_format, supported_formats):\n            raise ValueError(\n                \"[StreamGear:ERROR] :: Incorrect `format` parameter value! Did you mean `{}`?\".format(\n                    difflib.get_close_matches(_format, supported_formats)[0]\n                )\n            )\n        else:\n            raise ValueError(\n                \"[StreamGear:ERROR] :: The `format` parameter value `{}` not valid/supported!\".format(\n                    format\n                )\n            )\n    else:\n        raise ValueError(\n            \"[StreamGear:ERROR] :: The `format` parameter value is Missing or Invalid!\"\n        )\n\n    # handle Audio-Input\n    audio = self.__params.pop(\"-audio\", False)\n    if audio and isinstance(audio, str):\n        if os.path.isfile(audio):\n            self.__audio = os.path.abspath(audio)\n        elif is_valid_url(self.__ffmpeg, url=audio, logging=self.__logging):\n            self.__audio = audio\n        else:\n            self.__audio = False\n    elif audio and isinstance(audio, list):\n        self.__audio = audio\n    else:\n        self.__audio = False\n    # log external audio source\n    self.__audio and self.__logging and logger.debug(\n        \"External audio source `{}` detected.\".format(self.__audio)\n    )\n\n    # handle Video-Source input\n    source = self.__params.pop(\"-video_source\", False)\n    # Check if input is valid string\n    if source and isinstance(source, str) and len(source) > 1:\n        # Differentiate input\n        if os.path.isfile(source):\n            self.__video_source = os.path.abspath(source)\n        elif is_valid_url(self.__ffmpeg, url=source, logging=self.__logging):\n            self.__video_source = source\n        else:\n            # discard the value otherwise\n            self.__video_source = False\n\n        # Validate input\n        if self.__video_source:\n            validation_results = validate_video(\n                self.__ffmpeg, video_path=self.__video_source\n            )\n            assert not (\n                validation_results is None\n            ), \"[StreamGear:ERROR] :: Given `{}` video_source is Invalid, Check Again!\".format(\n                self.__video_source\n            )\n            self.__aspect_source = validation_results[\"resolution\"]\n            self.__fps_source = validation_results[\"framerate\"]\n            # log it\n            self.__logging and logger.debug(\n                \"Given video_source is valid and has {}x{} resolution, and a framerate of {} fps.\".format(\n                    self.__aspect_source[0],\n                    self.__aspect_source[1],\n                    self.__fps_source,\n                )\n            )\n        else:\n            # log warning\n            logger.warning(\"Discarded invalid `-video_source` value provided.\")\n    else:\n        if source:\n            # log warning if source provided\n            logger.warning(\"Invalid `-video_source` value provided.\")\n        else:\n            # log normally\n            logger.info(\"No `-video_source` value provided.\")\n        # discard the value otherwise\n        self.__video_source = False\n\n    # handle user-defined framerate\n    self.__inputframerate = self.__params.pop(\"-input_framerate\", 0.0)\n    if isinstance(self.__inputframerate, (float, int)):\n        # must be float\n        self.__inputframerate = float(self.__inputframerate)\n    else:\n        # reset improper values\n        self.__inputframerate = 0.0\n\n    # handle old assets\n    clear_assets = self.__params.pop(\"-clear_prev_assets\", False)\n    if isinstance(clear_assets, bool):\n        self.__clear_assets = clear_assets\n        # log if clearing assets is enabled\n        clear_assets and logger.info(\n            \"The `-clear_prev_assets` parameter is enabled successfully. All previous StreamGear API assets for `{}` format will be removed for this run.\".format(\n                self.__format.upper()\n            )\n        )\n    else:\n        # reset improper values\n        self.__clear_assets = False\n\n    # handle whether to livestream?\n    livestreaming = self.__params.pop(\"-livestream\", False)\n    if isinstance(livestreaming, bool) and livestreaming:\n        # NOTE:  `livestream` is only available with real-time mode.\n        self.__livestreaming = livestreaming if not (self.__video_source) else False\n        if self.__video_source:\n            logger.error(\n                \"Live-Streaming is only available with Real-time Mode. Refer docs for more information.\"\n            )\n        else:\n            # log if live streaming is enabled\n            livestreaming and logger.info(\n                \"Live-Streaming is successfully enabled for this run.\"\n            )\n    else:\n        # reset improper values\n        self.__livestreaming = False\n\n    # handle the special-case of forced-termination\n    enable_force_termination = self.__params.pop(\"-enable_force_termination\", False)\n    # check if value is valid\n    if isinstance(enable_force_termination, bool):\n        self.__forced_termination = enable_force_termination\n        # log if forced termination is enabled\n        self.__forced_termination and logger.warning(\n            \"Forced termination is enabled for this run. This may result in corrupted output in certain scenarios!\"\n        )\n    else:\n        # handle improper values\n        self.__forced_termination = False\n\n    # handle streaming format\n    supported_formats = [\"dash\", \"hls\"]  # TODO will be extended in future\n    if format and isinstance(format, str):\n        _format = format.strip().lower()\n        if _format in supported_formats:\n            self.__format = _format\n            logger.info(\n                \"StreamGear will generate asset files for {} streaming format.\".format(\n                    self.__format.upper()\n                )\n            )\n        elif difflib.get_close_matches(_format, supported_formats):\n            raise ValueError(\n                \"[StreamGear:ERROR] :: Incorrect `format` parameter value! Did you mean `{}`?\".format(\n                    difflib.get_close_matches(_format, supported_formats)[0]\n                )\n            )\n        else:\n            raise ValueError(\n                \"[StreamGear:ERROR] :: The `format` parameter value `{}` not valid/supported!\".format(\n                    format\n                )\n            )\n    else:\n        raise ValueError(\n            \"[StreamGear:ERROR] :: The `format` parameter value is Missing or Invalid!\"\n        )\n\n    # handles output asset filenames\n    if output:\n        # validate this class has the access rights to specified directory or not\n        abs_path = os.path.abspath(output)\n        # check if given output is a valid system path\n        if check_WriteAccess(\n            os.path.dirname(abs_path),\n            is_windows=self.__os_windows,\n            logging=self.__logging,\n        ):\n            # get all assets extensions\n            valid_extension = \"mpd\" if self.__format == \"dash\" else \"m3u8\"\n            assets_exts = [\n                (\"chunk-stream\", \".m4s\"),  # filename prefix, extension\n                (\"chunk-stream\", \".ts\"),  # filename prefix, extension\n                \".{}\".format(valid_extension),\n            ]\n            # add source file extension too\n            self.__video_source and assets_exts.append(\n                (\n                    \"chunk-stream\",\n                    os.path.splitext(self.__video_source)[1],\n                )  # filename prefix, extension\n            )\n            # handle output\n            # check if path is a directory\n            if os.path.isdir(abs_path):\n                # clear previous assets if specified\n                self.__clear_assets and delete_ext_safe(\n                    abs_path, assets_exts, logging=self.__logging\n                )\n                # auto-assign valid name and adds it to path\n                abs_path = os.path.join(\n                    abs_path,\n                    \"{}-{}.{}\".format(\n                        self.__format,\n                        time.strftime(\"%Y%m%d-%H%M%S\"),\n                        valid_extension,\n                    ),\n                )\n            # or check if path is a file\n            elif os.path.isfile(abs_path) and self.__clear_assets:\n                # clear previous assets if specified\n                delete_ext_safe(\n                    os.path.dirname(abs_path),\n                    assets_exts,\n                    logging=self.__logging,\n                )\n            # check if path has valid file extension\n            assert abs_path.endswith(\n                valid_extension\n            ), \"Given `{}` path has invalid file-extension w.r.t selected format: `{}`!\".format(\n                output, self.__format.upper()\n            )\n            self.__logging and logger.debug(\n                \"Output Path:`{}` is successfully configured for generating streaming assets.\".format(\n                    abs_path\n                )\n            )\n            # workaround patch for Windows only,\n            # others platforms will not be affected\n            self.__out_file = abs_path.replace(\"\\\\\", \"/\")\n        # check if given output is a valid URL\n        elif is_valid_url(self.__ffmpeg, url=output, logging=self.__logging):\n            self.__logging and logger.debug(\n                \"URL:`{}` is valid and successfully configured for generating streaming assets.\".format(\n                    output\n                )\n            )\n            self.__out_file = output\n        # raise ValueError otherwise\n        else:\n            raise ValueError(\n                \"[StreamGear:ERROR] :: The output parameter value:`{}` is not valid/supported!\".format(\n                    output\n                )\n            )\n    else:\n        # raise ValueError otherwise\n        raise ValueError(\n            \"[StreamGear:ERROR] :: Kindly provide a valid `output` parameter value. Refer Docs for more information.\"\n        )\n\n    # log Mode of operation\n    self.__video_source and logger.info(\n        \"StreamGear has been successfully configured for {} Mode.\".format(\n            \"Single-Source\" if self.__video_source else \"Real-time Frames\"\n        )\n    )\n
"},{"location":"bonus/reference/streamgear/#vidgear.gears.streamgear.StreamGear.close","title":"close(self)","text":"

Safely terminates various StreamGear process.

Source code in vidgear/gears/streamgear.py
def close(self):\n    \"\"\"\n    Safely terminates various StreamGear process.\n    \"\"\"\n    # log termination\n    self.__logging and logger.debug(\"Terminating StreamGear Processes.\")\n\n    # return if no process was initiated at first place\n    if self.__process is None or not (self.__process.poll() is None):\n        return\n    # close `stdin` output\n    self.__process.stdin and self.__process.stdin.close()\n    # close `stdout` output\n    self.__process.stdout and self.__process.stdout.close()\n    # forced termination if specified.\n    if self.__forced_termination:\n        self.__process.terminate()\n    # handle device audio streams\n    elif self.__audio and isinstance(self.__audio, list):\n        # send `CTRL_BREAK_EVENT` signal if Windows else `SIGINT`\n        self.__process.send_signal(\n            signal.CTRL_BREAK_EVENT if self.__os_windows else signal.SIGINT\n        )\n    # wait if process is still processing\n    self.__process.wait()\n    # discard process\n    self.__process = None\n
"},{"location":"bonus/reference/streamgear/#vidgear.gears.streamgear.StreamGear.stream","title":"stream(self, frame, rgb_mode=False)","text":"

Pipes ndarray frames to FFmpeg Pipeline for transcoding them into chunked-encoded media segments of streaming formats such as MPEG-DASH and HLS.

[DEPRECATION NOTICE]: The rgb_mode parameter is deprecated and will be removed in a future version.

Parameters:

Name Type Description Default frame ndarray

a valid numpy frame

required rgb_mode boolean

enable this flag to activate RGB mode (i.e. specifies that incoming frames are of RGB format instead of default BGR).

False Source code in vidgear/gears/streamgear.py
@deprecated(\n    parameter=\"rgb_mode\",\n    message=\"The `rgb_mode` parameter is deprecated and will be removed in a future version. Only BGR format frames will be supported going forward.\",\n)\ndef stream(self, frame, rgb_mode=False):\n    \"\"\"\n    Pipes `ndarray` frames to FFmpeg Pipeline for transcoding them into chunked-encoded media segments of\n    streaming formats such as MPEG-DASH and HLS.\n\n    !!! warning \"[DEPRECATION NOTICE]: The `rgb_mode` parameter is deprecated and will be removed in a future version.\"\n\n    Parameters:\n        frame (ndarray): a valid numpy frame\n        rgb_mode (boolean): enable this flag to activate RGB mode _(i.e. specifies that incoming frames are of RGB format instead of default BGR)_.\n    \"\"\"\n    # check if function is called in correct context\n    if self.__video_source:\n        raise RuntimeError(\n            \"[StreamGear:ERROR] :: The `stream()` method cannot be used when streaming from a `-video_source` input file. Kindly refer vidgear docs!\"\n        )\n    # None-Type frames will be skipped\n    if frame is None:\n        return\n    # extract height, width and number of channels of frame\n    height, width = frame.shape[:2]\n    channels = frame.shape[-1] if frame.ndim == 3 else 1\n    # assign values to class variables on first run\n    if self.__initiate_stream:\n        self.__inputheight = height\n        self.__inputwidth = width\n        self.__inputchannels = channels\n        self.__sourceframerate = (\n            25.0 if not (self.__inputframerate) else self.__inputframerate\n        )\n        self.__logging and logger.debug(\n            \"InputFrame => Height:{} Width:{} Channels:{}\".format(\n                self.__inputheight, self.__inputwidth, self.__inputchannels\n            )\n        )\n    # validate size of frame\n    if height != self.__inputheight or width != self.__inputwidth:\n        raise ValueError(\"[StreamGear:ERROR] :: All frames must have same size!\")\n    # validate number of channels\n    if channels != self.__inputchannels:\n        raise ValueError(\n            \"[StreamGear:ERROR] :: All frames must have same number of channels!\"\n        )\n    # initiate FFmpeg process on first run\n    if self.__initiate_stream:\n        # launch pre-processing\n        self.__PreProcess(channels=channels, rgb=rgb_mode)\n        # Check status of the process\n        assert self.__process is not None\n\n    # write the frame to pipeline\n    try:\n        self.__process.stdin.write(frame.tobytes())\n    except (OSError, IOError):\n        # log something is wrong!\n        logger.error(\n            \"BrokenPipeError caught, Wrong values passed to FFmpeg Pipe, Kindly Refer Docs!\"\n        )\n        raise ValueError  # for testing purpose only\n
"},{"location":"bonus/reference/streamgear/#vidgear.gears.streamgear.StreamGear.terminate","title":"terminate(self)","text":"

[DEPRECATION NOTICE]: This method is now deprecated and will be removed in a future release.

This function ensures backward compatibility for the terminate() method to maintain the API on existing systems. It achieves this by calling the new close() method to terminate various StreamGear processes.

Source code in vidgear/gears/streamgear.py
@deprecated(\n    message=\"The `terminate()` method will be removed in the next release. Kindly use `close()` method instead.\"\n)\ndef terminate(self):\n    \"\"\"\n    !!! warning \"[DEPRECATION NOTICE]: This method is now deprecated and will be removed in a future release.\"\n\n    This function ensures backward compatibility for the `terminate()` method to maintain the API on existing systems.\n    It achieves this by calling the new `close()` method to terminate various\n    StreamGear processes.\n    \"\"\"\n\n    self.close()\n
"},{"location":"bonus/reference/streamgear/#vidgear.gears.streamgear.StreamGear.transcode_source","title":"transcode_source(self)","text":"

Transcodes an entire video file (with or without audio) into chunked-encoded media segments of streaming formats such as MPEG-DASH and HLS.

Source code in vidgear/gears/streamgear.py
def transcode_source(self):\n    \"\"\"\n    Transcodes an entire video file _(with or without audio)_ into chunked-encoded media segments of\n    streaming formats such as MPEG-DASH and HLS.\n    \"\"\"\n    # check if function is called in correct context\n    if not (self.__video_source):\n        raise RuntimeError(\n            \"[StreamGear:ERROR] :: The `transcode_source()` method cannot be used without a valid `-video_source` input. Kindly refer vidgear docs!\"\n        )\n    # assign height, width and framerate\n    self.__inputheight = int(self.__aspect_source[1])\n    self.__inputwidth = int(self.__aspect_source[0])\n    self.__sourceframerate = float(self.__fps_source)\n    # launch pre-processing\n    self.__PreProcess()\n
"},{"location":"bonus/reference/videogear/","title":"VideoGear API References","text":"

VideoGear API usage examples can be found here \u27b6

VideoGear API parameters are explained here \u27b6

VideoGear API provides a special internal wrapper around VidGear's exclusive Video Stabilizer class. VideoGear also acts as a Common Video-Capture API that provides internal access for both CamGear and PiGear APIs and their parameters with an exclusive enablePiCamera boolean flag.

VideoGear is ideal when you need to switch to different video sources without changing your code much. Also, it enables easy stabilization for various video-streams (real-time or not) with minimum effort and writing way fewer lines of code.

Source code in vidgear/gears/videogear.py
class VideoGear:\n    \"\"\"\n    VideoGear API provides a special internal wrapper around VidGear's exclusive Video Stabilizer class.\n    VideoGear also acts as a Common Video-Capture API that provides internal access for both CamGear and PiGear APIs and\n    their parameters with an exclusive enablePiCamera boolean flag.\n\n    VideoGear is ideal when you need to switch to different video sources without changing your code much. Also, it enables\n    easy stabilization for various video-streams (real-time or not)\n    with minimum effort and writing way fewer lines of code.\n    \"\"\"\n\n    def __init__(\n        self,\n        # VideoGear parameters\n        enablePiCamera=False,\n        stabilize=False,\n        # PiGear parameters\n        camera_num=0,\n        resolution=(640, 480),\n        framerate=30,\n        # CamGear parameters\n        source=0,\n        stream_mode=False,\n        backend=0,\n        # common parameters\n        time_delay=0,\n        colorspace=None,\n        logging=False,\n        **options\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the VideoGear class.\n\n        Parameters:\n            enablePiCamera (bool): provide access to PiGear(if True) or CamGear(if False) APIs respectively.\n            stabilize (bool): enable access to Stabilizer Class for stabilizing frames.\n            camera_num (int): selects the camera module index which will be used as Rpi source.\n            resolution (tuple): sets the resolution (i.e. `(width,height)`) of the Rpi source.\n            framerate (int/float): sets the framerate of the Rpi source.\n            source (based on input): defines the source for the input stream.\n            stream_mode (bool): controls the exclusive YouTube Mode.\n            backend (int): selects the backend for OpenCV's VideoCapture class.\n            colorspace (str): selects the colorspace of the input stream.\n            logging (bool): enables/disables logging.\n            time_delay (int): time delay (in sec) before start reading the frames.\n            options (dict): provides ability to alter Tweak Parameters of CamGear, PiGear & Stabilizer.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # initialize stabilizer\n        self.__stabilization_mode = stabilize\n\n        # reformat dictionary\n        options = {str(k).strip(): v for k, v in options.items()}\n\n        if self.__stabilization_mode:\n            from .stabilizer import Stabilizer\n\n            s_radius = options.pop(\"SMOOTHING_RADIUS\", 25)\n            if not isinstance(s_radius, int):\n                s_radius = 25\n\n            border_size = options.pop(\"BORDER_SIZE\", 0)\n            if not isinstance(border_size, int):\n                border_size = 0\n\n            border_type = options.pop(\"BORDER_TYPE\", \"black\")\n            if not isinstance(border_type, str):\n                border_type = \"black\"\n\n            crop_n_zoom = options.pop(\"CROP_N_ZOOM\", False)\n            if not isinstance(crop_n_zoom, bool):\n                crop_n_zoom = False\n\n            self.__stabilizer_obj = Stabilizer(\n                smoothing_radius=s_radius,\n                border_type=border_type,\n                border_size=border_size,\n                crop_n_zoom=crop_n_zoom,\n                logging=logging,\n            )\n            self.__logging and logger.debug(\n                \"Enabling Stabilization Mode for the current video source!\"\n            )  # log info\n\n        if enablePiCamera:\n            # only import the pigear module only if required\n            from .pigear import PiGear\n\n            # initialize the picamera stream by enabling PiGear API\n            self.stream = PiGear(\n                camera_num=camera_num,\n                resolution=resolution,\n                framerate=framerate,\n                colorspace=colorspace,\n                logging=logging,\n                time_delay=time_delay,\n                **options\n            )\n        else:\n            # otherwise, we are using OpenCV so initialize the webcam\n            # stream by activating CamGear API\n            self.stream = CamGear(\n                source=source,\n                stream_mode=stream_mode,\n                backend=backend,\n                colorspace=colorspace,\n                logging=logging,\n                time_delay=time_delay,\n                **options\n            )\n\n        # initialize framerate variable\n        self.framerate = self.stream.framerate\n\n    def start(self):\n        \"\"\"\n        Launches the internal *Threaded Frames Extractor* daemon of API in use.\n\n        **Returns:** A reference to the selected class object.\n        \"\"\"\n        self.stream.start()\n        return self\n\n    def read(self):\n        \"\"\"\n        Extracts frames synchronously from selected API's monitored deque, while maintaining a fixed-length frame\n        buffer in the memory, and blocks the thread if the deque is full.\n\n        **Returns:** A n-dimensional numpy array.\n        \"\"\"\n        while self.__stabilization_mode:\n            frame = self.stream.read()\n            if frame is None:\n                break\n            frame_stab = self.__stabilizer_obj.stabilize(frame)\n            if not (frame_stab is None):\n                return frame_stab\n        return self.stream.read()\n\n    def stop(self):\n        \"\"\"\n        Safely terminates the thread, and release the respective multi-threaded resources.\n        \"\"\"\n        self.stream.stop()\n        # logged\n        self.__logging and logger.debug(\"Terminating VideoGear.\")\n        # clean queue\n        self.__stabilization_mode and self.__stabilizer_obj.clean()\n

"},{"location":"bonus/reference/videogear/#vidgear.gears.videogear.VideoGear.__init__","title":"__init__(self, enablePiCamera=False, stabilize=False, camera_num=0, resolution=(640, 480), framerate=30, source=0, stream_mode=False, backend=0, time_delay=0, colorspace=None, logging=False, **options) special","text":"

This constructor method initializes the object state and attributes of the VideoGear class.

Parameters:

Name Type Description Default enablePiCamera bool

provide access to PiGear(if True) or CamGear(if False) APIs respectively.

False stabilize bool

enable access to Stabilizer Class for stabilizing frames.

False camera_num int

selects the camera module index which will be used as Rpi source.

0 resolution tuple

sets the resolution (i.e. (width,height)) of the Rpi source.

(640, 480) framerate int/float

sets the framerate of the Rpi source.

30 source based on input

defines the source for the input stream.

0 stream_mode bool

controls the exclusive YouTube Mode.

False backend int

selects the backend for OpenCV's VideoCapture class.

0 colorspace str

selects the colorspace of the input stream.

None logging bool

enables/disables logging.

False time_delay int

time delay (in sec) before start reading the frames.

0 options dict

provides ability to alter Tweak Parameters of CamGear, PiGear & Stabilizer.

{} Source code in vidgear/gears/videogear.py
def __init__(\n    self,\n    # VideoGear parameters\n    enablePiCamera=False,\n    stabilize=False,\n    # PiGear parameters\n    camera_num=0,\n    resolution=(640, 480),\n    framerate=30,\n    # CamGear parameters\n    source=0,\n    stream_mode=False,\n    backend=0,\n    # common parameters\n    time_delay=0,\n    colorspace=None,\n    logging=False,\n    **options\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the VideoGear class.\n\n    Parameters:\n        enablePiCamera (bool): provide access to PiGear(if True) or CamGear(if False) APIs respectively.\n        stabilize (bool): enable access to Stabilizer Class for stabilizing frames.\n        camera_num (int): selects the camera module index which will be used as Rpi source.\n        resolution (tuple): sets the resolution (i.e. `(width,height)`) of the Rpi source.\n        framerate (int/float): sets the framerate of the Rpi source.\n        source (based on input): defines the source for the input stream.\n        stream_mode (bool): controls the exclusive YouTube Mode.\n        backend (int): selects the backend for OpenCV's VideoCapture class.\n        colorspace (str): selects the colorspace of the input stream.\n        logging (bool): enables/disables logging.\n        time_delay (int): time delay (in sec) before start reading the frames.\n        options (dict): provides ability to alter Tweak Parameters of CamGear, PiGear & Stabilizer.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # initialize stabilizer\n    self.__stabilization_mode = stabilize\n\n    # reformat dictionary\n    options = {str(k).strip(): v for k, v in options.items()}\n\n    if self.__stabilization_mode:\n        from .stabilizer import Stabilizer\n\n        s_radius = options.pop(\"SMOOTHING_RADIUS\", 25)\n        if not isinstance(s_radius, int):\n            s_radius = 25\n\n        border_size = options.pop(\"BORDER_SIZE\", 0)\n        if not isinstance(border_size, int):\n            border_size = 0\n\n        border_type = options.pop(\"BORDER_TYPE\", \"black\")\n        if not isinstance(border_type, str):\n            border_type = \"black\"\n\n        crop_n_zoom = options.pop(\"CROP_N_ZOOM\", False)\n        if not isinstance(crop_n_zoom, bool):\n            crop_n_zoom = False\n\n        self.__stabilizer_obj = Stabilizer(\n            smoothing_radius=s_radius,\n            border_type=border_type,\n            border_size=border_size,\n            crop_n_zoom=crop_n_zoom,\n            logging=logging,\n        )\n        self.__logging and logger.debug(\n            \"Enabling Stabilization Mode for the current video source!\"\n        )  # log info\n\n    if enablePiCamera:\n        # only import the pigear module only if required\n        from .pigear import PiGear\n\n        # initialize the picamera stream by enabling PiGear API\n        self.stream = PiGear(\n            camera_num=camera_num,\n            resolution=resolution,\n            framerate=framerate,\n            colorspace=colorspace,\n            logging=logging,\n            time_delay=time_delay,\n            **options\n        )\n    else:\n        # otherwise, we are using OpenCV so initialize the webcam\n        # stream by activating CamGear API\n        self.stream = CamGear(\n            source=source,\n            stream_mode=stream_mode,\n            backend=backend,\n            colorspace=colorspace,\n            logging=logging,\n            time_delay=time_delay,\n            **options\n        )\n\n    # initialize framerate variable\n    self.framerate = self.stream.framerate\n
"},{"location":"bonus/reference/videogear/#vidgear.gears.videogear.VideoGear.read","title":"read(self)","text":"

Extracts frames synchronously from selected API's monitored deque, while maintaining a fixed-length frame buffer in the memory, and blocks the thread if the deque is full.

Returns: A n-dimensional numpy array.

Source code in vidgear/gears/videogear.py
def read(self):\n    \"\"\"\n    Extracts frames synchronously from selected API's monitored deque, while maintaining a fixed-length frame\n    buffer in the memory, and blocks the thread if the deque is full.\n\n    **Returns:** A n-dimensional numpy array.\n    \"\"\"\n    while self.__stabilization_mode:\n        frame = self.stream.read()\n        if frame is None:\n            break\n        frame_stab = self.__stabilizer_obj.stabilize(frame)\n        if not (frame_stab is None):\n            return frame_stab\n    return self.stream.read()\n
"},{"location":"bonus/reference/videogear/#vidgear.gears.videogear.VideoGear.start","title":"start(self)","text":"

Launches the internal Threaded Frames Extractor daemon of API in use.

Returns: A reference to the selected class object.

Source code in vidgear/gears/videogear.py
def start(self):\n    \"\"\"\n    Launches the internal *Threaded Frames Extractor* daemon of API in use.\n\n    **Returns:** A reference to the selected class object.\n    \"\"\"\n    self.stream.start()\n    return self\n
"},{"location":"bonus/reference/videogear/#vidgear.gears.videogear.VideoGear.stop","title":"stop(self)","text":"

Safely terminates the thread, and release the respective multi-threaded resources.

Source code in vidgear/gears/videogear.py
def stop(self):\n    \"\"\"\n    Safely terminates the thread, and release the respective multi-threaded resources.\n    \"\"\"\n    self.stream.stop()\n    # logged\n    self.__logging and logger.debug(\"Terminating VideoGear.\")\n    # clean queue\n    self.__stabilization_mode and self.__stabilizer_obj.clean()\n
"},{"location":"bonus/reference/webgear/","title":"WebGear API References","text":"

WebGear API usage examples can be found here \u27b6

WebGear API parameters are explained here \u27b6

WebGear is a powerful ASGI Video-Broadcaster API ideal for transmitting Motion-JPEG-frames from a single source to multiple recipients via the browser.

WebGear API works on Starlette's ASGI application and provides a highly extensible and flexible async wrapper around its complete framework. WebGear can flexibly interact with Starlette's ecosystem of shared middleware, mountable applications, Response classes, Routing tables, Static Files, Templating engine(with Jinja2), etc.

WebGear API uses an intraframe-only compression scheme under the hood where the sequence of video-frames are first encoded as JPEG-DIB (JPEG with Device-Independent Bit compression) and then streamed over HTTP using Starlette's Multipart Streaming Response and a Uvicorn ASGI Server. This method imposes lower processing and memory requirements, but the quality is not the best, since JPEG compression is not very efficient for motion video.

In layman's terms, WebGear acts as a powerful Video Broadcaster that transmits live video-frames to any web-browser in the network. Additionally, WebGear API also provides internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs, thereby granting it exclusive power for transferring frames incoming from any source to the network.

Source code in vidgear/gears/asyncio/webgear.py
class WebGear:\n    \"\"\"\n    WebGear is a powerful ASGI Video-Broadcaster API ideal for transmitting Motion-JPEG-frames from a single source to multiple recipients via the browser.\n\n    WebGear API works on Starlette's ASGI application and provides a highly extensible and flexible async wrapper around its complete framework. WebGear can\n    flexibly interact with Starlette's ecosystem of shared middleware, mountable applications, Response classes, Routing tables, Static Files, Templating\n    engine(with Jinja2), etc.\n\n    WebGear API uses an intraframe-only compression scheme under the hood where the sequence of video-frames are first encoded as JPEG-DIB (JPEG with Device-Independent Bit compression)\n    and then streamed over HTTP using Starlette's Multipart Streaming Response and a Uvicorn ASGI Server. This method imposes lower processing and memory requirements, but the quality\n    is not the best, since JPEG compression is not very efficient for motion video.\n\n    In layman's terms, WebGear acts as a powerful Video Broadcaster that transmits live video-frames to any web-browser in the network. Additionally, WebGear API also provides internal\n    wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs, thereby granting it exclusive power for transferring frames incoming from any source to the network.\n    \"\"\"\n\n    def __init__(\n        self,\n        enablePiCamera=False,\n        stabilize=False,\n        source=None,\n        camera_num=0,\n        stream_mode=False,\n        backend=0,\n        colorspace=None,\n        resolution=(640, 480),\n        framerate=25,\n        logging=False,\n        time_delay=0,\n        **options\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the WebGear class.\n\n        Parameters:\n            enablePiCamera (bool): provide access to PiGear(if True) or CamGear(if False) APIs respectively.\n            stabilize (bool): enable access to Stabilizer Class for stabilizing frames.\n            camera_num (int): selects the camera module index which will be used as Rpi source.\n            resolution (tuple): sets the resolution (i.e. `(width,height)`) of the Rpi source.\n            framerate (int/float): sets the framerate of the Rpi source.\n            source (based on input): defines the source for the input stream.\n            stream_mode (bool): controls the exclusive YouTube Mode.\n            backend (int): selects the backend for OpenCV's VideoCapture class.\n            colorspace (str): selects the colorspace of the input stream.\n            logging (bool): enables/disables logging.\n            time_delay (int): time delay (in sec) before start reading the frames.\n            options (dict): provides ability to alter Tweak Parameters of WebGear, CamGear, PiGear & Stabilizer.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # raise error(s) for critical Class imports\n        import_dependency_safe(\"starlette\" if starlette is None else \"\")\n        import_dependency_safe(\n            \"simplejpeg\" if simplejpeg is None else \"\", min_version=\"1.6.1\"\n        )\n\n        # initialize global params\n        self.__skip_generate_webdata = False  # generate webgear data by default\n        # define frame-compression handler\n        self.__jpeg_compression_quality = 90  # 90% quality\n        self.__jpeg_compression_fastdct = True  # fastest DCT on by default\n        self.__jpeg_compression_fastupsample = False  # fastupsample off by default\n        self.__jpeg_compression_colorspace = \"BGR\"  # use BGR colorspace by default\n        self.__frame_size_reduction = 25  # use 25% reduction\n        # retrieve interpolation for reduction\n        self.__interpolation = retrieve_best_interpolation(\n            [\"INTER_LINEAR_EXACT\", \"INTER_LINEAR\", \"INTER_AREA\"]\n        )\n\n        custom_video_endpoint = \"\"  # custom video endpoint path\n        custom_data_location = \"\"  # path to save data-files to custom location\n        data_path = \"\"  # path to WebGear data-files\n        overwrite_default = False\n        self.__enable_inf = False  # continue frames even when video ends.\n\n        # reformat dictionary\n        options = {str(k).strip(): v for k, v in options.items()}\n\n        # assign values to global variables if specified and valid\n        if options:\n            # check whether to disable Data-Files Auto-Generation WorkFlow\n            if \"skip_generate_webdata\" in options:\n                value = options[\"skip_generate_webdata\"]\n                # enable jpeg fastdct\n                if isinstance(value, bool):\n                    self.__skip_generate_webdata = value\n                else:\n                    logger.warning(\"Skipped invalid `skip_generate_webdata` value!\")\n                del options[\"skip_generate_webdata\"]  # clean\n\n            if \"jpeg_compression_colorspace\" in options:\n                value = options[\"jpeg_compression_colorspace\"]\n                if isinstance(value, str) and value.strip().upper() in [\n                    \"RGB\",\n                    \"BGR\",\n                    \"RGBX\",\n                    \"BGRX\",\n                    \"XBGR\",\n                    \"XRGB\",\n                    \"GRAY\",\n                    \"RGBA\",\n                    \"BGRA\",\n                    \"ABGR\",\n                    \"ARGB\",\n                    \"CMYK\",\n                ]:\n                    # set encoding colorspace\n                    self.__jpeg_compression_colorspace = value.strip().upper()\n                else:\n                    logger.warning(\n                        \"Skipped invalid `jpeg_compression_colorspace` value!\"\n                    )\n                del options[\"jpeg_compression_colorspace\"]  # clean\n\n            if \"jpeg_compression_quality\" in options:\n                value = options[\"jpeg_compression_quality\"]\n                # set valid jpeg quality\n                if isinstance(value, (int, float)) and value >= 10 and value <= 100:\n                    self.__jpeg_compression_quality = int(value)\n                else:\n                    logger.warning(\"Skipped invalid `jpeg_compression_quality` value!\")\n                del options[\"jpeg_compression_quality\"]  # clean\n\n            if \"jpeg_compression_fastdct\" in options:\n                value = options[\"jpeg_compression_fastdct\"]\n                # enable jpeg fastdct\n                if isinstance(value, bool):\n                    self.__jpeg_compression_fastdct = value\n                else:\n                    logger.warning(\"Skipped invalid `jpeg_compression_fastdct` value!\")\n                del options[\"jpeg_compression_fastdct\"]  # clean\n\n            if \"jpeg_compression_fastupsample\" in options:\n                value = options[\"jpeg_compression_fastupsample\"]\n                # enable jpeg  fastupsample\n                if isinstance(value, bool):\n                    self.__jpeg_compression_fastupsample = value\n                else:\n                    logger.warning(\n                        \"Skipped invalid `jpeg_compression_fastupsample` value!\"\n                    )\n                del options[\"jpeg_compression_fastupsample\"]  # clean\n\n            if \"frame_size_reduction\" in options:\n                value = options[\"frame_size_reduction\"]\n                if isinstance(value, (int, float)) and value >= 0 and value <= 90:\n                    self.__frame_size_reduction = value\n                else:\n                    logger.warning(\"Skipped invalid `frame_size_reduction` value!\")\n                del options[\"frame_size_reduction\"]  # clean\n\n            if \"custom_video_endpoint\" in options:\n                value = options[\"custom_video_endpoint\"]\n                if value and isinstance(value, str) and value.strip().isalnum():\n                    custom_video_endpoint = value.strip()\n                    logging and logger.critical(\n                        \"Using custom video endpoint path: `/{}`\".format(\n                            custom_video_endpoint\n                        )\n                    )\n                else:\n                    logger.warning(\"Skipped invalid `custom_video_endpoint` value!\")\n                del options[\"custom_video_endpoint\"]  # clean\n\n            if \"custom_data_location\" in options:\n                value = options[\"custom_data_location\"]\n                if value and isinstance(value, str):\n                    assert os.access(\n                        value, os.W_OK\n                    ), \"[WebGear:ERROR] :: Permission Denied!, cannot write WebGear data-files to '{}' directory!\".format(\n                        value\n                    )\n                    assert os.path.isdir(\n                        os.path.abspath(value)\n                    ), \"[WebGear:ERROR] :: `custom_data_location` value must be the path to a directory and not to a file!\"\n                    custom_data_location = os.path.abspath(value)\n                else:\n                    logger.warning(\"Skipped invalid `custom_data_location` value!\")\n                del options[\"custom_data_location\"]  # clean\n\n            if \"overwrite_default_files\" in options:\n                value = options[\"overwrite_default_files\"]\n                if isinstance(value, bool):\n                    overwrite_default = value\n                else:\n                    logger.warning(\"Skipped invalid `overwrite_default_files` value!\")\n                del options[\"overwrite_default_files\"]  # clean\n\n            if \"enable_infinite_frames\" in options:\n                value = options[\"enable_infinite_frames\"]\n                if isinstance(value, bool):\n                    self.__enable_inf = value\n                else:\n                    logger.warning(\"Skipped invalid `enable_infinite_frames` value!\")\n                del options[\"enable_infinite_frames\"]  # clean\n\n        # check if disable Data-Files Auto-Generation WorkFlow is disabled\n        if not self.__skip_generate_webdata:\n            # check if custom data path is specified\n            if custom_data_location:\n                data_path = generate_webdata(\n                    custom_data_location,\n                    c_name=\"webgear\",\n                    overwrite_default=overwrite_default,\n                    logging=logging,\n                )\n            else:\n                # otherwise generate suitable path\n                data_path = generate_webdata(\n                    os.path.join(expanduser(\"~\"), \".vidgear\"),\n                    c_name=\"webgear\",\n                    overwrite_default=overwrite_default,\n                    logging=logging,\n                )\n\n            # log it\n            self.__logging and logger.debug(\n                \"`{}` is the default location for saving WebGear data-files.\".format(\n                    data_path\n                )\n            )\n            # define Jinja2 templates handler\n            self.__templates = Jinja2Templates(\n                directory=\"{}/templates\".format(data_path)\n            )\n            # define routing tables\n            self.routes = [\n                Route(\"/\", endpoint=self.__homepage),\n                Route(\n                    \"/{}\".format(\n                        custom_video_endpoint if custom_video_endpoint else \"video\"\n                    ),\n                    endpoint=self.__video,\n                ),\n                Mount(\n                    \"/static\",\n                    app=StaticFiles(directory=\"{}/static\".format(data_path)),\n                    name=\"static\",\n                ),\n            ]\n        else:\n            # log it\n            self.__logging and logger.critical(\n                \"WebGear Data-Files Auto-Generation WorkFlow has been manually disabled.\"\n            )\n            # define routing tables\n            self.routes = [\n                Route(\n                    \"/{}\".format(\n                        custom_video_endpoint if custom_video_endpoint else \"video\"\n                    ),\n                    endpoint=self.__video,\n                ),\n            ]\n            # log exceptions\n            self.__logging and logger.warning(\n                \"Only `/video` route is available for this instance.\"\n            )\n\n        # define custom exception handlers\n        self.__exception_handlers = {404: self.__not_found, 500: self.__server_error}\n        # define middleware support\n        self.middleware = []\n        # Handle video source\n        if source is None:\n            self.config = {\"generator\": None}\n            self.__stream = None\n        else:\n            # define stream with necessary params\n            self.__stream = VideoGear(\n                enablePiCamera=enablePiCamera,\n                stabilize=stabilize,\n                source=source,\n                camera_num=camera_num,\n                stream_mode=stream_mode,\n                backend=backend,\n                colorspace=colorspace,\n                resolution=resolution,\n                framerate=framerate,\n                logging=logging,\n                time_delay=time_delay,\n                **options\n            )\n            # define default frame generator in configuration\n            self.config = {\"generator\": self.__producer}\n\n        # log if specified\n        if self.__logging:\n            if source is None:\n                logger.warning(\n                    \"Given source is of NoneType. Therefore, JPEG Frame-Compression is disabled!\"\n                )\n            else:\n                logger.debug(\n                    \"Enabling JPEG Frame-Compression with Colorspace:`{}`, Quality:`{}`%, Fastdct:`{}`, and Fastupsample:`{}`.\".format(\n                        self.__jpeg_compression_colorspace,\n                        self.__jpeg_compression_quality,\n                        \"enabled\" if self.__jpeg_compression_fastdct else \"disabled\",\n                        (\n                            \"enabled\"\n                            if self.__jpeg_compression_fastupsample\n                            else \"disabled\"\n                        ),\n                    )\n                )\n\n        # copying original routing tables for further validation\n        self.__rt_org_copy = self.routes[:]\n        # initialize blank frame\n        self.blank_frame = None\n        # keeps check if producer loop should be running\n        self.__isrunning = True\n\n    def __call__(self):\n        \"\"\"\n        Implements a custom Callable method for WebGear application.\n        \"\"\"\n        # validate routing tables\n        assert not (self.routes is None), \"Routing tables are NoneType!\"\n        if not isinstance(self.routes, list) or not all(\n            x in self.routes for x in self.__rt_org_copy\n        ):\n            raise RuntimeError(\"[WebGear:ERROR] :: Routing tables are not valid!\")\n\n        # validate middlewares\n        assert not (self.middleware is None), \"Middlewares are NoneType!\"\n        if self.middleware and (\n            not isinstance(self.middleware, list)\n            or not all(isinstance(x, Middleware) for x in self.middleware)\n        ):\n            raise RuntimeError(\"[WebGear:ERROR] :: Middlewares are not valid!\")\n\n        # validate assigned frame generator in WebGear configuration\n        if isinstance(self.config, dict) and \"generator\" in self.config:\n            # check if its  assigned value is a asynchronous generator\n            if self.config[\"generator\"] is None or not inspect.isasyncgen(\n                self.config[\"generator\"]()\n            ):\n                # otherwise raise error\n                raise ValueError(\n                    \"[WebGear:ERROR] :: Invalid configuration. Assigned generator must be a asynchronous generator function/method only!\"\n                )\n        else:\n            # raise error if validation fails\n            raise RuntimeError(\"[WebGear:ERROR] :: Assigned configuration is invalid!\")\n\n        # initiate stream\n        self.__logging and logger.debug(\"Initiating Video Streaming.\")\n        if not (self.__stream is None):\n            self.__stream.start()\n        # return Starlette application\n        self.__logging and logger.debug(\"Running Starlette application.\")\n        return Starlette(\n            debug=(True if self.__logging else False),\n            routes=self.routes,\n            middleware=self.middleware,\n            exception_handlers=self.__exception_handlers,\n            lifespan=self.__lifespan,\n        )\n\n    async def __producer(self):\n        \"\"\"\n        WebGear's default asynchronous frame producer/generator.\n        \"\"\"\n        # loop over frames\n        while self.__isrunning:\n            # read frame\n            frame = self.__stream.read()\n\n            # display blank if NoneType\n            if frame is None:\n                frame = (\n                    self.blank_frame\n                    if self.blank_frame is None\n                    else self.blank_frame[:]\n                )\n                if not self.__enable_inf:\n                    self.__isrunning = False\n            else:\n                # create blank\n                if self.blank_frame is None:\n                    self.blank_frame = create_blank_frame(\n                        frame=frame,\n                        text=\"No Input\" if self.__enable_inf else \"The End\",\n                        logging=self.__logging,\n                    )\n\n            # reducer frames size if specified\n            if self.__frame_size_reduction:\n                frame = await reducer(\n                    frame,\n                    percentage=self.__frame_size_reduction,\n                    interpolation=self.__interpolation,\n                )\n\n            # handle JPEG encoding\n            if self.__jpeg_compression_colorspace == \"GRAY\":\n                if frame.ndim == 2:\n                    # patch for https://gitlab.com/jfolz/simplejpeg/-/issues/11\n                    frame = np.expand_dims(frame, axis=2)\n                encodedImage = simplejpeg.encode_jpeg(\n                    frame,\n                    quality=self.__jpeg_compression_quality,\n                    colorspace=self.__jpeg_compression_colorspace,\n                    fastdct=self.__jpeg_compression_fastdct,\n                )\n            else:\n                encodedImage = simplejpeg.encode_jpeg(\n                    frame,\n                    quality=self.__jpeg_compression_quality,\n                    colorspace=self.__jpeg_compression_colorspace,\n                    colorsubsampling=\"422\",\n                    fastdct=self.__jpeg_compression_fastdct,\n                )\n\n            # yield frame in byte format\n            yield (\n                b\"--frame\\r\\nContent-Type:image/jpeg\\r\\n\\r\\n\" + encodedImage + b\"\\r\\n\"\n            )\n            # sleep for sometime.\n            await asyncio.sleep(0)\n\n    async def __video(self, scope):\n        \"\"\"\n        Returns a async video streaming response.\n        \"\"\"\n        assert scope[\"type\"] in [\"http\", \"https\"]\n        return StreamingResponse(\n            self.config[\"generator\"](),\n            media_type=\"multipart/x-mixed-replace; boundary=frame\",\n        )\n\n    async def __homepage(self, request):\n        \"\"\"\n        Returns an HTML index page.\n        \"\"\"\n        return (\n            self.__templates.TemplateResponse(request, \"index.html\")\n            if not self.__skip_generate_webdata\n            else JSONResponse(\n                {\n                    \"detail\": \"MESSAGE : WebGear Data-Files Auto-Generation WorkFlow is disabled!\"\n                },\n                status_code=404,\n            )\n        )\n\n    async def __not_found(self, request, exc):\n        \"\"\"\n        Returns an HTML 404 page.\n        \"\"\"\n        return (\n            self.__templates.TemplateResponse(request, \"404.html\", status_code=404)\n            if not self.__skip_generate_webdata\n            else JSONResponse(\n                {\n                    \"detail\": \"ERROR : {} :: MESSAGE : WebGear Data-Files Auto-Generation WorkFlow is disabled.\".format(\n                        exc.detail\n                    )\n                },\n                status_code=404,\n            )\n        )\n\n    async def __server_error(self, request, exc):\n        \"\"\"\n        Returns an HTML 500 page.\n        \"\"\"\n        return (\n            self.__templates.TemplateResponse(request, \"500.html\", status_code=500)\n            if not self.__skip_generate_webdata\n            else JSONResponse(\n                {\n                    \"detail\": \"ERROR : {} :: MESSAGE : WebGear Data-Files Auto-Generation WorkFlow is disabled.\".format(\n                        exc.detail if hasattr(exc, \"detail\") else repr(exc)\n                    )\n                },\n                status_code=500,\n            )\n        )\n\n    @contextlib.asynccontextmanager\n    async def __lifespan(self, context):\n        try:\n            yield\n        finally:\n            # close Video Server\n            self.shutdown()\n\n    def shutdown(self):\n        \"\"\"\n        Implements a Callable to be run on application shutdown\n        \"\"\"\n        if not (self.__stream is None):\n            self.__logging and logger.debug(\"Closing Video Streaming.\")\n            # stops producer\n            self.__isrunning = False\n            # stops VideoGear stream\n            self.__stream.stop()\n            # prevent any re-iteration\n            self.__stream = None\n

"},{"location":"bonus/reference/webgear/#vidgear.gears.asyncio.webgear.WebGear.__call__","title":"__call__(self) special","text":"

Implements a custom Callable method for WebGear application.

Source code in vidgear/gears/asyncio/webgear.py
def __call__(self):\n    \"\"\"\n    Implements a custom Callable method for WebGear application.\n    \"\"\"\n    # validate routing tables\n    assert not (self.routes is None), \"Routing tables are NoneType!\"\n    if not isinstance(self.routes, list) or not all(\n        x in self.routes for x in self.__rt_org_copy\n    ):\n        raise RuntimeError(\"[WebGear:ERROR] :: Routing tables are not valid!\")\n\n    # validate middlewares\n    assert not (self.middleware is None), \"Middlewares are NoneType!\"\n    if self.middleware and (\n        not isinstance(self.middleware, list)\n        or not all(isinstance(x, Middleware) for x in self.middleware)\n    ):\n        raise RuntimeError(\"[WebGear:ERROR] :: Middlewares are not valid!\")\n\n    # validate assigned frame generator in WebGear configuration\n    if isinstance(self.config, dict) and \"generator\" in self.config:\n        # check if its  assigned value is a asynchronous generator\n        if self.config[\"generator\"] is None or not inspect.isasyncgen(\n            self.config[\"generator\"]()\n        ):\n            # otherwise raise error\n            raise ValueError(\n                \"[WebGear:ERROR] :: Invalid configuration. Assigned generator must be a asynchronous generator function/method only!\"\n            )\n    else:\n        # raise error if validation fails\n        raise RuntimeError(\"[WebGear:ERROR] :: Assigned configuration is invalid!\")\n\n    # initiate stream\n    self.__logging and logger.debug(\"Initiating Video Streaming.\")\n    if not (self.__stream is None):\n        self.__stream.start()\n    # return Starlette application\n    self.__logging and logger.debug(\"Running Starlette application.\")\n    return Starlette(\n        debug=(True if self.__logging else False),\n        routes=self.routes,\n        middleware=self.middleware,\n        exception_handlers=self.__exception_handlers,\n        lifespan=self.__lifespan,\n    )\n
"},{"location":"bonus/reference/webgear/#vidgear.gears.asyncio.webgear.WebGear.__init__","title":"__init__(self, enablePiCamera=False, stabilize=False, source=None, camera_num=0, stream_mode=False, backend=0, colorspace=None, resolution=(640, 480), framerate=25, logging=False, time_delay=0, **options) special","text":"

This constructor method initializes the object state and attributes of the WebGear class.

Parameters:

Name Type Description Default enablePiCamera bool

provide access to PiGear(if True) or CamGear(if False) APIs respectively.

False stabilize bool

enable access to Stabilizer Class for stabilizing frames.

False camera_num int

selects the camera module index which will be used as Rpi source.

0 resolution tuple

sets the resolution (i.e. (width,height)) of the Rpi source.

(640, 480) framerate int/float

sets the framerate of the Rpi source.

25 source based on input

defines the source for the input stream.

None stream_mode bool

controls the exclusive YouTube Mode.

False backend int

selects the backend for OpenCV's VideoCapture class.

0 colorspace str

selects the colorspace of the input stream.

None logging bool

enables/disables logging.

False time_delay int

time delay (in sec) before start reading the frames.

0 options dict

provides ability to alter Tweak Parameters of WebGear, CamGear, PiGear & Stabilizer.

{} Source code in vidgear/gears/asyncio/webgear.py
def __init__(\n    self,\n    enablePiCamera=False,\n    stabilize=False,\n    source=None,\n    camera_num=0,\n    stream_mode=False,\n    backend=0,\n    colorspace=None,\n    resolution=(640, 480),\n    framerate=25,\n    logging=False,\n    time_delay=0,\n    **options\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the WebGear class.\n\n    Parameters:\n        enablePiCamera (bool): provide access to PiGear(if True) or CamGear(if False) APIs respectively.\n        stabilize (bool): enable access to Stabilizer Class for stabilizing frames.\n        camera_num (int): selects the camera module index which will be used as Rpi source.\n        resolution (tuple): sets the resolution (i.e. `(width,height)`) of the Rpi source.\n        framerate (int/float): sets the framerate of the Rpi source.\n        source (based on input): defines the source for the input stream.\n        stream_mode (bool): controls the exclusive YouTube Mode.\n        backend (int): selects the backend for OpenCV's VideoCapture class.\n        colorspace (str): selects the colorspace of the input stream.\n        logging (bool): enables/disables logging.\n        time_delay (int): time delay (in sec) before start reading the frames.\n        options (dict): provides ability to alter Tweak Parameters of WebGear, CamGear, PiGear & Stabilizer.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # raise error(s) for critical Class imports\n    import_dependency_safe(\"starlette\" if starlette is None else \"\")\n    import_dependency_safe(\n        \"simplejpeg\" if simplejpeg is None else \"\", min_version=\"1.6.1\"\n    )\n\n    # initialize global params\n    self.__skip_generate_webdata = False  # generate webgear data by default\n    # define frame-compression handler\n    self.__jpeg_compression_quality = 90  # 90% quality\n    self.__jpeg_compression_fastdct = True  # fastest DCT on by default\n    self.__jpeg_compression_fastupsample = False  # fastupsample off by default\n    self.__jpeg_compression_colorspace = \"BGR\"  # use BGR colorspace by default\n    self.__frame_size_reduction = 25  # use 25% reduction\n    # retrieve interpolation for reduction\n    self.__interpolation = retrieve_best_interpolation(\n        [\"INTER_LINEAR_EXACT\", \"INTER_LINEAR\", \"INTER_AREA\"]\n    )\n\n    custom_video_endpoint = \"\"  # custom video endpoint path\n    custom_data_location = \"\"  # path to save data-files to custom location\n    data_path = \"\"  # path to WebGear data-files\n    overwrite_default = False\n    self.__enable_inf = False  # continue frames even when video ends.\n\n    # reformat dictionary\n    options = {str(k).strip(): v for k, v in options.items()}\n\n    # assign values to global variables if specified and valid\n    if options:\n        # check whether to disable Data-Files Auto-Generation WorkFlow\n        if \"skip_generate_webdata\" in options:\n            value = options[\"skip_generate_webdata\"]\n            # enable jpeg fastdct\n            if isinstance(value, bool):\n                self.__skip_generate_webdata = value\n            else:\n                logger.warning(\"Skipped invalid `skip_generate_webdata` value!\")\n            del options[\"skip_generate_webdata\"]  # clean\n\n        if \"jpeg_compression_colorspace\" in options:\n            value = options[\"jpeg_compression_colorspace\"]\n            if isinstance(value, str) and value.strip().upper() in [\n                \"RGB\",\n                \"BGR\",\n                \"RGBX\",\n                \"BGRX\",\n                \"XBGR\",\n                \"XRGB\",\n                \"GRAY\",\n                \"RGBA\",\n                \"BGRA\",\n                \"ABGR\",\n                \"ARGB\",\n                \"CMYK\",\n            ]:\n                # set encoding colorspace\n                self.__jpeg_compression_colorspace = value.strip().upper()\n            else:\n                logger.warning(\n                    \"Skipped invalid `jpeg_compression_colorspace` value!\"\n                )\n            del options[\"jpeg_compression_colorspace\"]  # clean\n\n        if \"jpeg_compression_quality\" in options:\n            value = options[\"jpeg_compression_quality\"]\n            # set valid jpeg quality\n            if isinstance(value, (int, float)) and value >= 10 and value <= 100:\n                self.__jpeg_compression_quality = int(value)\n            else:\n                logger.warning(\"Skipped invalid `jpeg_compression_quality` value!\")\n            del options[\"jpeg_compression_quality\"]  # clean\n\n        if \"jpeg_compression_fastdct\" in options:\n            value = options[\"jpeg_compression_fastdct\"]\n            # enable jpeg fastdct\n            if isinstance(value, bool):\n                self.__jpeg_compression_fastdct = value\n            else:\n                logger.warning(\"Skipped invalid `jpeg_compression_fastdct` value!\")\n            del options[\"jpeg_compression_fastdct\"]  # clean\n\n        if \"jpeg_compression_fastupsample\" in options:\n            value = options[\"jpeg_compression_fastupsample\"]\n            # enable jpeg  fastupsample\n            if isinstance(value, bool):\n                self.__jpeg_compression_fastupsample = value\n            else:\n                logger.warning(\n                    \"Skipped invalid `jpeg_compression_fastupsample` value!\"\n                )\n            del options[\"jpeg_compression_fastupsample\"]  # clean\n\n        if \"frame_size_reduction\" in options:\n            value = options[\"frame_size_reduction\"]\n            if isinstance(value, (int, float)) and value >= 0 and value <= 90:\n                self.__frame_size_reduction = value\n            else:\n                logger.warning(\"Skipped invalid `frame_size_reduction` value!\")\n            del options[\"frame_size_reduction\"]  # clean\n\n        if \"custom_video_endpoint\" in options:\n            value = options[\"custom_video_endpoint\"]\n            if value and isinstance(value, str) and value.strip().isalnum():\n                custom_video_endpoint = value.strip()\n                logging and logger.critical(\n                    \"Using custom video endpoint path: `/{}`\".format(\n                        custom_video_endpoint\n                    )\n                )\n            else:\n                logger.warning(\"Skipped invalid `custom_video_endpoint` value!\")\n            del options[\"custom_video_endpoint\"]  # clean\n\n        if \"custom_data_location\" in options:\n            value = options[\"custom_data_location\"]\n            if value and isinstance(value, str):\n                assert os.access(\n                    value, os.W_OK\n                ), \"[WebGear:ERROR] :: Permission Denied!, cannot write WebGear data-files to '{}' directory!\".format(\n                    value\n                )\n                assert os.path.isdir(\n                    os.path.abspath(value)\n                ), \"[WebGear:ERROR] :: `custom_data_location` value must be the path to a directory and not to a file!\"\n                custom_data_location = os.path.abspath(value)\n            else:\n                logger.warning(\"Skipped invalid `custom_data_location` value!\")\n            del options[\"custom_data_location\"]  # clean\n\n        if \"overwrite_default_files\" in options:\n            value = options[\"overwrite_default_files\"]\n            if isinstance(value, bool):\n                overwrite_default = value\n            else:\n                logger.warning(\"Skipped invalid `overwrite_default_files` value!\")\n            del options[\"overwrite_default_files\"]  # clean\n\n        if \"enable_infinite_frames\" in options:\n            value = options[\"enable_infinite_frames\"]\n            if isinstance(value, bool):\n                self.__enable_inf = value\n            else:\n                logger.warning(\"Skipped invalid `enable_infinite_frames` value!\")\n            del options[\"enable_infinite_frames\"]  # clean\n\n    # check if disable Data-Files Auto-Generation WorkFlow is disabled\n    if not self.__skip_generate_webdata:\n        # check if custom data path is specified\n        if custom_data_location:\n            data_path = generate_webdata(\n                custom_data_location,\n                c_name=\"webgear\",\n                overwrite_default=overwrite_default,\n                logging=logging,\n            )\n        else:\n            # otherwise generate suitable path\n            data_path = generate_webdata(\n                os.path.join(expanduser(\"~\"), \".vidgear\"),\n                c_name=\"webgear\",\n                overwrite_default=overwrite_default,\n                logging=logging,\n            )\n\n        # log it\n        self.__logging and logger.debug(\n            \"`{}` is the default location for saving WebGear data-files.\".format(\n                data_path\n            )\n        )\n        # define Jinja2 templates handler\n        self.__templates = Jinja2Templates(\n            directory=\"{}/templates\".format(data_path)\n        )\n        # define routing tables\n        self.routes = [\n            Route(\"/\", endpoint=self.__homepage),\n            Route(\n                \"/{}\".format(\n                    custom_video_endpoint if custom_video_endpoint else \"video\"\n                ),\n                endpoint=self.__video,\n            ),\n            Mount(\n                \"/static\",\n                app=StaticFiles(directory=\"{}/static\".format(data_path)),\n                name=\"static\",\n            ),\n        ]\n    else:\n        # log it\n        self.__logging and logger.critical(\n            \"WebGear Data-Files Auto-Generation WorkFlow has been manually disabled.\"\n        )\n        # define routing tables\n        self.routes = [\n            Route(\n                \"/{}\".format(\n                    custom_video_endpoint if custom_video_endpoint else \"video\"\n                ),\n                endpoint=self.__video,\n            ),\n        ]\n        # log exceptions\n        self.__logging and logger.warning(\n            \"Only `/video` route is available for this instance.\"\n        )\n\n    # define custom exception handlers\n    self.__exception_handlers = {404: self.__not_found, 500: self.__server_error}\n    # define middleware support\n    self.middleware = []\n    # Handle video source\n    if source is None:\n        self.config = {\"generator\": None}\n        self.__stream = None\n    else:\n        # define stream with necessary params\n        self.__stream = VideoGear(\n            enablePiCamera=enablePiCamera,\n            stabilize=stabilize,\n            source=source,\n            camera_num=camera_num,\n            stream_mode=stream_mode,\n            backend=backend,\n            colorspace=colorspace,\n            resolution=resolution,\n            framerate=framerate,\n            logging=logging,\n            time_delay=time_delay,\n            **options\n        )\n        # define default frame generator in configuration\n        self.config = {\"generator\": self.__producer}\n\n    # log if specified\n    if self.__logging:\n        if source is None:\n            logger.warning(\n                \"Given source is of NoneType. Therefore, JPEG Frame-Compression is disabled!\"\n            )\n        else:\n            logger.debug(\n                \"Enabling JPEG Frame-Compression with Colorspace:`{}`, Quality:`{}`%, Fastdct:`{}`, and Fastupsample:`{}`.\".format(\n                    self.__jpeg_compression_colorspace,\n                    self.__jpeg_compression_quality,\n                    \"enabled\" if self.__jpeg_compression_fastdct else \"disabled\",\n                    (\n                        \"enabled\"\n                        if self.__jpeg_compression_fastupsample\n                        else \"disabled\"\n                    ),\n                )\n            )\n\n    # copying original routing tables for further validation\n    self.__rt_org_copy = self.routes[:]\n    # initialize blank frame\n    self.blank_frame = None\n    # keeps check if producer loop should be running\n    self.__isrunning = True\n
"},{"location":"bonus/reference/webgear/#vidgear.gears.asyncio.webgear.WebGear.shutdown","title":"shutdown(self)","text":"

Implements a Callable to be run on application shutdown

Source code in vidgear/gears/asyncio/webgear.py
def shutdown(self):\n    \"\"\"\n    Implements a Callable to be run on application shutdown\n    \"\"\"\n    if not (self.__stream is None):\n        self.__logging and logger.debug(\"Closing Video Streaming.\")\n        # stops producer\n        self.__isrunning = False\n        # stops VideoGear stream\n        self.__stream.stop()\n        # prevent any re-iteration\n        self.__stream = None\n
"},{"location":"bonus/reference/webgear_rtc/","title":"WebGear_RTC API References","text":"

WebGear_RTC API usage examples can be found here \u27b6

WebGear_RTC API parameters are explained here \u27b6

WebGear_RTC is similar to WeGear API in many aspects but utilizes WebRTC technology under the hood instead of Motion JPEG, which makes it suitable for building powerful video-streaming solutions for all modern browsers as well as native clients available on all major platforms.

WebGear_RTC is implemented with the help of aiortc library which is built on top of asynchronous I/O framework for Web Real-Time Communication (WebRTC) and Object Real-Time Communication (ORTC) and supports many features like SDP generation/parsing, Interactive Connectivity Establishment with half-trickle and mDNS support, DTLS key and certificate generation, DTLS handshake, etc.

WebGear_RTC can handle multiple consumers seamlessly and provides native support for ICE (Interactive Connectivity Establishment) protocol, STUN (Session Traversal Utilities for NAT), and TURN (Traversal Using Relays around NAT) servers that help us to easily establish direct media connection with the remote peers for uninterrupted data flow. It also allows us to define our custom Server as a source to transform frames easily before sending them across the network(see this doc example).

WebGear_RTC API works in conjunction with Starlette ASGI application and can also flexibly interact with Starlette's ecosystem of shared middleware, mountable applications, Response classes, Routing tables, Static Files, Templating engine(with Jinja2), etc.

Additionally, WebGear_RTC API also provides internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs.

Source code in vidgear/gears/asyncio/webgear_rtc.py
class WebGear_RTC:\n    \"\"\"\n    WebGear_RTC is similar to WeGear API in many aspects but utilizes WebRTC technology under the hood instead of Motion JPEG, which\n    makes it suitable for building powerful video-streaming solutions for all modern browsers as well as native clients available on\n    all major platforms.\n\n    WebGear_RTC is implemented with the help of aiortc library which is built on top of asynchronous I/O framework for Web Real-Time\n    Communication (WebRTC) and Object Real-Time Communication (ORTC) and supports many features like SDP generation/parsing, Interactive\n    Connectivity Establishment with half-trickle and mDNS support, DTLS key and certificate generation, DTLS handshake, etc.\n\n    WebGear_RTC can handle multiple consumers seamlessly and provides native support for ICE (Interactive Connectivity Establishment)\n    protocol, STUN (Session Traversal Utilities for NAT), and TURN (Traversal Using Relays around NAT) servers that help us to easily\n    establish direct media connection with the remote peers for uninterrupted data flow. It also allows us to define our custom Server\n    as a source to transform frames easily before sending them across the network(see this doc example).\n\n    WebGear_RTC API works in conjunction with Starlette ASGI application and can also flexibly interact with Starlette's ecosystem of\n    shared middleware, mountable applications, Response classes, Routing tables, Static Files, Templating engine(with Jinja2), etc.\n\n    Additionally, WebGear_RTC API also provides internal wrapper around VideoGear, which itself provides internal access to both\n    CamGear and PiGear APIs.\n    \"\"\"\n\n    def __init__(\n        self,\n        enablePiCamera=False,\n        stabilize=False,\n        source=None,\n        camera_num=0,\n        stream_mode=False,\n        backend=0,\n        colorspace=None,\n        resolution=(640, 480),\n        framerate=25,\n        logging=False,\n        time_delay=0,\n        **options\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the WebGear_RTC class.\n\n        Parameters:\n            enablePiCamera (bool): provide access to PiGear(if True) or CamGear(if False) APIs respectively.\n            stabilize (bool): enable access to Stabilizer Class for stabilizing frames.\n            camera_num (int): selects the camera module index which will be used as Rpi source.\n            resolution (tuple): sets the resolution (i.e. `(width,height)`) of the Rpi source.\n            framerate (int/float): sets the framerate of the Rpi source.\n            source (based on input): defines the source for the input stream.\n            stream_mode (bool): controls the exclusive YouTube Mode.\n            backend (int): selects the backend for OpenCV's VideoCapture class.\n            colorspace (str): selects the colorspace of the input stream.\n            logging (bool): enables/disables logging.\n            time_delay (int): time delay (in sec) before start reading the frames.\n            options (dict): provides ability to alter Tweak Parameters of WebGear_RTC, CamGear, PiGear & Stabilizer.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # raise error(s) for critical Class imports\n        import_dependency_safe(\"starlette\" if starlette is None else \"\")\n        import_dependency_safe(\"aiortc\" if aiortc is None else \"\")\n\n        # initialize global params\n        custom_data_location = \"\"  # path to save data-files to custom location\n        data_path = \"\"  # path to WebGear_RTC data-files\n        overwrite_default = False\n        self.__relay = None  # act as broadcaster\n\n        # reformat dictionary\n        options = {str(k).strip(): v for k, v in options.items()}\n\n        # assign values to global variables if specified and valid\n        if options:\n            if \"custom_data_location\" in options:\n                value = options[\"custom_data_location\"]\n                if isinstance(value, str):\n                    assert os.access(\n                        value, os.W_OK\n                    ), \"[WebGear_RTC:ERROR] :: Permission Denied!, cannot write WebGear_RTC data-files to '{}' directory!\".format(\n                        value\n                    )\n                    assert os.path.isdir(\n                        os.path.abspath(value)\n                    ), \"[WebGear_RTC:ERROR] :: `custom_data_location` value must be the path to a directory and not to a file!\"\n                    custom_data_location = os.path.abspath(value)\n                else:\n                    logger.warning(\"Skipped invalid `custom_data_location` value!\")\n                del options[\"custom_data_location\"]  # clean\n\n            if \"overwrite_default_files\" in options:\n                value = options[\"overwrite_default_files\"]\n                if isinstance(value, bool):\n                    overwrite_default = value\n                else:\n                    logger.warning(\"Skipped invalid `overwrite_default_files` value!\")\n                del options[\"overwrite_default_files\"]  # clean\n\n            if \"enable_live_broadcast\" in options:\n                value = options[\"enable_live_broadcast\"]\n                if isinstance(value, bool):\n                    if value:\n                        self.__relay = MediaRelay()\n                        options[\"enable_infinite_frames\"] = (\n                            True  # enforce infinite frames\n                        )\n                        logger.critical(\n                            \"Enabled live broadcasting for Peer connection(s).\"\n                        )\n                    else:\n                        None\n                else:\n                    logger.warning(\"Skipped invalid `enable_live_broadcast` value!\")\n                del options[\"enable_live_broadcast\"]  # clean\n\n        # check if custom certificates path is specified\n        if custom_data_location:\n            data_path = generate_webdata(\n                custom_data_location,\n                c_name=\"webgear_rtc\",\n                overwrite_default=overwrite_default,\n                logging=logging,\n            )\n        else:\n            # otherwise generate suitable path\n            data_path = generate_webdata(\n                os.path.join(expanduser(\"~\"), \".vidgear\"),\n                c_name=\"webgear_rtc\",\n                overwrite_default=overwrite_default,\n                logging=logging,\n            )\n\n        # log it\n        self.__logging and logger.debug(\n            \"`{}` is the default location for saving WebGear_RTC data-files.\".format(\n                data_path\n            )\n        )\n\n        # define Jinja2 templates handler\n        self.__templates = Jinja2Templates(directory=\"{}/templates\".format(data_path))\n\n        # define custom exception handlers\n        self.__exception_handlers = {404: self.__not_found, 500: self.__server_error}\n        # define routing tables\n        self.routes = [\n            Route(\"/\", endpoint=self.__homepage),\n            Route(\"/offer\", self.__offer, methods=[\"GET\", \"POST\"]),\n            Mount(\n                \"/static\",\n                app=StaticFiles(directory=\"{}/static\".format(data_path)),\n                name=\"static\",\n            ),\n        ]\n\n        # define middleware support\n        self.middleware = []\n\n        # Handle RTC video server\n        if \"custom_stream\" in options or not (source is None):\n            # Handle video source\n            self.__default_rtc_server = RTC_VideoServer(\n                enablePiCamera=enablePiCamera,\n                stabilize=stabilize,\n                source=source,\n                camera_num=camera_num,\n                stream_mode=stream_mode,\n                backend=backend,\n                colorspace=colorspace,\n                resolution=resolution,\n                framerate=framerate,\n                logging=logging,\n                time_delay=time_delay,\n                **options\n            )\n            # add exclusive reset connection node\n            self.routes.append(\n                Route(\"/close_connection\", self.__reset_connections, methods=[\"POST\"])\n            )\n        else:\n            raise ValueError(\n                \"[WebGear_RTC:ERROR] :: Source cannot be NoneType without Custom Stream(`custom_stream`) defined!\"\n            )\n\n        # copying original routing tables for further validation\n        self.__rt_org_copy = self.routes[:]\n        # collects peer RTC connections\n        self.__pcs = set()\n\n    def __call__(self):\n        \"\"\"\n        Implements a custom Callable method for WebGear_RTC application.\n        \"\"\"\n        # validate routing tables\n        assert not (self.routes is None), \"Routing tables are NoneType!\"\n        if not isinstance(self.routes, list) or not all(\n            x in self.routes for x in self.__rt_org_copy\n        ):\n            raise RuntimeError(\"[WebGear_RTC:ERROR] :: Routing tables are not valid!\")\n\n        # validate middlewares\n        assert not (self.middleware is None), \"Middlewares are NoneType!\"\n        if self.middleware and (\n            not isinstance(self.middleware, list)\n            or not all(isinstance(x, Middleware) for x in self.middleware)\n        ):\n            raise RuntimeError(\"[WebGear_RTC:ERROR] :: Middlewares are not valid!\")\n\n        # return Starlette application\n        self.__logging and logger.debug(\"Running Starlette application.\")\n        return Starlette(\n            debug=(True if self.__logging else False),\n            routes=self.routes,\n            middleware=self.middleware,\n            exception_handlers=self.__exception_handlers,\n            lifespan=self.__lifespan,\n        )\n\n    async def __offer(self, request):\n        \"\"\"\n        Generates JSON Response with a WebRTC Peer Connection of Video Server.\n        \"\"\"\n        # get offer from params\n        params = await request.json()\n        offer = RTCSessionDescription(sdp=params[\"sdp\"], type=params[\"type\"])\n\n        # initiate stream\n        if not (self.__default_rtc_server is None) and not (\n            self.__default_rtc_server.is_launched\n        ):\n            self.__logging and logger.debug(\"Initiating Video Streaming.\")\n            self.__default_rtc_server.launch()\n\n        # setup RTC peer connection - interface represents a WebRTC connection\n        # between the local computer and a remote peer.\n        pc = RTCPeerConnection()\n        self.__pcs.add(pc)\n        self.__logging and logger.info(\"Created WebRTC Peer Connection.\")\n\n        # track ICE connection state changes\n        @pc.on(\"iceconnectionstatechange\")\n        async def on_iceconnectionstatechange():\n            if pc.iceConnectionState == \"failed\":\n                logger.error(\"ICE connection state failed.\")\n                # check if Live Broadcasting is enabled\n                if self.__relay is None:\n                    # if not, close connection.\n                    await pc.close()\n                    self.__pcs.discard(pc)\n            else:\n                logger.debug(\"ICE connection state is %s\" % pc.iceConnectionState)\n\n        # Change the remote description associated with the connection.\n        await pc.setRemoteDescription(offer)\n        # retrieve list of RTCRtpTransceiver objects that are currently attached to the connection\n        for t in pc.getTransceivers():\n            # Increments performance significantly, IDK why this works as H265 codec is not even supported :D\n            capabilities = RTCRtpSender.getCapabilities(\"video\")\n            preferences = list(filter(lambda x: x.name == \"H265\", capabilities.codecs))\n            t.setCodecPreferences(preferences)\n            # add video server to peer track\n            if t.kind == \"video\":\n                pc.addTrack(\n                    self.__relay.subscribe(self.__default_rtc_server)\n                    if not (self.__relay is None)\n                    else self.__default_rtc_server\n                )\n\n        # Create an SDP answer to an offer received from a remote peer\n        answer = await pc.createAnswer()\n\n        # Change the local description for the answer\n        await pc.setLocalDescription(answer)\n\n        # return Starlette json response\n        return JSONResponse(\n            {\"sdp\": pc.localDescription.sdp, \"type\": pc.localDescription.type}\n        )\n\n    async def __homepage(self, request):\n        \"\"\"\n        Return an HTML index page.\n        \"\"\"\n        return self.__templates.TemplateResponse(request, \"index.html\")\n\n    async def __not_found(self, request, exc):\n        \"\"\"\n        Return an HTML 404 page.\n        \"\"\"\n        return self.__templates.TemplateResponse(request, \"404.html\", status_code=404)\n\n    async def __server_error(self, request, exc):\n        \"\"\"\n        Return an HTML 500 page.\n        \"\"\"\n        return self.__templates.TemplateResponse(request, \"500.html\", status_code=500)\n\n    async def __reset_connections(self, request):\n        \"\"\"\n        Resets all connections and recreates VideoServer timestamps\n        \"\"\"\n        # get additional parameter\n        parameter = await request.json()\n        # check if Live Broadcasting is enabled\n        if (\n            self.__relay is None\n            and not (self.__default_rtc_server is None)\n            and (self.__default_rtc_server.is_running)\n        ):\n            logger.critical(\"Resetting Server\")\n            # close old peer connections\n            if parameter != 0:  # disable if specified explicitly\n                coros = [\n                    pc.close() for pc in self.__pcs if pc.iceConnectionState != \"closed\"\n                ]\n                await asyncio.gather(*coros)\n                self.__pcs.clear()\n            await self.__default_rtc_server.reset()\n            return PlainTextResponse(\"OK\")\n        else:\n            # if does, then do nothing\n            return PlainTextResponse(\"DISABLED\")\n\n    @contextlib.asynccontextmanager\n    async def __lifespan(self, context):\n        try:\n            yield\n        finally:\n            # close Video Server\n            self.shutdown()\n            # collects peer RTC connections\n            coros = [\n                pc.close() for pc in self.__pcs if pc.iceConnectionState != \"closed\"\n            ]\n            await asyncio.gather(*coros)\n            self.__pcs.clear()\n\n    def shutdown(self):\n        \"\"\"\n        Gracefully shutdown video-server\n        \"\"\"\n        if not (self.__default_rtc_server is None):\n            self.__logging and logger.debug(\"Closing Video Server.\")\n            self.__default_rtc_server.terminate()\n            self.__default_rtc_server = None\n        # terminate internal server aswell.\n        self.__default_rtc_server = None\n

"},{"location":"bonus/reference/webgear_rtc/#vidgear.gears.asyncio.webgear_rtc.WebGear_RTC.__call__","title":"__call__(self) special","text":"

Implements a custom Callable method for WebGear_RTC application.

Source code in vidgear/gears/asyncio/webgear_rtc.py
def __call__(self):\n    \"\"\"\n    Implements a custom Callable method for WebGear_RTC application.\n    \"\"\"\n    # validate routing tables\n    assert not (self.routes is None), \"Routing tables are NoneType!\"\n    if not isinstance(self.routes, list) or not all(\n        x in self.routes for x in self.__rt_org_copy\n    ):\n        raise RuntimeError(\"[WebGear_RTC:ERROR] :: Routing tables are not valid!\")\n\n    # validate middlewares\n    assert not (self.middleware is None), \"Middlewares are NoneType!\"\n    if self.middleware and (\n        not isinstance(self.middleware, list)\n        or not all(isinstance(x, Middleware) for x in self.middleware)\n    ):\n        raise RuntimeError(\"[WebGear_RTC:ERROR] :: Middlewares are not valid!\")\n\n    # return Starlette application\n    self.__logging and logger.debug(\"Running Starlette application.\")\n    return Starlette(\n        debug=(True if self.__logging else False),\n        routes=self.routes,\n        middleware=self.middleware,\n        exception_handlers=self.__exception_handlers,\n        lifespan=self.__lifespan,\n    )\n
"},{"location":"bonus/reference/webgear_rtc/#vidgear.gears.asyncio.webgear_rtc.WebGear_RTC.__init__","title":"__init__(self, enablePiCamera=False, stabilize=False, source=None, camera_num=0, stream_mode=False, backend=0, colorspace=None, resolution=(640, 480), framerate=25, logging=False, time_delay=0, **options) special","text":"

This constructor method initializes the object state and attributes of the WebGear_RTC class.

Parameters:

Name Type Description Default enablePiCamera bool

provide access to PiGear(if True) or CamGear(if False) APIs respectively.

False stabilize bool

enable access to Stabilizer Class for stabilizing frames.

False camera_num int

selects the camera module index which will be used as Rpi source.

0 resolution tuple

sets the resolution (i.e. (width,height)) of the Rpi source.

(640, 480) framerate int/float

sets the framerate of the Rpi source.

25 source based on input

defines the source for the input stream.

None stream_mode bool

controls the exclusive YouTube Mode.

False backend int

selects the backend for OpenCV's VideoCapture class.

0 colorspace str

selects the colorspace of the input stream.

None logging bool

enables/disables logging.

False time_delay int

time delay (in sec) before start reading the frames.

0 options dict

provides ability to alter Tweak Parameters of WebGear_RTC, CamGear, PiGear & Stabilizer.

{} Source code in vidgear/gears/asyncio/webgear_rtc.py
def __init__(\n    self,\n    enablePiCamera=False,\n    stabilize=False,\n    source=None,\n    camera_num=0,\n    stream_mode=False,\n    backend=0,\n    colorspace=None,\n    resolution=(640, 480),\n    framerate=25,\n    logging=False,\n    time_delay=0,\n    **options\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the WebGear_RTC class.\n\n    Parameters:\n        enablePiCamera (bool): provide access to PiGear(if True) or CamGear(if False) APIs respectively.\n        stabilize (bool): enable access to Stabilizer Class for stabilizing frames.\n        camera_num (int): selects the camera module index which will be used as Rpi source.\n        resolution (tuple): sets the resolution (i.e. `(width,height)`) of the Rpi source.\n        framerate (int/float): sets the framerate of the Rpi source.\n        source (based on input): defines the source for the input stream.\n        stream_mode (bool): controls the exclusive YouTube Mode.\n        backend (int): selects the backend for OpenCV's VideoCapture class.\n        colorspace (str): selects the colorspace of the input stream.\n        logging (bool): enables/disables logging.\n        time_delay (int): time delay (in sec) before start reading the frames.\n        options (dict): provides ability to alter Tweak Parameters of WebGear_RTC, CamGear, PiGear & Stabilizer.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # raise error(s) for critical Class imports\n    import_dependency_safe(\"starlette\" if starlette is None else \"\")\n    import_dependency_safe(\"aiortc\" if aiortc is None else \"\")\n\n    # initialize global params\n    custom_data_location = \"\"  # path to save data-files to custom location\n    data_path = \"\"  # path to WebGear_RTC data-files\n    overwrite_default = False\n    self.__relay = None  # act as broadcaster\n\n    # reformat dictionary\n    options = {str(k).strip(): v for k, v in options.items()}\n\n    # assign values to global variables if specified and valid\n    if options:\n        if \"custom_data_location\" in options:\n            value = options[\"custom_data_location\"]\n            if isinstance(value, str):\n                assert os.access(\n                    value, os.W_OK\n                ), \"[WebGear_RTC:ERROR] :: Permission Denied!, cannot write WebGear_RTC data-files to '{}' directory!\".format(\n                    value\n                )\n                assert os.path.isdir(\n                    os.path.abspath(value)\n                ), \"[WebGear_RTC:ERROR] :: `custom_data_location` value must be the path to a directory and not to a file!\"\n                custom_data_location = os.path.abspath(value)\n            else:\n                logger.warning(\"Skipped invalid `custom_data_location` value!\")\n            del options[\"custom_data_location\"]  # clean\n\n        if \"overwrite_default_files\" in options:\n            value = options[\"overwrite_default_files\"]\n            if isinstance(value, bool):\n                overwrite_default = value\n            else:\n                logger.warning(\"Skipped invalid `overwrite_default_files` value!\")\n            del options[\"overwrite_default_files\"]  # clean\n\n        if \"enable_live_broadcast\" in options:\n            value = options[\"enable_live_broadcast\"]\n            if isinstance(value, bool):\n                if value:\n                    self.__relay = MediaRelay()\n                    options[\"enable_infinite_frames\"] = (\n                        True  # enforce infinite frames\n                    )\n                    logger.critical(\n                        \"Enabled live broadcasting for Peer connection(s).\"\n                    )\n                else:\n                    None\n            else:\n                logger.warning(\"Skipped invalid `enable_live_broadcast` value!\")\n            del options[\"enable_live_broadcast\"]  # clean\n\n    # check if custom certificates path is specified\n    if custom_data_location:\n        data_path = generate_webdata(\n            custom_data_location,\n            c_name=\"webgear_rtc\",\n            overwrite_default=overwrite_default,\n            logging=logging,\n        )\n    else:\n        # otherwise generate suitable path\n        data_path = generate_webdata(\n            os.path.join(expanduser(\"~\"), \".vidgear\"),\n            c_name=\"webgear_rtc\",\n            overwrite_default=overwrite_default,\n            logging=logging,\n        )\n\n    # log it\n    self.__logging and logger.debug(\n        \"`{}` is the default location for saving WebGear_RTC data-files.\".format(\n            data_path\n        )\n    )\n\n    # define Jinja2 templates handler\n    self.__templates = Jinja2Templates(directory=\"{}/templates\".format(data_path))\n\n    # define custom exception handlers\n    self.__exception_handlers = {404: self.__not_found, 500: self.__server_error}\n    # define routing tables\n    self.routes = [\n        Route(\"/\", endpoint=self.__homepage),\n        Route(\"/offer\", self.__offer, methods=[\"GET\", \"POST\"]),\n        Mount(\n            \"/static\",\n            app=StaticFiles(directory=\"{}/static\".format(data_path)),\n            name=\"static\",\n        ),\n    ]\n\n    # define middleware support\n    self.middleware = []\n\n    # Handle RTC video server\n    if \"custom_stream\" in options or not (source is None):\n        # Handle video source\n        self.__default_rtc_server = RTC_VideoServer(\n            enablePiCamera=enablePiCamera,\n            stabilize=stabilize,\n            source=source,\n            camera_num=camera_num,\n            stream_mode=stream_mode,\n            backend=backend,\n            colorspace=colorspace,\n            resolution=resolution,\n            framerate=framerate,\n            logging=logging,\n            time_delay=time_delay,\n            **options\n        )\n        # add exclusive reset connection node\n        self.routes.append(\n            Route(\"/close_connection\", self.__reset_connections, methods=[\"POST\"])\n        )\n    else:\n        raise ValueError(\n            \"[WebGear_RTC:ERROR] :: Source cannot be NoneType without Custom Stream(`custom_stream`) defined!\"\n        )\n\n    # copying original routing tables for further validation\n    self.__rt_org_copy = self.routes[:]\n    # collects peer RTC connections\n    self.__pcs = set()\n
"},{"location":"bonus/reference/webgear_rtc/#vidgear.gears.asyncio.webgear_rtc.WebGear_RTC.shutdown","title":"shutdown(self)","text":"

Gracefully shutdown video-server

Source code in vidgear/gears/asyncio/webgear_rtc.py
def shutdown(self):\n    \"\"\"\n    Gracefully shutdown video-server\n    \"\"\"\n    if not (self.__default_rtc_server is None):\n        self.__logging and logger.debug(\"Closing Video Server.\")\n        self.__default_rtc_server.terminate()\n        self.__default_rtc_server = None\n    # terminate internal server aswell.\n    self.__default_rtc_server = None\n
"},{"location":"bonus/reference/writegear/","title":"WriteGear API References","text":"

WriteGear API usage examples for: Compression Mode \u27b6 and Non-Compression Mode \u27b6

WriteGear API parameters are explained for: Compression Mode \u27b6 and Non-Compression Mode \u27b6

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 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 third-party API.

In addition to this, WriteGear also provides flexible access to OpenCV's VideoWriter API tools for video-frames encoding without compression.

Modes of Operation

WriteGear primarily operates in following modes:

Source code in vidgear/gears/writegear.py
class WriteGear:\n    \"\"\"\n    WriteGear handles various powerful Video-Writer Tools that provide us the freedom to do almost anything imaginable with multimedia data.\n\n    WriteGear API provides a complete, flexible, and robust wrapper around FFmpeg, a leading multimedia framework. WriteGear can process real-time frames into a lossless\n    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\n    Live-Streaming (such as for Twitch) and Multiplexing Video-Audio with real-time frames in way fewer lines of code.\n\n    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\n    third-party API.\n\n    In addition to this, WriteGear also provides flexible access to OpenCV's VideoWriter API tools for video-frames encoding without compression.\n\n    ??? tip \"Modes of Operation\"\n\n        WriteGear primarily operates in following modes:\n\n        * **Compression Mode**: In this mode, WriteGear utilizes powerful **FFmpeg** inbuilt encoders to encode lossless multimedia files.\n                                This mode provides us the ability to exploit almost any parameter available within FFmpeg, effortlessly and flexibly,\n                                and while doing that it robustly handles all errors/warnings quietly.\n\n        * **Non-Compression Mode**: In this mode, WriteGear utilizes basic **OpenCV's inbuilt VideoWriter API** tools. This mode also supports all\n                                    parameters manipulation available within VideoWriter API, but it lacks the ability to manipulate encoding parameters\n                                    and other important features like video compression, audio encoding, etc.\n\n    \"\"\"\n\n    def __init__(\n        self,\n        output=\"\",\n        compression_mode=True,\n        custom_ffmpeg=\"\",\n        logging=False,\n        **output_params\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the WriteGear class.\n\n        Parameters:\n            output (str): sets the valid filename/path/URL for encoding.\n            compression_mode (bool): selects the WriteGear's Primary Mode of Operation.\n            custom_ffmpeg (str): assigns the location of custom path/directory for custom FFmpeg executables.\n            logging (bool): enables/disables logging.\n            output_params (dict): provides the flexibility to control supported internal parameters and FFmpeg properties.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # check if user not using depreciated `output_filename` parameter\n        assert (\n            not \"output_filename\" in output_params\n        ), \"[WriteGear:ERROR] :: The `output_filename` parameter has been renamed to `output`. Refer Docs for more info.\"\n\n        # assign parameter values to class variables\n        # enables compression if enabled\n        self.__compression = (\n            compression_mode if isinstance(compression_mode, bool) else False\n        )\n        # specifies if machine in-use is running Windows OS or not\n        self.__os_windows = True if os.name == \"nt\" else False\n\n        # initialize various important class variables\n        self.__output_parameters = {}  # handles output parameters\n        self.__inputheight = None  # handles input frames height\n        self.__inputwidth = None  # handles input frames width\n        self.__inputchannels = None  # handles input frames channels\n        self.__inputdtype = None  # handles input frames dtype\n        self.__process = None  # handles Encoding class/process\n        self.__ffmpeg = \"\"  # handles valid FFmpeg binaries location\n        self.__initiate_process = (\n            True  # handles initiate one-time process for generating pipeline\n        )\n        self.__ffmpeg_window_disabler_patch = (\n            False  # handles disabling window for ffmpeg subprocess on Windows\n        )\n        self.__out_file = None  # handles output\n        gstpipeline_mode = False  # handles GStreamer Pipeline Mode\n\n        # handles output\n        if not output:\n            # raise error otherwise\n            raise ValueError(\n                \"[WriteGear:ERROR] :: Kindly provide a valid `output` value. Refer Docs for more info.\"\n            )\n        else:\n            # validate output is a system file/directory\n            # and Whether WriteGear has the write rights\n            # to specified file/directory or not\n            abs_path = os.path.abspath(output)\n            if check_WriteAccess(\n                os.path.dirname(abs_path),\n                is_windows=self.__os_windows,\n                logging=self.__logging,\n            ):\n                # check if given path is directory\n                if os.path.isdir(abs_path):\n                    # then, auto-assign valid name and adds it to path\n                    abs_path = os.path.join(\n                        abs_path,\n                        \"VidGear-{}.mp4\".format(time.strftime(\"%Y%m%d-%H%M%S\")),\n                    )\n                # assign output file absolute\n                # path to class variable if valid\n                self.__out_file = abs_path\n            else:\n                # log note otherwise\n                logger.info(\n                    \"`{}` isn't a valid system path or directory. Skipped!\".format(\n                        output\n                    )\n                )\n\n        # cleans and reformat output parameters\n        self.__output_parameters = {\n            str(k).strip(): (v.strip() if isinstance(v, str) else v)\n            for k, v in output_params.items()\n        }\n        # log it if specified\n        self.__logging and logger.debug(\n            \"Output Parameters: `{}`\".format(self.__output_parameters)\n        )\n\n        # handles FFmpeg binaries validity\n        # in Compression mode\n        if self.__compression:\n            # log it if specified\n            self.__logging and logger.debug(\n                \"Compression Mode is enabled therefore checking for valid FFmpeg executable.\"\n            )\n\n            # handles where to save the downloaded FFmpeg Static Binaries\n            # on Windows(if specified)\n            __ffmpeg_download_path = self.__output_parameters.pop(\n                \"-ffmpeg_download_path\", \"\"\n            )\n            # check if value is valid\n            if not isinstance(__ffmpeg_download_path, (str)):\n                # reset improper values\n                __ffmpeg_download_path = \"\"\n\n            # handle user-defined output resolution (must be a tuple or list)\n            # in Compression Mode only.\n            self.__output_dimensions = self.__output_parameters.pop(\n                \"-output_dimensions\", None\n            )\n            # check if value is valid\n            if not isinstance(self.__output_dimensions, (list, tuple)):\n                # reset improper values\n                self.__output_dimensions = None\n\n            # handle user defined input framerate of encoding pipeline\n            # in Compression Mode only.\n            self.__inputframerate = self.__output_parameters.pop(\n                \"-input_framerate\", 0.0\n            )\n            # check if value is valid\n            if not isinstance(self.__inputframerate, (float, int)):\n                # reset improper values\n                self.__inputframerate = 0.0\n            else:\n                # must be float\n                self.__inputframerate = float(self.__inputframerate)\n\n            # handle user-defined input frames pixel-format in Compression Mode only.\n            self.__inputpixfmt = self.__output_parameters.pop(\"-input_pixfmt\", None)\n            # check if value is valid\n            if not isinstance(self.__inputpixfmt, str):\n                # reset improper values\n                self.__inputpixfmt = None\n            else:\n                # must be exact\n                self.__inputpixfmt = self.__inputpixfmt.strip()\n\n            # handle user-defined FFmpeg command pre-headers(must be a list)\n            # in Compression Mode only.\n            self.__ffmpeg_preheaders = self.__output_parameters.pop(\"-ffpreheaders\", [])\n            # check if value is valid\n            if not isinstance(self.__ffmpeg_preheaders, list):\n                # reset improper values\n                self.__ffmpeg_preheaders = []\n\n            # handle the special-case of forced-termination (only for Compression mode)\n            disable_force_termination = self.__output_parameters.pop(\n                \"-disable_force_termination\",\n                False if (\"-i\" in self.__output_parameters) else True,\n            )\n            # check if value is valid\n            if isinstance(disable_force_termination, bool):\n                self.__forced_termination = not (disable_force_termination)\n            else:\n                # handle improper values\n                self.__forced_termination = (\n                    True if (\"-i\" in self.__output_parameters) else False\n                )\n\n            # handles disabling window for ffmpeg subprocess on Windows OS (only for Compression mode)\n            # this patch prevents ffmpeg creation window from opening when building exe files\n            ffmpeg_window_disabler_patch = self.__output_parameters.pop(\n                \"-disable_ffmpeg_window\", False\n            )\n            # check if value is valid\n            if not self.__os_windows or logging:\n                logger.warning(\n                    \"Optional `-disable_ffmpeg_window` flag is only available on Windows OS with `logging=False`. Discarding!\"\n                )\n            elif isinstance(ffmpeg_window_disabler_patch, bool):\n                self.__ffmpeg_window_disabler_patch = ffmpeg_window_disabler_patch\n            else:\n                # handle improper values\n                self.__ffmpeg_window_disabler_patch = False\n\n            # validate the FFmpeg path/binaries and returns valid executable FFmpeg\n            # location/path (also auto-downloads static binaries on Windows OS)\n            self.__ffmpeg = get_valid_ffmpeg_path(\n                custom_ffmpeg,\n                self.__os_windows,\n                ffmpeg_download_path=__ffmpeg_download_path,\n                logging=self.__logging,\n            )\n            # check if valid executable FFmpeg location/path\n            if self.__ffmpeg:\n                # log it if found\n                self.__logging and logger.debug(\n                    \"Found valid FFmpeg executable: `{}`.\".format(self.__ffmpeg)\n                )\n            else:\n                # otherwise disable Compression Mode\n                # and switch to Non-compression mode\n                logger.warning(\n                    \"Disabling Compression Mode since no valid FFmpeg executable found on this machine!\"\n                )\n                if self.__logging and not self.__os_windows:\n                    logger.debug(\n                        \"Kindly install a working FFmpeg module or provide a valid custom FFmpeg binary path. See docs for more info.\"\n                    )\n                # compression mode disabled\n                self.__compression = False\n        else:\n            # handle GStreamer Pipeline Mode (only for Non-compression mode)\n            if \"-gst_pipeline_mode\" in self.__output_parameters:\n                # check if value is valid\n                if isinstance(self.__output_parameters[\"-gst_pipeline_mode\"], bool):\n                    gstpipeline_mode = self.__output_parameters[\n                        \"-gst_pipeline_mode\"\n                    ] and check_gstreamer_support(logging=logging)\n                    self.__logging and logger.debug(\n                        \"GStreamer Pipeline Mode successfully activated!\"\n                    )\n                else:\n                    # reset improper values\n                    gstpipeline_mode = False\n                    # log it\n                    self.__logging and logger.warning(\n                        \"GStreamer Pipeline Mode failed to activate!\"\n                    )\n\n        # handle output differently in Compression/Non-compression Modes\n        if self.__compression and self.__ffmpeg:\n            # check if output falls in exclusive cases\n            if self.__out_file is None:\n                if (\n                    platform.system() == \"Linux\"\n                    and pathlib.Path(output).is_char_device()\n                ):\n                    # check whether output is a Linux video device path (such as `/dev/video0`)\n                    self.__logging and logger.debug(\n                        \"Path:`{}` is a valid Linux Video Device path.\".format(output)\n                    )\n                    self.__out_file = output\n                elif is_valid_url(self.__ffmpeg, url=output, logging=self.__logging):\n                    # check whether output is a valid URL instead\n                    self.__logging and logger.debug(\n                        \"URL:`{}` is valid and successfully configured for streaming.\".format(\n                            output\n                        )\n                    )\n                    self.__out_file = output\n                else:\n                    # raise error otherwise\n                    raise ValueError(\n                        \"[WriteGear:ERROR] :: output value:`{}` is not supported in Compression Mode.\".format(\n                            output\n                        )\n                    )\n            # log if forced termination is enabled\n            self.__forced_termination and logger.debug(\n                \"Forced termination is enabled for this FFmpeg process.\"\n            )\n            # log Compression is enabled\n            self.__logging and logger.debug(\n                \"Compression Mode with FFmpeg backend is configured properly.\"\n            )\n        else:\n            # raise error if not valid input\n            if self.__out_file is None and not gstpipeline_mode:\n                raise ValueError(\n                    \"[WriteGear:ERROR] :: output value:`{}` is not supported in Non-Compression Mode.\".format(\n                        output\n                    )\n                )\n\n            # check if GStreamer Pipeline Mode is enabled\n            if gstpipeline_mode:\n                # enforce GStreamer backend\n                self.__output_parameters[\"-backend\"] = \"CAP_GSTREAMER\"\n                # enforce original output value\n                self.__out_file = output\n\n            # log it\n            self.__logging and logger.debug(\n                \"Non-Compression Mode is successfully configured in GStreamer Pipeline Mode.\"\n            )\n\n            # log if Compression is disabled\n            logger.critical(\n                \"Compression Mode is disabled, Activating OpenCV built-in Writer!\"\n            )\n\n    def write(self, frame, rgb_mode=False):\n        \"\"\"\n        Pipelines `ndarray` frames to respective API _(**FFmpeg** in Compression Mode & **OpenCV's VideoWriter API** in Non-Compression Mode)_.\n\n        Parameters:\n            frame (ndarray): a valid numpy frame\n            rgb_mode (boolean): enable this flag to activate RGB mode _(i.e. specifies that incoming frames are of RGB format(instead of default BGR)_.\n\n        \"\"\"\n        if frame is None:  # None-Type frames will be skipped\n            return\n\n        # get height, width, number of channels, and dtype of current frame\n        height, width = frame.shape[:2]\n        channels = frame.shape[-1] if frame.ndim == 3 else 1\n        dtype = frame.dtype\n\n        # assign values to class variables on first run\n        if self.__initiate_process:\n            self.__inputheight = height\n            self.__inputwidth = width\n            self.__inputchannels = channels\n            self.__inputdtype = dtype\n            self.__logging and logger.debug(\n                \"InputFrame => Height:{} Width:{} Channels:{} Datatype:{}\".format(\n                    self.__inputheight,\n                    self.__inputwidth,\n                    self.__inputchannels,\n                    self.__inputdtype,\n                )\n            )\n\n        # validate frame size\n        if height != self.__inputheight or width != self.__inputwidth:\n            raise ValueError(\n                \"[WriteGear:ERROR] :: All video-frames must have same size!\"\n            )\n        # validate number of channels in frame\n        if channels != self.__inputchannels:\n            raise ValueError(\n                \"[WriteGear:ERROR] :: All video-frames must have same number of channels!\"\n            )\n        # validate frame datatype\n        if dtype != self.__inputdtype:\n            raise ValueError(\n                \"[WriteGear:ERROR] :: All video-frames must have same datatype!\"\n            )\n\n        # checks if compression mode is enabled\n        if self.__compression:\n            # initiate FFmpeg process on first run\n            if self.__initiate_process:\n                # start pre-processing of FFmpeg parameters, and initiate process\n                self.__PreprocessFFParams(channels, dtype=dtype, rgb=rgb_mode)\n                # Check status of the process\n                assert self.__process is not None\n            try:\n                # try writing the frame bytes to the subprocess pipeline\n                self.__process.stdin.write(frame.tobytes())\n            except (OSError, IOError):\n                # log if something is wrong!\n                logger.error(\n                    \"BrokenPipeError caught, Wrong values passed to FFmpeg Pipe. Kindly Refer Docs!\"\n                )\n                raise ValueError  # for testing purpose only\n        else:\n            # otherwise initiate OpenCV's VideoWriter Class process\n            if self.__initiate_process:\n                # start VideoWriter Class process\n                self.__start_CVProcess()\n                # Check status of the process\n                assert self.__process is not None\n                # log one-time OpenCV warning\n                self.__logging and logger.info(\n                    \"RGBA and 16-bit grayscale video frames are not supported by OpenCV yet. Kindly switch on `compression_mode` to use them!\"\n                )\n            # write frame directly to\n            # VideoWriter Class process\n            self.__process.write(frame)\n\n    def __PreprocessFFParams(self, channels, dtype=None, rgb=False):\n        \"\"\"\n        Internal method that pre-processes FFmpeg Parameters before beginning to pipeline frames.\n\n        Parameters:\n            channels (int): Number of channels in input frame.\n            dtype (str): Datatype of input frame.\n            rgb_mode (boolean): Whether to activate `RGB mode`?\n        \"\"\"\n        # turn off initiate flag\n        self.__initiate_process = False\n        # initialize input parameters\n        input_parameters = {}\n\n        # handle output frames dimensions\n        dimensions = \"\"\n        if self.__output_dimensions is None:  # check if dimensions are given\n            dimensions += \"{}x{}\".format(\n                self.__inputwidth, self.__inputheight\n            )  # auto derive from frame\n        else:\n            dimensions += \"{}x{}\".format(\n                self.__output_dimensions[0], self.__output_dimensions[1]\n            )  # apply if defined\n        input_parameters[\"-s\"] = str(dimensions)\n\n        # handles user-defined and auto-assigned input pixel-formats\n        if not (\n            self.__inputpixfmt is None\n        ) and self.__inputpixfmt in get_supported_pixfmts(self.__ffmpeg):\n            # assign directly if valid\n            input_parameters[\"-pix_fmt\"] = self.__inputpixfmt\n        else:\n            # handles pix_fmt based on channels and dtype(HACK)\n            if dtype.kind == \"u\" and dtype.itemsize == 2:\n                # handle pix_fmt for frames with higher than 8-bit depth\n                pix_fmt = None\n                if channels == 1:\n                    pix_fmt = \"gray16\"\n                elif channels == 2:\n                    pix_fmt = \"ya16\"\n                elif channels == 3:\n                    pix_fmt = \"rgb48\" if rgb else \"bgr48\"\n                elif channels == 4:\n                    pix_fmt = \"rgba64\" if rgb else \"bgra64\"\n                else:\n                    # raise error otherwise\n                    raise ValueError(\n                        \"[WriteGear:ERROR] :: Frames with channels outside range 1-to-4 are not supported!\"\n                    )\n                # Add endianness suffix (w.r.t byte-order)\n                input_parameters[\"-pix_fmt\"] = pix_fmt + (\n                    \"be\" if dtype.byteorder == \">\" else \"le\"\n                )\n            else:\n                # handle pix_fmt for frames with exactly 8-bit depth(`uint8`)\n                if channels == 1:\n                    input_parameters[\"-pix_fmt\"] = \"gray\"\n                elif channels == 2:\n                    input_parameters[\"-pix_fmt\"] = \"ya8\"\n                elif channels == 3:\n                    input_parameters[\"-pix_fmt\"] = \"rgb24\" if rgb else \"bgr24\"\n                elif channels == 4:\n                    input_parameters[\"-pix_fmt\"] = \"rgba\" if rgb else \"bgra\"\n                else:\n                    # raise error otherwise\n                    raise ValueError(\n                        \"[WriteGear:ERROR] :: Frames with channels outside range 1-to-4 are not supported!\"\n                    )\n\n        # handles user-defined output video framerate\n        if self.__inputframerate > 0.0:\n            # assign input framerate if valid\n            self.__logging and logger.debug(\n                \"Setting Input framerate: {}\".format(self.__inputframerate)\n            )\n            input_parameters[\"-framerate\"] = str(self.__inputframerate)\n\n        # initiate FFmpeg process\n        self.__start_FFProcess(\n            input_params=input_parameters, output_params=self.__output_parameters\n        )\n\n    def __start_FFProcess(self, input_params, output_params):\n        \"\"\"\n        An Internal method that launches FFmpeg subprocess pipeline in Compression Mode\n        for pipelining frames to `stdin`.\n\n        Parameters:\n            input_params (dict): Input FFmpeg parameters\n            output_params (dict): Output FFmpeg parameters\n        \"\"\"\n        # convert input parameters to argument list\n        input_parameters = dict2Args(input_params)\n\n        # handle output video encoder.\n        # get list of supported video-encoders\n        supported_vcodecs = get_supported_vencoders(self.__ffmpeg)\n        # dynamically select default encoder\n        default_vcodec = [\n            vcodec\n            for vcodec in [\"libx264\", \"libx265\", \"libxvid\", \"mpeg4\"]\n            if vcodec in supported_vcodecs\n        ][0] or \"unknown\"\n        # extract any user-defined encoder\n        if \"-c:v\" in output_params:\n            # assign it to the pipeline\n            output_params[\"-vcodec\"] = output_params.pop(\"-c:v\", default_vcodec)\n        if not \"-vcodec\" in output_params:\n            # auto-assign default video-encoder (if not assigned by user).\n            output_params[\"-vcodec\"] = default_vcodec\n        if (\n            default_vcodec != \"unknown\"\n            and not output_params[\"-vcodec\"] in supported_vcodecs\n        ):\n            # reset to default if not supported\n            logger.critical(\n                \"Provided FFmpeg does not support `{}` video-encoder. Switching to default supported `{}` encoder!\".format(\n                    output_params[\"-vcodec\"], default_vcodec\n                )\n            )\n            output_params[\"-vcodec\"] = default_vcodec\n\n        # assign optimizations based on selected video encoder(if any)\n        if output_params[\"-vcodec\"] in supported_vcodecs:\n            if output_params[\"-vcodec\"] in [\"libx265\", \"libx264\"]:\n                if not \"-crf\" in output_params:\n                    output_params[\"-crf\"] = \"18\"\n                if not \"-preset\" in output_params:\n                    output_params[\"-preset\"] = \"fast\"\n            if output_params[\"-vcodec\"] in [\"libxvid\", \"mpeg4\"]:\n                if not \"-qscale:v\" in output_params:\n                    output_params[\"-qscale:v\"] = \"3\"\n        else:\n            # raise error otherwise\n            raise RuntimeError(\n                \"[WriteGear:ERROR] :: Provided FFmpeg does not support any suitable/usable video-encoders for compression.\"\n                \" Kindly disable compression mode or switch to another FFmpeg binaries(if available).\"\n            )\n\n        # convert output parameters to argument list\n        output_parameters = dict2Args(output_params)\n\n        # format FFmpeg command\n        cmd = (\n            [self.__ffmpeg, \"-y\"]\n            + self.__ffmpeg_preheaders\n            + [\"-f\", \"rawvideo\", \"-vcodec\", \"rawvideo\"]\n            + input_parameters\n            + [\"-i\", \"-\"]\n            + output_parameters\n            + [self.__out_file]\n        )\n        # Launch the process with FFmpeg command\n        if self.__logging:\n            # log command in logging mode\n            logger.debug(\"Executing FFmpeg command: `{}`\".format(\" \".join(cmd)))\n            # In logging mode\n            self.__process = sp.Popen(cmd, stdin=sp.PIPE, stdout=sp.PIPE, stderr=None)\n        else:\n            # In silent mode\n            self.__process = sp.Popen(\n                cmd,\n                stdin=sp.PIPE,\n                stdout=sp.DEVNULL,\n                stderr=sp.STDOUT,\n                creationflags=(  # this prevents ffmpeg creation window from opening when building exe files on Windows\n                    sp.DETACHED_PROCESS if self.__ffmpeg_window_disabler_patch else 0\n                ),\n            )\n\n    def __enter__(self):\n        \"\"\"\n        Handles entry with the `with` statement. See [PEP343 -- The 'with' statement'](https://peps.python.org/pep-0343/).\n\n        **Returns:** Returns a reference to the WriteGear Class\n        \"\"\"\n        return self\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        \"\"\"\n        Handles exit with the `with` statement. See [PEP343 -- The 'with' statement'](https://peps.python.org/pep-0343/).\n        \"\"\"\n        self.close()\n\n    def execute_ffmpeg_cmd(self, command=None):\n        \"\"\"\n\n        Executes user-defined FFmpeg Terminal command, formatted as a python list(in Compression Mode only).\n\n        Parameters:\n            command (list): inputs list data-type command.\n\n        \"\"\"\n        # check if valid command\n        if command is None or not (command):\n            logger.warning(\"Input command is empty, Nothing to execute!\")\n            return\n        else:\n            if not (isinstance(command, list)):\n                raise ValueError(\n                    \"[WriteGear:ERROR] :: Invalid input command datatype! Kindly read docs.\"\n                )\n\n        # check if Compression Mode is enabled\n        if not (self.__compression):\n            # raise error otherwise\n            raise RuntimeError(\n                \"[WriteGear:ERROR] :: Compression Mode is disabled, Kindly enable it to access this function.\"\n            )\n\n        # add configured FFmpeg path\n        cmd = [self.__ffmpeg] + command\n\n        try:\n            # write frames to pipeline\n            if self.__logging:\n                # log command in logging mode\n                logger.debug(\"Executing FFmpeg command: `{}`\".format(\" \".join(cmd)))\n                # In logging mode\n                sp.run(cmd, stdin=sp.PIPE, stdout=sp.PIPE, stderr=None)\n            else:\n                # In silent mode\n                sp.run(cmd, stdin=sp.PIPE, stdout=sp.DEVNULL, stderr=sp.STDOUT)\n        except (OSError, IOError) as e:\n            # re-raise error\n            if self.__logging:\n                raise ValueError(\n                    \"BrokenPipeError caught, Wrong command passed to FFmpeg Pipe, Kindly Refer Docs!\"\n                ) from None\n            else:\n                raise ValueError(\n                    \"BrokenPipeError caught, Wrong command passed to FFmpeg Pipe, Kindly Refer Docs!\"\n                ) from e\n\n    def __start_CVProcess(self):\n        \"\"\"\n        An Internal method that launches OpenCV VideoWriter process in Non-Compression\n        Mode with given settings.\n        \"\"\"\n        # turn off initiate flag\n        self.__initiate_process = False\n\n        # initialize essential variables\n        FPS = 0\n        BACKEND = \"\"\n        FOURCC = 0\n        COLOR = True\n\n        # pre-assign default parameters (if not assigned by user).\n        if \"-fourcc\" not in self.__output_parameters:\n            FOURCC = cv2.VideoWriter_fourcc(*\"MJPG\")\n        if \"-fps\" not in self.__output_parameters:\n            FPS = 25\n\n        # auto-assign frame dimensions\n        HEIGHT = self.__inputheight\n        WIDTH = self.__inputwidth\n\n        # assign dict parameter values to variables\n        try:\n            for key, value in self.__output_parameters.items():\n                if key == \"-fourcc\":\n                    FOURCC = cv2.VideoWriter_fourcc(*(value.upper()))\n                elif key == \"-fps\":\n                    FPS = int(value)\n                elif key == \"-backend\":\n                    BACKEND = capPropId(value.upper())\n                elif key == \"-color\":\n                    COLOR = bool(value)\n                else:\n                    pass\n        except Exception as e:\n            # log and raise error if something is wrong\n            self.__logging and logger.exception(str(e))\n            raise ValueError(\n                \"[WriteGear:ERROR] :: Wrong Values passed to OpenCV Writer, Kindly Refer Docs!\"\n            )\n\n        # log values for debugging\n        self.__logging and logger.debug(\n            \"FILE_PATH: {}, FOURCC = {}, FPS = {}, WIDTH = {}, HEIGHT = {}, BACKEND = {}\".format(\n                self.__out_file, FOURCC, FPS, WIDTH, HEIGHT, BACKEND\n            )\n        )\n        # start different OpenCV VideoCapture processes\n        # for with and without Backend.\n        if BACKEND:\n            self.__process = cv2.VideoWriter(\n                self.__out_file,\n                apiPreference=BACKEND,\n                fourcc=FOURCC,\n                fps=FPS,\n                frameSize=(WIDTH, HEIGHT),\n                isColor=COLOR,\n            )\n        else:\n            self.__process = cv2.VideoWriter(\n                self.__out_file,\n                fourcc=FOURCC,\n                fps=FPS,\n                frameSize=(WIDTH, HEIGHT),\n                isColor=COLOR,\n            )\n        # check if OpenCV VideoCapture is opened successfully\n        assert (\n            self.__process.isOpened()\n        ), \"[WriteGear:ERROR] :: Failed to initialize OpenCV Writer!\"\n\n    def close(self):\n        \"\"\"\n        Safely terminates various WriteGear process.\n        \"\"\"\n        # log termination\n        self.__logging and logger.debug(\"Terminating WriteGear Processes.\")\n        # handle termination separately\n        if self.__compression:\n            # when Compression Mode is enabled\n            if self.__process is None or not (self.__process.poll() is None):\n                # return if no process initiated\n                # at first place\n                return\n            # close `stdin` output\n            self.__process.stdin and self.__process.stdin.close()\n            # close `stdout` output\n            self.__process.stdout and self.__process.stdout.close()\n            # forced termination if specified.\n            self.__forced_termination and self.__process.terminate()\n            # wait if process is still processing\n            self.__process.wait()\n        else:\n            # when Compression Mode is disabled\n            if self.__process is None:\n                # return if no process initiated\n                # at first place\n                return\n            # close it\n            self.__process.release()\n        # discard process\n        self.__process = None\n

"},{"location":"bonus/reference/writegear/#vidgear.gears.writegear.WriteGear.__enter__","title":"__enter__(self) special","text":"

Handles entry with the with statement. See PEP343 -- The 'with' statement'.

Returns: Returns a reference to the WriteGear Class

Source code in vidgear/gears/writegear.py
def __enter__(self):\n    \"\"\"\n    Handles entry with the `with` statement. See [PEP343 -- The 'with' statement'](https://peps.python.org/pep-0343/).\n\n    **Returns:** Returns a reference to the WriteGear Class\n    \"\"\"\n    return self\n
"},{"location":"bonus/reference/writegear/#vidgear.gears.writegear.WriteGear.__exit__","title":"__exit__(self, exc_type, exc_val, exc_tb) special","text":"

Handles exit with the with statement. See PEP343 -- The 'with' statement'.

Source code in vidgear/gears/writegear.py
def __exit__(self, exc_type, exc_val, exc_tb):\n    \"\"\"\n    Handles exit with the `with` statement. See [PEP343 -- The 'with' statement'](https://peps.python.org/pep-0343/).\n    \"\"\"\n    self.close()\n
"},{"location":"bonus/reference/writegear/#vidgear.gears.writegear.WriteGear.__init__","title":"__init__(self, output='', compression_mode=True, custom_ffmpeg='', logging=False, **output_params) special","text":"

This constructor method initializes the object state and attributes of the WriteGear class.

Parameters:

Name Type Description Default output str

sets the valid filename/path/URL for encoding.

'' compression_mode bool

selects the WriteGear's Primary Mode of Operation.

True custom_ffmpeg str

assigns the location of custom path/directory for custom FFmpeg executables.

'' logging bool

enables/disables logging.

False output_params dict

provides the flexibility to control supported internal parameters and FFmpeg properties.

{} Source code in vidgear/gears/writegear.py
def __init__(\n    self,\n    output=\"\",\n    compression_mode=True,\n    custom_ffmpeg=\"\",\n    logging=False,\n    **output_params\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the WriteGear class.\n\n    Parameters:\n        output (str): sets the valid filename/path/URL for encoding.\n        compression_mode (bool): selects the WriteGear's Primary Mode of Operation.\n        custom_ffmpeg (str): assigns the location of custom path/directory for custom FFmpeg executables.\n        logging (bool): enables/disables logging.\n        output_params (dict): provides the flexibility to control supported internal parameters and FFmpeg properties.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # check if user not using depreciated `output_filename` parameter\n    assert (\n        not \"output_filename\" in output_params\n    ), \"[WriteGear:ERROR] :: The `output_filename` parameter has been renamed to `output`. Refer Docs for more info.\"\n\n    # assign parameter values to class variables\n    # enables compression if enabled\n    self.__compression = (\n        compression_mode if isinstance(compression_mode, bool) else False\n    )\n    # specifies if machine in-use is running Windows OS or not\n    self.__os_windows = True if os.name == \"nt\" else False\n\n    # initialize various important class variables\n    self.__output_parameters = {}  # handles output parameters\n    self.__inputheight = None  # handles input frames height\n    self.__inputwidth = None  # handles input frames width\n    self.__inputchannels = None  # handles input frames channels\n    self.__inputdtype = None  # handles input frames dtype\n    self.__process = None  # handles Encoding class/process\n    self.__ffmpeg = \"\"  # handles valid FFmpeg binaries location\n    self.__initiate_process = (\n        True  # handles initiate one-time process for generating pipeline\n    )\n    self.__ffmpeg_window_disabler_patch = (\n        False  # handles disabling window for ffmpeg subprocess on Windows\n    )\n    self.__out_file = None  # handles output\n    gstpipeline_mode = False  # handles GStreamer Pipeline Mode\n\n    # handles output\n    if not output:\n        # raise error otherwise\n        raise ValueError(\n            \"[WriteGear:ERROR] :: Kindly provide a valid `output` value. Refer Docs for more info.\"\n        )\n    else:\n        # validate output is a system file/directory\n        # and Whether WriteGear has the write rights\n        # to specified file/directory or not\n        abs_path = os.path.abspath(output)\n        if check_WriteAccess(\n            os.path.dirname(abs_path),\n            is_windows=self.__os_windows,\n            logging=self.__logging,\n        ):\n            # check if given path is directory\n            if os.path.isdir(abs_path):\n                # then, auto-assign valid name and adds it to path\n                abs_path = os.path.join(\n                    abs_path,\n                    \"VidGear-{}.mp4\".format(time.strftime(\"%Y%m%d-%H%M%S\")),\n                )\n            # assign output file absolute\n            # path to class variable if valid\n            self.__out_file = abs_path\n        else:\n            # log note otherwise\n            logger.info(\n                \"`{}` isn't a valid system path or directory. Skipped!\".format(\n                    output\n                )\n            )\n\n    # cleans and reformat output parameters\n    self.__output_parameters = {\n        str(k).strip(): (v.strip() if isinstance(v, str) else v)\n        for k, v in output_params.items()\n    }\n    # log it if specified\n    self.__logging and logger.debug(\n        \"Output Parameters: `{}`\".format(self.__output_parameters)\n    )\n\n    # handles FFmpeg binaries validity\n    # in Compression mode\n    if self.__compression:\n        # log it if specified\n        self.__logging and logger.debug(\n            \"Compression Mode is enabled therefore checking for valid FFmpeg executable.\"\n        )\n\n        # handles where to save the downloaded FFmpeg Static Binaries\n        # on Windows(if specified)\n        __ffmpeg_download_path = self.__output_parameters.pop(\n            \"-ffmpeg_download_path\", \"\"\n        )\n        # check if value is valid\n        if not isinstance(__ffmpeg_download_path, (str)):\n            # reset improper values\n            __ffmpeg_download_path = \"\"\n\n        # handle user-defined output resolution (must be a tuple or list)\n        # in Compression Mode only.\n        self.__output_dimensions = self.__output_parameters.pop(\n            \"-output_dimensions\", None\n        )\n        # check if value is valid\n        if not isinstance(self.__output_dimensions, (list, tuple)):\n            # reset improper values\n            self.__output_dimensions = None\n\n        # handle user defined input framerate of encoding pipeline\n        # in Compression Mode only.\n        self.__inputframerate = self.__output_parameters.pop(\n            \"-input_framerate\", 0.0\n        )\n        # check if value is valid\n        if not isinstance(self.__inputframerate, (float, int)):\n            # reset improper values\n            self.__inputframerate = 0.0\n        else:\n            # must be float\n            self.__inputframerate = float(self.__inputframerate)\n\n        # handle user-defined input frames pixel-format in Compression Mode only.\n        self.__inputpixfmt = self.__output_parameters.pop(\"-input_pixfmt\", None)\n        # check if value is valid\n        if not isinstance(self.__inputpixfmt, str):\n            # reset improper values\n            self.__inputpixfmt = None\n        else:\n            # must be exact\n            self.__inputpixfmt = self.__inputpixfmt.strip()\n\n        # handle user-defined FFmpeg command pre-headers(must be a list)\n        # in Compression Mode only.\n        self.__ffmpeg_preheaders = self.__output_parameters.pop(\"-ffpreheaders\", [])\n        # check if value is valid\n        if not isinstance(self.__ffmpeg_preheaders, list):\n            # reset improper values\n            self.__ffmpeg_preheaders = []\n\n        # handle the special-case of forced-termination (only for Compression mode)\n        disable_force_termination = self.__output_parameters.pop(\n            \"-disable_force_termination\",\n            False if (\"-i\" in self.__output_parameters) else True,\n        )\n        # check if value is valid\n        if isinstance(disable_force_termination, bool):\n            self.__forced_termination = not (disable_force_termination)\n        else:\n            # handle improper values\n            self.__forced_termination = (\n                True if (\"-i\" in self.__output_parameters) else False\n            )\n\n        # handles disabling window for ffmpeg subprocess on Windows OS (only for Compression mode)\n        # this patch prevents ffmpeg creation window from opening when building exe files\n        ffmpeg_window_disabler_patch = self.__output_parameters.pop(\n            \"-disable_ffmpeg_window\", False\n        )\n        # check if value is valid\n        if not self.__os_windows or logging:\n            logger.warning(\n                \"Optional `-disable_ffmpeg_window` flag is only available on Windows OS with `logging=False`. Discarding!\"\n            )\n        elif isinstance(ffmpeg_window_disabler_patch, bool):\n            self.__ffmpeg_window_disabler_patch = ffmpeg_window_disabler_patch\n        else:\n            # handle improper values\n            self.__ffmpeg_window_disabler_patch = False\n\n        # validate the FFmpeg path/binaries and returns valid executable FFmpeg\n        # location/path (also auto-downloads static binaries on Windows OS)\n        self.__ffmpeg = get_valid_ffmpeg_path(\n            custom_ffmpeg,\n            self.__os_windows,\n            ffmpeg_download_path=__ffmpeg_download_path,\n            logging=self.__logging,\n        )\n        # check if valid executable FFmpeg location/path\n        if self.__ffmpeg:\n            # log it if found\n            self.__logging and logger.debug(\n                \"Found valid FFmpeg executable: `{}`.\".format(self.__ffmpeg)\n            )\n        else:\n            # otherwise disable Compression Mode\n            # and switch to Non-compression mode\n            logger.warning(\n                \"Disabling Compression Mode since no valid FFmpeg executable found on this machine!\"\n            )\n            if self.__logging and not self.__os_windows:\n                logger.debug(\n                    \"Kindly install a working FFmpeg module or provide a valid custom FFmpeg binary path. See docs for more info.\"\n                )\n            # compression mode disabled\n            self.__compression = False\n    else:\n        # handle GStreamer Pipeline Mode (only for Non-compression mode)\n        if \"-gst_pipeline_mode\" in self.__output_parameters:\n            # check if value is valid\n            if isinstance(self.__output_parameters[\"-gst_pipeline_mode\"], bool):\n                gstpipeline_mode = self.__output_parameters[\n                    \"-gst_pipeline_mode\"\n                ] and check_gstreamer_support(logging=logging)\n                self.__logging and logger.debug(\n                    \"GStreamer Pipeline Mode successfully activated!\"\n                )\n            else:\n                # reset improper values\n                gstpipeline_mode = False\n                # log it\n                self.__logging and logger.warning(\n                    \"GStreamer Pipeline Mode failed to activate!\"\n                )\n\n    # handle output differently in Compression/Non-compression Modes\n    if self.__compression and self.__ffmpeg:\n        # check if output falls in exclusive cases\n        if self.__out_file is None:\n            if (\n                platform.system() == \"Linux\"\n                and pathlib.Path(output).is_char_device()\n            ):\n                # check whether output is a Linux video device path (such as `/dev/video0`)\n                self.__logging and logger.debug(\n                    \"Path:`{}` is a valid Linux Video Device path.\".format(output)\n                )\n                self.__out_file = output\n            elif is_valid_url(self.__ffmpeg, url=output, logging=self.__logging):\n                # check whether output is a valid URL instead\n                self.__logging and logger.debug(\n                    \"URL:`{}` is valid and successfully configured for streaming.\".format(\n                        output\n                    )\n                )\n                self.__out_file = output\n            else:\n                # raise error otherwise\n                raise ValueError(\n                    \"[WriteGear:ERROR] :: output value:`{}` is not supported in Compression Mode.\".format(\n                        output\n                    )\n                )\n        # log if forced termination is enabled\n        self.__forced_termination and logger.debug(\n            \"Forced termination is enabled for this FFmpeg process.\"\n        )\n        # log Compression is enabled\n        self.__logging and logger.debug(\n            \"Compression Mode with FFmpeg backend is configured properly.\"\n        )\n    else:\n        # raise error if not valid input\n        if self.__out_file is None and not gstpipeline_mode:\n            raise ValueError(\n                \"[WriteGear:ERROR] :: output value:`{}` is not supported in Non-Compression Mode.\".format(\n                    output\n                )\n            )\n\n        # check if GStreamer Pipeline Mode is enabled\n        if gstpipeline_mode:\n            # enforce GStreamer backend\n            self.__output_parameters[\"-backend\"] = \"CAP_GSTREAMER\"\n            # enforce original output value\n            self.__out_file = output\n\n        # log it\n        self.__logging and logger.debug(\n            \"Non-Compression Mode is successfully configured in GStreamer Pipeline Mode.\"\n        )\n\n        # log if Compression is disabled\n        logger.critical(\n            \"Compression Mode is disabled, Activating OpenCV built-in Writer!\"\n        )\n
"},{"location":"bonus/reference/writegear/#vidgear.gears.writegear.WriteGear.close","title":"close(self)","text":"

Safely terminates various WriteGear process.

Source code in vidgear/gears/writegear.py
def close(self):\n    \"\"\"\n    Safely terminates various WriteGear process.\n    \"\"\"\n    # log termination\n    self.__logging and logger.debug(\"Terminating WriteGear Processes.\")\n    # handle termination separately\n    if self.__compression:\n        # when Compression Mode is enabled\n        if self.__process is None or not (self.__process.poll() is None):\n            # return if no process initiated\n            # at first place\n            return\n        # close `stdin` output\n        self.__process.stdin and self.__process.stdin.close()\n        # close `stdout` output\n        self.__process.stdout and self.__process.stdout.close()\n        # forced termination if specified.\n        self.__forced_termination and self.__process.terminate()\n        # wait if process is still processing\n        self.__process.wait()\n    else:\n        # when Compression Mode is disabled\n        if self.__process is None:\n            # return if no process initiated\n            # at first place\n            return\n        # close it\n        self.__process.release()\n    # discard process\n    self.__process = None\n
"},{"location":"bonus/reference/writegear/#vidgear.gears.writegear.WriteGear.execute_ffmpeg_cmd","title":"execute_ffmpeg_cmd(self, command=None)","text":"

Executes user-defined FFmpeg Terminal command, formatted as a python list(in Compression Mode only).

Parameters:

Name Type Description Default command list

inputs list data-type command.

None Source code in vidgear/gears/writegear.py
def execute_ffmpeg_cmd(self, command=None):\n    \"\"\"\n\n    Executes user-defined FFmpeg Terminal command, formatted as a python list(in Compression Mode only).\n\n    Parameters:\n        command (list): inputs list data-type command.\n\n    \"\"\"\n    # check if valid command\n    if command is None or not (command):\n        logger.warning(\"Input command is empty, Nothing to execute!\")\n        return\n    else:\n        if not (isinstance(command, list)):\n            raise ValueError(\n                \"[WriteGear:ERROR] :: Invalid input command datatype! Kindly read docs.\"\n            )\n\n    # check if Compression Mode is enabled\n    if not (self.__compression):\n        # raise error otherwise\n        raise RuntimeError(\n            \"[WriteGear:ERROR] :: Compression Mode is disabled, Kindly enable it to access this function.\"\n        )\n\n    # add configured FFmpeg path\n    cmd = [self.__ffmpeg] + command\n\n    try:\n        # write frames to pipeline\n        if self.__logging:\n            # log command in logging mode\n            logger.debug(\"Executing FFmpeg command: `{}`\".format(\" \".join(cmd)))\n            # In logging mode\n            sp.run(cmd, stdin=sp.PIPE, stdout=sp.PIPE, stderr=None)\n        else:\n            # In silent mode\n            sp.run(cmd, stdin=sp.PIPE, stdout=sp.DEVNULL, stderr=sp.STDOUT)\n    except (OSError, IOError) as e:\n        # re-raise error\n        if self.__logging:\n            raise ValueError(\n                \"BrokenPipeError caught, Wrong command passed to FFmpeg Pipe, Kindly Refer Docs!\"\n            ) from None\n        else:\n            raise ValueError(\n                \"BrokenPipeError caught, Wrong command passed to FFmpeg Pipe, Kindly Refer Docs!\"\n            ) from e\n
"},{"location":"bonus/reference/writegear/#vidgear.gears.writegear.WriteGear.write","title":"write(self, frame, rgb_mode=False)","text":"

Pipelines ndarray frames to respective API (FFmpeg in Compression Mode & OpenCV's VideoWriter API in Non-Compression Mode).

Parameters:

Name Type Description Default frame ndarray

a valid numpy frame

required rgb_mode boolean

enable this flag to activate RGB mode (i.e. specifies that incoming frames are of RGB format(instead of default BGR).

False Source code in vidgear/gears/writegear.py
def write(self, frame, rgb_mode=False):\n    \"\"\"\n    Pipelines `ndarray` frames to respective API _(**FFmpeg** in Compression Mode & **OpenCV's VideoWriter API** in Non-Compression Mode)_.\n\n    Parameters:\n        frame (ndarray): a valid numpy frame\n        rgb_mode (boolean): enable this flag to activate RGB mode _(i.e. specifies that incoming frames are of RGB format(instead of default BGR)_.\n\n    \"\"\"\n    if frame is None:  # None-Type frames will be skipped\n        return\n\n    # get height, width, number of channels, and dtype of current frame\n    height, width = frame.shape[:2]\n    channels = frame.shape[-1] if frame.ndim == 3 else 1\n    dtype = frame.dtype\n\n    # assign values to class variables on first run\n    if self.__initiate_process:\n        self.__inputheight = height\n        self.__inputwidth = width\n        self.__inputchannels = channels\n        self.__inputdtype = dtype\n        self.__logging and logger.debug(\n            \"InputFrame => Height:{} Width:{} Channels:{} Datatype:{}\".format(\n                self.__inputheight,\n                self.__inputwidth,\n                self.__inputchannels,\n                self.__inputdtype,\n            )\n        )\n\n    # validate frame size\n    if height != self.__inputheight or width != self.__inputwidth:\n        raise ValueError(\n            \"[WriteGear:ERROR] :: All video-frames must have same size!\"\n        )\n    # validate number of channels in frame\n    if channels != self.__inputchannels:\n        raise ValueError(\n            \"[WriteGear:ERROR] :: All video-frames must have same number of channels!\"\n        )\n    # validate frame datatype\n    if dtype != self.__inputdtype:\n        raise ValueError(\n            \"[WriteGear:ERROR] :: All video-frames must have same datatype!\"\n        )\n\n    # checks if compression mode is enabled\n    if self.__compression:\n        # initiate FFmpeg process on first run\n        if self.__initiate_process:\n            # start pre-processing of FFmpeg parameters, and initiate process\n            self.__PreprocessFFParams(channels, dtype=dtype, rgb=rgb_mode)\n            # Check status of the process\n            assert self.__process is not None\n        try:\n            # try writing the frame bytes to the subprocess pipeline\n            self.__process.stdin.write(frame.tobytes())\n        except (OSError, IOError):\n            # log if something is wrong!\n            logger.error(\n                \"BrokenPipeError caught, Wrong values passed to FFmpeg Pipe. Kindly Refer Docs!\"\n            )\n            raise ValueError  # for testing purpose only\n    else:\n        # otherwise initiate OpenCV's VideoWriter Class process\n        if self.__initiate_process:\n            # start VideoWriter Class process\n            self.__start_CVProcess()\n            # Check status of the process\n            assert self.__process is not None\n            # log one-time OpenCV warning\n            self.__logging and logger.info(\n                \"RGBA and 16-bit grayscale video frames are not supported by OpenCV yet. Kindly switch on `compression_mode` to use them!\"\n            )\n        # write frame directly to\n        # VideoWriter Class process\n        self.__process.write(frame)\n
"},{"location":"contribution/PR/","title":"Submitting Pull Request(PR) Guidelines","text":""},{"location":"contribution/PR/#submitting-pull-requestpr-guidelines","title":"Submitting Pull Request(PR) Guidelines:","text":"

The following guidelines tells you how to submit a valid PR for vidGear:

Working on your first Pull Request for VidGear?

"},{"location":"contribution/PR/#clone-testing-branch","title":"Clone Testing branch","text":"

Base Branch must be testing in your Pull Request

Every PR MUST be pushed against VidGear's testing branch only, in order to trigger must needed CI testing workflows. If your's not, then change the base branch to testing \u27b6

Make sure the testing branch of your Forked repository is up-to-date with VidGear, before starting working on Pull Request.

You can clone your Forked remote git to local and create your PR working branch as a sub-branch of latest testing branch as follows:

Functions of different VidGear's Github Branches

Following are the base branches for VidGear's code in its Github Repository:

Master/MainTestingDevelopment

Branch Features:

Cloning:

# clone your forked repository and `cd` inside\ngit clone https://github.com/abhiTronix/vidgear.git && cd vidgear\n

Branch Features:

Cloning:

# clone your forked repository and `cd` inside\ngit clone https://github.com/abhiTronix/vidgear.git && cd vidgear\n\n# checkout the latest testing branch\ngit checkout testing\n

Branch Features:

Cloning:

# clone your forked repository and `cd` inside\ngit clone https://github.com/abhiTronix/vidgear.git && cd vidgear\n\n# checkout the latest development branch\ngit checkout development\n

Workflow:

Typically any feature/improvement/bug-fix code flows as follows:

# clone your forked repository(change with your username) and get inside\ngit clone https://github.com/{YOUR USERNAME}/vidgear.git && cd vidgear\n\n# pull any recent updates\ngit pull\n\n# checkout the latest testing branch\ngit checkout testing\n\n# Now create your new branch with suitable name(such as \"subbranch_of_testing\")\ngit checkout -b subbranch_of_testing\n

Now after working with this newly created branch for your Pull Request, you can commit and push or merge it locally or remotely as usual.

"},{"location":"contribution/PR/#pr-submission-checklist","title":"PR Submission Checklist","text":"

There are some important checks you need to perform while submitting your Pull Request(s) for VidGear library:

"},{"location":"contribution/PR/#testing-formatting-linting","title":"Testing, Formatting & Linting","text":"

All Pull Request(s) must be tested, formatted & linted against our library standards as discussed below:

"},{"location":"contribution/PR/#requirements","title":"Requirements","text":"

Testing VidGear requires additional test dependencies and dataset, which can be handled manually as follows:

"},{"location":"contribution/PR/#running-tests","title":"Running Tests","text":"

All tests can be run with pytest(in VidGear's root folder) as follows:

 pytest -sv  #-sv for verbose output.\n
"},{"location":"contribution/PR/#formatting-linting","title":"Formatting & Linting","text":"

For formatting and linting, following libraries are used:

"},{"location":"contribution/PR/#frequently-asked-questions","title":"Frequently Asked Questions","text":"

Q1. Why do my changes taking so long to be Reviewed and/or Merged?

Submission Aftermaths

Pull requests will be reviewed by the maintainers and the rationale behind the maintainer\u2019s decision to accept or deny the changes will be posted in the pull request. Please wait for our code review and approval, possibly enhancing your change on request.

Q2. Would you accept a huge Pull Request with Lots of Changes?

First, make sure that the changes are somewhat related. Otherwise, please create separate pull requests. Anyway, before submitting a huge change, it's probably a good idea to open an issue in the VidGear Github repository to ask the maintainers if they agree with your proposed changes. Otherwise, they could refuse your proposal after you put all that hard work into making the changes. We definitely don't want you to waste your time!

"},{"location":"contribution/issue/","title":"Submitting an Issue Guidelines","text":""},{"location":"contribution/issue/#submitting-an-issue-guidelines","title":"Submitting an Issue Guidelines","text":"

If you've found a new bug or you've come up with some new feature which can improve the quality of the VidGear, then related issues are welcomed! But, Before you do, please read the following guidelines:

First Issue on GitHub?

You can easily learn about it from creating an issue wiki.

Info

Please note that your issue will be fixed much faster if you spend about half an hour preparing it, including the exact reproduction steps and a demo. If you're in a hurry or don't feel confident, it's fine to report issues with less details, but this makes it less likely they'll get fixed soon.

"},{"location":"contribution/issue/#search-the-docs-and-previous-issues","title":"Search the Docs and Previous Issues","text":""},{"location":"contribution/issue/#gather-required-information","title":"Gather Required Information","text":""},{"location":"contribution/issue/#follow-the-issue-template","title":"Follow the Issue Template","text":""},{"location":"contribution/issue/#raise-the-issue","title":"Raise the Issue","text":""},{"location":"gears/camgear/overview/","title":"Overview","text":""},{"location":"gears/camgear/overview/#camgear-api","title":"CamGear API","text":"CamGear API's generalized workflow"},{"location":"gears/camgear/overview/#overview","title":"Overview","text":"

CamGear supports a diverse range of video streams which can handle/control video stream almost any IP/USB Cameras, multimedia video file format (upto 4k tested), any network stream URL such as http(s), rtp, rtsp, rtmp, mms, etc. In addition to this, it also supports Gstreamer's RAW pipelines and various live video streaming sites like YouTube, Twitch, Dailymotion etc.

CamGear API provides a flexible, high-level multi-threaded wrapper around OpenCV's VideoCapture API with direct access to almost all of its available parameters. It relies on Threaded Queue mode for threaded, error-free and synchronized frame handling.

CamGear internally implements yt_dlp backend class for seamlessly pipelining live video-frames and metadata from various streaming services like YouTube, Twitch, and many more \u27b6

Helpful Tips

"},{"location":"gears/camgear/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through CamGear Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/camgear/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/camgear/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/camgear/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/camgear/params/","title":"Parameters","text":""},{"location":"gears/camgear/params/#camgear-api-parameters","title":"CamGear API Parameters","text":""},{"location":"gears/camgear/params/#source","title":"source","text":"

CamGear API will throw RuntimeError if source provided is invalid.

This parameter defines the source for the input stream.

Data-Type: Based on input.

Default Value: Its default value is 0.

Its valid input can be one of the following:

"},{"location":"gears/camgear/params/#stream_mode","title":"stream_mode","text":"

This parameter controls the Stream Mode, .i.e if enabled(stream_mode=True), the CamGear API will interpret the given source input as YouTube URL address.

Due to a FFmpeg bug that causes video to freeze frequently in OpenCV, It is advised to always use GStreamer backend for any livestream videos. Checkout this FAQ for compiling OpenCV with GStreamer support.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

Supported Streaming Websites

The complete list of all supported Streaming Websites URLs can be found here \u27b6

CamGear(source='https://youtu.be/bvetuLwJIkA', stream_mode=True)\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/camgear/params/#colorspace","title":"colorspace","text":"

This parameter selects the colorspace of the input stream.

Data-Type: String

Default Value: Its default value is None.

Usage:

All supported colorspace values are given here \u27b6

CamGear(source=0, colorspace=\"COLOR_BGR2HSV\")\n

Its complete usage example is given here \u27b6

"},{"location":"gears/camgear/params/#backend","title":"backend","text":"

This parameter manually selects the backend for OpenCV's VideoCapture class (only if specified).

Data-Type: Integer

Default Value: Its default value is 0

Usage:

All supported backends are listed here \u27b6

Its value can be for e.g. backend = cv2.CAP_DSHOW for selecting Direct Show as backend:

CamGear(source=0, backend = cv2.CAP_DSHOW)\n

"},{"location":"gears/camgear/params/#options","title":"options","text":"

This parameter provides the ability to alter various Source Tweak Parameters available within OpenCV's VideoCapture API properties.

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

All supported parameters are listed here \u27b6

The desired parameters can be passed to CamGear API by formatting them as this parameter's attributes, as follows:

# formatting parameters as dictionary attributes\noptions = {\"CAP_PROP_FRAME_WIDTH\":320, \"CAP_PROP_FRAME_HEIGHT\":240, \"CAP_PROP_FPS\":60}\n# assigning it\nCamGear(source=0, **options)\n

"},{"location":"gears/camgear/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

CamGear(source=0, logging=True)\n

"},{"location":"gears/camgear/params/#time_delay","title":"time_delay","text":"

This parameter set the time delay (in seconds) before the CamGear API start reading the frames. This delay is only required if the source required some warm-up delay before starting up.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

CamGear(source=0, time_delay=1) # set 1 seconds time delay\n

"},{"location":"gears/camgear/usage/","title":"Usage Examples","text":""},{"location":"gears/camgear/usage/#camgear-api-usage-examples","title":"CamGear API Usage Examples:","text":"

After going through following Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/camgear/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with CamGear API:

# import required libraries\nfrom vidgear.gears import CamGear\nimport cv2\n\n\n# open any valid video stream(for e.g `myvideo.avi` file)\nstream = CamGear(source=\"myvideo.avi\").start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/camgear/usage/#using-camgear-with-streaming-websites","title":"Using Camgear with Streaming Websites","text":"

CamGear internally implements yt_dlp backend class for seamlessly pipelining live video-frames and metadata from various streaming services like Twitch, Vimeo, Dailymotion, and many more \u27b6. All you have to do is to provide the desired Video's URL to its source parameter, and enable its stream_mode parameter.

The complete usage example for Dailymotion and Twitch URLs are as follows:

Bug in OpenCV's FFmpeg

To workaround a FFmpeg bug that causes video to freeze frequently in OpenCV, It is advised to always use GStreamer backend for Livestream videos.

Checkout this FAQ \u27b6 for compiling OpenCV with GStreamer support.

Not all resolutions are supported with GStreamer Backend. See issue #244

Exclusive CamGear Attributes for yt_dlp backend

CamGear also provides exclusive attributes:

with its options dictionary parameter. More information can be found here \u27b6

Supported Streaming Websites

The list of all supported Streaming Websites URLs can be found here \u27b6

Accessing Stream's Metadata

CamGear now provides ytv_metadata global parameter for accessing given Video's metadata as JSON Object. It can used as follows:

New in v0.2.4

ytv_metadata global parameter was added in v0.2.4.

# import required libraries\nfrom vidgear.gears import CamGear\n\n# Add YouTube Video URL as input source (for e.g https://www.dailymotion.com/video/x2yrnum)\n# and enable Stream Mode (`stream_mode = True`)\nstream = CamGear(\n    source=\"https://www.dailymotion.com/video/x2yrnum\", stream_mode=True, logging=True, **options\n).start()\n\n# get Video's metadata as JSON object\nvideo_metadata =  stream.ytv_metadata\n\n# print all available keys\nprint(video_metadata.keys())\n\n# get data like `title`\nprint(video_metadata[\"title\"])\n
Dailymotion Twitch
# import required libraries\nfrom vidgear.gears import CamGear\nimport cv2\n\n# set desired quality as 720p\noptions = {\"STREAM_RESOLUTION\": \"720p\"}\n\n# Add any desire Video URL as input source\n# for e.g https://vimeo.com/151666798\n# and enable Stream Mode (`stream_mode = True`)\nstream = CamGear(\n    source=\"https://www.dailymotion.com/video/x2yrnum\",\n    stream_mode=True,\n    logging=True,\n    **options\n).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

If Twitch user is offline, CamGear will throw ValueError.

# import required libraries\nfrom vidgear.gears import CamGear\nimport cv2\n\n# set desired quality as 720p\noptions = {\"STREAM_RESOLUTION\": \"720p\"}\n\n# Add any desire Video URL as input source\n# for e.g hhttps://www.twitch.tv/shroud\n# and enable Stream Mode (`stream_mode = True`)\nstream = CamGear(\n    source=\"https://www.twitch.tv/shroud\",\n    stream_mode=True,\n    logging=True,\n    **options\n).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/camgear/usage/#using-camgear-with-youtube-videos","title":"Using Camgear with Youtube Videos","text":"

CamGear API also provides out-of-the-box support for pipelining live video-frames and metadata from YouTube (Livestream + Normal) Videos.

YouTube Playlists are not supported yet.

The complete usage example is as follows:

Bug in OpenCV's FFmpeg

To workaround a FFmpeg bug that causes video to freeze frequently in OpenCV, It is advised to always use GStreamer backend for Livestream videos.

Checkout this FAQ \u27b6 for compiling OpenCV with GStreamer support.

Not all resolutions are supported with GStreamer Backend. See issue #244

Exclusive CamGear Attributes for yt_dlp backend

CamGear also provides exclusive attributes:

with its options dictionary parameter. More information can be found here \u27b6

Accessing Stream's Metadata

CamGear now provides ytv_metadata global parameter for accessing given Video's metadata as JSON Object. It can used as follows:

New in v0.2.4

ytv_metadata global parameter was added in v0.2.4.

# import required libraries\nfrom vidgear.gears import CamGear\n\n# Add YouTube Video URL as input source (for e.g https://youtu.be/uCy5OuSQnyA)\n# and enable Stream Mode (`stream_mode = True`)\nstream = CamGear(\n    source=\"https://youtu.be/uCy5OuSQnyA\", stream_mode=True, logging=True, **options\n).start()\n\n# get Video's metadata as JSON object\nvideo_metadata =  stream.ytv_metadata\n\n# print all available keys\nprint(video_metadata.keys())\n\n# get data like `title`\nprint(video_metadata[\"title\"])\n
# import required libraries\nfrom vidgear.gears import CamGear\nimport cv2\n\n# Add YouTube Video URL as input source (for e.g https://youtu.be/uCy5OuSQnyA)\n# and enable Stream Mode (`stream_mode = True`)\nstream = CamGear(\n    source=\"https://youtu.be/uCy5OuSQnyA\", \n    stream_mode=True,\n    logging=True\n).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/camgear/usage/#using-camgear-with-variable-camera-properties","title":"Using CamGear with Variable Camera Properties","text":"

CamGear API also flexibly support various Source Tweak Parameters available within OpenCV's VideoCapture API. These tweak parameters can be used to transform input source Camera-Device properties (such as its brightness, saturation, framerate, resolution, gain etc.) seamlessly, and can be easily applied in CamGear API through its options dictionary parameter by formatting them as its attributes.

The complete usage example is as follows:

All the supported Source Tweak Parameters can be found here \u27b6

# import required libraries\nfrom vidgear.gears import CamGear\nimport cv2\n\n\n# define suitable tweak parameters for your stream.\noptions = {\n    \"CAP_PROP_FRAME_WIDTH\": 320, # resolution 320x240\n    \"CAP_PROP_FRAME_HEIGHT\": 240,\n    \"CAP_PROP_FPS\": 60, # framerate 60fps\n}\n\n# To open live video stream on webcam at first index(i.e. 0) \n# device and apply source tweak parameters\nstream = CamGear(source=0, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/camgear/usage/#using-camgear-with-direct-colorspace-manipulation","title":"Using Camgear with Direct Colorspace Manipulation","text":"

CamGear API also supports Direct Colorspace Manipulation, which is ideal for changing source colorspace on the run.

A more detailed information on colorspace manipulation can be found here \u27b6

In following example code, we will start with HSV as source colorspace, and then we will switch to GRAY colorspace when W key is pressed, and then LAB colorspace when E key is pressed, finally default colorspace (i.e. BGR) when S key is pressed. Also, quit when Q key is pressed:

Any incorrect or None-type value, will immediately revert the colorspace to default i.e. BGR.

# import required libraries\nfrom vidgear.gears import CamGear\nimport cv2\n\n# Open any source of your choice, like Webcam first index(i.e. 0)\n# and change its colorspace to `HSV`\nstream = CamGear(source=0, colorspace=\"COLOR_BGR2HSV\", logging=True).start()\n\n# loop over\nwhile True:\n\n    # read HSV frames\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the HSV frame here}\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for key if pressed\n    key = cv2.waitKey(1) & 0xFF\n\n    # check if 'w' key is pressed\n    if key == ord(\"w\"):\n        # directly change colorspace at any instant\n        stream.color_space = cv2.COLOR_BGR2GRAY  # Now colorspace is GRAY\n\n    # check for 'e' key is pressed\n    if key == ord(\"e\"):\n        stream.color_space = cv2.COLOR_BGR2LAB  # Now colorspace is CieLAB\n\n    # check for 's' key is pressed\n    if key == ord(\"s\"):\n        stream.color_space = None  # Now colorspace is default(ie BGR)\n\n    # check for 'q' key is pressed\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/camgear/advanced/source_params/","title":"Source Tweak Parameters","text":""},{"location":"gears/camgear/advanced/source_params/#source-tweak-parameters-for-camgear-api","title":"Source Tweak Parameters for CamGear API","text":""},{"location":"gears/camgear/advanced/source_params/#overview","title":"Overview","text":"

With CamGear's options dictionary parameter, the user has the ability to alter various tweak parameters available within OpenCV's VideoCapture Class by formatting them as its attributes.

These tweak parameters can be used to transform input Camera-Source properties (such as its brightness, saturation, resolution, iso, gain etc.) seamlessly. All parameters supported by CamGear API are disscussed in this document.

"},{"location":"gears/camgear/advanced/source_params/#exclusive-camgear-attributes","title":"Exclusive CamGear Attributes","text":"CamGear's Exclusive Attributes

In addition to Source Tweak Parameters, CamGear also provides some exclusive attributes for its options dictionary parameters.

These attributes are as follows:

"},{"location":"gears/camgear/advanced/source_params/#supported-source-tweak-parameters","title":"Supported Source Tweak Parameters","text":"

All Source Tweak Parameters supported by CamGear API are as follows:

Remember, Not all parameters are supported by all cameras devices, which is one of the most troublesome thing with OpenCV library. Each camera type, from android cameras, to USB cameras , to professional ones, offers a different interface to modify its parameters. Therefore, there are many branches in OpenCV code to support as many of them, but of course, not all possible devices are covered, and thereby works. Furthermore, OpenCV does not return any helpful error messages regarding this problem, so it\u2019s pretty much based on trial and error.

You can easily check parameter values supported by your webcam, by hooking it to a Linux machine, and using the command v4l2-ctl -d 0 --list-formats-ext (where 0 is an index of the given camera) to list the supported video parameters and their values. If that doesn't works, refer to its datasheet (if available).

These parameters can be passed to CamGear's options dictionary parameter by formatting them as its string attributes. Its complete usage example is here \u27b6

Values Description CAP_PROP_POS_MSEC Current position of the video file in milliseconds. CAP_PROP_POS_FRAMES 0-based index of the frame to be decoded/captured next. CAP_PROP_POS_AVI_RATIO Relative position of the video file: 0=start of the film, 1=end of the film. CAP_PROP_FRAME_WIDTH Width of the frames in the video stream. CAP_PROP_FRAME_HEIGHT Height of the frames in the video stream. CAP_PROP_FPS Frame rate. CAP_PROP_FOURCC 4-character code of codec. see VideoWriter::fourcc. CAP_PROP_FRAME_COUNT Number of frames in the video file. CAP_PROP_FORMAT Format of the Mat objects returned by VideoCapture::retrieve(). CAP_PROP_MODE Backend-specific value indicating the current capture mode. CAP_PROP_BRIGHTNESS Brightness of the image (only for those cameras that support). CAP_PROP_CONTRAST Contrast of the image (only for cameras). CAP_PROP_SATURATION Saturation of the image (only for cameras). CAP_PROP_HUE Hue of the image (only for cameras). CAP_PROP_GAIN Gain of the image (only for those cameras that support). CAP_PROP_EXPOSURE Exposure (only for those cameras that support). CAP_PROP_CONVERT_RGB Boolean flags indicating whether images should be converted to RGB. CAP_PROP_WHITE_BALANCE_BLUE_U Currently unsupported. CAP_PROP_RECTIFICATION Rectification flag for stereo cameras (note: only supported by DC1394 v 2.x backend currently). CAP_PROP_MONOCHROME CAP_PROP_SHARPNESS CAP_PROP_AUTO_EXPOSURE DC1394: exposure control done by camera, user can adjust reference level using this feature. CAP_PROP_GAMMA CAP_PROP_TEMPERATURE CAP_PROP_TRIGGER CAP_PROP_TRIGGER_DELAY CAP_PROP_WHITE_BALANCE_RED_V CAP_PROP_ZOOM CAP_PROP_FOCUS CAP_PROP_GUID CAP_PROP_ISO_SPEED CAP_PROP_BACKLIGHT CAP_PROP_PAN CAP_PROP_TILT CAP_PROP_ROLL CAP_PROP_IRIS CAP_PROP_SETTINGS Pop up video/camera filter dialog (note: only supported by DSHOW backend currently. The property value is ignored) CAP_PROP_BUFFERSIZE CAP_PROP_AUTOFOCUS CAP_PROP_SAR_NUM Sample aspect ratio: num/den (num) CAP_PROP_SAR_DEN Sample aspect ratio: num/den (den) CAP_PROP_BACKEND Current backend (enum VideoCapture APIs). Read-only property. CAP_PROP_CHANNEL Video input or Channel Number (only for those cameras that support) CAP_PROP_AUTO_WB enable/ disable auto white-balance CAP_PROP_WB_TEMPERATURE white-balance color temperature

"},{"location":"gears/netgear/overview/","title":"Overview","text":""},{"location":"gears/netgear/overview/#netgear-api","title":"NetGear API","text":"NetGear API generalized"},{"location":"gears/netgear/overview/#overview","title":"Overview","text":"

NetGear is exclusively designed to transfer video frames synchronously and asynchronously between interconnecting systems over the network in real-time.

NetGear implements a high-level wrapper around PyZmQ python library that contains python bindings for ZeroMQ - a high-performance asynchronous distributed messaging library that provides a message queue, but unlike message-oriented middleware, its system can run without a dedicated message broker.

NetGear also enables real-time JPEG Frame Compression capabilities for boosting performance significantly while sending video-frames over the network in real-time.

Lazy Pirate pattern in NetGear API

NetGear API now internally implements robust Lazy Pirate pattern (auto-reconnection) for its synchronous messaging patterns(zmq.PAIR & zmq.REQ/zmq.REP) at both Server and Client ends, where its API instead of doing a blocking receive, will:

Netgear API also provides max_retries and request_timeout like attributes for controlling this polling.

NetGear as of now seamlessly supports three ZeroMQ messaging patterns:

whereas the supported protocol are: tcp and ipc.

"},{"location":"gears/netgear/overview/#modes-of-operation","title":"Modes of Operation","text":""},{"location":"gears/netgear/overview/#primary-modes","title":"Primary Modes","text":"

NetGear API primarily has two modes of operations:

"},{"location":"gears/netgear/overview/#exclusive-modes","title":"Exclusive Modes","text":"

In addition to the primary modes, NetGear API also offers application-specific Exclusive Modes:

Also, checkout this compatibility chart for these modes interoperability.

"},{"location":"gears/netgear/overview/#a-multi-servers-mode","title":"A. Multi-Servers Mode","text":""},{"location":"gears/netgear/overview/#b-multi-clients-mode","title":"B. Multi-Clients Mode","text":""},{"location":"gears/netgear/overview/#c-bidirectional-mode","title":"C. Bidirectional Mode","text":""},{"location":"gears/netgear/overview/#d-ssh-tunneling-mode","title":"D. SSH Tunneling Mode","text":""},{"location":"gears/netgear/overview/#e-secure-mode","title":"E. Secure Mode","text":"

Important Information

"},{"location":"gears/netgear/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through NetGear Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/netgear/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/netgear/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/netgear/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/netgear/params/","title":"Parameters","text":""},{"location":"gears/netgear/params/#netgear-api-parameters","title":"NetGear API Parameters","text":""},{"location":"gears/netgear/params/#address","title":"address","text":"

This parameter sets the valid Network IP address for Server/Client. Network addresses are unique identifiers across the network.

Data-Type: String

Default Value: Its default value is based on selected primary mode, i.e 'localhost' for Send Mode and '*' for Receive Mode on a local machine.

Usage:

NetGear(address=\"192.168.0.145\")\n

"},{"location":"gears/netgear/params/#port","title":"port","text":"

This parameter sets the valid Network Port for Server/Client. Network port is a number that identifies one side of a connection between two devices on the network and is used determine to which process or application a message should be delivered.

Exception for Exclusive Modes

In Multi-Servers Mode:

In Multi-Client Mode:

Data-Type: String or List/Tuple

Default Value: Its default value is '5555'

Usage:

NetGear(port=\"5575\")\n

"},{"location":"gears/netgear/params/#protocol","title":"protocol","text":"

This parameter sets the valid messaging protocol between server and client. A network protocol is a set of established rules that dictates how to format, transmit and receive data so computer network devices - from servers and routers to endpoints - can communicate regardless of the differences in their underlying infrastructures, designs or standards. Supported protocol are: 'tcp' and 'ipc'.

Data-Type: String

Default Value: Its default value is 'tcp'

Usage:

NetGear(protocol=\"ipc\")\n

"},{"location":"gears/netgear/params/#pattern","title":"pattern","text":"

This parameter sets the supported messaging pattern(flow of communication) between server and client. Messaging patterns are the network-oriented architectural pattern that describes the flow of communication between interconnecting systems. NetGear provides access to ZeroMQ's pre-optimized sockets which enables you to take advantage of these patterns.

Data-Type: Integer

Default Value: Its default value is 0 (i.e zmq.PAIR).

Supported ZMQ patterns

All supported ZMQ patterns for NetGear are:

Usage:

NetGear(pattern=1) # sets zmq.REQ/zmq.REP pattern\n

"},{"location":"gears/netgear/params/#receive_mode","title":"receive_mode","text":"

This parameter select the Netgear's Mode of operation. It basically activates Receive Mode(if True) and Send Mode(if False). Furthermore, recv() method will only work when this flag is enabled(i.e. Receive Mode), whereas send() method will only work when this flag is disabled(i.e.Send Mode).

Data-Type: Boolean

Default Value: Its default value is False(i.e. Send Mode is activated by default).

Usage:

NetGear(receive_mode=True) # activates Recieve Mode\n

"},{"location":"gears/netgear/params/#options","title":"options","text":"

This parameter provides the flexibility to alter various NetGear API's internal properties, modes, and some PyZMQ flags.

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

Supported dictionary attributes for NetGear API

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

The desired attributes can be passed to NetGear API as follows:

# formatting parameters as dictionary attributes\noptions = {\n    \"secure_mode\": 2,\n    \"custom_cert_location\": \"/home/foo/foo1/foo2\",\n    \"overwrite_cert\": True,\n    \"flag\": 0, \n    \"copy\": True, \n    \"track\": False\n}\n# assigning it\nNetGear(logging=True, **options)\n

"},{"location":"gears/netgear/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

NetGear_Async(logging=True)\n

"},{"location":"gears/netgear/usage/","title":"Usage Examples","text":""},{"location":"gears/netgear/usage/#netgear-api-usage-examples","title":"NetGear API Usage Examples:","text":"

Important Information

After going through following Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/netgear/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with NetGear API:

"},{"location":"gears/netgear/usage/#servers-end","title":"Server's End","text":"

Open your favorite terminal and execute the following python code:

You can terminate both sides anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\n\n# open any valid video stream(for e.g `test.mp4` file)\nstream = VideoGear(source=\"test.mp4\").start()\n\n# Define Netgear Server with default parameters\nserver = NetGear()\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n
"},{"location":"gears/netgear/usage/#clients-end","title":"Client's End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n\n# define Netgear Client with `receive_mode = True` and default parameter\nclient = NetGear(receive_mode=True)\n\n# loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/usage/#using-netgear-with-variable-parameters","title":"Using NetGear with Variable Parameters","text":""},{"location":"gears/netgear/usage/#clients-end_1","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from the Server) and execute the following python code:

Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# define various tweak flags\noptions = {\"flag\": 0, \"copy\": True, \"track\": False}\n\n# Define Netgear Client at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n
"},{"location":"gears/netgear/usage/#servers-end_1","title":"Server's End","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\n\n# define various tweak flags\noptions = {\"flag\": 0, \"copy\": True, \"track\": False}\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = VideoGear(source=0).start()\n\n# Define Netgear server at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/usage/#using-netgear-with-opencv","title":"Using NetGear with OpenCV","text":"

You can easily use NetGear directly with any Video Processing library such as OpenCV itself. The complete usage example is as follows:

"},{"location":"gears/netgear/usage/#clients-end_2","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from the Server) and execute the following python code:

Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# define tweak flags\noptions = {\"flag\": 0, \"copy\": True, \"track\": False}\n\n# Define Netgear Client at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=0,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the received frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n
"},{"location":"gears/netgear/usage/#servers-end_2","title":"Server's End","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = cv2.VideoCapture(0)\n\n# define tweak flags\noptions = {\"flag\": 0, \"copy\": True, \"track\": False}\n\n# Define Netgear Client at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=0,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        (grabbed, frame) = stream.read()\n\n        # check for frame if not grabbed\n        if not grabbed:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.release()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/usage/#using-netgear-with-other-videocapture-gears","title":"Using NetGear with Other VideoCapture Gears","text":"

You can use any VideoCapture Gear in the similar manner. Let's implement given usage example with ScreenGear:

"},{"location":"gears/netgear/usage/#clients-end_3","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from the Server) and execute the following python code:

Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# define various tweak flags\noptions = {\"flag\": 0, \"copy\": True, \"track\": False}\n\n# Define Netgear Client at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n
"},{"location":"gears/netgear/usage/#servers-end_3","title":"Server's End","text":"

Now, Open the terminal on another Server System (let's say you want to transmit Monitor Screen Frames from a Laptop), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import ScreenGear\nfrom vidgear.gears import NetGear\n\n# define various tweak flags\noptions = {\"flag\": 0, \"copy\": True, \"track\": False}\n\n# Start capturing live Monitor screen frames with default settings\nstream = ScreenGear().start()\n\n# Define Netgear server at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/bidirectional_mode/","title":"Bidirectional Mode","text":""},{"location":"gears/netgear/advanced/bidirectional_mode/#bidirectional-mode-for-netgear-api","title":"Bidirectional Mode for NetGear API","text":"NetGear's Bidirectional Mode"},{"location":"gears/netgear/advanced/bidirectional_mode/#overview","title":"Overview","text":"

Bidirectional Mode enables seamless support for Bidirectional data transmission between Client/Consumer and Sender/Publisher along with video-frames through its synchronous messaging patterns such as zmq.PAIR (ZMQ Pair Pattern) & zmq.REQ/zmq.REP (ZMQ Request/Reply Pattern).

In Bidirectional Mode, we utilizes the NetGear API's message parameter of send() method for sending data from Server-to-Client, and return_data parameter of recv() method to return data back from Client-to-Server all while transferring frames in real-time.

This mode can be easily activated in NetGear through bidirectional_mode attribute of its options dictionary parameter during initialization.

Important Information regarding Bidirectional Mode

"},{"location":"gears/netgear/advanced/bidirectional_mode/#features-of-bidirectional-mode","title":"Features of Bidirectional Mode","text":"

"},{"location":"gears/netgear/advanced/bidirectional_mode/#exclusive-parameters","title":"Exclusive Parameters","text":"

To send data bidirectionally, NetGear API provides two exclusive parameters for its methods:

"},{"location":"gears/netgear/advanced/bidirectional_mode/#usage-examples","title":"Usage Examples","text":""},{"location":"gears/netgear/advanced/bidirectional_mode/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with Bidirectional Mode in NetGear API:

"},{"location":"gears/netgear/advanced/bidirectional_mode/#server-end","title":"Server End","text":"

Open your favorite terminal and execute the following python code:

You can terminate both sides anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\n\n# open any valid video stream(for e.g `test.mp4` file)\nstream = VideoGear(source=\"test.mp4\").start()\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# Define NetGear Server with defined parameters\nserver = NetGear(logging=True, **options)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # send frame & data and also receive data from Client\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # print data just received from Client\n        if not (recv_data is None):\n            print(recv_data)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.
"},{"location":"gears/netgear/advanced/bidirectional_mode/#client-end","title":"Client End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# define NetGear Client with `receive_mode = True` and defined parameter\nclient = NetGear(receive_mode=True, logging=True, **options)\n\n# loop over\nwhile True:\n\n    # prepare data to be sent\n    target_data = \"Hi, I am a Client here.\"\n\n    # receive data from server and also send our data\n    data = client.recv(return_data=target_data)\n\n    # check for data if None\n    if data is None:\n        break\n\n    # extract server_data & frame from data\n    server_data, frame = data\n\n    # again check for frame if None\n    if frame is None:\n        break\n\n    # {do something with the extracted frame and data here}\n\n    # lets print extracted server data\n    if not (server_data is None):\n        print(server_data)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/bidirectional_mode/#using-bidirectional-mode-with-variable-parameters","title":"Using Bidirectional Mode with Variable Parameters","text":""},{"location":"gears/netgear/advanced/bidirectional_mode/#clients-end","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from the Server) and execute the following python code:

Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# Define NetGear Client at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# loop over\nwhile True:\n\n    # prepare data to be sent\n    target_data = \"Hi, I am a Client here.\"\n\n    # receive data from server and also send our data\n    data = client.recv(return_data=target_data)\n\n    # check for data if None\n    if data is None:\n        break\n\n    # extract server_data & frame from data\n    server_data, frame = data\n\n    # again check for frame if None\n    if frame is None:\n        break\n\n    # {do something with the extracted frame and data here}\n\n    # lets print received server data\n    if not (server_data is None):\n        print(server_data)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/bidirectional_mode/#server-end_1","title":"Server End","text":"

Now, Open the terminal on another Server System (a Raspberry Pi with Camera Module), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this backend, otherwise nothing will work.

New Picamera2 backendLegacy Picamera backend
# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import PiGear\nfrom libcamera import Transform\n\n# add various Picamera2 API tweaks\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# Define NetGear server at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # send frame & data and also receive data from Client\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # print data just received from Client\n        if not (recv_data is None):\n            print(recv_data)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import PiGear\n\n# add various Picamera tweak parameters to dictionary\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# Define NetGear server at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # send frame & data and also receive data from Client\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # print data just received from Client\n        if not (recv_data is None):\n            print(recv_data)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.

"},{"location":"gears/netgear/advanced/bidirectional_mode/#using-bidirectional-mode-for-video-frames-transfer","title":"Using Bidirectional Mode for Video-Frames Transfer","text":"

In this example we are going to implement a bare-minimum example, where we will be sending video-frames (3-Dimensional numpy arrays) of the same Video bidirectionally at the same time, for testing the real-time performance and synchronization between the Server and the Client using this(Bidirectional) Mode.

This example is useful for building applications like Real-Time Video Chat.

We're also using reducer() method for reducing frame-size on-the-go for additional performance.

Remember, Sending large HQ video-frames may required more network bandwidth and packet size which may lead to video latency!

"},{"location":"gears/netgear/advanced/bidirectional_mode/#server-end_2","title":"Server End","text":"

Open your favorite terminal and execute the following python code:

You can terminate both side anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears.helper import reducer\nimport numpy as np\nimport cv2\n\n# open any valid video stream(for e.g `test.mp4` file)\nstream = cv2.VideoCapture(\"test.mp4\")\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# Define NetGear Server with defined parameters\nserver = NetGear(pattern=1, logging=True, **options)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        (grabbed, frame) = stream.read()\n\n        # check for frame if not grabbed\n        if not grabbed:\n            break\n\n        # reducer frames size if you want more performance, otherwise comment this line\n        frame = reducer(frame, percentage=30)  # reduce frame by 30%\n\n        # {do something with the frame here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # send frame & data and also receive data from Client\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # check data just received from Client is of numpy datatype\n        if not (recv_data is None) and isinstance(recv_data, np.ndarray):\n\n            # {do something with received numpy array here}\n\n            # Let's show it on output window\n            cv2.imshow(\"Received Frame\", recv_data)\n            key = cv2.waitKey(1) & 0xFF\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.release()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.

"},{"location":"gears/netgear/advanced/bidirectional_mode/#client-end_1","title":"Client End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears.helper import reducer\nimport cv2\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# again open the same video stream\nstream = cv2.VideoCapture(\"test.mp4\")\n\n# define NetGear Client with `receive_mode = True` and defined parameter\nclient = NetGear(receive_mode=True, pattern=1, logging=True, **options)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n        break\n\n    # reducer frames size if you want more performance, otherwise comment this line\n    frame = reducer(frame, percentage=30)  # reduce frame by 30%\n\n    # receive data from server and also send our data\n    data = client.recv(return_data=frame)\n\n    # check for data if None\n    if data is None:\n        break\n\n    # extract server_data & frame from data\n    server_data, frame = data\n\n    # again check for frame if None\n    if frame is None:\n        break\n\n    # {do something with the extracted frame and data here}\n\n    # lets print extracted server data\n    if not (server_data is None):\n        print(server_data)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.release()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/bidirectional_mode/#using-bidirectional-mode-for-video-frames-transfer-with-frame-compression","title":"Using Bidirectional Mode for Video-Frames Transfer with Frame Compression","text":"

This usage examples can be found here \u27b6

  1. Additional data of numpy.ndarray data-type is ONLY SUPPORTED at Client's end with its return_data parameter.

    \u21a9\u21a9

"},{"location":"gears/netgear/advanced/compression/","title":"Frame Compression","text":""},{"location":"gears/netgear/advanced/compression/#frame-compression-for-netgear-api","title":"Frame Compression for NetGear API","text":""},{"location":"gears/netgear/advanced/compression/#overview","title":"Overview","text":"

NetGear API enables real-time JPEG Frame Compression capabilities for optimizing performance significantly while sending frames over the network.

For enabling Frame Compression, NetGear uses powerful simplejpeg library at its backend, which is based on recent versions of libjpeg-turbo JPEG image codec, to accelerate baseline JPEG compression and decompression on all modern systems. NetGear API employs its exposed decode_jpeg and encode_jpeg methods to encode video-frames to JFIF format before sending it at Server, and cleverly decode it at the Client(s) all in real-time, thereby leveraging performance at cost of minor loss in frame quality.

Frame Compression is enabled by default in NetGear, and can be easily controlled through jpeg_compression_quality, jpeg_compression_fastdct, jpeg_compression_fastupsample like attributes of its options dictionary parameter during initialization.

Useful Information about Frame Compression

Frame Compression is primarily controlled by Server end. That means, if Frame Compression is enabled at Server, then Client(s) will automatically enforce the Frame Compression with defined performance attributes. Otherwise if it is disabled, then Client(s) disables it too.

"},{"location":"gears/netgear/advanced/compression/#exclusive-attributes","title":"Exclusive Attributes","text":"

For implementing Frame Compression, NetGear API currently provide following exclusive attribute for its options dictionary parameter to leverage performance with Frame Compression:

"},{"location":"gears/netgear/advanced/compression/#performance-attributes","title":"Performance Attributes","text":""},{"location":"gears/netgear/advanced/compression/#usage-examples","title":"Usage Examples","text":""},{"location":"gears/netgear/advanced/compression/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with Frame Compression in NetGear API:

"},{"location":"gears/netgear/advanced/compression/#server-end","title":"Server End","text":"

Open your favorite terminal and execute the following python code:

You can terminate both sides anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\nimport cv2\n\n# open any valid video stream(for e.g `test.mp4` file)\nstream = VideoGear(source=\"test.mp4\").start()\n\n# activate jpeg encoding and specify other related parameters\noptions = {\n    \"jpeg_compression\": True,\n    \"jpeg_compression_quality\": 90,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# Define NetGear Server with defined parameters\nserver = NetGear(pattern=1, logging=True, **options)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if None-type\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/compression/#client-end","title":"Client End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

You can terminate client anytime by pressing Ctrl+C on your keyboard!

If compression is enabled at Server, then Client will automatically enforce Frame Compression with its performance attributes.

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# define NetGear Client with `receive_mode = True` and defined parameter\nclient = NetGear(receive_mode=True, pattern=1, logging=True)\n\n# loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/compression/#bare-minimum-usage-with-variable-colorspace","title":"Bare-Minimum Usage with Variable Colorspace","text":"

Frame Compression also supports specify incoming frames colorspace with compression. In following bare-minimum code, we will be sending GRAY frames from Server to Client:

New in v0.2.2

This example was added in v0.2.2.

This example works in conjunction with Source ColorSpace manipulation for VideoCapture Gears \u27b6

Supported colorspace values are RGB, BGR, RGBX, BGRX, XBGR, XRGB, GRAY, RGBA, BGRA, ABGR, ARGB, CMYK. More information can be found here \u27b6

"},{"location":"gears/netgear/advanced/compression/#server-end_1","title":"Server End","text":"

Open your favorite terminal and execute the following python code:

You can terminate both sides anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\nimport cv2\n\n# open any valid video stream(for e.g `test.mp4` file) and change its colorspace to grayscale\nstream = VideoGear(source=\"test.mp4\", colorspace=\"COLOR_BGR2GRAY\").start()\n\n# activate jpeg encoding and specify other related parameters\noptions = {\n    \"jpeg_compression\": \"GRAY\", # set grayscale\n    \"jpeg_compression_quality\": 90,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# Define NetGear Server with defined parameters\nserver = NetGear(pattern=1, logging=True, **options)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read grayscale frames from stream\n        frame = stream.read()\n\n        # check for frame if None-type\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send grayscale frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/compression/#client-end_1","title":"Client End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

You can terminate client anytime by pressing Ctrl+C on your keyboard!

If compression is enabled at Server, then Client will automatically enforce Frame Compression with its performance attributes.

Client's end also automatically enforces Server's colorspace, there's no need to define it again.

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# define NetGear Client with `receive_mode = True` and defined parameter\nclient = NetGear(receive_mode=True, pattern=1, logging=True)\n\n# loop over\nwhile True:\n\n    # receive grayscale frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the grayscale frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Grayscale Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/compression/#using-frame-compression-with-variable-parameters","title":"Using Frame Compression with Variable Parameters","text":""},{"location":"gears/netgear/advanced/compression/#clients-end","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from the Server) and execute the following python code:

Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

If compression is enabled at Server, then Client will automatically enforce Frame Compression with its performance attributes.

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n\n# Define NetGear Client at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n#  loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/compression/#server-end_2","title":"Server End","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate jpeg encoding and specify other related parameters\noptions = {\n    \"jpeg_compression\": True,\n    \"jpeg_compression_quality\": 90,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = VideoGear(source=0).start()\n\n# Define NetGear server at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/compression/#using-bidirectional-mode-for-video-frames-transfer-with-frame-compression","title":"Using Bidirectional Mode for Video-Frames Transfer with Frame Compression","text":"

NetGear now supports Dual Frame Compression for transferring video-frames with its exclusive Bidirectional Mode for achieving unmatchable performance bidirectionally. You can easily enable Frame Compression with its performance attributes at both ends to boost performance bidirectionally.

In this example we are going to implement a bare-minimum example, where we will be sending video-frames (3-Dimensional numpy arrays) of the same Video bidirectionally at the same time for testing the real-time performance and synchronization between the Server and Client using Bidirectional Mode. Furthermore, we're going to use optimal Dual Frame Compression Setting for Sending and Receiving frames at both Server and Client end.

This example is great for building applications like Real-time Video Chat System.

This Dual Frame Compression feature also available for Multi-Clients Mode.

We're also using reducer() Helper method for reducing frame-size on-the-go for additional performance.

Remember to define Frame Compression's performance attributes both on Server and Client ends in Dual Frame Compression to boost performance bidirectionally!

"},{"location":"gears/netgear/advanced/compression/#server-end_3","title":"Server End","text":"

Open your favorite terminal and execute the following python code:

You can terminate both side anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears.helper import reducer\nimport numpy as np\nimport cv2\n\n# open any valid video stream(for e.g `test.mp4` file)\nstream = cv2.VideoCapture(\"test.mp4\")\n\n# activate Bidirectional mode and Frame Compression\noptions = {\n    \"bidirectional_mode\": True,\n    \"jpeg_compression\": True,\n    \"jpeg_compression_quality\": 95,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# Define NetGear Server with defined parameters\nserver = NetGear(pattern=1, logging=True, **options)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        (grabbed, frame) = stream.read()\n\n        # check for frame if not grabbed\n        if not grabbed:\n            break\n\n        # reducer frames size if you want even more performance, otherwise comment this line\n        frame = reducer(frame, percentage=20)  # reduce frame by 20%\n\n        # {do something with the frame here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # send frame & data and also receive data from Client\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # check data just received from Client is of numpy datatype\n        if not (recv_data is None) and isinstance(recv_data, np.ndarray):\n\n            # {do something with received numpy array here}\n\n            # Let's show it on output window\n            cv2.imshow(\"Received Frame\", recv_data)\n            key = cv2.waitKey(1) & 0xFF\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.release()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.

"},{"location":"gears/netgear/advanced/compression/#client-end_2","title":"Client End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears.helper import reducer\nimport cv2\n\n# activate Bidirectional mode and Frame Compression\noptions = {\n    \"bidirectional_mode\": True,\n    \"jpeg_compression\": True,\n    \"jpeg_compression_quality\": 95,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# again open the same video stream\nstream = cv2.VideoCapture(\"test.mp4\")\n\n# define NetGear Client with `receive_mode = True` and defined parameter\nclient = NetGear(receive_mode=True, pattern=1, logging=True, **options)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n        break\n\n    # reducer frames size if you want even more performance, otherwise comment this line\n    frame = reducer(frame, percentage=20)  # reduce frame by 20%\n\n    # receive data from server and also send our data\n    data = client.recv(return_data=frame)\n\n    # check for data if None\n    if data is None:\n        break\n\n    # extract server_data & frame from data\n    server_data, frame = data\n\n    # again check for frame if None\n    if frame is None:\n        break\n\n    # {do something with the extracted frame and data here}\n\n    # lets print extracted server data\n    if not (server_data is None):\n        print(server_data)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.release()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/multi_client/","title":"Multi-Clients Mode","text":""},{"location":"gears/netgear/advanced/multi_client/#multi-clients-mode-for-netgear-api","title":"Multi-Clients Mode for NetGear API","text":"NetGear's Multi-Clients Mode"},{"location":"gears/netgear/advanced/multi_client/#overview","title":"Overview","text":"

In Multi-Clients Mode, NetGear robustly handles Multiple Clients at once thereby able to broadcast frames and data across multiple Clients/Consumers in the network at same time. This mode works contrary to Multi-Servers Mode such that every new Client that connects to single Server can be identified by its unique port address on the network.

The supported patterns for this mode are Publish/Subscribe (zmq.PUB/zmq.SUB) and Request/Reply(zmq.REQ/zmq.REP) and can be easily activated in NetGear API through multiclient_mode attribute of its options dictionary parameter during initialization.

Multi-Clients Mode is best for broadcasting Meta-Data with Video-frames to specific limited number of clients in real time. But if you're looking to scale broadcast to a very large pool of clients, then see our WebGear or WebGear_RTC APIs.

Important Information regarding Multi-Clients Mode

"},{"location":"gears/netgear/advanced/multi_client/#features-of-multi-clients-mode","title":"Features of Multi-Clients Mode","text":"

"},{"location":"gears/netgear/advanced/multi_client/#usage-examples","title":"Usage Examples","text":"

Important

"},{"location":"gears/netgear/advanced/multi_client/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

In this example, we will capturing live video-frames from a source (a.k.a Server) with a webcam connected to it. Afterwards, those captured frame will be sent over the network to two independent system (a.k.a Clients) using this Multi-Clients Mode in NetGear API. Finally, both Clients will be displaying received frames in Output Windows in real time.

This example is useful for building applications like Real-Time Video Broadcasting to multiple clients in local network.

"},{"location":"gears/netgear/advanced/multi_client/#servers-end","title":"Server's End","text":"

Now, Open the terminal on a Server System (with a webcam connected to it at index 0). Now execute the following python code:

Important Notes

Frame/Data transmission will NOT START untill all given Client(s) are connected to this Server.

You can terminate streaming anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import CamGear\n\n# Open suitable video stream (webcam on first index in our case)\nstream = CamGear(source=0).start()\n\n# activate multiclient_mode mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple of\n# all unique Server((5577,5578) in our case) and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.x.x\",\n    port=(5567, 5577),\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# Define received data dictionary\ndata_dict = {}\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if not None-type\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame and also receive data from Client(s)\n        recv_data = server.send(frame)\n\n        # check if valid data received\n        if not (recv_data is None):\n            # extract unique port address and its respective data\n            unique_address, data = recv_data\n            # update the extracted data in the data dictionary\n            data_dict[unique_address] = data\n\n        if data_dict:\n            # print data just received from Client(s)\n            for key, value in data_dict.items():\n                print(\"Client at port {} said: {}\".format(key, value))\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#client-1s-end","title":"Client-1's End","text":"

Now, Open a terminal on another Client System (where you want to display the input frames received from Server), let's name it Client-1. Execute the following python code:

Replace the IP address in the following code with Server's IP address you noted earlier and also assign a unique port address (required by Server to identify this system).

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate Multi-Clients mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at Server's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5567\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n) \n\n# loop over\nwhile True:\n    # receive data from server\n    frame = client.recv()\n\n    # check for frame if None\n    if frame is None:\n        break\n\n    # {do something with frame here}\n\n    # Show output window\n    cv2.imshow(\"Client 5567 Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#client-2s-end","title":"Client-2's End","text":"

Finally, Open a terminal on another Client System (where you want to display the input frames received from Server), let's name it Client-2. Execute the following python code:

Replace the IP address in the following code with Server's IP address you noted earlier and also assign a unique port address (required by Server to identify this system).

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate Multi-Clients mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at Server's IP address and assign a unique port address and other parameters\n # !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5577\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# loop over\nwhile True:\n\n    # receive data from server\n    frame = client.recv()\n\n    # check for frame if None\n    if frame is None:\n        break\n\n    # {do something with frame here}\n\n    # Show output window\n    cv2.imshow(\"Client 5577 Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#bare-minimum-usage-with-opencv","title":"Bare-Minimum Usage with OpenCV","text":"

In this example, we will be re-implementing previous bare-minimum example with OpenCV and NetGear API.

"},{"location":"gears/netgear/advanced/multi_client/#servers-end_1","title":"Server's End","text":"

Now, Open the terminal on a Server System (with a webcam connected to it at index 0). Now execute the following python code:

Important Notes

Frame/Data transmission will NOT START untill all given Client(s) are connected to this Server.

You can terminate streaming anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# Open suitable video stream (webcam on first index in our case)\nstream = cv2.VideoCapture(0)\n\n# activate multiclient_mode mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple of all unique Server((5577,5578) in our case) and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.x.x\",\n    port=(5567, 5577),\n    protocol=\"tcp\",\n    pattern=2,\n    logging=True,\n    **options\n)\n\n# Define received data dictionary\ndata_dict = {}\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        (grabbed, frame) = stream.read()\n\n        # check for frame if not grabbed\n        if not grabbed:\n            break\n\n        # {do something with the frame here}\n\n        # send frame and also receive data from Client(s)\n        recv_data = server.send(frame)\n\n        # check if valid data received\n        if not (recv_data is None):\n            # extract unique port address and its respective data\n            unique_address, data = recv_data\n            # update the extracted data in the data dictionary\n            data_dict[unique_address] = data\n\n        if data_dict:\n            # print data just received from Client(s)\n            for key, value in data_dict.items():\n                print(\"Client at port {} said: {}\".format(key, value))\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.release()\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#client-1s-end_1","title":"Client-1's End","text":"

Now, Open a terminal on another Client System (where you want to display the input frames received from Server), let's name it Client-1. Execute the following python code:

Replace the IP address in the following code with Server's IP address you noted earlier and also assign a unique port address (required by Server to identify this system).

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate Multi-Clients mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at Server's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5567\",\n    protocol=\"tcp\",\n    pattern=2,\n    receive_mode=True,\n    logging=True,\n    **options\n) \n\n# loop over\nwhile True:\n    # receive data from server\n    frame = client.recv()\n\n    # check for frame if None\n    if frame is None:\n        break\n\n    # {do something with frame here}\n\n    # Show output window\n    cv2.imshow(\"Client 5567 Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#client-2s-end_1","title":"Client-2's End","text":"

Finally, Open a terminal on another Client System (also, where you want to display the input frames received from Server), let's name it Client-2. Execute the following python code:

Replace the IP address in the following code with Server's IP address you noted earlier and also assign a unique port address (required by Server to identify this system).

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate Multi-Clients mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at Server's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5577\",\n    protocol=\"tcp\",\n    pattern=2,\n    receive_mode=True,\n    logging=True,\n    **options\n) \n\n# loop over\nwhile True:\n    # receive data from server\n    frame = client.recv()\n\n    # check for frame if None\n    if frame is None:\n        break\n\n    # {do something with frame here}\n\n    # Show output window\n    cv2.imshow(\"Client 5577 Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#using-multi-clients-mode-for-unidirectional-custom-data-transfer","title":"Using Multi-Clients Mode for Unidirectional Custom Data Transfer","text":"

Abstract

With Multi-Clients Mode, you can also send additional data of any data-type (such as list, tuple, string, int, ndarray etc.) along with frame, from all connected Clients(s) back to a Server unidirectionally.

In Multi-Clients Mode, unidirectional data transfer ONLY works with pattern 1 (i.e. Request/Reply zmq.REQ/zmq.REP), and NOT with pattern 2 (i.e. Publish/Subscribe zmq.PUB/zmq.SUB)!

In this example, We will be transferring video-frames from a single Server (consisting of Raspberry Pi with Camera Module) over the network to two independent Client for displaying them in real-time. At the same time, we will be sending data (a Text String, for the sake of simplicity) from both the Client(s) back to our Server, which will be printed onto the terminal.

"},{"location":"gears/netgear/advanced/multi_client/#servers-end_2","title":"Server's End","text":"

Now, Open the terminal on a Server System (with a webcam connected to it at index 0). Now execute the following python code:

Important Notes

Frame/Data transmission will NOT START untill all given Client(s) are connected to this Server.

You can terminate streaming anytime by pressing Ctrl+C on your keyboard!

Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this backend, otherwise nothing will work.

New Picamera2 backendLegacy Picamera backend
# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import NetGear\n\n# add various Picamera2 tweak parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate multiclient_mode mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple of all unique Server((5577,5578) in our case) and other parameters\nserver = NetGear(\n    address=\"192.168.x.x\",\n    port=(5577, 5578),\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)  # !!! change following IP address '192.168.x.xxx' with yours !!!\n\n# Define received data dictionary\ndata_dict = {}\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame and also receive data from Client(s)\n        recv_data = server.send(frame)\n\n        # check if valid data received\n        if not (recv_data is None):\n            # extract unique port address and its respective data\n            unique_address, data = recv_data\n            # update the extracted data in the data dictionary\n            data_dict[unique_address] = data\n\n        if data_dict:\n            # print data just received from Client(s)\n            for key, value in data_dict.items():\n                print(\"Client at port {} said: {}\".format(key, value))\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import NetGear\n\n# add various Picamera tweak parameters to dictionary\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate multiclient_mode mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple of all unique Server((5577,5578) in our case) and other parameters\nserver = NetGear(\n    address=\"192.168.x.x\",\n    port=(5577, 5578),\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)  # !!! change following IP address '192.168.x.xxx' with yours !!!\n\n# Define received data dictionary\ndata_dict = {}\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame and also receive data from Client(s)\n        recv_data = server.send(frame)\n\n        # check if valid data received\n        if not (recv_data is None):\n            # extract unique port address and its respective data\n            unique_address, data = recv_data\n            # update the extracted data in the data dictionary\n            data_dict[unique_address] = data\n\n        if data_dict:\n            # print data just received from Client(s)\n            for key, value in data_dict.items():\n                print(\"Client at port {} said: {}\".format(key, value))\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#client-1s-end_2","title":"Client-1's End","text":"

Now, Open a terminal on another Client System (where you want to display the input frames received from Server), let's name it Client-1. Execute the following python code:

Replace the IP address in the following code with Server's IP address you noted earlier and also assign a unique port address (required by Server to identify this system).

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate Multi-Clients mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at Server's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5577\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# loop over\nwhile True:\n\n    # prepare data to be sent\n    target_data = \"Hi, I am 5577 Client here.\"\n\n    # receive data from server and also send our data\n    frame = client.recv(return_data=target_data)\n\n    # check for frame if None\n    if frame is None:\n        break\n\n    # {do something with frame here}\n\n    # Show output window\n    cv2.imshow(\"Client 5577 Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#client-2s-end_2","title":"Client-2's End","text":"

Finally, Open a terminal on another Client System (also, where you want to display the input frames received from Server), let's name it Client-2. Execute the following python code:

Replace the IP address in the following code with Server's IP address you noted earlier and also assign a unique port address (required by Server to identify this system).

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate Multi-Clients mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at Server's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5578\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n) \n\n# loop over\nwhile True:\n\n    # prepare data to be sent\n    target_data = \"Hi, I am 5578 Client here.\"\n\n    # receive data from server and also send our data\n    frame = client.recv(return_data=target_data)\n\n    # check for frame if None\n    if frame is None:\n        break\n\n    # {do something with frame here}\n\n    # Show output window\n    cv2.imshow(\"Client 5578 Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#using-multi-clients-mode-with-bidirectional-mode","title":"Using Multi-Clients Mode with Bidirectional Mode","text":"

Abstract

Multi-Clients Mode now also compatible with Bidirectional Mode, which lets you send additional data of any datatype1 along with frame in real-time bidirectionally between a single Server and all connected Client(s).

Important Information

New in v0.2.5

This example was added in v0.2.5.

In this example, We will be transferring video-frames and data (a Text String, for the sake of simplicity) from a single Server (In this case, Raspberry Pi with Camera Module) over the network to two independent Clients for displaying them both in real-time. At the same time, we will be sending data (a Text String, for the sake of simplicity) back from both the Client(s) to our Server, which will be printed onto the terminal.

"},{"location":"gears/netgear/advanced/multi_client/#servers-end_3","title":"Server's End","text":"

Now, Open the terminal on a Server System (with a webcam connected to it at index 0). Now execute the following python code:

Important Notes

Frame/Data transmission will NOT START untill all given Client(s) are connected to this Server.

You can terminate streaming anytime by pressing Ctrl+C on your keyboard!

Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this backend, otherwise nothing will work.

New Picamera2 backendLegacy Picamera backend
# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import NetGear\nfrom libcamera import Transform\n\n# add various Picamera2 tweak parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate both multiclient and bidirectional modes\noptions = {\"multiclient_mode\": True, \"bidirectional_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple of \n# all unique Server((5577,5578) in our case) and other parameters\nserver = NetGear(\n    address=\"192.168.x.x\",\n    port=(5577, 5578),\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)  # !!! change following IP address '192.168.x.xxx' with yours !!!\n\n# Define received data dictionary\ndata_dict = {}\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # send frame & data and also receive data from Client(s)\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # check if valid data received\n        if not (recv_data is None):\n            # extract unique port address and its respective data\n            unique_address, data = recv_data\n            # update the extracted data in the data dictionary\n            data_dict[unique_address] = data\n\n        if data_dict:\n            # print data just received from Client(s)\n            for key, value in data_dict.items():\n                print(\"Client at port {} said: {}\".format(key, value))\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import NetGear\n\n# add various Picamera tweak parameters to dictionary\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate both multiclient and bidirectional modes\noptions = {\"multiclient_mode\": True, \"bidirectional_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple of \n# all unique Server((5577,5578) in our case) and other parameters\nserver = NetGear(\n    address=\"192.168.x.x\",\n    port=(5577, 5578),\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)  # !!! change following IP address '192.168.x.xxx' with yours !!!\n\n# Define received data dictionary\ndata_dict = {}\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # send frame & data and also receive data from Client(s)\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # check if valid data received\n        if not (recv_data is None):\n            # extract unique port address and its respective data\n            unique_address, data = recv_data\n            # update the extracted data in the data dictionary\n            data_dict[unique_address] = data\n\n        if data_dict:\n            # print data just received from Client(s)\n            for key, value in data_dict.items():\n                print(\"Client at port {} said: {}\".format(key, value))\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.

"},{"location":"gears/netgear/advanced/multi_client/#client-1s-end_3","title":"Client-1's End","text":"

Now, Open a terminal on another Client System (where you want to display the input frames received from Server), let's name it Client-1. Execute the following python code:

Replace the IP address in the following code with Server's IP address you noted earlier and also assign a unique port address (required by Server to identify this system).

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate both multiclient and bidirectional modes\noptions = {\"multiclient_mode\": True, \"bidirectional_mode\": True}\n\n# Define NetGear Client at Server's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5577\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# loop over\nwhile True:\n\n    # prepare data to be sent\n    target_data = \"Hi, I am 5577 Client here.\"\n\n    # receive data from server and also send our data\n    data = client.recv(return_data=target_data)\n\n    # check for data if None\n    if data is None:\n        break\n\n    # extract server_data & frame from data\n    server_data, frame = data\n\n    # again check for frame if None\n    if frame is None:\n        break\n\n    # {do something with the extracted frame and data here}\n\n    # lets print extracted server data\n    if not (server_data is None):\n        print(server_data)\n\n    # Show output window\n    cv2.imshow(\"Client 5577 Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#client-2s-end_3","title":"Client-2's End","text":"

Finally, Open a terminal on another Client System (also, where you want to display the input frames received from Server), let's name it Client-2. Execute the following python code:

Replace the IP address in the following code with Server's IP address you noted earlier and also assign a unique port address (required by Server to identify this system).

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate both multiclient and bidirectional modes\noptions = {\"multiclient_mode\": True, \"bidirectional_mode\": True}\n\n# Define NetGear Client at Server's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5578\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n) \n\n# loop over\nwhile True:\n\n    # prepare data to be sent\n    target_data = \"Hi, I am 5578 Client here.\"\n\n    # receive data from server and also send our data\n    data = client.recv(return_data=target_data)\n\n    # check for data if None\n    if data is None:\n        break\n\n    # extract server_data & frame from data\n    server_data, frame = data\n\n    # again check for frame if None\n    if frame is None:\n        break\n\n    # {do something with the extracted frame and data here}\n\n    # lets print extracted server data\n    if not (server_data is None):\n        print(server_data)\n\n    # Show output window\n    cv2.imshow(\"Client 5578 Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

  1. Additional data of numpy.ndarray data-type is NOT SUPPORTED at Server's end with its message parameter.

    \u21a9

"},{"location":"gears/netgear/advanced/multi_server/","title":"Multi-Servers Mode","text":""},{"location":"gears/netgear/advanced/multi_server/#multi-servers-mode-for-netgear-api","title":"Multi-Servers Mode for NetGear API","text":"NetGear's Multi-Servers Mode"},{"location":"gears/netgear/advanced/multi_server/#overview","title":"Overview","text":"

In Multi-Servers Mode, NetGear API robustly handles Multiple Servers at once, thereby providing seamless access to frames and unidirectional data transfer across multiple Publishers/Servers in the network at the same time. Each new server connects to a single client can be identified by its unique port address on the network.

The supported patterns for this mode are Publish/Subscribe (zmq.PUB/zmq.SUB) and Request/Reply(zmq.REQ/zmq.REP) and can be easily activated in NetGear API through multiserver_mode attribute of its options dictionary parameter during initialization.

Important Information regarding Multi-Servers Mode

"},{"location":"gears/netgear/advanced/multi_server/#key-features","title":"Key Features","text":"

"},{"location":"gears/netgear/advanced/multi_server/#usage-examples","title":"Usage Examples","text":"

Example Assumptions

"},{"location":"gears/netgear/advanced/multi_server/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

In this example, we will capturing live video-frames on two independent sources (a.k.a Servers), each with a webcam connected to it. Afterwards, these frames will be sent over the network to a single system (a.k.a Client) using this Multi-Servers Mode in NetGear API in real time, and will be displayed as a live montage.

This example is useful for building applications like Real-Time Security System with multiple cameras.

"},{"location":"gears/netgear/advanced/multi_server/#clients-end","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from Multiple Servers) and execute the following python code:

Important Notes

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom imutils import build_montages # (1)\nimport cv2\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple \n# of all unique Server((5566,5567) in our case) and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=(5566, 5567),\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    **options\n)\n\n# Define received frame dictionary\nframe_dict = {}\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # receive data from network\n        data = client.recv()\n\n        # check if data received isn't None\n        if data is None:\n            break\n\n        # extract unique port address and its respective frame\n        unique_address, frame = data\n\n        # {do something with the extracted frame here}\n\n        # get extracted frame's shape\n        (h, w) = frame.shape[:2]\n\n        # update the extracted frame in the received frame dictionary\n        frame_dict[unique_address] = frame\n\n        # build a montage using data dictionary\n        montages = build_montages(frame_dict.values(), (w, h), (2, 1))\n\n        # display the montage(s) on the screen\n        for (i, montage) in enumerate(montages):\n\n            cv2.imshow(\"Montage Footage {}\".format(i), montage)\n\n        # check for 'q' key if pressed\n        key = cv2.waitKey(1) & 0xFF\n        if key == ord(\"q\"):\n            break\n\n    except KeyboardInterrupt:\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n
  1. For building Frames Montage you'll need imutils python library. Install it with pip install imutils command.

"},{"location":"gears/netgear/advanced/multi_server/#server-1s-end","title":"Server-1's End","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and let's called it Server-1. Now execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier and also assign a unique port address (required by Client to identify this system).

You can terminate stream anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import CamGear\n\n# Open suitable video stream (webcam on first index in our case)\nstream = CamGear(source=0).start()\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.x.x\", port=\"5566\", protocol=\"tcp\", pattern=1, **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if not None-type\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/multi_server/#server-2s-end","title":"Server-2's End","text":"

Finally, Open the terminal on another Server System (also with a webcam connected to it at index 0), and let's called it Server-2. Now execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier and also assign a unique port address (required by Client to identify this system).

You can terminate stream anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import CamGear\n\n# Open suitable video stream (webcam on first index in our case)\nstream = CamGear(source=0).start()\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.x.x\", port=\"5567\", protocol=\"tcp\", pattern=1, **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if not None-type\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/multi_server/#bare-minimum-usage-with-opencv","title":"Bare-Minimum Usage with OpenCV","text":"

In this example, we will be re-implementing previous bare-minimum example with OpenCV and NetGear API.

"},{"location":"gears/netgear/advanced/multi_server/#clients-end_1","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from Mutiple Servers) and execute the following python code:

Important Notes

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom imutils import build_montages # (1)\nimport cv2\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple of all \n# unique Server((5566,5567) in our case) and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=(5566, 5567),\n    protocol=\"tcp\",\n    pattern=2,\n    receive_mode=True,\n    **options\n)\n\n# Define received frame dictionary\nframe_dict = {}\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # receive data from network\n        data = client.recv()\n\n        # check if data received isn't None\n        if data is None:\n            break\n\n        # extract unique port address and its respective frame\n        unique_address, frame = data\n\n        # {do something with the extracted frame here}\n\n        # get extracted frame's shape\n        (h, w) = frame.shape[:2]\n\n        # update the extracted frame in the received frame dictionary\n        frame_dict[unique_address] = frame\n\n        # build a montage using data dictionary\n        montages = build_montages(frame_dict.values(), (w, h), (2, 1))\n\n        # display the montage(s) on the screen\n        for (i, montage) in enumerate(montages):\n\n            cv2.imshow(\"Montage Footage {}\".format(i), montage)\n\n        # check for 'q' key if pressed\n        key = cv2.waitKey(1) & 0xFF\n        if key == ord(\"q\"):\n            break\n\n    except KeyboardInterrupt:\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n
  1. For building Frames Montage you'll need imutils python library. Install it with pip install imutils command.

"},{"location":"gears/netgear/advanced/multi_server/#server-1s-end_1","title":"Server-1's End","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and let's called it Server-1. Now execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier and also assign a unique port address (required by Client to identify this system).

You can terminate stream anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# Open suitable video stream (webcam on first index in our case)\nstream = cv2.VideoCapture(0)\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameter\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.x.x\", port=\"5566\", protocol=\"tcp\", pattern=2, **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        (grabbed, frame) = stream.read()\n\n        # check for frame if not grabbed\n        if not grabbed:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.release()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/multi_server/#server-2s-end_1","title":"Server-2's End","text":"

Finally, Open the terminal on another Server System (also with a webcam connected to it at index 0), and let's called it Server-2. Now execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier and also assign a unique port address (required by Client to identify this system).

You can terminate stream anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# Open suitable video stream (webcam on first index in our case)\nstream = cv2.VideoCapture(0)\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.x.x\", port=\"5567\", protocol=\"tcp\", pattern=2, **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        (grabbed, frame) = stream.read()\n\n        # check for frame if not grabbed\n        if not grabbed:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.release()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/multi_server/#using-multi-servers-mode-for-unidirectional-custom-data-transfer","title":"Using Multi-Servers Mode for Unidirectional Custom Data Transfer","text":"

Abstract

With Multi-Servers Mode, you can send additional data of any datatype1 along with frame with frame in real-time, from all connected Server(s) to a single Client unidirectionally.

But numpy.ndarray data-type is NOT supported as data.

In this example, We will be transferring video-frames and data (a Text String, for the sake of simplicity) from two Servers (consisting of a Raspberry Pi with Camera Module & a Laptop with webcam) to a single Client over the network in real-time. The received video-frames at Client's end will displayed as a live montage, whereas the received data will be printed to the terminal.

"},{"location":"gears/netgear/advanced/multi_server/#clients-end_2","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from Mutiple Servers) and execute the following python code:

Important Notes

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom imutils import build_montages # (1)\nimport cv2\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple of all unique Server((5577,5578) in our case) and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=(5577, 5578),\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)  \n# Define received frame dictionary\nframe_dict = {}\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # receive data from network\n        data = client.recv()\n\n        # check if data received isn't None\n        if data is None:\n            break\n\n        # extract unique port address and its respective frame and received data\n        unique_address, extracted_data, frame = data\n\n        # {do something with the extracted frame and data here}\n        # let's display extracted data on our extracted frame\n        cv2.putText(\n            frame,\n            extracted_data,\n            (10, frame.shape[0] - 10),\n            cv2.FONT_HERSHEY_SIMPLEX,\n            0.6,\n            (0, 255, 0),\n            2,\n        )\n\n        # get extracted frame's shape\n        (h, w) = frame.shape[:2]\n\n        # update the extracted frame in the frame dictionary\n        frame_dict[unique_address] = frame\n\n        # build a montage using data dictionary\n        montages = build_montages(frame_dict.values(), (w, h), (2, 1))\n\n        # display the montage(s) on the screen\n        for (i, montage) in enumerate(montages):\n\n            cv2.imshow(\"Montage Footage {}\".format(i), montage)\n\n        # check for 'q' key if pressed\n        key = cv2.waitKey(1) & 0xFF\n        if key == ord(\"q\"):\n            break\n\n    except KeyboardInterrupt:\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n
  1. For building Frames Montage you'll need imutils python library. Install it with pip install imutils command.

"},{"location":"gears/netgear/advanced/multi_server/#server-1s-end_2","title":"Server-1's End","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and let's called it Server-1. Now execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier and also assign a unique port address (required by Client to identify this system).

You can terminate stream anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import VideoGear\nimport cv2\n\n# Open suitable video stream (webcam on first index in our case)\nstream = VideoGear(source=0).start()\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5577\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with frame and data(to be sent) here}\n\n        # let's prepare a text string as data\n        target_data = \"I'm Server-1 at Port: 5577\"\n\n        # send frame and data through server\n        server.send(frame, message=target_data) # (1)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.

"},{"location":"gears/netgear/advanced/multi_server/#server-2s-end_2","title":"Server-2's End","text":"

Finally, Open the terminal on another Server System (this time a Raspberry Pi with Camera Module connected to it), and let's called it Server-2. Now execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier and also assign a unique port address (required by Client to identify this system).

You can terminate stream anytime by pressing Ctrl+C on your keyboard!

Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this backend, otherwise nothing will work.

New Picamera2 backendLegacy Picamera backend
# import libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import PiGear\nfrom libcamera import Transform\nimport cv2\n\n# add various Picamera tweak parameters to dictionary\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.1.xxx\",\n    port=\"5578\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with frame and data(to be sent) here}\n\n        # let's prepare a text string as data\n        text = \"I'm Server-2 at Port: 5578\"\n\n        # send frame and data through server\n        server.send(frame, message=text)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream.\nstream.stop()\n\n# safely close server\nserver.close()\n
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import PiGear\nimport cv2\n\n# add various Picamera tweak parameters to dictionary\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.1.xxx\",\n    port=\"5578\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with frame and data(to be sent) here}\n\n        # let's prepare a text string as data\n        text = \"I'm Server-2 at Port: 5578\"\n\n        # send frame and data through server\n        server.send(frame, message=text)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream.\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/multi_server/#using-multi-servers-mode-with-bidirectional-mode","title":"Using Multi-Servers Mode with Bidirectional Mode","text":"

Abstract

Multi-Servers Mode now also compatible with Bidirectional Mode, which lets you send additional data of any datatype1 along with frame in real-time bidirectionally between a single Client and all connected Server(s).

Important Information

New in v0.2.5

This example was added in v0.2.5.

In this example, We will be transferring video-frames and data (a Text String, for the sake of simplicity) from two Servers (consisting of a Raspberry Pi with Camera Module & a Laptop with webcam) to a single Client, and at same time sending back data (a Text String, for the sake of simplicity) to them over the network all in real-time. The received video-frames at Client's end will displayed as a live montage, whereas the received data will be printed to the terminal.

"},{"location":"gears/netgear/advanced/multi_server/#clients-end_3","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from Mutiple Servers) and execute the following python code:

Important Notes

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom imutils import build_montages # (1)\nimport cv2\n\n# activate both multiserver and bidirectional modes\noptions = {\"multiserver_mode\": True, \"bidirectional_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple of all unique Server((5577,5578) in our case) and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=(5577, 5578),\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)  \n# Define received frame dictionary\nframe_dict = {}\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # prepare data to be sent\n        target_data = \"Hi, I am a Client here.\"\n\n        # receive data from server(s) and also send our data\n        data = client.recv(return_data=target_data)\n\n        # check if data received isn't None\n        if data is None:\n            break\n\n        # extract unique port address and its respective frame and received data\n        unique_address, extracted_data, frame = recv_data\n\n        # {do something with the extracted frame and data here}\n        # let's display extracted data on our extracted frame\n        cv2.putText(\n            frame,\n            extracted_data,\n            (10, frame.shape[0] - 10),\n            cv2.FONT_HERSHEY_SIMPLEX,\n            0.6,\n            (0, 255, 0),\n            2,\n        )\n\n        # get extracted frame's shape\n        (h, w) = frame.shape[:2]\n\n        # update the extracted frame in the frame dictionary\n        frame_dict[unique_address] = frame\n\n        # build a montage using data dictionary\n        montages = build_montages(frame_dict.values(), (w, h), (2, 1))\n\n        # display the montage(s) on the screen\n        for (i, montage) in enumerate(montages):\n\n            cv2.imshow(\"Montage Footage {}\".format(i), montage)\n\n        # check for 'q' key if pressed\n        key = cv2.waitKey(1) & 0xFF\n        if key == ord(\"q\"):\n            break\n\n    except KeyboardInterrupt:\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n
  1. For building Frames Montage you'll need imutils python library. Install it with pip install imutils command.

"},{"location":"gears/netgear/advanced/multi_server/#server-1s-end_3","title":"Server-1's End","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and let's called it Server-1. Now execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier and also assign a unique port address (required by Client to identify this system).

You can terminate stream anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import VideoGear\nimport cv2\n\n# Open suitable video stream (webcam on first index in our case)\nstream = VideoGear(source=0).start()\n\n# activate both multiserver and bidirectional modes\noptions = {\"multiserver_mode\": True, \"bidirectional_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5577\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with frame and data(to be sent) here}\n\n        # let's prepare a text string as data\n        target_data = \"I'm Server-1 at Port: 5577\"\n\n        # send frame & data and also receive data from Client\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # print data just received from Client\n        if not (recv_data is None):\n            print(recv_data)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.

"},{"location":"gears/netgear/advanced/multi_server/#server-2s-end_3","title":"Server-2's End","text":"

Finally, Open the terminal on another Server System (this time a Raspberry Pi with Camera Module connected to it), and let's called it Server-2. Now execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier and also assign a unique port address (required by Client to identify this system).

You can terminate stream anytime by pressing Ctrl+C on your keyboard!

Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this backend, otherwise nothing will work.

New Picamera2 backendLegacy Picamera backend
# import libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import PiGear\nfrom libcamera import Transform\nimport cv2\n\n# add various Picamera2 tweak parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate both multiserver and bidirectional modes\noptions = {\"multiserver_mode\": True, \"bidirectional_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.1.xxx\",\n    port=\"5578\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with frame and data(to be sent) here}\n\n        # let's prepare a text string as data\n        target_data = \"I'm Server-2 at Port: 5578\"\n\n        # send frame & data and also receive data from Client\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # print data just received from Client\n        if not (recv_data is None):\n            print(recv_data)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream.\nstream.stop()\n\n# safely close server\nserver.close()\n
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import PiGear\nimport cv2\n\n# add various Picamera tweak parameters to dictionary\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate both multiserver and bidirectional modes\noptions = {\"multiserver_mode\": True, \"bidirectional_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.1.xxx\",\n    port=\"5578\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with frame and data(to be sent) here}\n\n        # let's prepare a text string as data\n        target_data = \"I'm Server-2 at Port: 5578\"\n\n        # send frame & data and also receive data from Client\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # print data just received from Client\n        if not (recv_data is None):\n            print(recv_data)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream.\nstream.stop()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.

  1. Additional data of numpy.ndarray data-type is NOT SUPPORTED at Server(s) with their message parameter.

    \u21a9\u21a9\u21a9

"},{"location":"gears/netgear/advanced/secure_mode/","title":"Secure Mode","text":""},{"location":"gears/netgear/advanced/secure_mode/#secure-mode-for-netgear-api","title":"Secure Mode for NetGear API","text":""},{"location":"gears/netgear/advanced/secure_mode/#overview","title":"Overview","text":"

Secure Mode provides easy access to powerful, smart & secure ZeroMQ's Security Layers in NetGear API that enables strong encryption on data, and unbreakable authentication between the Server and the Client with the help of custom Certificates/keys and brings cheap, standardized privacy and authentication for distributed systems over the network.

Secure Mode uses a new wire protocol, ZMTP 3.0 that adds a security handshake to all ZeroMQ connections and a new security protocol, CurveZMQ, that implements \"perfect forward security\" between two ZeroMQ peers over a TCP connection.

Secure Mode can be easily activated in NetGear API through secure_mode attribute of its options dictionary parameter, during initialization. Furthermore, for managing this mode, NetGear API provides additional custom_cert_location & overwrite_cert like attribute too.

"},{"location":"gears/netgear/advanced/secure_mode/#supported-zmq-security-layers","title":"Supported ZMQ Security Layers","text":"

Secure mode supports the two most powerful ZMQ security layers:

Important Information regarding Secure Mode

"},{"location":"gears/netgear/advanced/secure_mode/#features","title":"Features","text":"

"},{"location":"gears/netgear/advanced/secure_mode/#exclusive-attributes","title":"Exclusive Attributes","text":"

For implementing Secure Mode, NetGear API currently provide following exclusive attribute for its options dictionary parameter:

"},{"location":"gears/netgear/advanced/secure_mode/#usage-examples","title":"Usage Examples","text":""},{"location":"gears/netgear/advanced/secure_mode/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with Secure Mode in NetGear API:

In Secure Mode, Client's end MUST run before the Server's end to establish a secure connection!

"},{"location":"gears/netgear/advanced/secure_mode/#clients-end","title":"Client's End","text":"

Open your favorite terminal and execute the following python code:

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate StoneHouse security mechanism\noptions = {\"secure_mode\": 1}\n\n# define NetGear Client with `receive_mode = True` and defined parameter\nclient = NetGear(pattern=1, receive_mode=True, logging=True, **options)\n\n# loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n
"},{"location":"gears/netgear/advanced/secure_mode/#servers-end","title":"Server's End","text":"

Then open another terminal on the same system and execute the following python code to send the frames to our client:

You can terminate both sides anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\n\n# open any valid video stream(for e.g `test.mp4` file)\nstream = VideoGear(source=\"test.mp4\").start()\n\n# activate StoneHouse security mechanism\noptions = {\"secure_mode\": 1}\n\n# Define NetGear Server with defined parameters\nserver = NetGear(pattern=1, logging=True, **options)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/secure_mode/#using-secure-mode-with-variable-parameters","title":"Using Secure Mode with Variable Parameters","text":""},{"location":"gears/netgear/advanced/secure_mode/#clients-end_1","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from the Server) and execute the following python code:

In Secure Mode, Client's end MUST run before the Server's end to establish a secure connection!

Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

You need to paste the Public+Secret Keypairs (generated at the Server End) at the $HOME/.vidgear/keys directory of your Client machine for a successful authentication!

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate IronHouse security mechanism\noptions = {\"secure_mode\": 2}\n\n# Define NetGear Client at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=2,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/secure_mode/#servers-end_1","title":"Server's End","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

You also need to copy the Public+Secret Keypairs (generated on running this example code) present in the $HOME/.vidgear/keys directory, and make available at Client's end for a successful authentication.

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\n\n# activate IronHouse security mechanism, and \n# [BEWARE!!!] generating new Keypairs for this example !!!\noptions = {\"secure_mode\": 2, \"overwrite_cert\": True}\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = VideoGear(source=0).start()\n\n# Define NetGear server at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=2,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/ssh_tunnel/","title":"SSH Tunneling Mode","text":""},{"location":"gears/netgear/advanced/ssh_tunnel/#ssh-tunneling-mode-for-netgear-api","title":"SSH Tunneling Mode for NetGear API","text":"NetGear's Bidirectional Mode"},{"location":"gears/netgear/advanced/ssh_tunnel/#overview","title":"Overview","text":"New in v0.2.2

This document was added in v0.2.2.

SSH Tunneling Mode allows you to connect NetGear client and server via secure SSH connection over the untrusted network and access its intranet services across firewalls. This mode works with pyzmq's zmq.ssh module for tunneling ZeroMQ connections over ssh.

This mode implements SSH Remote Port Forwarding which enables accessing Host(client) machine outside the network by exposing port to the public Internet. Thereby, once you have established the tunnel, connections to local machine will actually be connections to remote machine as seen from the server.

Beware \u2620\ufe0f

Cybercriminals or malware could exploit SSH tunnels to hide their unauthorized communications, or to exfiltrate stolen data from the network. More information can be found here \u27b6

All patterns are valid for this mode and it can be easily activated in NetGear API at server end through ssh_tunnel_mode string attribute of its options dictionary parameter during initialization.

Important

Useful Tips

"},{"location":"gears/netgear/advanced/ssh_tunnel/#prerequisites","title":"Prerequisites","text":"

SSH Tunnel Mode requires pexpect or paramiko as an additional dependency which is not part of standard VidGear package. It can be easily installed via pypi as follows:

PramikoPexpect

paramiko is compatible with all platforms.

paramiko support is automatically enabled in ZeroMQ if installed.

# install paramiko\npip install paramiko\n

pexpect is NOT compatible with Windows Machines.

# install pexpect\npip install pexpect\n

"},{"location":"gears/netgear/advanced/ssh_tunnel/#exclusive-attributes","title":"Exclusive Attributes","text":"

All these attributes will work on Server end only whereas Client end will simply discard them.

For implementing SSH Tunneling Mode, NetGear API currently provide following exclusive attribute for its options dictionary parameter:

"},{"location":"gears/netgear/advanced/ssh_tunnel/#usage-example","title":"Usage Example","text":"Assumptions for this Example

In this particular example, we assume that:

"},{"location":"gears/netgear/advanced/ssh_tunnel/#clients-end","title":"Client's End","text":"

Open a terminal on Client System (A Regular PC where you want to display the input frames received from the Server) and execute the following python code:

Requirements for Client's End

To ensure a successful Remote NetGear Connection with Server:

Finding Public IP Address

Only IPv4 IP-addresses are supported

Enabling Dynamic DNS

SSH tunneling requires public IP address to able to access host on public Internet. Thereby, if it's troublesome to remember Public IP address or your IP address change constantly, then you can use dynamic DNS services like https://www.noip.com/

How to TCP Port Forward in your Router

For more information on Forwarding Port in Popular Home Routers. See this document \u27b6

Secsh channel X open FAILED: open failed: Administratively prohibited

Error: This error means that installed OpenSSH is preventing connections to forwarded ports from outside your Client Machine.

Solution: You need to change GatewayPorts no option to GatewayPorts yes in the OpenSSH server configuration file sshd_config to allows anyone to connect to the forwarded ports on Client Machine.

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# Define NetGear Client at given IP address and define parameters \nclient = NetGear(\n    address=\"127.0.0.1\", # don't change this\n    port=\"5454\",\n    pattern=2,\n    receive_mode=True,\n    logging=True,\n)\n\n# loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/ssh_tunnel/#servers-end","title":"Server's End","text":"

Now, Open the terminal on Remote Server System (A Raspberry Pi with a webcam connected to it at index 0), and execute the following python code:

Make sure to replace the Client's Public IP Address and Forwarded TCP port(default is 22) in SSH URL with yours in the following example.

On Server end, NetGear automatically validates if the port is open at specified Client's Public IP Address or not, and if it fails (i.e. port is closed), NetGear will throw AssertionError!

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\n\n# activate SSH tunneling with SSH URL, and\n# [BEWARE!!!] Change SSH URL and SSH password with yours for this example !!!\noptions = {\n    \"ssh_tunnel_mode\": \"test@52.155.1.89\", # defaults to port 22\n    \"ssh_tunnel_pwd\": \"pas$wd\",\n}\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = VideoGear(source=0).start()\n\n# Define NetGear server at given IP address and define parameters\nserver = NetGear(\n    address=\"127.0.0.1\", # don't change this\n    port=\"5454\",\n    pattern=2, \n    logging=True, \n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear_async/overview/","title":"Overview","text":""},{"location":"gears/netgear_async/overview/#netgear_async-api","title":"NetGear_Async API","text":""},{"location":"gears/netgear_async/overview/#overview","title":"Overview","text":"

NetGear_Async can generate the same performance as NetGear API at about one-third the memory consumption, and also provide complete server-client handling with various options to use variable protocols/patterns similar to NetGear, but lacks in term of flexibility as it supports only a few NetGear's Exclusive Modes.

NetGear_Async is built on zmq.asyncio, and powered by a high-performance asyncio event loop called uvloop to achieve unmatchable high-speed and lag-free video streaming over the network with minimal resource constraints. NetGear_Async can transfer thousands of frames in just a few seconds without causing any significant load on your system.

NetGear_Async provides complete server-client handling and options to use variable protocols/patterns similar to NetGear API. Furthermore, NetGear_Async allows us to define our custom Server as source to transform frames easily before sending them across the network(see this doc example).

NetGear_Async now supports additional bidirectional data transmission between receiver(client) and sender(server) while transferring frames. Users can easily build complex applications such as like Real-Time Video Chat in just few lines of code.

In addition to all this, NetGear_Async API also provides internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs, thereby granting it exclusive power for transferring frames incoming from any source to the network.

NetGear_Async as of now supports four ZeroMQ messaging patterns:

Whereas supported protocol are: tcp and ipc.

Helpful Tips

"},{"location":"gears/netgear_async/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through NetGear_Async Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/netgear_async/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/netgear_async/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/netgear_async/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/netgear_async/params/","title":"Parameters","text":""},{"location":"gears/netgear_async/params/#netgear_async-api-parameters","title":"NetGear_Async API Parameters","text":"

NetGear_Async provides a special internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs and their parameters.

"},{"location":"gears/netgear_async/params/#enablepicamera","title":"enablePiCamera","text":"

This parameter provide access to PiGear or CamGear APIs respectively. This means the if enablePiCamera flag is True, the PiGear API will be accessed, and if False, the CamGear API will be accessed.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

NetGear_Async(enablePiCamera=True) # enable access to PiGear API\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/netgear_async/params/#address","title":"address","text":"

This parameter sets the valid network address of the Server/Client. Network addresses unique identifiers across the network.

Data-Type: String

Default Value: Its default value is based on selected primary mode, i.e 'localhost' for Send Mode and '*' for Receive Mode.

Usage:

NetGear_Async(address=\"192.168.0.145\")\n

"},{"location":"gears/netgear_async/params/#port","title":"port","text":"

This parameter sets the valid Network Port of the Server/Client. A network port is a number that identifies one side of a connection between two devices on the network and is used determine to which process or application a message should be delivered.

Data-Type: String

Default Value: Its default value is '5555'

Usage:

NetGear_Async(port=\"5575\")\n

"},{"location":"gears/netgear_async/params/#protocol","title":"protocol","text":"

This parameter sets the valid messaging protocol between Server/Client. A network protocol is a set of established rules that dictates how to format, transmit and receive data so computer network devices - from servers and routers to endpoints - can communicate regardless of the differences in their underlying infrastructures, designs or standards. Supported protocol are: 'tcp' and 'ipc'.

Data-Type: String

Default Value: Its default value is 'tcp'

Usage:

NetGear_Async(protocol=\"ipc\")\n

"},{"location":"gears/netgear_async/params/#pattern","title":"pattern","text":"

This parameter sets the supported messaging pattern(flow of communication) between Server/Client. Messaging patterns are the network-oriented architectural pattern that describes the flow of communication between interconnecting systems. NetGear provides access to ZeroMQ's pre-optimized sockets which enables you to take advantage of these patterns.

Data-Type: Integer

Default Value: Its default value is 0 (i.e zmq.PAIR).

All supported ZMQ patterns for NetGear_Async are:

Usage:

NetGear_Async(pattern=1) # sets zmq.REQ/zmq.REP pattern\n

"},{"location":"gears/netgear_async/params/#receive_mode","title":"receive_mode","text":"

This parameter select the Netgear's Mode of operation. It basically activates Receive Mode(if True) and Send Mode(if False). Furthermore, recv() method will only work when this flag is enabled(i.e. Receive Mode), whereas send() method will only work when this flag is disabled(i.e.Send Mode).

Data-Type: Boolean

Default Value: Its default value is False(i.e. Send Mode is activated by default).

Usage:

NetGear_Async(receive_mode=True) # activates Recieve Mode\n

"},{"location":"gears/netgear_async/params/#timeout","title":"timeout","text":"

In NetGear_Async, the Receiver-end keeps tracks if frames are received from Server-end within this specified timeout value (in seconds), Otherwise TimeoutError will be raised, which helps to close the Receiver-end safely if the Server has lost connection prematurely. This parameter controls that timeout value (i.e. the maximum waiting time (in seconds)) after which Client exit itself with a TimeoutError to save resources. Its minimum value is 0.0 but no max limit.

Data-Type: Float/Integer

Default Value: Its default value is 10.0.

Usage:

NetGear_Async(timeout=5.0) # sets 5secs timeout\n
"},{"location":"gears/netgear_async/params/#options","title":"options","text":"

This parameter provides the flexibility to alter various NetGear_Async API's internal properties and modes.

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

Supported dictionary attributes for NetGear_Async API

"},{"location":"gears/netgear_async/params/#parameters-for-stabilizer-backend","title":"Parameters for Stabilizer Backend","text":"

Enable this backend with stabilize=True in NetGear_Async.

"},{"location":"gears/netgear_async/params/#stabilize","title":"stabilize","text":"

This parameter enable access to Stabilizer Class for stabilizing frames, i.e. can be set to True(to enable) or unset to False(to disable).

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

NetGear_Async(stabilize=True) # enable stablization\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/netgear_async/params/#options_1","title":"options","text":"

This parameter can be used in addition, to pass user-defined parameters supported by Stabilizer Class. These parameters can be formatted as this parameter's attribute.

Supported dictionary attributes for Stabilizer Class are:

"},{"location":"gears/netgear_async/params/#parameters-for-camgear-backend","title":"Parameters for CamGear backend","text":"

Enable this backend with enablePiCamera=False in NetGear_Async. Default is also False.

"},{"location":"gears/netgear_async/params/#source","title":"source","text":"

NetGear_Async API will throw RuntimeError if source provided is invalid.

This parameter defines the source for the input stream.

Data-Type: Based on input.

Default Value: Its default value is 0.

Its valid input can be one of the following:

"},{"location":"gears/netgear_async/params/#stream_mode","title":"stream_mode","text":"

This parameter controls the Stream Mode, .i.e if enabled(stream_mode=True), the CamGear API will interpret the given source input as YouTube URL address.

Due to a FFmpeg bug that causes video to freeze frequently in OpenCV, It is advised to always use GStreamer backend for any livestream videos. Checkout this FAQ for compiling OpenCV with GStreamer support.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

Supported Streaming Websites

The complete list of all supported Streaming Websites URLs can be found here \u27b6

NetGear_Async(source='https://youtu.be/bvetuLwJIkA', stream_mode=True)\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/netgear_async/params/#backend","title":"backend","text":"

This parameter manually selects the backend for OpenCV's VideoCapture class (only if specified).

Data-Type: Integer

Default Value: Its default value is 0

Usage:

All supported backends are listed here \u27b6

Its value can be for e.g. backend = cv2.CAP_DSHOW for selecting Direct Show as backend:

NetGear_Async(source=0, backend = cv2.CAP_DSHOW)\n

"},{"location":"gears/netgear_async/params/#options_2","title":"options","text":"

This parameter provides the ability to alter various Source Tweak Parameters available within OpenCV's VideoCapture API properties.

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

All supported parameters are listed here \u27b6

The desired parameters can be passed to NetGear_Async API by formatting them as this parameter's attributes, as follows:

# formatting parameters as dictionary attributes\noptions = {\"CAP_PROP_FRAME_WIDTH\":320, \"CAP_PROP_FRAME_HEIGHT\":240, \"CAP_PROP_FPS\":60}\n# assigning it\nNetGear_Async(source=0, **options)\n

"},{"location":"gears/netgear_async/params/#parameters-for-pigear-backend","title":"Parameters for PiGear backend","text":"

Enable this backend with enablePiCamera=True in NetGear_Async.

"},{"location":"gears/netgear_async/params/#camera_num","title":"camera_num","text":"

This parameter selects the camera index to be used as the source, allowing you to drive these multiple cameras simultaneously from within a single Python session. Its value can only be zero or greater, otherwise, NetGear_Async API will throw ValueError for any negative value.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

# select Camera Module at index `1`\nNetGear_Async(enablePiCamera=True, camera_num=1)\n

The complete usage example demonstrating the usage of the camera_num parameter is available here \u27b6.

"},{"location":"gears/netgear_async/params/#resolution","title":"resolution","text":"

This parameter controls the resolution - a tuple (i.e. (width,height)) of two values giving the width and height of the output frames.

Make sure both width and height values should be at least 64.

When using the Picamera2 backend, the resolution parameter will be OVERRIDDEN, if the user explicitly defines the output_size property of the sensor configurational parameter.

Data-Type: Tuple

Default Value: Its default value is (640,480).

Usage:

NetGear_Async(enablePiCamera=True, resolution=(1280,720)) # sets 1280x720 resolution\n

"},{"location":"gears/netgear_async/params/#framerate","title":"framerate","text":"

This parameter controls the framerate of the source.

Data-Type: integer/float

Default Value: Its default value is 30.

Usage:

NetGear_Async(enablePiCamera=True, framerate=60) # sets 60fps framerate\n

"},{"location":"gears/netgear_async/params/#options_3","title":"options","text":"

This dictionary parameter in the internal PiGear API backend allows you to control various camera settings for both the picamera2 and legacy picamera backends and some internal API tasks. These settings include:

"},{"location":"gears/netgear_async/params/#a-configurational-camera-parameters","title":"A. Configurational Camera Parameters","text":""},{"location":"gears/netgear_async/params/#b-user-defined-parameters","title":"B. User-defined Parameters","text":""},{"location":"gears/netgear_async/params/#common-parameters","title":"Common Parameters","text":"

These are common parameters that works with every backend in NetGear_Async.

"},{"location":"gears/netgear_async/params/#colorspace","title":"colorspace","text":"

This parameter selects the colorspace of the source stream.

Data-Type: String

Default Value: Its default value is None.

Usage:

All supported colorspace values are given here \u27b6

NetGear_Async(colorspace=\"COLOR_BGR2HSV\")\n

Its complete usage example is given here \u27b6

"},{"location":"gears/netgear_async/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

NetGear_Async(logging=True)\n

"},{"location":"gears/netgear_async/params/#time_delay","title":"time_delay","text":"

This parameter set the time delay (in seconds) before the NetGear_Async API start reading the frames. This delay is only required if the source required some warm-up delay before starting up.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

NetGear_Async(time_delay=1)  # set 1 seconds time delay\n

"},{"location":"gears/netgear_async/usage/","title":"Usage Examples","text":""},{"location":"gears/netgear_async/usage/#netgear_async-api-usage-examples","title":"NetGear_Async API Usage Examples:","text":"

Helpful Tips

After going through following Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/netgear_async/usage/#requirement","title":"Requirement","text":"

NetGear_Async API is the part of asyncio package of VidGear, thereby you need to install VidGear with asyncio support as follows:

pip install vidgear[asyncio]\n

"},{"location":"gears/netgear_async/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with NetGear_Async API:

"},{"location":"gears/netgear_async/usage/#servers-end","title":"Server's End","text":"

Open your favorite terminal and execute the following python code:

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nimport asyncio\n\n# initialize Server with suitable source\nserver = NetGear_Async(source=\"/home/foo/foo1.mp4\").launch()\n\nif __name__ == \"__main__\":\n    # set event loop\n    asyncio.set_event_loop(server.loop)\n    try:\n        # run your main function task until it is complete\n        server.loop.run_until_complete(server.task)\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    finally:\n        # finally close the server\n        server.close()\n
"},{"location":"gears/netgear_async/usage/#clients-end","title":"Client's End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

Client will throw TimeoutError if it fails to connect to the Server in given timeout value!

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nimport cv2, asyncio\n\n# define and launch Client with `receive_mode=True`\nclient = NetGear_Async(receive_mode=True).launch()\n\n# Create a async function where you want to show/manipulate your received frames\nasync def main():\n    # loop over Client's Asynchronous Frame Generator\n    async for frame in client.recv_generator():\n\n        # do something with received frames here\n\n        # Show output window\n        cv2.imshow(\"Output Frame\", frame)\n        key = cv2.waitKey(1) & 0xFF\n\n        # await before continuing\n        await asyncio.sleep(0)\n\nif __name__ == \"__main__\":\n    # Set event loop to client's\n    asyncio.set_event_loop(client.loop)\n    try:\n        # run your main function task until it is complete\n        client.loop.run_until_complete(main())\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n\n    # close all output window\n    cv2.destroyAllWindows()\n    # safely close client\n    client.close()\n

"},{"location":"gears/netgear_async/usage/#using-netgear_async-with-variable-parameters","title":"Using NetGear_Async with Variable Parameters","text":""},{"location":"gears/netgear_async/usage/#clients-end_1","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from the Server) and execute the following python code:

Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

Client will throw TimeoutError if it fails to connect to the Server in given timeout value!

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nimport cv2, asyncio\n\n# define and launch Client with `receive_mode=True`. #change following IP address '192.168.x.xxx' with yours\nclient = NetGear_Async(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=2,\n    receive_mode=True,\n    logging=True,\n).launch()\n\n\n# Create a async function where you want to show/manipulate your received frames\nasync def main():\n    # loop over Client's Asynchronous Frame Generator\n    async for frame in client.recv_generator():\n\n        # do something with received frames here\n\n        # Show output window\n        cv2.imshow(\"Output Frame\", frame)\n        key = cv2.waitKey(1) & 0xFF\n\n        # await before continuing\n        await asyncio.sleep(0)\n\n\nif __name__ == \"__main__\":\n    # Set event loop to client's\n    asyncio.set_event_loop(client.loop)\n    try:\n        # run your main function task until it is complete\n        client.loop.run_until_complete(main())\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n\n    # close all output window\n    cv2.destroyAllWindows()\n    # safely close client\n    client.close()\n
"},{"location":"gears/netgear_async/usage/#servers-end_1","title":"Server's End","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nimport asyncio\n\n# initialize Server with suitable source\nserver = NetGear_Async(\n    source=0,\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=2,\n    logging=True,\n).launch()\n\nif __name__ == \"__main__\":\n    # set event loop\n    asyncio.set_event_loop(server.loop)\n    try:\n        # run your main function task until it is complete\n        server.loop.run_until_complete(server.task)\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    finally:\n        # finally close the server\n        server.close()\n

"},{"location":"gears/netgear_async/usage/#using-netgear_async-with-a-custom-sourceopencv","title":"Using NetGear_Async with a Custom Source(OpenCV)","text":"

NetGear_Async allows you to easily define your own custom Source at Server-end that you want to use to transform your frames before sending them onto the network.

Let's implement a bare-minimum example with a Custom Source using NetGear_Async API and OpenCV:

"},{"location":"gears/netgear_async/usage/#servers-end_2","title":"Server's End","text":"

Open your favorite terminal and execute the following python code:

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import library\nfrom vidgear.gears.asyncio import NetGear_Async\nimport cv2, asyncio\n\n# initialize Server without any source\nserver = NetGear_Async(source=None, logging=True)\n\n# !!! define your own video source here !!!\n# Open any video stream such as live webcam\n# video stream on first index(i.e. 0) device\nstream = cv2.VideoCapture(0)\n\n# Create a async frame generator as custom source\nasync def my_frame_generator():\n\n    # loop over stream until its terminated\n    while True:\n\n        # read frames\n        (grabbed, frame) = stream.read()\n\n        # check if frame empty\n        if not grabbed:\n            break\n\n        # do something with the frame to be sent here\n\n        # yield frame\n        yield frame\n        # sleep for sometime\n        await asyncio.sleep(0)\n\n\nif __name__ == \"__main__\":\n    # set event loop\n    asyncio.set_event_loop(server.loop)\n    # Add your custom source generator to Server configuration\n    server.config[\"generator\"] = my_frame_generator()\n    # Launch the Server\n    server.launch()\n    try:\n        # run your main function task until it is complete\n        server.loop.run_until_complete(server.task)\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    finally:\n        # close stream\n        stream.release()\n        # finally close the server\n        server.close()\n
"},{"location":"gears/netgear_async/usage/#clients-end_2","title":"Client's End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

Client will throw TimeoutError if it fails to connect to the Server in given timeout value!

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nimport cv2, asyncio\n\n# define and launch Client with `receive_mode=True`\nclient = NetGear_Async(receive_mode=True, logging=True).launch()\n\n\n# Create a async function where you want to show/manipulate your received frames\nasync def main():\n    # loop over Client's Asynchronous Frame Generator\n    async for frame in client.recv_generator():\n\n        # {do something with received frames here}\n\n        # Show output window\n        cv2.imshow(\"Output Frame\", frame)\n        key = cv2.waitKey(1) & 0xFF\n\n        # await before continuing\n        await asyncio.sleep(0)\n\n\nif __name__ == \"__main__\":\n    # Set event loop to client's\n    asyncio.set_event_loop(client.loop)\n    try:\n        # run your main function task until it is complete\n        client.loop.run_until_complete(main())\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n\n    # close all output window\n    cv2.destroyAllWindows()\n    # safely close client\n    client.close()\n

"},{"location":"gears/netgear_async/usage/#using-netgear_async-with-other-gears","title":"Using NetGear_Async with Other Gears","text":"

NetGear_Async can be used with any other Gears without any compatibility issues.

Let's implement a bare-minimum example where we are sending Stabilized frames from Server-end and saving them at Client's end with WriteGear as follows:

"},{"location":"gears/netgear_async/usage/#servers-end_3","title":"Server's End","text":"

Open your favorite terminal and execute the following python code:

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nimport asyncio\n\n# initialize Server with suitable source and enable stabilization\nserver = NetGear_Async(\n    source=\"/home/foo/foo1.mp4\", stabilize=True, logging=True\n).launch()\n\nif __name__ == \"__main__\":\n    # set event loop\n    asyncio.set_event_loop(server.loop)\n    try:\n        # run your main function task until it is complete\n        server.loop.run_until_complete(server.task)\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    finally:\n        # finally close the server\n        server.close()\n
"},{"location":"gears/netgear_async/usage/#clients-end_3","title":"Client's End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

Client will throw TimeoutError if it fails to connect to the Server in given timeout value!

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nfrom vidgear.gears import WriteGear\nimport cv2, asyncio\n\n# define and launch Client with `receive_mode=True`\nclient = NetGear_Async(receive_mode=True).launch()\n\n# Define writer with output filename 'Output.mp4'\nwriter = WriteGear(output=\"Output.mp4\", logging=True)\n\n# Create a async function where you want to show/manipulate your received frames\nasync def main():\n    # loop over Client's Asynchronous Frame Generator\n    async for frame in client.recv_generator():\n\n        # {do something with received frames here}\n\n        # write a modified frame to writer\n        writer.write(frame)\n\n        # Show output window\n        cv2.imshow(\"Output Frame\", frame)\n        key = cv2.waitKey(1) & 0xFF\n\n        # await before continuing\n        await asyncio.sleep(0)\n\n\nif __name__ == \"__main__\":\n    # Set event loop to client's\n    asyncio.set_event_loop(client.loop)\n    try:\n        # run your main function task until it is complete\n        client.loop.run_until_complete(main())\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n\n    # close all output window\n    cv2.destroyAllWindows()\n    # safely close client\n    client.close()\n    # safely close writer\n    writer.close()\n

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/","title":"Bidirectional Mode","text":""},{"location":"gears/netgear_async/advanced/bidirectional_mode/#bidirectional-mode-for-netgear_async-api","title":"Bidirectional Mode for NetGear_Async API","text":"NetGear_Async's Bidirectional Mode"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#overview","title":"Overview","text":"New in v0.2.2

This document was added in v0.2.2.

Bidirectional Mode enables seamless support for Bidirectional data transmission between Client and Sender along with video-frames through its synchronous messaging patterns such as zmq.PAIR (ZMQ Pair Pattern) & zmq.REQ/zmq.REP (ZMQ Request/Reply Pattern) in NetGear_Async API.

In Bidirectional Mode, we utilizes the NetGear_Async API's transceive_data method for transmitting data (at Client's end) and receiving data (in Server's end) all while transferring frames in real-time.

This mode can be easily activated in NetGear_Async through bidirectional_mode attribute of its options dictionary parameter during initialization.

Important

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#exclusive-method-and-parameter","title":"Exclusive Method and Parameter","text":"

To send data bidirectionally, NetGear_Async API provides following exclusive method and parameter:

transceive_data only works when Bidirectional Mode is enabled.

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#usage-examples","title":"Usage Examples","text":"

For Bidirectional Mode, NetGear_Async must need User-defined Custom Source at its Server end otherwise it will throw ValueError.

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#bare-minimum-usage-with-opencv","title":"Bare-Minimum Usage with OpenCV","text":"

Following is the bare-minimum code you need to get started with Bidirectional Mode over Custom Source Server built using OpenCV and NetGear_Async API:

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#server-end","title":"Server End","text":"

Open your favorite terminal and execute the following python code:

You can terminate both sides anytime by pressing Ctrl+C on your keyboard!

# import library\nfrom vidgear.gears.asyncio import NetGear_Async\nimport cv2, asyncio\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# initialize Server without any source\nserver = NetGear_Async(source=None, logging=True, **options)\n\n# Create a async frame generator as custom source\nasync def my_frame_generator():\n\n    # !!! define your own video source here !!!\n    # Open any valid video stream(for e.g `foo.mp4` file)\n    stream = cv2.VideoCapture(\"foo.mp4\")\n\n    # loop over stream until its terminated\n    while True:\n        # read frames\n        (grabbed, frame) = stream.read()\n\n        # check for empty frame\n        if not grabbed:\n            break\n\n        # {do something with the frame to be sent here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # receive data from Client\n        recv_data = await server.transceive_data()\n\n        # print data just received from Client\n        if not (recv_data is None):\n            print(recv_data)\n\n        # send our frame & data\n        yield (target_data, frame) # (1)\n\n        # sleep for sometime\n        await asyncio.sleep(0)\n\n    # safely close video stream\n    stream.release()\n\n\nif __name__ == \"__main__\":\n    # set event loop\n    asyncio.set_event_loop(server.loop)\n    # Add your custom source generator to Server configuration\n    server.config[\"generator\"] = my_frame_generator()\n    # Launch the Server\n    server.launch()\n    try:\n        # run your main function task until it is complete\n        server.loop.run_until_complete(server.task)\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    finally:\n        # finally close the server\n        server.close()\n
  1. Everything except numpy.ndarray datatype data is accepted in target_data.
"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#client-end","title":"Client End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nimport cv2, asyncio\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# define and launch Client with `receive_mode=True`\nclient = NetGear_Async(receive_mode=True, logging=True, **options).launch()\n\n\n# Create a async function where you want to show/manipulate your received frames\nasync def main():\n    # loop over Client's Asynchronous Frame Generator\n    async for (data, frame) in client.recv_generator():\n\n        # do something with receive data from server\n        if not (data is None):\n            # let's print it\n            print(data)\n\n        # {do something with received frames here}\n\n        # Show output window(comment these lines if not required)\n        cv2.imshow(\"Output Frame\", frame)\n        cv2.waitKey(1) & 0xFF\n\n        # prepare data to be sent\n        target_data = \"Hi, I am a Client here.\"\n        # send our data to server\n        await client.transceive_data(data=target_data)\n\n        # await before continuing\n        await asyncio.sleep(0)\n\n\nif __name__ == \"__main__\":\n    # Set event loop to client's\n    asyncio.set_event_loop(client.loop)\n    try:\n        # run your main function task until it is complete\n        client.loop.run_until_complete(main())\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n\n    # close all output window\n    cv2.destroyAllWindows()\n\n    # safely close client\n    client.close()\n

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#using-bidirectional-mode-with-variable-parameters","title":"Using Bidirectional Mode with Variable Parameters","text":""},{"location":"gears/netgear_async/advanced/bidirectional_mode/#clients-end","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from the Server) and execute the following python code:

Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nimport cv2, asyncio\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# Define NetGear_Async Client at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear_Async(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# Create a async function where you want to show/manipulate your received frames\nasync def main():\n    # loop over Client's Asynchronous Frame Generator\n    async for (data, frame) in client.recv_generator():\n\n        # do something with receive data from server\n        if not (data is None):\n            # let's print it\n            print(data)\n\n        # {do something with received frames here}\n\n        # Show output window(comment these lines if not required)\n        cv2.imshow(\"Output Frame\", frame)\n        cv2.waitKey(1) & 0xFF\n\n        # prepare data to be sent\n        target_data = \"Hi, I am a Client here.\"\n        # send our data to server\n        await client.transceive_data(data=target_data)\n\n        # await before continuing\n        await asyncio.sleep(0)\n\n\nif __name__ == \"__main__\":\n    # Set event loop to client's\n    asyncio.set_event_loop(client.loop)\n    try:\n        # run your main function task until it is complete\n        client.loop.run_until_complete(main())\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n\n    # close all output window\n    cv2.destroyAllWindows()\n\n    # safely close client\n    client.close()\n

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#server-end_1","title":"Server End","text":"

Now, Open the terminal on another Server System (a Raspberry Pi with Camera Module), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this backend, otherwise nothing will work.

New Picamera2 backendLegacy Picamera backend
# import libs\nfrom vidgear.gears.asyncio import NetGear_Async\nfrom vidgear.gears import VideoGear\nfrom libcamera import Transform\nimport cv2, asyncio\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# initialize Server without any source at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear_Async(\n    source=None,\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# Create a async frame generator as custom source\nasync def my_frame_generator():\n\n    # !!! define your own video source below !!!\n\n    # define various Picamera2 tweak parameters\n    options = {\n        \"queue\": True,\n        \"buffer_count\": 4,\n        \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n        \"transform\": Transform(hflip=1),\n        \"auto_align_output_config\": True,  # auto-align camera configuration\n    }\n\n    # open pi video stream with defined parameters\n    stream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n    # loop over stream until its terminated\n    while True:\n        # read frames\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame to be sent here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # receive data from Client\n        recv_data = await server.transceive_data()\n\n        # print data just received from Client\n        if not (recv_data is None):\n            print(recv_data)\n\n        # send our frame & data\n        yield (target_data, frame) # (1)\n\n        # sleep for sometime\n        await asyncio.sleep(0)\n\n    # safely close video stream\n    stream.stop()\n\n\nif __name__ == \"__main__\":\n    # set event loop\n    asyncio.set_event_loop(server.loop)\n    # Add your custom source generator to Server configuration\n    server.config[\"generator\"] = my_frame_generator()\n    # Launch the Server\n    server.launch()\n    try:\n        # run your main function task until it is complete\n        server.loop.run_until_complete(server.task)\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    finally:\n        # finally close the server\n        server.close()\n
  1. Everything except numpy.ndarray datatype data is accepted in target_data.
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import library\nfrom vidgear.gears.asyncio import NetGear_Async\nfrom vidgear.gears import VideoGear\nimport cv2, asyncio\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# initialize Server without any source at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear_Async(\n    source=None,\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# Create a async frame generator as custom source\nasync def my_frame_generator():\n\n    # !!! define your own video source below !!!\n\n    # define various Picamera tweak parameters\n    options = {\n        \"hflip\": True,\n        \"exposure_mode\": \"auto\",\n        \"iso\": 800,\n        \"exposure_compensation\": 15,\n        \"awb_mode\": \"horizon\",\n        \"sensor_mode\": 0,\n    }\n\n    # open pi video stream with defined parameters\n    stream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n    # loop over stream until its terminated\n    while True:\n        # read frames\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame to be sent here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # receive data from Client\n        recv_data = await server.transceive_data()\n\n        # print data just received from Client\n        if not (recv_data is None):\n            print(recv_data)\n\n        # send our frame & data\n        yield (target_data, frame) # (1)\n\n        # sleep for sometime\n        await asyncio.sleep(0)\n\n    # safely close video stream\n    stream.stop()\n\n\nif __name__ == \"__main__\":\n    # set event loop\n    asyncio.set_event_loop(server.loop)\n    # Add your custom source generator to Server configuration\n    server.config[\"generator\"] = my_frame_generator()\n    # Launch the Server\n    server.launch()\n    try:\n        # run your main function task until it is complete\n        server.loop.run_until_complete(server.task)\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    finally:\n        # finally close the server\n        server.close()\n
  1. Everything except numpy.ndarray datatype data is accepted in target_data.

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#using-bidirectional-mode-for-video-frames-transfer","title":"Using Bidirectional Mode for Video-Frames Transfer","text":"

In this example we are going to implement a bare-minimum example, where we will be sending video-frames (3-Dimensional numpy arrays) of the same Video bidirectionally at the same time, for testing the real-time performance and synchronization between the Server and the Client using this(Bidirectional) Mode.

This feature is great for building applications like Real-Time Video Chat.

We're also using reducer() method for reducing frame-size on-the-go for additional performance.

Remember, Sending large HQ video-frames may required more network bandwidth and packet size which may lead to video latency!

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#server-end_2","title":"Server End","text":"

Open your favorite terminal and execute the following python code:

You can terminate both side anytime by pressing Ctrl+C on your keyboard!

Server end can only send numpy.ndarray datatype as frame but not as data.

# import library\nfrom vidgear.gears.asyncio import NetGear_Async\nfrom vidgear.gears.asyncio.helper import reducer\nimport cv2, asyncio\nimport numpy as np\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# Define NetGear Server without any source and with defined parameters\nserver = NetGear_Async(source=None, pattern=1, logging=True, **options)\n\n# Create a async frame generator as custom source\nasync def my_frame_generator():\n    # !!! define your own video source here !!!\n    # Open any valid video stream(for e.g `foo.mp4` file)\n    stream = cv2.VideoCapture(\"foo.mp4\")\n    # loop over stream until its terminated\n    while True:\n\n        # read frames\n        (grabbed, frame) = stream.read()\n\n        # check for empty frame\n        if not grabbed:\n            break\n\n        # reducer frames size if you want more performance, otherwise comment this line\n        frame = await reducer(frame, percentage=30)  # reduce frame by 30%\n\n        # {do something with the frame to be sent here}\n\n        # send frame & data and also receive data from Client\n        recv_data = await server.transceive_data()\n\n        # receive data from Client\n        if not (recv_data is None):\n            # check data is a numpy frame\n            if isinstance(recv_data, np.ndarray):\n\n                # {do something with received numpy frame here}\n\n                # Let's show it on output window\n                cv2.imshow(\"Received Frame\", recv_data)\n                cv2.waitKey(1) & 0xFF\n            else:\n                # otherwise just print data\n                print(recv_data)\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # send our frame & data to client\n        yield (target_data, frame) # (1)\n\n        # sleep for sometime\n        await asyncio.sleep(0)\n\n    # safely close video stream\n    stream.release()\n\n\nif __name__ == \"__main__\":\n    # set event loop\n    asyncio.set_event_loop(server.loop)\n    # Add your custom source generator to Server configuration\n    server.config[\"generator\"] = my_frame_generator()\n    # Launch the Server\n    server.launch()\n    try:\n        # run your main function task until it is complete\n        server.loop.run_until_complete(server.task)\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    finally:\n        # finally close the server\n        server.close()\n
  1. Everything except numpy.ndarray datatype data is accepted in target_data.

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#client-end_1","title":"Client End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nfrom vidgear.gears.asyncio.helper import reducer\nimport cv2, asyncio\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# define and launch Client with `receive_mode=True`\nclient = NetGear_Async(pattern=1, receive_mode=True, logging=True, **options).launch()\n\n# Create a async function where you want to show/manipulate your received frames\nasync def main():\n    # !!! define your own video source here !!!\n    # again open the same video stream for comparison\n    stream = cv2.VideoCapture(\"foo.mp4\")\n    # loop over Client's Asynchronous Frame Generator\n    async for (server_data, frame) in client.recv_generator():\n\n        # check for server data\n        if not (server_data is None):\n\n            # {do something with the server data here}\n\n            # lets print extracted server data\n            print(server_data)\n\n        # {do something with received frames here}\n\n        # Show output window\n        cv2.imshow(\"Output Frame\", frame)\n        key = cv2.waitKey(1) & 0xFF\n\n        # read frame target data from stream to be sent to server\n        (grabbed, target_data) = stream.read()\n        # check for frame\n        if grabbed:\n            # reducer frames size if you want more performance, otherwise comment this line\n            target_data = await reducer(\n                target_data, percentage=30\n            )  # reduce frame by 30%\n            # send our frame data\n            await client.transceive_data(data=target_data)\n\n        # await before continuing\n        await asyncio.sleep(0)\n\n    # safely close video stream\n    stream.release()\n\n\nif __name__ == \"__main__\":\n    # Set event loop to client's\n    asyncio.set_event_loop(client.loop)\n    try:\n        # run your main function task until it is complete\n        client.loop.run_until_complete(main())\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    # close all output window\n    cv2.destroyAllWindows()\n    # safely close client\n    client.close()\n

  1. Additional data of numpy.ndarray datatype is ONLY SUPPORTED at Client's end with transceive_data method using its data parameter. Whereas Server end can only send numpy.ndarray datatype as frame but not as data.

    \u21a9\u21a9

"},{"location":"gears/pigear/overview/","title":"Overview","text":""},{"location":"gears/pigear/overview/#pigear-api","title":"PiGear API","text":"Raspberry Pi Camera Module"},{"location":"gears/pigear/overview/#overview","title":"Overview","text":"

PiGear is a specialized API similar to the CamGear API but optimized for Raspberry Pi Boards, offering comprehensive support for camera modules (e.g., OmniVision OV5647, Sony IMX219), along with limited compatibility for USB cameras.

PiGear implements a seamless and robust wrapper around the picamera2 python library, simplifying integration with minimal code changes and ensuring a smooth transition for developers already familiar with the Picamera2 API. PiGear leverages the libcamera API under the hood with multi-threading, providing high-performance , enhanced control and functionality for Raspberry Pi camera modules.

PiGear handles common configuration parameters and non-standard settings for various camera types, simplifying the integration process. PiGear currently supports PiCamera2 API parameters such as sensor, controls, transform, and format etc., with internal type and sanity checks for robust performance.

While primarily focused on Raspberry Pi camera modules, PiGear also provides basic functionality for USB webcams only with Picamera2 API, along with the ability to accurately differentiate between USB and Raspberry Pi cameras using metadata.

Backward compatibility with picamera library

PiGear seamlessly switches to the legacy picamera library if the picamera2 library is unavailable, ensuring seamless backward compatibility. For this, PiGear also provides a flexible multi-threaded framework around complete picamera API, allowing developers to effortlessly exploit a wide range of parameters, such as brightness, saturation, sensor_mode, iso, exposure, and more.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

Furthermore, PiGear supports the use of multiple camera modules, including those found on Raspberry Pi Compute Module IO boards and USB cameras (only with Picamera2 API).

Threaded Internal Timer

PiGear ensures proper resource release during the termination of the API, preventing potential issues or resource leaks. PiGear API internally implements a Threaded Internal Timer that silently keeps active track of any frozen-threads or hardware-failures and exits safely if any do occur. This means that if you're running the PiGear API in your script and someone accidentally pulls the Camera-Module cable out, instead of going into a possible kernel panic, the API will exit safely to save resources.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this API, otherwise nothing will work.

Helpful Tips

"},{"location":"gears/pigear/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through PiGear Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/pigear/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/pigear/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/pigear/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/pigear/params/","title":"Parameters","text":""},{"location":"gears/pigear/params/#pigear-api-parameters","title":"PiGear API Parameters","text":""},{"location":"gears/pigear/params/#camera_num","title":"camera_num","text":"

This parameter selects the camera index to be used as the source, allowing you to drive these multiple cameras simultaneously from within a single Python session. Its value can only be zero or greater, otherwise, PiGear API will throw ValueError for any negative value.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

# select Camera Module at index `1`\nPiGear(camera_num=1)\n

The complete usage example demonstrating the usage of the camera_num parameter is available here \u27b6.

"},{"location":"gears/pigear/params/#resolution","title":"resolution","text":"

This parameter controls the resolution - a tuple (i.e. (width,height)) of two values giving the width and height of the output frames.

Make sure both width and height values should be at least 64.

When using the Picamera2 backend, the resolution parameter will be OVERRIDDEN, if the user explicitly defines the output_size property of the sensor configurational parameter in PiGear API.

Data-Type: Tuple

Default Value: Its default value is (640,480).

Usage:

PiGear(resolution=(1280,720)) # sets 1280x720 resolution\n

"},{"location":"gears/pigear/params/#framerate","title":"framerate","text":"

This parameter controls the framerate of the source.

Data-Type: integer/float

Default Value: Its default value is 30.

Usage:

PiGear(framerate=60) # sets 60fps framerate\n

"},{"location":"gears/pigear/params/#colorspace","title":"colorspace","text":"

This parameter controls the colorspace of the output frames.

With the Picamera2 backend, you can also define a custom format (format of output frame pixels) in PiGear API. Checkout this bonus example \u27b6

Data-Type: String

Default Value: Its default value is None (i.e. Default BGR colorspace).

Usage:

All supported colorspace values are described here \u27b6

PiGear(colorspace=\"COLOR_BGR2HSV\")\n

Its complete usage example is given here \u27b6

"},{"location":"gears/pigear/params/#options","title":"options","text":"

This dictionary parameter in the PiGear API allows you to control various camera settings for both the picamera2 and legacy picamera backends and some internal API tasks. These settings include:

"},{"location":"gears/pigear/params/#a-configurational-camera-parameters","title":"A. Configurational Camera Parameters","text":""},{"location":"gears/pigear/params/#b-user-defined-parameters","title":"B. User-defined Parameters","text":"

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

The complete usage example demonstrating the usage of the options parameter is available here \u27b6.

You can format these user-defined and configurational parameters as attributes of this options dictionary parameter as follows:

New Picamera2 backendLegacy Picamera backend
# formulate various Picamera2 API parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"exposure_compensation\": 15,\n    \"sensor\": {\"output_size\": (480, 320)},  # !!! will override `resolution` !!!\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n
# formulate various Picamera API parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n

"},{"location":"gears/pigear/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

PiGear(logging=True)\n

"},{"location":"gears/pigear/params/#time_delay","title":"time_delay","text":"

This parameter set the time delay (in seconds) before the PiGear API start reading the frames. This delay is only required if the source required some warm-up delay before starting up.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

PiGear(time_delay=1)  # set 1 seconds time delay\n

"},{"location":"gears/pigear/usage/","title":"Usage Examples","text":""},{"location":"gears/pigear/usage/#pigear-api-usage-examples","title":"PiGear API Usage Examples:","text":"

PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this API, otherwise nothing will work.

After going through following Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/pigear/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with PiGear API:

Under the hood, PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, PiGear API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

Disabling common libcamera API messages in silent mode.

The picamera2 backend can be a bit verbose with logging messages from the underlying libcamera library, even when logging is disabled (logging=False) in the PiGear API.

Linux Windows (Powershell) MacOS
export LIBCAMERA_LOG_LEVELS=2\n
$Env:LIBCAMERA_LOG_LEVELS=2\n
export LIBCAMERA_LOG_LEVELS=2\n
# import required libraries\nfrom vidgear.gears import PiGear\nimport cv2\n\n# open stream with default parameters\nstream = PiGear().start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/pigear/usage/#using-pigear-with-variable-camera-properties","title":"Using PiGear with Variable Camera Properties","text":"New Picamera2 backendLegacy Picamera backend

PiGear provides a user-friendly interface for the underlying picamera2 library, offering access to almost all of its important configurational parameters. It simplifies configuration for developers with even basic knowledge of Raspberry Pi camera modules, allowing them to easily configure and control the camera functionality with just a few lines of code.

This example doc showcases the capabilities of PiGear and demonstrates how it simplifies camera configuration with Picamera2 API backend.

All supported Picamera2 Library Configurational Parameters [IMPORTANT]

Following are the list of Picamera2 parameters, i.e. if supported, can be applied to the source stream in PiGear API through its options dictionary parameter by formatting them as its attributes.

Few Important points Parameters Datatype Description Supported Supported on USB Cameras Remarks buffer_count int, >=1 number of sets of buffers to allocate for the camera system Read Docs here \u27b6 queue bool whether the system is allowed to queue up a frame ready for a capture request Read Docs here \u27b6 controls dict specify a set of runtime controls that can be regarded as part of the camera configuration Read Docs here \u27b6 sensor dict allow to select a particular mode of operation for the sensor Read Docs here \u27b6 format str Pixel formats Read Docs here \u27b6 and see Bonus example \u27b6 transform Transform1 The 2D plane transform that is applied to all images from all the configured streams. Read Docs here \u27b6 colour_space colour space of the output images Handled by colorspace parameter of PiGear API size A tuple of two values giving the width and height of the output image. (Both numbers should be no less than 64) Handled by resolution parameter of PiGear API display name of the stream that will be displayed in the preview window. Not-Required encode name of the stream that will be used for video recording. Not-Required Limited support for USB Cameras

This example also works with USB Cameras, However:

Enabling verbose logs for backend PiCamera2 Library

The PiGear API allows you to enable more detailed logging from the picamera2 backend library using the enable_verbose_logs user-defined optional parameter attribute. This can be used in conjunction with enabling general logging (logging=True) in the PiGear API for even more granular control over logging output.

PiGear also support changing parameter at runtime. Checkout this bonus example here \u27b6

# import required libraries\nfrom vidgear.gears import PiGear\nfrom libcamera import Transform\nimport cv2\n\n# formulate various Picamera2 API \n# configurational parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"sensor\": {\"output_size\": (480, 320)},  # !!! will override `resolution` !!!\n    \"auto_align_output_size\": True,  # auto-align output size\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

PiGear API switches to the legacy picamerabackend if the picamera2 library is unavailable.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

PiGear also supports almost every parameter available within picamera python library. These parameters can be easily applied to the source stream in PiGear API through its options dictionary parameter by formatting them as its attributes. The complete usage example is as follows:

All supported parameters are listed in PiCamera Docs \u27b6

PiGear also support changing parameter at runtime. Checkout this bonus example here \u27b6

# import required libraries\nfrom vidgear.gears import PiGear\nimport cv2\n\n# formulate various Picamera API \n# configurational parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/pigear/usage/#using-pigear-with-direct-colorspace-manipulation","title":"Using PiGear with Direct Colorspace Manipulation","text":"

PiGear API also supports Direct Colorspace Manipulation, which is ideal for changing source colorspace on the run.

A more detailed information on colorspace manipulation can be found here \u27b6

In following example code, we will start with HSV as source colorspace, and then we will switch to GRAY colorspace when W key is pressed, and then LAB colorspace when E key is pressed, finally default colorspace (i.e. BGR) when S key is pressed. Also, quit when Q key is pressed:

Any incorrect or None-Type value will immediately revert the colorspace to default (i.e. BGR).

# import required libraries\nfrom vidgear.gears import PiGear\nimport cv2\n\n# open pi video stream with defined parameters and change colorspace to `HSV`\nstream = PiGear(\n    resolution=(640, 480),\n    framerate=60,\n    colorspace=\"COLOR_BGR2HSV\",\n    logging=True\n).start()\n\n\n# loop over\nwhile True:\n\n    # read HSV frames\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the HSV frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for key if pressed\n    key = cv2.waitKey(1) & 0xFF\n\n    # check if 'w' key is pressed\n    if key == ord(\"w\"):\n        # directly change colorspace at any instant\n        stream.color_space = cv2.COLOR_BGR2GRAY  # Now colorspace is GRAY\n\n    # check for 'e' key is pressed\n    if key == ord(\"e\"):\n        stream.color_space = cv2.COLOR_BGR2LAB  # Now colorspace is CieLAB\n\n    # check for 's' key is pressed\n    if key == ord(\"s\"):\n        stream.color_space = None  # Now colorspace is default(ie BGR)\n\n    # check for 'q' key is pressed\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/pigear/usage/#using-pigear-with-writegear-api","title":"Using PiGear with WriteGear API","text":"

PiGear can be easily used with WriteGear API directly without any compatibility issues. The suitable example is as follows:

New Picamera2 backendLegacy Picamera backend
# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import WriteGear\nfrom libcamera import Transform\nimport cv2\n\n# formulate various Picamera2 API \n# configurational parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"sensor\": {\"output_size\": (480, 320)},  # will override `resolution`\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# define suitable (Codec,CRF,preset) FFmpeg parameters for writer\noutput_params = {\"-vcodec\": \"libx264\", \"-crf\": 0, \"-preset\": \"fast\"}\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n    # lets convert frame to gray for this example\n    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n\n    # write gray frame to writer\n    writer.write(gray)\n\n    # Show output window\n    cv2.imshow(\"Output Gray Frame\", gray)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n
PiGear API switches to the legacy picamerabackend if the picamera2 library is unavailable.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# formulate various Picamera API \n# configurational parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# define suitable (Codec,CRF,preset) FFmpeg parameters for writer\noutput_params = {\"-vcodec\": \"libx264\", \"-crf\": 0, \"-preset\": \"fast\"}\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n    # lets convert frame to gray for this example\n    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n\n    # write gray frame to writer\n    writer.write(gray)\n\n   # Show output window\n    cv2.imshow(\"Output Gray Frame\", gray)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

  1. A custom libcamera API class. Must be imported as from libcamera import Transform.\u00a0\u21a9

"},{"location":"gears/screengear/overview/","title":"Overview","text":""},{"location":"gears/screengear/overview/#screengear-api","title":"ScreenGear API","text":"ScreenGear API in action"},{"location":"gears/screengear/overview/#overview","title":"Overview","text":"

ScreenGear is designed exclusively for targeting rapid Screencasting Capabilities, which means it can grab frames from your monitor in real-time, either by defining an area on the computer screen or full-screen, at the expense of inconsiderable latency. ScreenGear also seamlessly support frame capturing from multiple monitors as well as supports multiple backends.

ScreenGear API implements a multi-threaded wrapper around dxcam, pyscreenshot & python-mss python library, and also flexibly supports its internal parameter.

Helpful Tips

"},{"location":"gears/screengear/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through ScreenGear Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/screengear/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/screengear/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/screengear/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/screengear/params/","title":"Parameters","text":""},{"location":"gears/screengear/params/#screengear-api-parameters","title":"ScreenGear API Parameters","text":""},{"location":"gears/screengear/params/#monitor","title":"monitor","text":"

This parameter enforces dxcam (if installed) and mss (otherwise) usage, and it is suitable for selecting index of a specific screen/monitor device (from where you want retrieve frames) in multi-monitor setup. For example, its value can be assign to 2, to fetch frames from a secondary monitor screen.

Implication of using monitor parameter

Any value on monitor parameter other than None in ScreenGear API:

Data-Type: Integer, Tuple (only if dxcam backend on Windows)

Default Value: Its default value is None (i.e. disabled by default).

Usage:

With dxcam on Windows With mss backend Using GPU acceleration on Windows

With dxcam library backend, you can also assign which GPU devices ids to use along with monitor device ids as tuple (monitor_idx, gpu_idx), as follows:

# open video stream with defined parameters with \n# monitor at index `1` and GPU at index `0`.\nstream = ScreenGear(monitor=(1,0), logging=True).start()\n

Getting a complete list of monitor devices and GPUs

To get a complete list of monitor devices and outputs(GPUs), you can use dxcam library itself:

>>> import dxcam\n>>> dxcam.device_info()\n'Device[0]:<Device Name:NVIDIA GeForce RTX 3090 Dedicated VRAM:24348Mb VendorId:4318>\\n'\n>>> dxcam.output_info()\n'Device[0] Output[0]: Res:(1920, 1080) Rot:0 Primary:True\\nDevice[0] Output[1]: Res:(1920, 1080) Rot:0 Primary:False\\n'\n

# open video stream with defined parameters \n# with monitor at index `1` selected\nScreenGear(monitor=1)\n

With mss library backend, You can also assign monitor value to -1 to fetch frames from all connected multiple monitor screens with mss backend.

With mss library backend, API will output BGRA colorspace frames instead of default BGR.

# open video stream with defined parameters \n# with monitor at index `1` selected\nScreenGear(monitor=1)\n

"},{"location":"gears/screengear/params/#backend","title":"backend","text":"

This parameter enables pyscreenshot usage and select suitable backend for extracting frames in ScreenGear. The user have the authority of selecting suitable backend which generates best performance as well as the most compatible with their machines. The possible values are: dxcam (Windows only), pil, mss, scrot, maim, imagemagick, pyqt5, pyqt, pyside2, pyside, wx, pygdk3, mac_screencapture, mac_quartz, gnome_dbus, gnome-screenshot, kwin_dbus.

Performance Benchmarking of all backend can be found here \u27b6 and here \u27b6

Remember to install backend library and all of its dependencies you're planning to use with ScreenGear API.

Any value on monitor parameter will disable the backend parameter. You cannot use both parameters at same time.

Backend defaults to dxcam library on Windows (if installed), and pyscreenshot otherwise.

Data-Type: String

Default Value: Its default value is \"\" (i.e. default backend).

Usage:

ScreenGear(backend=\"pil\") # to enforce `pil` as backend for extracting frames.\n

"},{"location":"gears/screengear/params/#colorspace","title":"colorspace","text":"

This parameter selects the colorspace of the source stream.

Data-Type: String

Default Value: Its default value is None.

Usage:

All supported colorspace values are given here \u27b6.

ScreenGear(colorspace=\"COLOR_BGR2HSV\")\n

Its complete usage example is given here \u27b6

"},{"location":"gears/screengear/params/#options","title":"options","text":"

This parameter provides the flexibility to manually set the dimensions of capture screen area.

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, defined below:

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

The desired dimensional coordinates parameters can be passed to ScreenGear API by formatting them as attributes, as follows:

# formatting dimensional parameters as dictionary attributes\noptions = {'top': 40, 'left': 0, 'width': 100, 'height': 100}\n# assigning it\nScreenGear(**options)\n

"},{"location":"gears/screengear/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

ScreenGear(logging=True)\n

"},{"location":"gears/screengear/usage/","title":"Usage Examples","text":""},{"location":"gears/screengear/usage/#screengear-api-usage-examples","title":"ScreenGear API Usage Examples:","text":"

After going through ScreenGear Usage Examples, Checkout more of its advanced configurations here \u27b6

Recommended: Install DXcam library on Windows Machines

On Windows Machines, if installed, ScreenGear API uses dxcam backend machines for higher FPS performance. Thereby, it is highly recommended to install it via pip as follows:

pip install dxcam\n

"},{"location":"gears/screengear/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with ScreenGear API:

# import required libraries\nfrom vidgear.gears import ScreenGear\nimport cv2\n\n# open video stream with default parameters\nstream = ScreenGear().start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/screengear/usage/#using-screengear-with-variable-screen-dimensions","title":"Using ScreenGear with Variable Screen Dimensions","text":"

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 dictionary parameter by formatting them as its attributes.

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, defined below:

The complete usage example is as follows:

# import required libraries\nfrom vidgear.gears import ScreenGear\nimport cv2\n\n# define dimensions of screen w.r.t to given monitor to be captured\noptions = {\"top\": 40, \"left\": 0, \"width\": 100, \"height\": 100}\n\n# open video stream with defined parameters\nstream = ScreenGear(logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/screengear/usage/#using-screengear-with-multiple-screens","title":"Using ScreenGear with Multiple Screens","text":"

ScreenGear API provides us the flexibility to select any connected display for fetching frames, with its monitor parameter:

Implication of using monitor parameter

Any value on monitor parameter other than None in ScreenGear API:

With dxcam on Windows With mss backend Using GPU acceleration on Windows

With dxcam library backend, you can also assign which GPU devices ids to use along with monitor device ids as tuple (monitor_idx, gpu_idx), as follows:

# open video stream with defined parameters with \n# monitor at index `1` and GPU at index `0`.\nstream = ScreenGear(monitor=(1,0), logging=True).start()\n

Getting a complete list of monitor devices and GPUs

To get a complete list of monitor devices and outputs(GPUs), you can use dxcam library itself:

>>> import dxcam\n>>> dxcam.device_info()\n'Device[0]:<Device Name:NVIDIA GeForce RTX 3090 Dedicated VRAM:24348Mb VendorId:4318>\\n'\n>>> dxcam.output_info()\n'Device[0] Output[0]: Res:(1920, 1080) Rot:0 Primary:True\\nDevice[0] Output[1]: Res:(1920, 1080) Rot:0 Primary:False\\n'\n

# import required libraries\nfrom vidgear.gears import ScreenGear\nimport cv2\n\n# open video stream with defined parameters with monitor at index `1` selected\nstream = ScreenGear(monitor=1, logging=True).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

With mss library backend, You can also assign monitor value to -1 to fetch frames from all connected multiple monitor screens with mss backend.

With mss library backend, API will output BGRA colorspace frames instead of default BGR.

# import required libraries\nfrom vidgear.gears import ScreenGear\nimport cv2\n\n# open video stream with defined parameters with monitor at index `1` selected\nstream = ScreenGear(monitor=1, logging=True).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/screengear/usage/#using-screengear-with-variable-backend","title":"Using ScreenGear with Variable Backend","text":"

With ScreenGear API, you can select from many different backends that generates best performance as well as the most compatible with our machine by employing its backend parameter that supports many different backends:

Supported backend values

Its possible values are: dxcam (Windows only), pil, mss, scrot, maim, imagemagick, pyqt5, pyqt, pyside2, pyside, wx, pygdk3, mac_screencapture, mac_quartz, gnome_dbus, gnome-screenshot, kwin_dbus.

Remember to install backend library and all of its dependencies you're planning to use with ScreenGear API. More information on all these backends (except dxcam) can be found here \u27b6

Backend defaults to dxcam library on Windows (if installed), and pyscreenshot otherwise.

Any value on monitor parameter will disable the backend parameter. You cannot use them simultaneously.

# import required libraries\nfrom vidgear.gears import ScreenGear\nimport cv2\n\n# open video stream with defined parameters and `mss` backend \n# for extracting frames.\nstream = ScreenGear(backend=\"mss\", logging=True).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/screengear/usage/#using-screengear-with-direct-colorspace-manipulation","title":"Using ScreenGear with Direct Colorspace Manipulation","text":"

ScreenGear API also supports Direct Colorspace Manipulation, which is ideal for changing source colorspace on the run.

A more detailed information on colorspace manipulation can be found here \u27b6

In following example code, we will start with HSV as source colorspace, and then we will switch to GRAY colorspace when W key is pressed, and then LAB colorspace when E key is pressed, finally default colorspace (i.e. BGR) when S key is pressed. Also, quit when Q key is pressed:

Any incorrect or None-type value, will immediately revert the colorspace to default i.e. BGR.

# import required libraries\nfrom vidgear.gears import ScreenGear\nimport cv2\n\n# Change colorspace to `HSV`\nstream = ScreenGear(colorspace=\"COLOR_BGR2HSV\", logging=True).start()\n\n# loop over\nwhile True:\n\n    # read HSV frames\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the HSV frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for key if pressed\n    key = cv2.waitKey(1) & 0xFF\n\n    # check if 'w' key is pressed\n    if key == ord(\"w\"):\n        # directly change colorspace at any instant\n        stream.color_space = cv2.COLOR_BGR2GRAY  # Now colorspace is GRAY\n\n    # check for 'e' key is pressed\n    if key == ord(\"e\"):\n        stream.color_space = cv2.COLOR_BGR2LAB  # Now colorspace is CieLAB\n\n    # check for 's' key is pressed\n    if key == ord(\"s\"):\n        stream.color_space = None  # Now colorspace is default(ie BGR)\n\n    # check for 'q' key is pressed\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/screengear/usage/#using-screengear-with-writegear-api","title":"Using ScreenGear with WriteGear API","text":"

ScreenGear can be used in conjunction with WriteGear API directly without any compatibility issues. The suitable example is as follows:

# import required libraries\nfrom vidgear.gears import ScreenGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n\n# define dimensions of screen w.r.t to given monitor to be captured\noptions = {\"top\": 40, \"left\": 0, \"width\": 100, \"height\": 100}\n\n# define suitable (Codec,CRF,preset) FFmpeg parameters for writer\noutput_params = {\"-vcodec\": \"libx264\", \"-crf\": 0, \"-preset\": \"fast\"}\n\n# open video stream with defined parameters\nstream = ScreenGear(monitor=1, logging=True, **options).start()\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n    # lets convert frame to gray for this example\n    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n\n    # write gray frame to writer\n    writer.write(gray)\n\n    # Show output window\n    cv2.imshow(\"Output Gray Frame\", gray)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/stabilizer/overview/","title":"Overview","text":""},{"location":"gears/stabilizer/overview/#stabilizer-class","title":"Stabilizer Class","text":"

VidGear's Stabilizer in Action(Video Credits @SIGGRAPH2013)

This video is transcoded with StreamGear API and hosted on GitHub Repository and served with raw.githack.com

"},{"location":"gears/stabilizer/overview/#overview","title":"Overview","text":"

Stabilizer is an auxiliary class that enables Video Stabilization for vidgear with minimalistic latency, and at the expense of little to no additional computational requirements.

The basic idea behind it is to tracks and save the salient feature array for the given number of frames and then uses these anchor point to cancel out all perturbations relative to it for the incoming frames in the queue. This class relies on Fixed-Size Python Queues for error-free & ultra-fast frame handling.

For more detailed information on Stabilizer working, See this blogpost \u27b6

"},{"location":"gears/stabilizer/overview/#features","title":"Features","text":"

Important

"},{"location":"gears/stabilizer/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through Stabilizer Class Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/stabilizer/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/stabilizer/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/stabilizer/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/stabilizer/params/","title":"Parameters","text":""},{"location":"gears/stabilizer/params/#stabilizer-class-parameters","title":"Stabilizer Class Parameters","text":""},{"location":"gears/stabilizer/params/#smoothing_radius","title":"smoothing_radius","text":"

This parameter can be used to alter averaging window size. It basically handles the quality of stabilization at the expense of latency and sudden panning. Larger its value, less will be panning, more will be latency and vice-versa.

Data-Type: Integer

Default Value: Its default value is 25.

Usage:

You can easily pass this parameter as follows:

Stabilizer(smoothing_radius=30)\n

"},{"location":"gears/stabilizer/params/#border_size","title":"border_size","text":"

This parameter enables and set the value for extended border size that compensates for reduction of black borders during stabilization.

Data-Type: Integer

Default Value: Its default value is 0(no borders).

Usage:

You can easily pass this parameter as follows:

Stabilizer(border_size=10)\n

"},{"location":"gears/stabilizer/params/#crop_n_zoom","title":"crop_n_zoom","text":"

This parameter enables cropping and zooming of frames (to original size) to reduce the black borders from being too noticeable (similar to the Stabilized, cropped and Auto-Scaled feature available in Adobe AfterEffects) during stabilization. It simply works in conjunction with the border_size parameter, i.e. when this parameter is enabled, border_size will be used for cropping border instead of extending them.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

You can easily pass this parameter as follows:

Stabilizer(border_size=10, crop_n_zoom=True)\n

"},{"location":"gears/stabilizer/params/#border_type","title":"border_type","text":"

This parameter can be used to change the extended border type. Valid border types are 'black', 'reflect', 'reflect_101', 'replicate' and 'wrap', learn more about it here.

Altering border_type parameter is DISABLED when crop_n_zoom is enabled!

Data-Type: String

Default Value: Its default value is 'black'.

Usage:

You can easily pass this parameter as follows:

Stabilizer(border_type='reflect')\n

"},{"location":"gears/stabilizer/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

Stabilizer(logging=True)\n

"},{"location":"gears/stabilizer/usage/","title":"Usage Examples","text":""},{"location":"gears/stabilizer/usage/#stabilizer-class-usage-examples","title":"Stabilizer Class Usage Examples:","text":"

The stabilizer may not perform well against High-frequency jitter in video. Use at your own risk!

The stabilizer might be slower for High-Quality/Resolution videos-frames.

It is advised to enable logging on the first run for easily identifying any runtime errors.

After going through Stabilizer Class Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/stabilizer/usage/#bare-minimum-usage-with-videocapture-gears","title":"Bare-Minimum Usage with VideoCapture Gears","text":"

Following is the bare-minimum code you need to get started with Stabilizer Class and various VideoCapture Gears:

You can use any VideoCapture Gear instead of CamGear in the similar manner, as shown in this usage example.

# import required libraries\nfrom vidgear.gears.stabilizer import Stabilizer\nfrom vidgear.gears import CamGear\nimport cv2\n\n# To open live video stream on webcam at first index(i.e. 0) device\nstream = CamGear(source=0).start()\n\n# initiate stabilizer object with default parameters\nstab = Stabilizer()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # send current frame to stabilizer for processing\n    stabilized_frame = stab.stabilize(frame)\n\n    # wait for stabilizer which still be initializing\n    if stabilized_frame is None:\n        continue\n\n    # {do something with the stabilized frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Stabilized Frame\", stabilized_frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# clear stabilizer resources\nstab.clean()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/stabilizer/usage/#bare-minimum-usage-with-opencv","title":"Bare-Minimum Usage with OpenCV","text":"

The VidGear's stabilizer class can also work standalone easily with any Computer Vision library such as OpenCV itself. Following is the bare-minimum code you need to get started with Stabilizer Class and OpenCV:

# import required libraries\nfrom vidgear.gears.stabilizer import Stabilizer\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = cv2.VideoCapture(0)\n\n# initiate stabilizer object with default parameters\nstab = Stabilizer()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n        break\n\n    # send current frame to stabilizer for processing\n    stabilized_frame = stab.stabilize(frame)\n\n    # wait for stabilizer which still be initializing\n    if stabilized_frame is None:\n        continue\n\n    # {do something with the stabilized frame here}\n\n    # Show output window\n    cv2.imshow(\"Stabilized Frame\", stabilized_frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# clear stabilizer resources\nstab.clean()\n\n# safely close video stream\nstream.release()\n

"},{"location":"gears/stabilizer/usage/#using-stabilizer-with-variable-parameters","title":"Using Stabilizer with Variable Parameters","text":"

Stabilizer class provide certain parameters which you can use to tweak its internal properties. The complete usage example is as follows:

# import required libraries\nfrom vidgear.gears.stabilizer import Stabilizer\nfrom vidgear.gears import CamGear\nimport cv2\n\n# To open live video stream on webcam at first index(i.e. 0) device\nstream = CamGear(source=0).start()\n\n# initiate stabilizer object with defined parameters\nstab = Stabilizer(smoothing_radius=30, crop_n_zoom=True, border_size=5, logging=True)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # send current frame to stabilizer for processing\n    stabilized_frame = stab.stabilize(frame)\n\n    # wait for stabilizer which still be initializing\n    if stabilized_frame is None:\n        continue\n\n    # {do something with the stabilized frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Stabilized Frame\", stabilized_frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# clear stabilizer resources\nstab.clean()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/stabilizer/usage/#using-stabilizer-with-writegear","title":"Using Stabilizer with WriteGear","text":"

VideoGear's stabilizer can be used in conjunction with WriteGear API directly without any compatibility issues. The complete usage example is as follows:

You can also add live audio input to WriteGear pipeline. See this bonus example \u27b6

# import required libraries\nfrom vidgear.gears.stabilizer import Stabilizer\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# Open suitable video stream\nstream = CamGear(source=\"unstabilized_stream.mp4\").start()\n\n# initiate stabilizer object with default parameters\nstab = Stabilizer()\n\n# Define writer with default parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\")\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if not None-type\n    if frame is None:\n        break\n\n    # send current frame to stabilizer for processing\n    stabilized_frame = stab.stabilize(frame)\n\n    # wait for stabilizer which still be initializing\n    if stabilized_frame is None:\n        continue\n\n    # {do something with the stabilized frame here}\n\n    # write stabilized frame to writer\n    writer.write(stabilized_frame)\n\n    # Show output window\n    cv2.imshow(\"Stabilized Frame\", stabilized_frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# clear stabilizer resources\nstab.clean()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/stabilizer/usage/#using-videogear-with-stabilizer-backend","title":"Using VideoGear with Stabilizer backend","text":"

VideoGear API provides a special internal wrapper around Stabilizer class that enables easy stabilization for various video-streams (real-time or not) with minimum effort and writing way fewer lines of code.

The complete usage example can be found here \u27b6

"},{"location":"gears/streamgear/ffmpeg_install/","title":"FFmpeg Installation","text":""},{"location":"gears/streamgear/ffmpeg_install/#ffmpeg-installation-instructions","title":"FFmpeg Installation Instructions","text":"

StreamGear must requires FFmpeg executables for transcoding Media Chunks. You can following machine-specific instructions for its installation:

StreamGear API will throw RuntimeError, if it fails to detect valid FFmpeg executables on your system.

Enable logging (logging=True) for debugging FFmpeg validation process.

"},{"location":"gears/streamgear/ffmpeg_install/#linux-ffmpeg-installation","title":"Linux FFmpeg Installation","text":"

The StreamGear API supports Auto-Detection and Manual Configuration methods on a Linux machine:

"},{"location":"gears/streamgear/ffmpeg_install/#a-auto-detection","title":"A. Auto-Detection","text":"

This is a recommended approach on Linux Machines

If StreamGear API not receives any input from the user on custom_ffmpeg parameter, then on Linux system, it tries to auto-detects the required FFmpeg installed binaries through validation test that employs subprocess python module.

Installation: You can install easily install official FFmpeg according to your Linux Distro by following this post \u27b6

"},{"location":"gears/streamgear/ffmpeg_install/#b-manual-configuration","title":"B. Manual Configuration","text":"

"},{"location":"gears/streamgear/ffmpeg_install/#windows-ffmpeg-installation","title":"Windows FFmpeg Installation","text":"

The StreamGear API supports Auto-Installation and Manual Configuration methods on Windows systems.

"},{"location":"gears/streamgear/ffmpeg_install/#a-auto-installation","title":"A. Auto-Installation","text":"

This is a recommended approach on Windows Machines

If StreamGear API not receives any input from the user on custom_ffmpeg parameter, then on Windows system StreamGear API auto-generates the required FFmpeg Static Binaries from a dedicated Github Server into the temporary directory (for e.g. C:\\Temp) of your machine.

Warning

"},{"location":"gears/streamgear/ffmpeg_install/#b-manual-configuration_1","title":"B. Manual Configuration","text":"

"},{"location":"gears/streamgear/ffmpeg_install/#macos-ffmpeg-installation","title":"MacOS FFmpeg Installation","text":"

The StreamGear API supports Auto-Detection and Manual Configuration methods on a macOS machine.

"},{"location":"gears/streamgear/ffmpeg_install/#a-auto-detection_1","title":"A. Auto-Detection","text":"

This is a recommended approach on MacOS Machines

If StreamGear API not receives any input from the user on custom_ffmpeg parameter, then on macOS system, it tries to auto-detects the required FFmpeg installed binaries through validation test that employs subprocess python module.

Installation: You can easily install FFmpeg on your macOS machine by following this tutorial \u27b6

"},{"location":"gears/streamgear/ffmpeg_install/#b-manual-configuration_2","title":"B. Manual Configuration","text":"

"},{"location":"gears/streamgear/introduction/","title":"Introduction","text":""},{"location":"gears/streamgear/introduction/#streamgear-api","title":"StreamGear API","text":"StreamGear API's generalized workflow"},{"location":"gears/streamgear/introduction/#overview","title":"Overview","text":"

StreamGear streamlines and simplifies the transcoding workflow to generate Ultra-Low Latency, High-Quality, Dynamic & Adaptive Streaming Formats like MPEG-DASH and Apple HLS with just a few lines of Python code, allowing developers to focus on their application logic rather than dealing with the complexities of transcoding and chunking media files.

StreamGear API provides a standalone, highly extensible, and flexible wrapper around the FFmpeg multimedia framework for generating chunk-encoded media segments from your multimedia content effortlessly.

With StreamGear, you can transcode source video/audio files and real-time video frames into a sequence of multiple smaller chunks/segments of suitable lengths. These segments facilitate streaming at different quality levels (bitrates or spatial resolutions) and allow for seamless switching between quality levels during playback based on available bandwidth. You can serve these segments on a web server, making them easily accessible via standard HTTP GET requests.

SteamGear currently supports both MPEG-DASH (Dynamic Adaptive Streaming over HTTP, ISO/IEC 23009-1) and Apple HLS (HTTP Live Streaming).

Additionally, StreamGear generates a manifest file (such as MPD for DASH) or a master playlist (such as M3U8 for Apple HLS) alongside the segments. These files contain essential segment information, including timing, URLs, and media characteristics like video resolution and adaptive bitrate. They are provided to the client before the streaming session begins.

For streaming with older traditional protocols such as RTMP, RTSP/RTP you could use WriteGear API instead.

New in v0.2.2

Apple HLS support was added in v0.2.2.

Important

Useful Links

"},{"location":"gears/streamgear/introduction/#mode-of-operations","title":"Mode of Operations","text":"

StreamGear primarily operates in following independent modes for transcoding:

Real-time Frames Mode itself is NOT Live-Streaming

To enable live-streaming in Real-time Frames Mode, use the exclusive -livestream attribute of the stream_params dictionary parameter in the StreamGear API. Checkout this usage example \u27b6 for more information.

"},{"location":"gears/streamgear/introduction/#watch-demo","title":"Watch Demo","text":"Watch MPEG-DASH StreamWatch APPLE HLS Stream

Watch StreamGear transcoded MPEG-DASH Stream:

Powered by clappr & shaka-player

This video assets (Manifest and segments) are hosted on GitHub Repository and served with raw.githack.com

Video Credits: \"Tears of Steel\" - Project Mango Teaser

Watch StreamGear transcoded APPLE HLS Stream:

Powered by clappr & HlsjsPlayback

This video assets (Playlist and segments) are hosted on GitHub Repository and served with raw.githack.com

Video Credits: \"Sintel\" - Project Durian Teaser

"},{"location":"gears/streamgear/introduction/#recommended-players","title":"Recommended Players","text":"GUI PlayersCommand-Line PlayersOnline Players

To run Online players locally, you'll need a HTTP server. For creating one yourself, See this well-curated list \u27b6

"},{"location":"gears/streamgear/introduction/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/streamgear/introduction/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/streamgear/introduction/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/streamgear/params/","title":"Parameters","text":""},{"location":"gears/streamgear/params/#streamgear-api-parameters","title":"StreamGear API Parameters","text":""},{"location":"gears/streamgear/params/#output","title":"output","text":"

This parameter sets the valid filename/path for storing the StreamGear assets, including Manifest file (such as MPD in case of DASH) or a Master Playlist (such as M3U8 in case of Apple HLS) and generated sequence of chunks/segments.

StreamGear API will throw ValueError if the provided output is empty or invalid.

Make sure to provide a valid filename with a valid file extension for the selected format value (such as .mpd for MPEG-DASH and .m3u8 for APPLE-HLS), otherwise StreamGear will throw AssertionError.

You can easily delete all previous assets at the output location by using the -clear_prev_assets attribute of the stream_params dictionary parameter.

Data-Type: String

Usage:

Its valid input can be one of the following:

"},{"location":"gears/streamgear/params/#format","title":"format","text":"

This parameter enables the adaptive HTTP streaming format. This parameter currently supported these formats: dash (i.e MPEG-DASH) and hls (i.e Apple HLS).

Make sure to provide a valid filename with a valid file extension in the output parameter for the selected format value (i.e., .mpd for MPEG-DASH and .m3u8 for APPLE-HLS), otherwise StreamGear will throw an AssertionError.

Any improper value assigned to format parameter will result in a ValueError!

Data-Type: String

Default Value: Its default value is dash

Usage:

DASHHLS
# Define streamer with DASH format\nStreamGear(output = \"output_dash.mpd\", format=\"dash\")\n
# Define streamer with HLS format\nStreamGear(output = \"output_hls.m3u8\", format=\"hls\")\n

"},{"location":"gears/streamgear/params/#custom_ffmpeg","title":"custom_ffmpeg","text":"

This parameter assigns the custom path/directory where the custom/downloaded FFmpeg executables are located.

Behavior on Windows Systems

On Windows, if a custom FFmpeg executable's path/directory is not provided through this custom_ffmpeg parameter, the StreamGear API will automatically attempt to download and extract suitable Static FFmpeg binaries at a suitable location on your Windows machine. More information can be found here \u27b6.

Data-Type: String

Default Value: Its default value is None.

Usage:

# Define streamer with custom ffmpeg binary\nStreamGear(output = 'output_foo.mpd', custom_ffmpeg=\"C://foo//bar//ffmpeg.exe\")\n

"},{"location":"gears/streamgear/params/#stream_params","title":"stream_params","text":"

This parameter allows developers to leverage nearly all FFmpeg options, providing effortless and flexible control over its internal settings for transcoding and generating high-quality streams. All supported parameters can be formatted as attributes within this dictionary parameter.

Please read the FFmpeg Documentation carefully before passing any additional values to the stream_params parameter. Incorrect values may cause errors or result in no output.

Data-Type: Dictionary

Default Value: Its default value is {}.

"},{"location":"gears/streamgear/params/#supported-parameters","title":"Supported Parameters","text":""},{"location":"gears/streamgear/params/#a-exclusive-parameters","title":"A. Exclusive Parameters","text":"

StreamGear API provides some exclusive internal parameters to easily generate Streaming Assets and effortlessly tweak its internal properties. These parameters are discussed below:

"},{"location":"gears/streamgear/params/#b-ffmpeg-parameters","title":"B. FFmpeg Parameters","text":"

Almost all FFmpeg parameters can be passed as dictionary attributes in stream_params. For example, to use the libx264 encoder to produce a lossless output video, you can pass the required FFmpeg parameters as dictionary attributes as follows:

Please check the H.264 documentation \u27b6 and FFmpeg Documentation \u27b6 for more information on following parameters.

All FFmpeg parameters are case-sensitive. Double-check each parameter if any errors occur.

In addition to these parameters, almost any FFmpeg parameter (supported by the installed FFmpeg) is also supported. Be sure to read the FFmpeg Documentation carefully first.

# libx264 encoder and its supported parameters\nstream_params = {\"-vcodec\":\"libx264\", \"-crf\": 0, \"-preset\": \"fast\", \"-tune\": \"zerolatency\"} \n

"},{"location":"gears/streamgear/params/#supported-encoders-and-decoders","title":"Supported Encoders and Decoders","text":"

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:

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 for creating HLS/DASH chunks of the primary stream efficiently. However, consider the following points:

# for checking encoder\nffmpeg -encoders           # use `ffmpeg.exe -encoders` on windows\n# for checking decoders\nffmpeg -decoders           # use `ffmpeg.exe -decoders` on windows\n

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

"},{"location":"gears/streamgear/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

StreamGear(logging=True)\n

  1. In Real-time Frames Mode, the Primary Stream's framerate defaults to -input_framerate attribute value, if defined, else it will be 25fps.\u00a0\u21a9

"},{"location":"gears/streamgear/rtfm/overview/","title":"Overview","text":""},{"location":"gears/streamgear/rtfm/overview/#streamgear-api-real-time-frames-mode","title":"StreamGear API: Real-time Frames Mode","text":"Real-time Frames Mode generalized workflow"},{"location":"gears/streamgear/rtfm/overview/#overview","title":"Overview","text":"

When no valid input is received on -video_source attribute of stream_params dictionary parameter, StreamGear API activates this mode where it directly transcodes real-time numpy.ndarray video-frames (as opposed to a entire video file) into a sequence of multiple smaller chunks/segments for adaptive streaming.

This mode works exceptionally well when you desire to flexibility manipulate or transform video-frames in real-time before sending them onto FFmpeg Pipeline for processing. But on the downside, StreamGear DOES NOT automatically maps video-source's audio to generated streams with this mode. You need to manually assign separate audio-source through -audio attribute of stream_params dictionary parameter.

SteamGear supports both MPEG-DASH (Dynamic Adaptive Streaming over HTTP, ISO/IEC 23009-1) and Apple HLS (HTTP Live Streaming) with this mode.

For this mode, StreamGear API provides exclusive stream() method for directly trancoding video-frames into streamable chunks.

New in v0.2.2

Apple HLS support was added in v0.2.2.

Real-time Frames Mode itself is NOT Live-Streaming

To enable live-streaming in Real-time Frames Mode, use the exclusive -livestream attribute of the stream_params dictionary parameter in the StreamGear API. Checkout this usage example \u27b6 for more information.

Please Remember

"},{"location":"gears/streamgear/rtfm/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through StreamGear Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/streamgear/rtfm/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/streamgear/rtfm/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/streamgear/rtfm/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/streamgear/rtfm/usage/","title":"Usage Examples","text":""},{"location":"gears/streamgear/rtfm/usage/#streamgear-api-usage-examples-real-time-frames-mode","title":"StreamGear API Usage Examples: Real-time Frames Mode","text":"

Real-time Frames Mode itself is NOT Live-Streaming

To enable live-streaming in Real-time Frames Mode, use the exclusive -livestream attribute of the stream_params dictionary parameter in the StreamGear API. Checkout following usage example \u27b6 for more information.

Important Information

DEPRECATION NOTICES for v0.3.3 and above

After going through following Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/streamgear/rtfm/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with StreamGear API in Real-time Frames Mode:

We are using CamGear in this Bare-Minimum example, but any VideoCapture Gear will work in the similar manner.

In this mode, StreamGear DOES NOT automatically maps video-source audio to generated streams. You need to manually assign separate audio-source through -audio attribute of stream_params dictionary parameter.

DASHHLS
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# open any valid video stream(for e.g `foo1.mp4` file)\nstream = CamGear(source='foo1.mp4').start() \n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"dash_out.mpd\")\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# open any valid video stream(for e.g `foo1.mp4` file)\nstream = CamGear(source='foo1.mp4').start() \n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\")\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n

After running this bare-minimum example, StreamGear will produce a Manifest file (dash.mpd) with streamable chunks that contains information about a Primary Stream of same resolution and framerate1 as input (without any audio).

"},{"location":"gears/streamgear/rtfm/usage/#bare-minimum-usage-with-controlled-input-framerate","title":"Bare-Minimum Usage with controlled Input-framerate","text":"

In Real-time Frames Mode, StreamGear API provides the exclusive -input_framerate attribute for the stream_params dictionary parameter, which allows you to set the assumed constant framerate for incoming frames.

In this example, we will retrieve the framerate from a webcam video stream and set it as the value for the -input_framerate attribute in StreamGear.

Remember, the input framerate defaults to 25.0 fps if the -input_framerate attribute value is not defined in Real-time Frames mode.

DASHHLS
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = CamGear(source=0).start()\n\n# retrieve framerate from CamGear Stream and pass it as `-input_framerate` value\nstream_params = {\"-input_framerate\":stream.framerate}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = CamGear(source=0).start()\n\n# retrieve framerate from CamGear Stream and pass it as `-input_framerate` value\nstream_params = {\"-input_framerate\":stream.framerate}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n

"},{"location":"gears/streamgear/rtfm/usage/#bare-minimum-usage-with-live-streaming","title":"Bare-Minimum Usage with Live-Streaming","text":"

You can easily activate Low-latency Live-Streaming in Real-time Frames Mode, where chunks will contain information for new frames only and forget previous ones, using the exclusive -livestream attribute of the stream_params dictionary parameter. The complete example is as follows:

In this mode, StreamGear DOES NOT automatically maps video-source audio to generated streams. You need to manually assign separate audio-source through -audio attribute of stream_params dictionary parameter.

DASHHLS

Controlling chunk size in DASH

To control the number of frames kept in Chunks for the DASH stream (controlling latency), you can use the -window_size and -extra_window_size FFmpeg parameters. Lower values for these parameters will result in lower latency.

After every few chunks (equal to the sum of -window_size and -extra_window_size values), all chunks will be overwritten while Live-Streaming. This means that newer chunks in the manifest will contain NO information from older chunks, and the resulting DASH stream will only play the most recent frames, reducing latency.

# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# open any valid video stream(from web-camera attached at index `0`)\nstream = CamGear(source=0).start()\n\n# enable livestreaming and retrieve framerate from CamGear Stream and\n# pass it as `-input_framerate` parameter for controlled framerate\nstream_params = {\"-input_framerate\": stream.framerate, \"-livestream\": True}\n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n

Controlling chunk size in HLS

To control the number of frames kept in Chunks for the HLS stream (controlling latency), you can use the -hls_init_time & -hls_time FFmpeg parameters. Lower values for these parameters will result in lower latency.

After every few chunks (equal to the sum of -hls_init_time & -hls_time values), all chunks will be overwritten while Live-Streaming. This means that newer chunks in the master playlist will contain NO information from older chunks, and the resulting HLS stream will only play the most recent frames, reducing latency.

# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# open any valid video stream(from web-camera attached at index `0`)\nstream = CamGear(source=0).start()\n\n# enable livestreaming and retrieve framerate from CamGear Stream and\n# pass it as `-input_framerate` parameter for controlled framerate\nstream_params = {\"-input_framerate\": stream.framerate, \"-livestream\": True}\n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n

"},{"location":"gears/streamgear/rtfm/usage/#bare-minimum-usage-with-opencv","title":"Bare-Minimum Usage with OpenCV","text":"

You can easily use the StreamGear API directly with any other Video Processing library (for e.g. OpenCV) in Real-time Frames Mode.

The following is a complete StreamGear API usage example with OpenCV:

This is a bare-minimum example with OpenCV, but any other Real-time Frames Mode feature or example will work in a similar manner.

DASHHLS
# import required libraries\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = cv2.VideoCapture(0) \n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"dash_out.mpd\")\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n      break\n\n    # {do something with the frame here}\n    # lets convert frame to gray for this example\n    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n\n    # send frame to streamer\n    streamer.stream(gray)\n\n    # Show output window\n    cv2.imshow(\"Output Gray Frame\", gray)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.release()\n\n# safely close streamer\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = cv2.VideoCapture(0) \n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\")\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n      break\n\n    # {do something with the frame here}\n    # lets convert frame to gray for this example\n    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n\n    # send frame to streamer\n    streamer.stream(gray)\n\n    # Show output window\n    cv2.imshow(\"Output Gray Frame\", gray)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.release()\n\n# safely close streamer\nstreamer.close()\n

"},{"location":"gears/streamgear/rtfm/usage/#usage-with-additional-streams","title":"Usage with Additional Streams","text":"

Similar to Single-Source Mode, in addition to the Primary Stream, you can easily generate any number of additional Secondary Streams with variable bitrate or spatial resolution, using the exclusive -streams attribute of the stream_params dictionary parameter.

To generate Secondary Streams, add each desired resolution and bitrate/framerate as a list of dictionaries to the -streams attribute. StreamGear will handle the rest automatically. The complete example is as follows:

A more detailed information on -streams attribute can be found here \u27b6

In this mode, StreamGear DOES NOT automatically maps video-source audio to generated streams. You need to manually assign separate audio-source through -audio attribute of stream_params dictionary parameter.

Important Information about -streams attribute

Always use the -streams attribute to define additional streams safely. Duplicate or incorrect definitions can break the transcoding pipeline and corrupt the output chunks.

DASHHLS
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = CamGear(source=0).start() \n\n# define various streams\nstream_params = {\n    \"-streams\": [\n        {\"-resolution\": \"1280x720\", \"-framerate\": 30.0},  # Stream1: 1280x720 at 30fps framerate\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream2: 640x360 at 60fps framerate\n        {\"-resolution\": \"320x240\", \"-video_bitrate\": \"500k\"},  # Stream3: 320x240 at 500kbs bitrate\n    ],\n}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"dash_out.mpd\")\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = CamGear(source=0).start() \n\n# define various streams\nstream_params = {\n    \"-streams\": [\n        {\"-resolution\": \"1280x720\", \"-framerate\": 30.0},  # Stream1: 1280x720 at 30fps framerate\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream2: 640x360 at 60fps framerate\n        {\"-resolution\": \"320x240\", \"-video_bitrate\": \"500k\"},  # Stream3: 320x240 at 500kbs bitrate\n    ],\n}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\")\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n

"},{"location":"gears/streamgear/rtfm/usage/#usage-with-file-audio-input","title":"Usage with File Audio-Input","text":"

In Real-time Frames Mode, if you want to add audio to your streams, you need to use the exclusive -audio attribute of the stream_params dictionary parameter.

To add a audio source, provide the path to your audio file as a string to the -audio attribute. The API will automatically validate and map the audio to all generated streams. The complete example is as follows:

Ensure the provided -audio audio source is compatible with the input video source. Incompatibility can cause multiple errors or result in no output at all.

You MUST use -input_framerate attribute to set exact value of input framerate when using external audio in Real-time Frames mode, otherwise audio delay will occur in output streams.

You can also assign a valid audio URL as input instead of a file path. More details can be found here \u27b6

DASHHLS
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# open any valid video stream(for e.g `foo1.mp4` file)\nstream = CamGear(source='foo1.mp4').start() \n\n# add various streams, along with custom audio\nstream_params = {\n    \"-streams\": [\n        {\"-resolution\": \"1920x1080\", \"-video_bitrate\": \"4000k\"},  # Stream1: 1920x1080 at 4000kbs bitrate\n        {\"-resolution\": \"1280x720\", \"-framerate\": 30.0},  # Stream2: 1280x720 at 30fps\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream3: 640x360 at 60fps\n    ],\n    \"-input_framerate\": stream.framerate, # controlled framerate for audio-video sync !!! don't forget this line !!!\n    \"-audio\": \"/home/foo/foo1.aac\" # assign external audio-source\n}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# open any valid video stream(for e.g `foo1.mp4` file)\nstream = CamGear(source='foo1.mp4').start() \n\n# add various streams, along with custom audio\nstream_params = {\n    \"-streams\": [\n        {\"-resolution\": \"1920x1080\", \"-video_bitrate\": \"4000k\"},  # Stream1: 1920x1080 at 4000kbs bitrate\n        {\"-resolution\": \"1280x720\", \"-framerate\": 30.0},  # Stream2: 1280x720 at 30fps\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream3: 640x360 at 60fps\n    ],\n    \"-input_framerate\": stream.framerate, # controlled framerate for audio-video sync !!! don't forget this line !!!\n    \"-audio\": \"/home/foo/foo1.aac\" # assign external audio-source\n}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n

"},{"location":"gears/streamgear/rtfm/usage/#usage-with-device-audio-input","title":"Usage with Device Audio-Input","text":"

In Real-time Frames Mode, you can also use the exclusive -audio attribute of the stream_params dictionary parameter for streaming live audio from an external device.

To stream live audio, format your audio device name followed by a suitable demuxer as a list, and assign it to the -audio attribute. The API will automatically validate and map the audio to all generated streams. The complete example is as follows:

Example Assumptions

Using devices sources with -audio attribute on different OS platforms

To use device sources with the -audio attribute on different OS platforms, follow these instructions:

Windows Linux MacOS

Windows OS users can use the dshow (DirectShow) to list audio input device which is the preferred option for Windows users. You can refer following steps to identify and specify your sound card:

If audio still doesn't work then checkout this troubleshooting guide \u27b6 or reach us out on Gitter \u27b6 Community channel

Linux OS users can use the alsa to list input device to capture live audio input such as from a webcam. You can refer following steps to identify and specify your sound card:

If audio still doesn't work then reach us out on Gitter \u27b6 Community channel

MAC OS users can use the avfoundation to list input devices for grabbing audio from integrated iSight cameras as well as cameras connected via USB or FireWire. You can refer following steps to identify and specify your sound card on MacOS/OSX machines:

If audio still doesn't work then reach us out on Gitter \u27b6 Community channel

It is advised to use this example with live-streaming enabled(True) by using StreamGear API's exclusive -livestream attribute of stream_params dictionary parameter.

Ensure the provided -audio audio source is compatible with the video source device. Incompatibility can cause multiple errors or result in no output at all.

You MUST use -input_framerate attribute to set exact value of input framerate when using external audio in Real-time Frames mode, otherwise audio delay will occur in output streams.

DASHHLS
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# open any valid DEVICE video stream\nstream = CamGear(source=0).start()\n\n# add various streams, along with custom audio\nstream_params = {\n    \"-streams\": [\n        {\n            \"-resolution\": \"640x360\",\n            \"-video_bitrate\": \"4000k\",\n        },  # Stream1: 640x360 at 4000kbs bitrate\n        {\"-resolution\": \"320x240\", \"-framerate\": 30.0},  # Stream2: 320x240 at 30fps\n    ],\n    \"-input_framerate\": stream.framerate,  # controlled framerate for audio-video sync !!! don't forget this line !!!\n    \"-livestream\": True,\n    \"-audio\": [\n        \"-f\",\n        \"dshow\",\n        \"-i\",\n        \"audio=Microphone (USB2.0 Camera)\",\n    ],  # assign appropriate input audio-source device(compatible with video source) and its demuxer\n}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# open any valid DEVICE video stream\nstream = CamGear(source=0).start()\n\n# add various streams, along with custom audio\nstream_params = {\n    \"-streams\": [\n        {\n            \"-resolution\": \"640x360\",\n            \"-video_bitrate\": \"4000k\",\n        },  # Stream1: 640x360 at 4000kbs bitrate\n        {\"-resolution\": \"320x240\", \"-framerate\": 30.0},  # Stream2: 320x240 at 30fps\n    ],\n    \"-input_framerate\": stream.framerate,  # controlled framerate for audio-video sync !!! don't forget this line !!!\n    \"-livestream\": True,\n    \"-audio\": [\n        \"-f\",\n        \"dshow\",\n        \"-i\",\n        \"audio=Microphone (USB2.0 Camera)\",\n    ],  # assign appropriate input audio-source device(compatible with video source) and its demuxer\n}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"hls_out.m3u8\", format=\"hls\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n

"},{"location":"gears/streamgear/rtfm/usage/#usage-with-hardware-video-encoder","title":"Usage with Hardware Video-Encoder","text":"

In Real-time Frames Mode, you can easily change the video encoder according to your requirements by passing the -vcodec FFmpeg parameter as an attribute in the stream_params dictionary parameter. Additionally, you can specify additional properties, features, and optimizations for your system's GPU.

In this example, we will be using h264_vaapi as our Hardware Encoder and specifying the device hardware's location and compatible video filters by formatting them as attributes in the stream_params dictionary parameter.

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.

Checking VAAPI Support for Hardware Encoding

To use VAAPI (Video Acceleration API) as a hardware encoder in this example, follow these steps to ensure your FFmpeg supports VAAPI:

ffmpeg  -hide_banner -encoders | grep vaapi \n\n V..... h264_vaapi           H.264/AVC (VAAPI) (codec h264)\n V..... hevc_vaapi           H.265/HEVC (VAAPI) (codec hevc)\n V..... mjpeg_vaapi          MJPEG (VAAPI) (codec mjpeg)\n V..... mpeg2_vaapi          MPEG-2 (VAAPI) (codec mpeg2video)\n V..... vp8_vaapi            VP8 (VAAPI) (codec vp8)\n

Please read the FFmpeg Documentation carefully before passing any additional values to the stream_params parameter. Incorrect values may cause errors or result in no output.

DASHHLS
# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = VideoGear(source=0).start() \n\n# add various streams with custom Video Encoder and optimizations\nstream_params = {\n    \"-streams\": [\n        {\"-resolution\": \"1920x1080\", \"-video_bitrate\": \"4000k\"},  # Stream1: 1920x1080 at 4000kbs bitrate\n        {\"-resolution\": \"1280x720\", \"-framerate\": 30.0},  # Stream2: 1280x720 at 30fps\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream3: 640x360 at 60fps\n    ],\n    \"-vcodec\": \"h264_vaapi\", # define custom Video encoder\n    \"-vaapi_device\": \"/dev/dri/renderD128\", # define device location\n    \"-vf\": \"format=nv12,hwupload\",  # define video filters\n}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = VideoGear(source=0).start() \n\n# add various streams with custom Video Encoder and optimizations\nstream_params = {\n    \"-streams\": [\n        {\"-resolution\": \"1920x1080\", \"-video_bitrate\": \"4000k\"},  # Stream1: 1920x1080 at 4000kbs bitrate\n        {\"-resolution\": \"1280x720\", \"-framerate\": 30.0},  # Stream2: 1280x720 at 30fps\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream3: 640x360 at 60fps\n    ],\n    \"-vcodec\": \"h264_vaapi\", # define custom Video encoder\n    \"-vaapi_device\": \"/dev/dri/renderD128\", # define device location\n    \"-vf\": \"format=nv12,hwupload\",  # define video pixformat\n}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n

  1. In Real-time Frames Mode, the Primary Stream's framerate defaults to the value of the -input_framerate attribute, if defined. Otherwise, it will be set to 25 fps.\u00a0\u21a9\u21a9\u21a9

"},{"location":"gears/streamgear/ssm/overview/","title":"Overview","text":""},{"location":"gears/streamgear/ssm/overview/#streamgear-api-single-source-mode","title":"StreamGear API: Single-Source Mode","text":"Single-Source Mode generalized workflow"},{"location":"gears/streamgear/ssm/overview/#overview","title":"Overview","text":"

In this mode, StreamGear transcodes entire audio-video file (as opposed to frames-by-frame) into a sequence of multiple smaller chunks/segments for adaptive streaming.

This mode works exceptionally well when you're transcoding long-duration lossless videos(with audio) files for streaming that requires no interruptions. But on the downside, the provided source cannot be flexibly manipulated or transformed before sending onto FFmpeg Pipeline for processing.

SteamGear supports both MPEG-DASH (Dynamic Adaptive Streaming over HTTP, ISO/IEC 23009-1) and Apple HLS (HTTP Live Streaming) with this mode.

For this mode, StreamGear API provides exclusive transcode_source() method to easily process audio-video files into streamable chunks.

This mode can be easily activated by assigning suitable video path as input to -video_source attribute of stream_params dictionary parameter, during StreamGear initialization.

New in v0.2.2

Apple HLS support was added in v0.2.2.

Please Remember

"},{"location":"gears/streamgear/ssm/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through StreamGear Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/streamgear/ssm/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/streamgear/ssm/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/streamgear/ssm/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/streamgear/ssm/usage/","title":"Usage Examples","text":""},{"location":"gears/streamgear/ssm/usage/#streamgear-api-usage-examples-single-source-mode","title":"StreamGear API Usage Examples: Single-Source Mode","text":"

Important Information

DEPRECATION NOTICES for v0.3.3 and above 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:

After going through following Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/streamgear/ssm/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with StreamGear API in Single-Source Mode:

If input video-source (i.e. -video_source) contains any audio stream/channel, then it automatically gets mapped to all generated streams.

DASHHLS
# import required libraries\nfrom vidgear.gears import StreamGear\n\n# activate Single-Source Mode with valid video input\nstream_params = {\"-video_source\": \"foo.mp4\"}\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n# transcode source\nstreamer.transcode_source()\n# close\nstreamer.close()\n

After running this bare-minimum example, StreamGear will produce a Manifest file (dash_out.mpd) with streamable chunks, containing information about a Primary Stream with the same resolution and framerate as the input.

# import required libraries\nfrom vidgear.gears import StreamGear\n\n# activate Single-Source Mode with valid video input\nstream_params = {\"-video_source\": \"foo.mp4\"}\n# describe a suitable master playlist location/name and assign params\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", **stream_params)\n# transcode source\nstreamer.transcode_source()\n# close\nstreamer.close()\n

After running this bare-minimum example, StreamGear will produce a Master Playlist file (hls_out.mpd) with streamable chunks, containing information about a Primary Stream with the same resolution and framerate as the input.

"},{"location":"gears/streamgear/ssm/usage/#usage-with-additional-streams","title":"Usage with Additional Streams","text":"

In addition to the Primary Stream, you can easily generate any number of additional Secondary Streams with variable bitrate or spatial resolutions, using the exclusive -streams attribute of the stream_params dictionary parameter.

To generate Secondary Streams, add each desired resolution and bitrate/framerate as a list of dictionaries to the -streams attribute. StreamGear will handle the rest automatically. The complete example is as follows:

A more detailed information on -streams attribute can be found here \u27b6

If input video-source (i.e. -video_source) contains any audio stream/channel, then it automatically gets mapped to all generated streams without any extra efforts.

Important Information about -streams attribute

Always use the -streams attribute to define additional streams safely. Duplicate or incorrect definitions can break the transcoding pipeline and corrupt the output chunks.

DASHHLS
# import required libraries\nfrom vidgear.gears import StreamGear\n\n# activate Single-Source Mode and also define various streams\nstream_params = {\n    \"-video_source\": \"foo.mp4\",\n    \"-streams\": [\n        {\"-resolution\": \"1920x1080\", \"-video_bitrate\": \"4000k\"},  # Stream1: 1920x1080 at 4000kbs bitrate\n        {\"-resolution\": \"1280x720\", \"-framerate\": 30.0},  # Stream2: 1280x720 at 30fps framerate\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream3: 640x360 at 60fps framerate\n        {\"-resolution\": \"320x240\", \"-video_bitrate\": \"500k\"},  # Stream3: 320x240 at 500kbs bitrate\n    ],\n}\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n# transcode source\nstreamer.transcode_source()\n# close\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import StreamGear\n\n# activate Single-Source Mode and also define various streams\nstream_params = {\n    \"-video_source\": \"foo.mp4\",\n    \"-streams\": [\n        {\"-resolution\": \"1920x1080\", \"-video_bitrate\": \"4000k\"},  # Stream1: 1920x1080 at 4000kbs bitrate\n        {\"-resolution\": \"1280x720\", \"-framerate\": 30.0},  # Stream2: 1280x720 at 30fps framerate\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream3: 640x360 at 60fps framerate\n        {\"-resolution\": \"320x240\", \"-video_bitrate\": \"500k\"},  # Stream3: 320x240 at 500kbs bitrate\n    ],\n}\n# describe a suitable master playlist location/name and assign params\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", **stream_params)\n# transcode source\nstreamer.transcode_source()\n# close\nstreamer.close()\n

"},{"location":"gears/streamgear/ssm/usage/#usage-with-custom-audio-input","title":"Usage with Custom Audio-Input","text":"

In single source mode, by default, if the input video source (i.e., -video_source) contains audio, it gets automatically mapped to all generated streams. However, if you want to add a custom audio source, you can use the exclusive -audio attribute of the stream_params dictionary parameter.

To add a custom audio source, provide the path to your audio file as a string to the -audio attribute. The API will automatically validate and map the audio to all generated streams. The complete example is as follows:

Ensure the provided -audio audio source is compatible with the input video source (-video_source). Incompatibility can cause multiple errors or result in no output at all.

You can also assign a valid audio URL as input instead of a file path. More details can be found here \u27b6

DASHHLS
# import required libraries\nfrom vidgear.gears import StreamGear\n\n# activate Single-Source Mode and various streams, along with custom audio\nstream_params = {\n    \"-video_source\": \"foo.mp4\",\n    \"-streams\": [\n        {\"-resolution\": \"1280x720\", \"-video_bitrate\": \"4000k\"},  # Stream1: 1280x720 at 4000kbs bitrate\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream2: 640x360 at 60fps\n    ],\n    \"-audio\": \"/home/foo/foo1.aac\", # define custom audio-source\n    \"-acodec\": \"copy\", # define copy audio encoder\n}\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n# transcode source\nstreamer.transcode_source()\n# close\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import StreamGear\n\n# activate Single-Source Mode and various streams, along with custom audio\nstream_params = {\n    \"-video_source\": \"foo.mp4\",\n    \"-streams\": [\n        {\"-resolution\": \"1280x720\", \"-video_bitrate\": \"4000k\"},  # Stream1: 1280x720 at 4000kbs bitrate\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream2: 640x360 at 60fps\n    ],\n    \"-audio\": \"/home/foo/foo1.aac\",  # define custom audio-source\n    \"-acodec\": \"copy\", # define copy audio encoder\n}\n# describe a suitable master playlist location/name and assign params\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", **stream_params)\n# transcode source\nstreamer.transcode_source()\n# close\nstreamer.close()\n

"},{"location":"gears/streamgear/ssm/usage/#usage-with-variable-ffmpeg-parameters","title":"Usage with Variable FFmpeg Parameters","text":"

For fine-grained control over the transcoding process, StreamGear provides a highly extensible and flexible wrapper around FFmpeg library and access to almost all of its configurational parameter.

In this example, we'll use the H.265/HEVC video encoder and AAC audio encoder, apply various optimal FFmpeg configurational parameters.

This example assumes that the given input video source (-video_source) contains at least one audio stream.

This example is just conveying the idea on how to use FFmpeg's internal encoders/parameters with StreamGear API. You can use any FFmpeg parameter in the similar manner.

Please read the FFmpeg Documentation carefully before passing any additional values to the stream_params parameter. Incorrect values may cause errors or result in no output.

DASHHLS
# import required libraries\nfrom vidgear.gears import StreamGear\n\n# activate Single-Source Mode and various other parameters\nstream_params = {\n    \"-video_source\": \"foo.mp4\", # define Video-Source\n    \"-vcodec\": \"libx265\", # specify H.265/HEVC video encoder\n    \"-x265-params\": \"lossless=1\", # enables Lossless encoding\n    \"-bpp\": 0.15, # Bits-Per-Pixel(BPP), an Internal StreamGear parameter to ensure good quality of high motion scenes\n    \"-streams\": [\n        {\"-resolution\": \"640x360\", \"-video_bitrate\": \"4000k\"}, # Stream1: 1280x720 at 4000kbs bitrate\n        {\"-resolution\": \"320x240\", \"-framerate\": 60.0},  # Stream2: 640x360 at 60fps\n    ],\n    \"-acodec\": \"aac\", # specify AAC audio encoder\n}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"dash_out.mpd\", logging=True, **stream_params)\n# transcode source\nstreamer.transcode_source()\n# close\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import StreamGear\n\nstream_params = {\n    \"-video_source\": \"foo.mp4\", # define Video-Source\n    \"-vcodec\": \"libx265\", # specify H.265/HEVC video encoder\n    \"-x265-params\": \"lossless=1\", # enables Lossless encoding\n    \"-bpp\": 0.15, # Bits-Per-Pixel(BPP), an Internal StreamGear parameter to ensure good quality of high motion scenes\n    \"-streams\": [\n        {\"-resolution\": \"640x360\", \"-video_bitrate\": \"4000k\"}, # Stream1: 1280x720 at 4000kbs bitrate\n        {\"-resolution\": \"320x240\", \"-framerate\": 60.0},  # Stream2: 640x360 at 60fps\n    ],\n    \"-acodec\": \"aac\", # specify AAC audio encoder\n}\n\n# describe a suitable master playlist file location/name and assign params\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", logging=True, **stream_params)\n# transcode source\nstreamer.transcode_source()\n# close\nstreamer.close()\n

  1. In Real-time Frames Mode, the Primary Stream's framerate defaults to -input_framerate attribute value, if defined, else it will be 25fps.\u00a0\u21a9

"},{"location":"gears/videogear/overview/","title":"Overview","text":""},{"location":"gears/videogear/overview/#videogear-api","title":"VideoGear API","text":"VideoGear API's generalized workflow"},{"location":"gears/videogear/overview/#overview","title":"Overview","text":"

VideoGear API provides a special internal wrapper around VidGear's exclusive Video Stabilizer class.

VideoGear also acts as a Common Video-Capture API that provides internal access for both CamGear and PiGear APIs and their parameters with an exclusive enablePiCamera boolean flag.

VideoGear is ideal when you need to switch to different video sources without changing your code much. Also, it enables easy stabilization for various video-streams (real-time or not) with minimum effort and writing way fewer lines of code.

Helpful Tips

"},{"location":"gears/videogear/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through VideoGear Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/videogear/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/videogear/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/videogear/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/videogear/params/","title":"Parameters","text":""},{"location":"gears/videogear/params/#videogear-api-parameters","title":"VideoGear API Parameters","text":"

VideoGear acts as a Common Video-Capture API that provides internal access for both CamGear and PiGear APIs and their parameters.

"},{"location":"gears/videogear/params/#enablepicamera","title":"enablePiCamera","text":"

This parameter provide direct access to PiGear or CamGear APIs respectively in VideoGear. This means the if enablePiCamera flag is True, the PiGear API will be accessed, and if False, the CamGear API will be accessed.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

VideoGear(enablePiCamera=True) # enable access to PiGear API\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/videogear/params/#parameters-for-stabilizer-backend","title":"Parameters for Stabilizer Backend","text":"

Enable this backend with stabilize=True in VideoGear.

"},{"location":"gears/videogear/params/#stabilize","title":"stabilize","text":"

This parameter enable access to Stabilizer Class for stabilizing frames, i.e. can be set to True(to enable) or unset to False(to disable).

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

VideoGear(stabilize=True) # enable stablization\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/videogear/params/#options","title":"options","text":"

This parameter can be used in addition, to pass user-defined parameters supported by Stabilizer Class. These parameters can be formatted as this parameter's attribute.

Supported dictionary attributes for Stabilizer Class are:

"},{"location":"gears/videogear/params/#parameters-for-camgear-backend","title":"Parameters for CamGear backend","text":"

Enable this backend with enablePiCamera=False in VideoGear. Default is also False.

"},{"location":"gears/videogear/params/#source","title":"source","text":"

VideoGear API will throw RuntimeError if source provided is invalid.

This parameter defines the source for the input stream.

Data-Type: Based on input.

Default Value: Its default value is 0.

Its valid input can be one of the following:

"},{"location":"gears/videogear/params/#stream_mode","title":"stream_mode","text":"

This parameter controls the Stream Mode, .i.e if enabled(stream_mode=True), the VideoGear API will interpret the given source input as YouTube URL address.

Due to a FFmpeg bug that causes video to freeze frequently in OpenCV, It is advised to always use GStreamer backend (backend=cv2.CAP_GSTREAMER) for any livestreams (such as Twitch).

VideoGear automatically enforce GStreamer backend (backend=cv2.CAP_GSTREAMER) for YouTube-livestreams!

VideoGear will exit with RuntimeError for YouTube livestreams, if OpenCV is not compiled with GStreamer(>=v1.0.0) support. Checkout this FAQ for compiling OpenCV with GStreamer support.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

VideoGear(source='https://youtu.be/bvetuLwJIkA', stream_mode=True)\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/videogear/params/#backend","title":"backend","text":"

This parameter manually selects the backend for OpenCV's VideoCapture class (only if specified).

Data-Type: Integer

Default Value: Its default value is 0

Usage:

All supported backends are listed here \u27b6

Its value can be for e.g. backend = cv2.CAP_DSHOW for selecting Direct Show as backend:

VideoGear(source=0, backend = cv2.CAP_DSHOW)\n

"},{"location":"gears/videogear/params/#options_1","title":"options","text":"

This parameter provides the ability to alter various Source Tweak Parameters available within OpenCV's VideoCapture API properties.

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

All supported parameters are listed here \u27b6

The desired parameters can be passed to VideoGear API by formatting them as this parameter's attributes, as follows:

# formatting parameters as dictionary attributes\noptions = {\"CAP_PROP_FRAME_WIDTH\":320, \"CAP_PROP_FRAME_HEIGHT\":240, \"CAP_PROP_FPS\":60}\n# assigning it\nVideoGear(source=0, **options)\n

"},{"location":"gears/videogear/params/#parameters-for-pigear-backend","title":"Parameters for PiGear backend","text":"

Enable this backend with enablePiCamera=True in VideoGear.

"},{"location":"gears/videogear/params/#camera_num","title":"camera_num","text":"

This parameter selects the camera index to be used as the source, allowing you to drive these multiple cameras simultaneously from within a single Python session. Its value can only be zero or greater, otherwise, VideoGear API will throw ValueError for any negative value.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

# select Camera Module at index `1`\nVideoGear(enablePiCamera=True, camera_num=1)\n

The complete usage example demonstrating the usage of the camera_num parameter is available here \u27b6.

"},{"location":"gears/videogear/params/#resolution","title":"resolution","text":"

This parameter controls the resolution - a tuple (i.e. (width,height)) of two values giving the width and height of the output frames.

Make sure both width and height values should be at least 64.

When using the Picamera2 backend, the resolution parameter will be OVERRIDDEN, if the user explicitly defines the output_size property of the sensor configurational parameter.

Data-Type: Tuple

Default Value: Its default value is (640,480).

Usage:

VideoGear(enablePiCamera=True, resolution=(1280,720)) # sets 1280x720 resolution\n

"},{"location":"gears/videogear/params/#framerate","title":"framerate","text":"

This parameter controls the framerate of the source.

Data-Type: integer/float

Default Value: Its default value is 30.

Usage:

VideoGear(enablePiCamera=True, framerate=60) # sets 60fps framerate\n

"},{"location":"gears/videogear/params/#options_2","title":"options","text":"

This dictionary parameter in the internal PiGear API backend allows you to control various camera settings for both the picamera2 and legacy picamera backends and some internal API tasks. These settings include:

"},{"location":"gears/videogear/params/#a-configurational-camera-parameters","title":"A. Configurational Camera Parameters","text":""},{"location":"gears/videogear/params/#b-user-defined-parameters","title":"B. User-defined Parameters","text":"

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

The complete usage example demonstrating the usage of the options parameter is available here \u27b6.

You can format these user-defined and configurational parameters as attributes of this options dictionary parameter as follows:

New Picamera2 backendLegacy Picamera backend
# formulate various Picamera2 API parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"exposure_compensation\": 15,\n    \"sensor\": {\"output_size\": (480, 320)},  # !!! will override `resolution` !!!\n}\n\n# open pi video stream with defined parameters\nstream = VideoGear(enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options).start()\n
# formulate various Picamera API parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = VideoGear(enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options).start()\n

"},{"location":"gears/videogear/params/#common-parameters","title":"Common Parameters","text":"

These are common parameters that works with every backend in VideoGear.

"},{"location":"gears/videogear/params/#colorspace","title":"colorspace","text":"

This parameter selects the colorspace of the source stream.

Data-Type: String

Default Value: Its default value is None.

Usage:

All supported colorspace values are given here \u27b6

VideoGear(colorspace=\"COLOR_BGR2HSV\")\n

Its complete usage example is given here \u27b6

"},{"location":"gears/videogear/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

VideoGear(logging=True)\n

"},{"location":"gears/videogear/params/#time_delay","title":"time_delay","text":"

This parameter set the time delay (in seconds) before the VideoGear API start reading the frames. This delay is only required if the source required some warm-up delay before starting up.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

VideoGear(time_delay=1)  # set 1 seconds time delay\n

"},{"location":"gears/videogear/usage/","title":"Usage Examples","text":""},{"location":"gears/videogear/usage/#videogear-api-usage-examples","title":"VideoGear API Usage Examples:","text":"

After going through following Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/videogear/usage/#bare-minimum-usage-with-camgear-backend","title":"Bare-Minimum Usage with CamGear backend","text":"

VideoGear by default provides direct internal access to CamGear API.

Following is the bare-minimum code you need to access CamGear API with VideoGear:

# import required libraries\nfrom vidgear.gears import VideoGear\nimport cv2\n\n\n# open any valid video stream(for e.g `myvideo.avi` file)\nstream = VideoGear(source=\"myvideo.avi\").start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/videogear/usage/#bare-minimum-usage-with-pigear-backend","title":"Bare-Minimum Usage with PiGear backend","text":"

VideoGear contains a special enablePiCamera flag that when True provides internal access to PiGear API.

Following is the bare-minimum code you need to access PiGear API with VideoGear:

Under the hood, PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, PiGear API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this API, otherwise nothing will work.

# import required libraries\nfrom vidgear.gears import VideoGear\nimport cv2\n\n# enable enablePiCamera boolean flag to access PiGear API backend\nstream = VideoGear(enablePiCamera=True).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/videogear/usage/#using-videogear-with-video-stabilizer-backend","title":"Using VideoGear with Video Stabilizer backend","text":"

VideoGear API provides a special internal wrapper around VidGear's Exclusive Video Stabilizer class and provides easy way of activating stabilization for various video-streams (real-time or not) with its stabilize boolean parameter during initialization.

The usage example is as follows:

For a more detailed information on Video-Stabilizer Class, Read here \u27b6

The stabilizer might be slower for High-Quality/Resolution videos-frames.

# import required libraries\nfrom vidgear.gears import VideoGear\nimport numpy as np\nimport cv2\n\n# open any valid video stream with stabilization enabled(`stabilize = True`)\nstream_stab = VideoGear(source=\"test.mp4\", stabilize=True).start()\n\n# loop over\nwhile True:\n\n    # read stabilized frames\n    frame_stab = stream_stab.read()\n\n    # check for stabilized frame if None-type\n    if frame_stab is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Stabilized Output\", frame_stab)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close streams\nstream_stab.stop()\n

"},{"location":"gears/videogear/usage/#advanced-videogear-usage-with-camgear-backend","title":"Advanced VideoGear usage with CamGear Backend","text":"

VideoGear provides internal access to both CamGear and PiGear APIs, and thereby all additional parameters of PiGear API or CamGear API are also easily accessible within VideoGear API.

The usage example of VideoGear API with Variable Camera Properties is as follows:

This example demonstrates how to use the VideoGear API in a similar manner to the CamGear's example for controlling variable source properties. Any CamGear usage example can be implemented using the VideoGear API in a similar way.

All the supported Source Tweak Parameters can be found here \u27b6

# import required libraries\nfrom vidgear.gears import VideoGear\nimport cv2\n\n\n# define suitable tweak parameters for your stream.\noptions = {\n    \"CAP_PROP_FRAME_WIDTH\": 320, # resolution 320x240\n    \"CAP_PROP_FRAME_HEIGHT\": 240,\n    \"CAP_PROP_FPS\": 60, # framerate 60fps\n}\n\n# To open live video stream on webcam at first index(i.e. 0) \n# device and apply source tweak parameters\nstream = VideoGear(source=0, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/videogear/usage/#advanced-videogear-usage-with-pigear-backend","title":"Advanced VideoGear usage with PiGear Backend","text":"

VideoGear provides internal access to both CamGear and PiGear APIs, and thereby all additional parameters of PiGear API or CamGear API are also easily accessible within VideoGear API.

The usage example of VideoGear API with Variable Camera Properties is as follows:

This example demonstrates how to use the VideoGear API in a similar manner to the PiGear's example for using variable camera properties. Any PiGear usage example can be implemented using the VideoGear API in a similar way.

Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this backend, otherwise nothing will work.

New Picamera2 backendLegacy Picamera backend
# import required libraries\nfrom vidgear.gears import VideoGear\nfrom libcamera import Transform\nimport cv2\n\n# formulate various Picamera2 API \n# configurational parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream with defined parameters\nstream = VideoGear(enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import required libraries\nfrom vidgear.gears import VideoGear\nimport cv2\n\n# formulate various Picamera API \n# configurational parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = VideoGear(enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/videogear/usage/#using-videogear-with-colorspace-manipulation","title":"Using VideoGear with Colorspace Manipulation","text":"

VideoGear API also supports Colorspace Manipulation but NOT Direct like other VideoCapture Gears.

Important: color_space global variable is NOT Supported in VideoGear API

In following example code, we will convert source colorspace to HSV on initialization:

# import required libraries\nfrom vidgear.gears import VideoGear\nimport cv2\n\n# Open any source of your choice, like Webcam first index(i.e. 0) and change its colorspace to `HSV`\nstream = VideoGear(source=0, colorspace=\"COLOR_BGR2HSV\", logging=True).start()\n\n# loop over\nwhile True:\n\n    # read HSV frames\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the HSV frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for key if pressed\n    key = cv2.waitKey(1) & 0xFF\n\n    # check for 'q' key is pressed\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/videogear/usage/#bonus-examples","title":"Bonus Examples","text":"

Checkout more advanced VideoGear examples with unusual configuration here \u27b6

"},{"location":"gears/webgear/advanced/","title":"Advanced Usages","text":""},{"location":"gears/webgear/advanced/#webgear-api-advanced-usage","title":"WebGear API Advanced Usage:","text":"

This is a continuation of the WebGear doc \u27b6. Thereby, It's advised to first get familiarize with this API, and its requirements.

After going through following Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/webgear/advanced/#using-webgear-with-variable-colorspace","title":"Using WebGear with Variable Colorspace","text":"

WebGear by default only supports \"BGR\" colorspace frames as input, but you can use jpeg_compression_colorspace string attribute through its options dictionary parameter to specify incoming frames colorspace.

Let's implement a bare-minimum example using WebGear, where we will be sending GRAY frames to client browser:

New in v0.2.2

This example was added in v0.2.2.

This example works in conjunction with Source ColorSpace manipulation for VideoCapture Gears \u27b6

Supported jpeg_compression_colorspace colorspace values are RGB, BGR, RGBX, BGRX, XBGR, XRGB, GRAY, RGBA, BGRA, ABGR, ARGB, CMYK. More information can be found here \u27b6

# import required libraries\nimport uvicorn\nfrom vidgear.gears.asyncio import WebGear\n\n# various performance tweaks and enable grayscale input\noptions = {\n    \"frame_size_reduction\": 25,\n    \"jpeg_compression_colorspace\": \"GRAY\",  # set grayscale\n    \"jpeg_compression_quality\": 90,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# initialize WebGear app and change its colorspace to grayscale\nweb = WebGear(\n    source=\"foo.mp4\", colorspace=\"COLOR_BGR2GRAY\", logging=True, **options\n)\n\n# run this app on Uvicorn server at address http://0.0.0.0:8000/\nuvicorn.run(web(), host=\"0.0.0.0\", port=8000)\n\n# close app safely\nweb.shutdown()\n

And that's all, Now you can see output at http://localhost:8000/ address on your local machine.

"},{"location":"gears/webgear/advanced/#using-webgear-with-a-custom-sourceopencv","title":"Using WebGear with a Custom Source(OpenCV)","text":"New in v0.2.1

This example was added in v0.2.1.

WebGear allows you to easily define your own custom Source that you want to use to transform your frames before sending them onto the browser.

JPEG Frame-Compression and all of its performance enhancing attributes are disabled with a Custom Source!

Let's implement a bare-minimum example with a Custom Source using WebGear API and OpenCV:

# import necessary libs\nimport uvicorn, asyncio, cv2\nfrom vidgear.gears.asyncio import WebGear\nfrom vidgear.gears.asyncio.helper import reducer\n\n# initialize WebGear app without any source\nweb = WebGear(logging=True)\n\n# create your own custom frame producer\nasync def my_frame_producer():\n\n    # !!! define your own video source here !!!\n    # Open any video stream such as live webcam \n    # video stream on first index(i.e. 0) device\n    stream = cv2.VideoCapture(0)\n    # loop over frames\n    while True:\n        # read frame from provided source\n        (grabbed, frame) = stream.read()\n        # break if NoneType\n        if not grabbed:\n            break\n\n        # do something with your OpenCV frame here\n\n        # reducer frames size if you want more performance otherwise comment this line\n        frame = await reducer(frame, percentage=30, interpolation=cv2.INTER_AREA)  # reduce frame by 30%\n        # handle JPEG encoding\n        encodedImage = cv2.imencode(\".jpg\", frame)[1].tobytes()\n        # yield frame in byte format\n        yield (b\"--frame\\r\\nContent-Type:image/jpeg\\r\\n\\r\\n\" + encodedImage + b\"\\r\\n\")\n        await asyncio.sleep(0)\n    # close stream\n    stream.release()\n\n\n# add your custom frame producer to config\nweb.config[\"generator\"] = my_frame_producer\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

And that's all, Now you can see output at http://localhost:8000/ address.

"},{"location":"gears/webgear/advanced/#using-webgear-with-custom-mounting-points","title":"Using WebGear with Custom Mounting Points","text":"

With our highly extensible WebGear API, you can add your own mounting points, where additional files located, as follows:

# import libs\nimport uvicorn\nfrom starlette.routing import Mount\nfrom starlette.staticfiles import StaticFiles\nfrom vidgear.gears.asyncio import WebGear\n\n# various performance tweaks\noptions = {\n    \"frame_size_reduction\": 40,\n    \"jpeg_compression_quality\": 80,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": False,\n}\n\n# initialize WebGear app\nweb = WebGear(\n    source=\"foo.mp4\", logging=True, **options\n)  # enable source i.e. `test.mp4` and enable `logging` for debugging\n\n# append new route i.e. mount another folder called `test` located at `/home/foo/.vidgear/test` directory\nweb.routes.append(\n    Mount(\"/test\", app=StaticFiles(directory=\"/home/foo/.vidgear/test\"), name=\"test\")\n)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

Then you can use this folder in your HTML page, to host data-files. For example, if we have jQuery script jquery-3.3.1.slim.min.js in this folder and want to integrate it, then, we can do something like this:

<script src=\"{{ url_for('test', path='jquery-3.3.1.slim.min.js') }}\"></script>\n

"},{"location":"gears/webgear/advanced/#using-webgear-with-custom-webpage-routes","title":"Using WebGear with Custom Webpage Routes","text":"

With Webgear's flexible API, you can even add your additional HTML Static webpages without any extra efforts.

Suppose we want to add a simple hello world webpage to our WebGear server. So let's create a bare-minimum hello.html file with HTML code as follows:

<html>\n   <header>\n      <title>This is Hello world page</title>\n   </header>\n   <body>\n      <h1>Hello World</h1>\n      <p>how ya doing?</p>\n   </body>\n</html>\n

Then in our application code, we can integrate this webpage route, as follows:

# import libs\nimport uvicorn, asyncio\nfrom starlette.templating import Jinja2Templates\nfrom starlette.routing import Route\nfrom vidgear.gears.asyncio import WebGear\n\n# Build out Jinja2 template render at `/home/foo/.vidgear/custom_template` path in which our `hello.html` file is located\ntemplate = Jinja2Templates(directory=\"/home/foo/.vidgear/custom_template\")\n\n# render and return our webpage template\nasync def hello_world(request):\n    page = \"hello.html\"\n    context = {\"request\": request}\n    return template.TemplateResponse(page, context)\n\n\n# add various performance tweaks as usual\noptions = {\n    \"frame_size_reduction\": 40,\n    \"jpeg_compression_quality\": 80,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": False,\n}\n\n# initialize WebGear app with a valid source\nweb = WebGear(\n    source=\"/home/foo/foo1.mp4\", logging=True, **options\n)  # enable source i.e. `test.mp4` and enable `logging` for debugging\n\n# append new route to point our rendered webpage\nweb.routes.append(Route(\"/hello\", endpoint=hello_world))\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n
And that's all, Now you can see output at http://localhost:8000/hello address.

"},{"location":"gears/webgear/advanced/#using-webgear-with-middlewares","title":"Using WebGear with MiddleWares","text":"

WebGear natively supports ASGI middleware classes with Starlette for implementing behavior that is applied across your entire ASGI application easily.

New in v0.2.2

This example was added in v0.2.2.

All supported middlewares can be found here \u27b6

For this example, let's use CORSMiddleware for implementing appropriate CORS headers to outgoing responses in our application in order to allow cross-origin requests from browsers, as follows:

The default parameters used by the CORSMiddleware implementation are restrictive by default, so you'll need to explicitly enable particular origins, methods, or headers, in order for browsers to be permitted to use them in a Cross-Domain context.

Starlette provides several arguments for enabling origins, methods, or headers for CORSMiddleware API. More information can be found here \u27b6

# import libs\nimport uvicorn, asyncio\nfrom starlette.middleware import Middleware\nfrom starlette.middleware.cors import CORSMiddleware\nfrom vidgear.gears.asyncio import WebGear\n\n# add various performance tweaks as usual\noptions = {\n    \"frame_size_reduction\": 40,\n    \"jpeg_compression_quality\": 80,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": False,\n}\n\n# initialize WebGear app with a valid source\nweb = WebGear(\n    source=\"/home/foo/foo1.mp4\", logging=True, **options\n)  # enable source i.e. `test.mp4` and enable `logging` for debugging\n\n# define and assign suitable cors middlewares\nweb.middleware = [\n    Middleware(\n        CORSMiddleware,\n        allow_origins=[\"*\"],\n        allow_credentials=True,\n        allow_methods=[\"*\"],\n        allow_headers=[\"*\"],\n    )\n]\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n
And that's all, Now you can see output at http://localhost:8000 address.

"},{"location":"gears/webgear/advanced/#rules-for-altering-webgear-files-and-folders","title":"Rules for Altering WebGear Files and Folders","text":"

WebGear gives us complete freedom of altering data files generated in Auto-Generation Process, But you've to keep the following rules in mind:

"},{"location":"gears/webgear/advanced/#rules-for-altering-data-files","title":"Rules for Altering Data Files","text":""},{"location":"gears/webgear/advanced/#rules-for-altering-data-folders","title":"Rules for Altering Data Folders","text":""},{"location":"gears/webgear/advanced/#bonus-examples","title":"Bonus Examples","text":"

Checkout more advanced WebGear examples with unusual configuration here \u27b6

"},{"location":"gears/webgear/overview/","title":"Overview","text":""},{"location":"gears/webgear/overview/#webgear-api","title":"WebGear API","text":"WebGear API's Video Server running at http://localhost:8000/ address."},{"location":"gears/webgear/overview/#overview","title":"Overview","text":"

WebGear is a powerful ASGI Video-Broadcaster API ideal for transmitting Motion-JPEG-frames from a single source to multiple recipients via the browser.

WebGear API works on Starlette's ASGI application and provides a highly extensible and flexible async wrapper around its complete framework. WebGear can flexibly interact with Starlette's ecosystem of shared middleware, mountable applications, Response classes, Routing tables, Static Files, Templating engine(with Jinja2), etc.

WebGear API uses an intraframe-only compression scheme under the hood where the sequence of video-frames are first encoded as JPEG-DIB (JPEG with Device-Independent Bit compression) and then streamed over HTTP using Starlette's Multipart Streaming Response and a Uvicorn ASGI Server. This method imposes lower processing and memory requirements, but the quality is not the best, since JPEG compression is not very efficient for motion video.

In layman's terms, WebGear acts as a powerful Video Broadcaster that transmits live video-frames to any web-browser in the network. Additionally, WebGear API also provides internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs, thereby granting it exclusive power for transferring frames incoming from any source to the network.

"},{"location":"gears/webgear/overview/#data-files-auto-generation-workflow-for-webgear","title":"Data-Files Auto-Generation WorkFlow for WebGear","text":"Disabling Auto-Generation process in WebGear

Starting with vidgear v0.3.0, you can now completely disable Auto-Generation process in WebGear API using skip_generate_webdata optional boolean attribute. When {skip_generate_webdata:True}, no default data files will be downloaded or validated during initialization.

Only /video route is available when {skip_generate_webdata:True} in WebGear API. All other default routes will be JSONResponses with 404/500 status codes.

Customizing default video endpoint path

Starting with vidgear v0.3.1, you can change default /video video endpoint path to any alphanumeric string value, using custom_video_endpoint optional string attribute. For example:

Only alphanumeric string with no space in between are allowed as custom_video_endpoint value. Any other value will be discarded.

WebGear's Default Theme which expects only default /video video endpoint path, will fail to work, if it is customized to any other value using this custom_video_endpoint attribute.

# custom alphanumeric video endpoint string\noptions = {\"custom_video_endpoint\": \"xyz\"}\n\n# initialize WebGear app\nweb = WebGear(source=\"foo.mp4\", logging=True, **options)\n
Hence, default video endpoint will now be available at /xyz path.

On initializing WebGear API, it automatically checks for three critical data files(i.e index.html, 404.html & 500.html) inside the templates folder of the webgear directory at the default location which gives rise to the following two possible scenario:

"},{"location":"gears/webgear/overview/#default-location","title":"Default Location","text":"

Identifying Default location

You can set logging=True during initialization, for easily identifying the selected default location, which will be something like this on a Linux machine:

WebGear :: DEBUG :: `/home/foo/.vidgear` is the default location for saving WebGear data-files.\n
"},{"location":"gears/webgear/overview/#auto-generation-process","title":"Auto-Generation process","text":"

Info

"},{"location":"gears/webgear/overview/#webgears-default-template","title":"WebGear's Default Template","text":"New in v0.2.1

New Standalone WebGear's Default Theme was added in v0.2.1.

The WebGear API by default uses simple & elegant WebGear's Default Theme which looks like something as follows:

"},{"location":"gears/webgear/overview/#indexhtml","title":"Index.html","text":"

Can be accessed by visiting WebGear app server, running at http://localhost:8000/:

"},{"location":"gears/webgear/overview/#404html","title":"404.html","text":"

Appears when respective URL is not found, for example http://localhost:8000/ok:

"},{"location":"gears/webgear/overview/#500html","title":"500.html","text":"

Appears when an API Error is encountered:

If logging is enabled and an error occurs, then instead of displaying this 500 handler, WebGear will respond with a traceback response.

"},{"location":"gears/webgear/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through WebGear Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/webgear/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/webgear/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/webgear/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/webgear/params/","title":"Parameters","text":""},{"location":"gears/webgear/params/#webgear-api-parameters","title":"WebGear API Parameters","text":"

WebGear provides a special internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs and their parameters.

"},{"location":"gears/webgear/params/#enablepicamera","title":"enablePiCamera","text":"

This parameter provide direct access to PiGear or CamGear APIs respectively in WebGear. This means the if enablePiCamera flag is True, the PiGear API will be accessed, and if False, the CamGear API will be accessed.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

WebGear(enablePiCamera=True) # enable access to PiGear API\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/webgear/params/#options","title":"options","text":"

This parameter can be used to pass user-defined parameter to WebGear API by formatting them as this parameter's attribute.

Data-Type: Dictionary

Default Value: Its default value is {}

"},{"location":"gears/webgear/params/#webgear-specific-attributes","title":"WebGear Specific attributes","text":"

"},{"location":"gears/webgear/params/#parameters-for-stabilizer-backend","title":"Parameters for Stabilizer Backend","text":"

Enable this backend with stabilize=True in WebGear.

"},{"location":"gears/webgear/params/#stabilize","title":"stabilize","text":"

This parameter enable access to Stabilizer Class for stabilizing frames, i.e. can be set to True(to enable) or unset to False(to disable).

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

WebGear(stabilize=True) # enable stablization\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/webgear/params/#options_1","title":"options","text":"

This parameter can be used in addition, to pass user-defined parameters supported by Stabilizer Class. These parameters can be formatted as this parameter's attribute.

Supported dictionary attributes for Stabilizer Class are:

"},{"location":"gears/webgear/params/#parameters-for-camgear-backend","title":"Parameters for CamGear backend","text":"

Enable this backend with enablePiCamera=False in WebGear. Default is also False.

"},{"location":"gears/webgear/params/#source","title":"source","text":"

WebGear API will throw RuntimeError if source provided is invalid.

This parameter defines the source for the input stream.

Data-Type: Based on input.

Default Value: Its default value is 0.

Its valid input can be one of the following:

"},{"location":"gears/webgear/params/#stream_mode","title":"stream_mode","text":"

This parameter controls the Stream Mode, .i.e if enabled(stream_mode=True), the CamGear API will interpret the given source input as YouTube URL address.

Due to a FFmpeg bug that causes video to freeze frequently in OpenCV, It is advised to always use GStreamer backend for any livestream videos. Checkout this FAQ for compiling OpenCV with GStreamer support.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

Supported Streaming Websites

The complete list of all supported Streaming Websites URLs can be found here \u27b6

WebGear(source='https://youtu.be/bvetuLwJIkA', stream_mode=True)\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/webgear/params/#backend","title":"backend","text":"

This parameter manually selects the backend for OpenCV's VideoCapture class (only if specified).

Data-Type: Integer

Default Value: Its default value is 0

Usage:

All supported backends are listed here \u27b6

Its value can be for e.g. backend = cv2.CAP_DSHOW for selecting Direct Show as backend:

WebGear(source=0, backend = cv2.CAP_DSHOW)\n

"},{"location":"gears/webgear/params/#options_2","title":"options","text":"

This parameter provides the ability to alter various Source Tweak Parameters available within OpenCV's VideoCapture API properties.

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

All supported parameters are listed here \u27b6

The desired parameters can be passed to WebGear API by formatting them as this parameter's attributes, as follows:

# formatting parameters as dictionary attributes\noptions = {\"CAP_PROP_FRAME_WIDTH\":320, \"CAP_PROP_FRAME_HEIGHT\":240, \"CAP_PROP_FPS\":60}\n# assigning it\nWebGear(source=0, **options)\n

"},{"location":"gears/webgear/params/#parameters-for-pigear-backend","title":"Parameters for PiGear backend","text":"

Enable this backend with enablePiCamera=True in WebGear.

"},{"location":"gears/webgear/params/#camera_num","title":"camera_num","text":"

This parameter selects the camera index to be used as the source, allowing you to drive these multiple cameras simultaneously from within a single Python session. Its value can only be zero or greater, otherwise, WebGear API will throw ValueError for any negative value.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

# select Camera Module at index `1`\nWebGear(enablePiCamera=True, camera_num=1)\n

The complete usage example demonstrating the usage of the camera_num parameter is available here \u27b6.

"},{"location":"gears/webgear/params/#resolution","title":"resolution","text":"

This parameter controls the resolution - a tuple (i.e. (width,height)) of two values giving the width and height of the output frames.

Make sure both width and height values should be at least 64.

When using the Picamera2 backend, the resolution parameter will be OVERRIDDEN, if the user explicitly defines the output_size property of the sensor configurational parameter.

Data-Type: Tuple

Default Value: Its default value is (640,480).

Usage:

WebGear(enablePiCamera=True, resolution=(1280,720)) # sets 1280x720 resolution\n

"},{"location":"gears/webgear/params/#framerate","title":"framerate","text":"

This parameter controls the framerate of the source.

Data-Type: integer/float

Default Value: Its default value is 30.

Usage:

WebGear(enablePiCamera=True, framerate=60) # sets 60fps framerate\n

"},{"location":"gears/webgear/params/#options_3","title":"options","text":"

This dictionary parameter in the internal PiGear API backend allows you to control various camera settings for both the picamera2 and legacy picamera backends and some internal API tasks. These settings include:

"},{"location":"gears/webgear/params/#a-configurational-camera-parameters","title":"A. Configurational Camera Parameters","text":""},{"location":"gears/webgear/params/#b-user-defined-parameters","title":"B. User-defined Parameters","text":"

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

The complete usage example demonstrating the usage of the options parameter is available here \u27b6.

You can format these user-defined and configurational parameters as attributes of this options dictionary parameter as follows:

New Picamera2 backendLegacy Picamera backend
# formulate various Picamera2 API parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"exposure_compensation\": 15,\n    \"sensor\": {\"output_size\": (480, 320)},  # !!! will override `resolution` !!!\n}\n\n# open pi video stream with defined parameters\nstream = WebGear(enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options).start()\n
# formulate various Picamera API parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = WebGear(enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options).start()\n

"},{"location":"gears/webgear/params/#common-parameters","title":"Common Parameters","text":"

These are common parameters that works with every backend in WebGear.

"},{"location":"gears/webgear/params/#colorspace","title":"colorspace","text":"

This parameter selects the colorspace of the source stream.

Data-Type: String

Default Value: Its default value is None.

Usage:

All supported colorspace values are given here \u27b6

WebGear(colorspace=\"COLOR_BGR2HSV\")\n

Its complete usage example is given here \u27b6

"},{"location":"gears/webgear/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

WebGear(logging=True)\n

"},{"location":"gears/webgear/params/#time_delay","title":"time_delay","text":"

This parameter set the time delay (in seconds) before the WebGear API start reading the frames. This delay is only required if the source required some warm-up delay before starting up.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

WebGear(time_delay=1)  # set 1 seconds time delay\n

"},{"location":"gears/webgear/usage/","title":"Usage Examples","text":""},{"location":"gears/webgear/usage/#webgear-api-usage-examples","title":"WebGear API Usage Examples:","text":""},{"location":"gears/webgear/usage/#requirements","title":"Requirements","text":""},{"location":"gears/webgear/usage/#installation-with-asyncio-support","title":"Installation with Asyncio Support","text":"

WebGear API is the part of asyncio package of VidGear, thereby you need to install VidGear with asyncio support as follows:

pip install vidgear[asyncio]\n
"},{"location":"gears/webgear/usage/#asgi-server","title":"ASGI Server","text":"

You'll also need to install an ASGI Server to run following WebGear usage examples, and by default WebGear ships the state-of-the-art uvicorn Server. But you can also use other ASGI server such as daphne, or hypercorn with it.

"},{"location":"gears/webgear/usage/#performance-enhancements","title":"Performance Enhancements","text":"

WebGear provides certain performance enhancing attributes for its options dictionary parameter to cope with performance-throttling.

Performance Enhancing Attributes

"},{"location":"gears/webgear/usage/#bare-minimum-usage-with-performance-enhancements","title":"Bare-Minimum Usage with Performance Enhancements","text":"

Let's implement our Bare-Minimum usage example with these Performance Enhancing Attributes \u27b6 for speeding up the output.

"},{"location":"gears/webgear/usage/#running-programmatically","title":"Running Programmatically","text":"

You can access and run WebGear VideoStreamer Server programmatically in your python script in just a few lines of code, as follows:

For accessing WebGear on different Client Devices on the network, use \"0.0.0.0\" as host value instead of \"localhost\" on Host Machine. More information can be found here \u27b6

# import required libraries\nimport uvicorn\nfrom vidgear.gears.asyncio import WebGear\n\n# various performance tweaks\noptions = {\n    \"frame_size_reduction\": 40,\n    \"jpeg_compression_quality\": 80,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": False,\n}\n\n# initialize WebGear app\nweb = WebGear(source=\"foo.mp4\", logging=True, **options)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

which can be accessed on any browser on your machine at http://localhost:8000/.

"},{"location":"gears/webgear/usage/#running-from-terminal","title":"Running from Terminal","text":"

You can also access and run WebGear Server directly from the terminal commandline. The following command will run a WebGear VideoStreamer server at http://localhost:8000/:

Make sure your PYTHON_PATH is set to python 3.7+ versions only.

If you're using --options/-op flag, then kindly wrap your dictionary value in single '' quotes.

python3 -m vidgear.gears.asyncio --source test.avi --logging True --options '{\"frame_size_reduction\": 50, \"jpeg_compression_quality\": 80, \"jpeg_compression_fastdct\": True, \"jpeg_compression_fastupsample\": False}'\n

which can also be accessed on any browser on the network at http://localhost:8000/.

Advanced Usage from Terminal

You can run python3 -m vidgear.gears.asyncio -h help command to see all the advanced settings, as follows:

usage: python -m vidgear.gears.asyncio [-h] [-m MODE] [-s SOURCE] [-ep ENABLEPICAMERA] [-S STABILIZE]\n            [-cn CAMERA_NUM] [-yt stream_mode] [-b BACKEND] [-cs COLORSPACE]\n            [-r RESOLUTION] [-f FRAMERATE] [-td TIME_DELAY]\n            [-ip IPADDRESS] [-pt PORT] [-l LOGGING] [-op OPTIONS]\n\nRuns WebGear/WebGear_RTC Video Server through terminal.\n\noptional arguments:\n  -h, --help            show this help message and exit\n  -m {mjpeg,webrtc}, --mode {mjpeg,webrtc}\n                        Whether to use \"MJPEG\" or \"WebRTC\" mode for streaming.\n  -s SOURCE, --source SOURCE\n                        Path to input source for CamGear API.\n  -ep ENABLEPICAMERA, --enablePiCamera ENABLEPICAMERA\n                        Sets the flag to access PiGear(if True) or otherwise\n                        CamGear API respectively.\n  -S STABILIZE, --stabilize STABILIZE\n                        Enables/disables real-time video stabilization.\n  -cn CAMERA_NUM, --camera_num CAMERA_NUM\n                        Sets the camera module index that will be used by\n                        PiGear API.\n  -yt STREAM_MODE, --stream_mode STREAM_MODE\n                        Enables YouTube Mode in CamGear API.\n  -b BACKEND, --backend BACKEND\n                        Sets the backend of the video source in CamGear API.\n  -cs COLORSPACE, --colorspace COLORSPACE\n                        Sets the colorspace of the output video stream.\n  -r RESOLUTION, --resolution RESOLUTION\n                        Sets the resolution (width,height) for camera module\n                        in PiGear API.\n  -f FRAMERATE, --framerate FRAMERATE\n                        Sets the framerate for camera module in PiGear API.\n  -td TIME_DELAY, --time_delay TIME_DELAY\n                        Sets the time delay(in seconds) before start reading\n                        the frames.\n  -ip IPADDRESS, --ipaddress IPADDRESS\n                        Uvicorn binds the socket to this ipaddress.\n  -pt PORT, --port PORT\n                        Uvicorn binds the socket to this port.\n  -l LOGGING, --logging LOGGING\n                        Enables/disables error logging, essential for\n                        debugging.\n  -op OPTIONS, --options OPTIONS\n                        Sets the parameters supported by APIs(whichever being\n                        accessed) to the input videostream, But make sure to\n                        wrap your dict value in single or double quotes.\n

"},{"location":"gears/webgear_rtc/advanced/","title":"Advanced Usages","text":""},{"location":"gears/webgear_rtc/advanced/#webgear_rtc-api-advanced-usage","title":"WebGear_RTC API Advanced Usage:","text":"

This is a continuation of the WebGear_RTC doc \u27b6. Thereby, It's advised to first get familiarize with this API, and its requirements.

After going through following Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/webgear_rtc/advanced/#using-webgear_rtc-as-real-time-broadcaster","title":"Using WebGear_RTC as Real-time Broadcaster","text":"

WebGear_RTC by default only supports one-to-one peer connection with a single consumer or client. But you can use enable_live_broadcast boolean attribute through its options dictionary parameter to easily enable live broadcast/stream to multiple peer consumers/clients at the same time.

Let's implement a bare-minimum example using WebGear_RTC as Real-time Broadcaster:

enable_infinite_frames is enforced by default with this(enable_live_broadcast) attribute.

For accessing WebGear_RTC on different Client Devices on the network, we use \"0.0.0.0\" as host value instead of \"localhost\" on Host Machine. More information can be found here \u27b6

# import required libraries\nimport uvicorn\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# various performance tweaks and enable live broadcasting\noptions = {\n    \"frame_size_reduction\": 25,\n    \"enable_live_broadcast\": True,\n}\n\n# initialize WebGear_RTC app\nweb = WebGear_RTC(source=\"foo.mp4\", logging=True, **options)\n\n# run this app on Uvicorn server at address http://0.0.0.0:8000/\nuvicorn.run(web(), host=\"0.0.0.0\", port=8000)\n\n# close app safely\nweb.shutdown()\n

And that's all, Now you can see output at http://localhost:8000/ address on your local machine.

"},{"location":"gears/webgear_rtc/advanced/#using-webgear_rtc-with-a-custom-sourceopencv","title":"Using WebGear_RTC with a Custom Source(OpenCV)","text":"

WebGear_RTC provides custom_stream attribute with its options parameter that allows you to easily define your own Custom Streaming Class with suitable source that you want to use to transform your frames before sending them onto the browser.

Let's implement a bare-minimum example with a Custom Source using WebGear_RTC API and OpenCV:

New in v0.2.4

This implementation was added in v0.2.4.

Auto-Reconnection or Auto-Refresh works out-of-the-box with this implementation.

Make sure your Custom Streaming Class at-least implements read() and stop() methods as shown in following example, otherwise WebGear_RTC will throw ValueError!

Using Vidgear's VideoCapture APIs instead of OpenCV

You can directly replace Custom Streaming Class(Custom_Stream_Class in following example) with any VideoCapture APIs. These APIs implements read() and stop() methods by-default, so they're also supported out-of-the-box.

See this example \u27b6 for more information.

# import necessary libs\nimport uvicorn, cv2\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# create your own custom streaming class\nclass Custom_Stream_Class:\n    \"\"\"\n    Custom Streaming using OpenCV\n    \"\"\"\n\n    def __init__(self, source=0):\n\n        # !!! define your own video source here !!!\n        self.source = cv2.VideoCapture(source)\n\n        # define running flag\n        self.running = True\n\n    def read(self):\n\n        # don't forget this function!!!\n\n        # check if source was initialized or not\n        if self.source is None:\n            return None\n        # check if we're still running\n        if self.running:\n            # read frame from provided source\n            (grabbed, frame) = self.source.read()\n            # check if frame is available\n            if grabbed:\n\n                # do something with your OpenCV frame here\n\n                # lets convert frame to gray for this example\n                gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n\n                # return our gray frame\n                return gray\n            else:\n                # signal we're not running now\n                self.running = False\n        # return None-type\n        return None\n\n    def stop(self):\n\n        # don't forget this function!!!\n\n        # flag that we're not running\n        self.running = False\n        # close stream\n        if not self.source is None:\n            self.source.release()\n\n# assign your Custom Streaming Class with adequate source (for e.g. foo.mp4) \n# to `custom_stream` attribute in options parameter\noptions = {\"custom_stream\": Custom_Stream_Class(source=\"foo.mp4\")}\n\n# initialize WebGear_RTC app without any source\nweb = WebGear_RTC(logging=True, **options)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

And that's all, Now you can see output at http://localhost:8000/ address.

"},{"location":"gears/webgear_rtc/advanced/#using-webgear_rtc-with-custom-mounting-points","title":"Using WebGear_RTC with Custom Mounting Points","text":"

With our highly extensible WebGear_RTC API, you can add your own mounting points, where additional files located, as follows:

# import libs\nimport uvicorn\nfrom starlette.routing import Mount\nfrom starlette.staticfiles import StaticFiles\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# various performance tweaks\noptions = {\n    \"frame_size_reduction\": 25,\n}\n\n# initialize WebGear_RTC app\nweb = WebGear_RTC(\n    source=\"foo.mp4\", logging=True, **options\n)  # enable source i.e. `test.mp4` and enable `logging` for debugging\n\n# append new route i.e. mount another folder called `test` located at `/home/foo/.vidgear/test` directory\nweb.routes.append(\n    Mount(\"/test\", app=StaticFiles(directory=\"/home/foo/.vidgear/test\"), name=\"test\")\n)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

Then you can use this folder in your HTML page, to host data-files. For example, if we have jQuery script jquery-3.3.1.slim.min.js in this folder and want to integrate it, then, we can do something like this:

<script src=\"{{ url_for('test', path='jquery-3.3.1.slim.min.js') }}\"></script>\n

"},{"location":"gears/webgear_rtc/advanced/#using-webgear_rtc-with-custom-webpage-routes","title":"Using WebGear_RTC with Custom Webpage Routes","text":"

With Webgear_RTC's flexible API, you can even add your additional HTML Static webpages without any extra efforts.

Suppose we want to add a simple hello world webpage to our WebGear_RTC server. So let's create a bare-minimum hello.html file with HTML code as follows:

<html>\n   <header>\n      <title>This is Hello world page</title>\n   </header>\n   <body>\n      <h1>Hello World</h1>\n      <p>how ya doing?</p>\n   </body>\n</html>\n

Then in our application code, we can integrate this webpage route, as follows:

# import libs\nimport uvicorn, asyncio\nfrom starlette.templating import Jinja2Templates\nfrom starlette.routing import Route\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# Build out Jinja2 template render at `/home/foo/.vidgear/custom_template` path in which our `hello.html` file is located\ntemplate = Jinja2Templates(directory=\"/home/foo/.vidgear/custom_template\")\n\n# render and return our webpage template\nasync def hello_world(request):\n    page = \"hello.html\"\n    context = {\"request\": request}\n    return template.TemplateResponse(page, context)\n\n\n# add various performance tweaks as usual\noptions = {\n    \"frame_size_reduction\": 25,\n}\n\n# initialize WebGear_RTC app with a valid source\nweb = WebGear_RTC(\n    source=\"/home/foo/foo1.mp4\", logging=True, **options\n)  # enable source i.e. `test.mp4` and enable `logging` for debugging\n\n# append new route to point our rendered webpage\nweb.routes.append(Route(\"/hello\", endpoint=hello_world))\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n
And that's all, Now you can see output at http://localhost:8000/hello address.

"},{"location":"gears/webgear_rtc/advanced/#using-webgear_rtc-with-middlewares","title":"Using WebGear_RTC with MiddleWares","text":"

WebGear_RTC also natively supports ASGI middleware classes with Starlette for implementing behavior that is applied across your entire ASGI application easily.

New in v0.2.2

This example was added in v0.2.2.

All supported middlewares can be found here \u27b6

For this example, let's use CORSMiddleware for implementing appropriate CORS headers to outgoing responses in our application in order to allow cross-origin requests from browsers, as follows:

The default parameters used by the CORSMiddleware implementation are restrictive by default, so you'll need to explicitly enable particular origins, methods, or headers, in order for browsers to be permitted to use them in a Cross-Domain context.

Starlette provides several arguments for enabling origins, methods, or headers for CORSMiddleware API. More information can be found here \u27b6

# import libs\nimport uvicorn, asyncio\nfrom starlette.middleware import Middleware\nfrom starlette.middleware.cors import CORSMiddleware\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# add various performance tweaks as usual\noptions = {\n    \"frame_size_reduction\": 25,\n}\n\n# initialize WebGear_RTC app with a valid source\nweb = WebGear_RTC(\n    source=\"/home/foo/foo1.mp4\", logging=True, **options\n)  # enable source i.e. `test.mp4` and enable `logging` for debugging\n\n# define and assign suitable cors middlewares\nweb.middleware = [\n    Middleware(\n        CORSMiddleware,\n        allow_origins=[\"*\"],\n        allow_credentials=True,\n        allow_methods=[\"*\"],\n        allow_headers=[\"*\"],\n    )\n]\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

And that's all, Now you can see output at http://localhost:8000 address.

"},{"location":"gears/webgear_rtc/advanced/#rules-for-altering-webgear_rtc-files-and-folders","title":"Rules for Altering WebGear_RTC Files and Folders","text":"

WebGear_RTC gives us complete freedom of altering data files generated in Auto-Generation Process, But you've to keep the following rules in mind:

"},{"location":"gears/webgear_rtc/advanced/#rules-for-altering-data-files","title":"Rules for Altering Data Files","text":""},{"location":"gears/webgear_rtc/advanced/#rules-for-altering-data-folders","title":"Rules for Altering Data Folders","text":""},{"location":"gears/webgear_rtc/advanced/#bonus-examples","title":"Bonus Examples","text":"

Checkout more advanced WebGear_RTC examples with unusual configuration here \u27b6

"},{"location":"gears/webgear_rtc/overview/","title":"Overview","text":""},{"location":"gears/webgear_rtc/overview/#webgear_rtc-api","title":"WebGear_RTC API","text":"WebGear_RTC API's Video Server running at http://localhost:8000/ address."},{"location":"gears/webgear_rtc/overview/#overview","title":"Overview","text":"

WebGear_RTC is similar to WeGear API in many aspects but utilizes WebRTC technology under the hood instead of Motion JPEG, which makes it suitable for building powerful video-streaming solutions for all modern browsers as well as native clients available on all major platforms.

New in v0.2.1

WebGear_RTC API was added in v0.2.1.

WebGear_RTC is implemented with the help of aiortc library which is built on top of asynchronous I/O framework for Web Real-Time Communication (WebRTC) and Object Real-Time Communication (ORTC) and supports many features like SDP generation/parsing, Interactive Connectivity Establishment with half-trickle and mDNS support, DTLS key and certificate generation, DTLS handshake, etc.

WebGear_RTC can handle multiple consumers seamlessly and provides native support for ICE (Interactive Connectivity Establishment) protocol, STUN (Session Traversal Utilities for NAT), and TURN (Traversal Using Relays around NAT) servers that help us to seamlessly establish direct media connection with the remote peers for uninterrupted data flow. It also allows us to define our custom streaming class with suitable source to transform frames easily before sending them across the network(see this doc example).

WebGear_RTC API works in conjunction with Starlette ASGI application and can also flexibly interact with Starlette's ecosystem of shared middleware, mountable applications, Response classes, Routing tables, Static Files, Templating engine(with Jinja2), etc.

Additionally, WebGear_RTC API also provides internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs.

"},{"location":"gears/webgear_rtc/overview/#data-files-auto-generation-workflow-for-webgear_rtc","title":"Data-Files Auto-Generation WorkFlow for WebGear_RTC","text":"

Same as WebGear, WebGear_RTC API automatically checks for three critical data files(i.e index.html, 404.html & 500.html) on initialization inside the templates folder of the webgear_rtc directory at the default location which gives rise to the following two possible scenario:

"},{"location":"gears/webgear_rtc/overview/#default-location","title":"Default Location","text":"

Identifying Default location

You can set logging=True during initialization, for easily identifying the selected default location, which will be something like this on a Linux machine:

WebGear_RTC :: DEBUG :: `/home/foo/.vidgear` is the default location for saving WebGear_RTC data-files.\n
"},{"location":"gears/webgear_rtc/overview/#auto-generation-process","title":"Auto-Generation process","text":"

Info

"},{"location":"gears/webgear_rtc/overview/#webgear_rtcs-default-template","title":"WebGear_RTC's Default Template","text":"

The WebGear_RTC API by default uses simple & elegant WebGear_RTC's Default Theme which looks like something as follows:

"},{"location":"gears/webgear_rtc/overview/#indexhtml","title":"Index.html","text":"

Can be accessed by visiting WebGear_RTC app server, running at http://localhost:8000/:

"},{"location":"gears/webgear_rtc/overview/#404html","title":"404.html","text":"

Appears when respective URL is not found, for example http://localhost:8000/ok:

"},{"location":"gears/webgear_rtc/overview/#500html","title":"500.html","text":"

Appears when an API Error is encountered:

If logging is enabled and an error occurs, then instead of displaying this 500 handler, WebGear_RTC will respond with a traceback response.

"},{"location":"gears/webgear_rtc/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through WebGear_RTC Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/webgear_rtc/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/webgear_rtc/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/webgear_rtc/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/webgear_rtc/params/","title":"Parameters","text":""},{"location":"gears/webgear_rtc/params/#webgear_rtc-api-parameters","title":"WebGear_RTC API Parameters","text":"

WebGear_RTC provides a special internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs and their parameters.

"},{"location":"gears/webgear_rtc/params/#enablepicamera","title":"enablePiCamera","text":"

This parameter provide direct access to PiGear or CamGear APIs respectively in WebGear_RTC. This means the if enablePiCamera flag is True, the PiGear API will be accessed, and if False, the CamGear API will be accessed.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

WebGear_RTC(enablePiCamera=True) # enable access to PiGear API\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/webgear_rtc/params/#options","title":"options","text":"

This parameter can be used to pass user-defined parameter to WebGear_RTC API by formatting them as this parameter's attribute.

Data-Type: Dictionary

Default Value: Its default value is {}

"},{"location":"gears/webgear_rtc/params/#webgear_rtc-specific-attributes","title":"WebGear_RTC Specific attributes","text":"

"},{"location":"gears/webgear_rtc/params/#parameters-for-stabilizer-backend","title":"Parameters for Stabilizer Backend","text":"

Enable this backend with stabilize=True in WebGear_RTC. Default is also False.

"},{"location":"gears/webgear_rtc/params/#stabilize","title":"stabilize","text":"

This parameter enable access to Stabilizer Class for stabilizing frames, i.e. can be set to True(to enable) or unset to False(to disable).

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

WebGear_RTC(stabilize=True) # enable stablization\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/webgear_rtc/params/#options_1","title":"options","text":"

This parameter can be used in addition, to pass user-defined parameters supported by Stabilizer Class. These parameters can be formatted as this parameter's attribute.

Supported dictionary attributes for Stabilizer Class are:

"},{"location":"gears/webgear_rtc/params/#parameters-for-camgear-backend","title":"Parameters for CamGear backend","text":"

Enable this backend with enablePiCamera=False in WebGear_RTC.

"},{"location":"gears/webgear_rtc/params/#source","title":"source","text":"

WebGear_RTC API will throw RuntimeError if source provided is invalid.

This parameter defines the source for the input stream.

Data-Type: Based on input.

Default Value: Its default value is 0.

Its valid input can be one of the following:

"},{"location":"gears/webgear_rtc/params/#stream_mode","title":"stream_mode","text":"

This parameter controls the Stream Mode, .i.e if enabled(stream_mode=True), the CamGear API will interpret the given source input as YouTube URL address.

Due to a FFmpeg bug that causes video to freeze frequently in OpenCV, It is advised to always use GStreamer backend for any livestream videos. Checkout this FAQ for compiling OpenCV with GStreamer support.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

Supported Streaming Websites

The complete list of all supported Streaming Websites URLs can be found here \u27b6

WebGear_RTC(source='https://youtu.be/bvetuLwJIkA', stream_mode=True)\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/webgear_rtc/params/#backend","title":"backend","text":"

This parameter manually selects the backend for OpenCV's VideoCapture class (only if specified).

Data-Type: Integer

Default Value: Its default value is 0

Usage:

All supported backends are listed here \u27b6

Its value can be for e.g. backend = cv2.CAP_DSHOW for selecting Direct Show as backend:

WebGear_RTC(source=0, backend = cv2.CAP_DSHOW)\n

"},{"location":"gears/webgear_rtc/params/#options_2","title":"options","text":"

This parameter provides the ability to alter various Source Tweak Parameters available within OpenCV's VideoCapture API properties.

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

All supported parameters are listed here \u27b6

The desired parameters can be passed to WebGear_RTC API by formatting them as this parameter's attributes, as follows:

# formatting parameters as dictionary attributes\noptions = {\"CAP_PROP_FRAME_WIDTH\":320, \"CAP_PROP_FRAME_HEIGHT\":240, \"CAP_PROP_FPS\":60}\n# assigning it\nWebGear_RTC(source=0, **options)\n

"},{"location":"gears/webgear_rtc/params/#parameters-for-pigear-backend","title":"Parameters for PiGear backend","text":"

Enable this backend with enablePiCamera=True in WebGear_RTC.

"},{"location":"gears/webgear_rtc/params/#camera_num","title":"camera_num","text":"

This parameter selects the camera index to be used as the source, allowing you to drive these multiple cameras simultaneously from within a single Python session. Its value can only be zero or greater, otherwise, WebGear_RTC API will throw ValueError for any negative value.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

# select Camera Module at index `1`\nWebGear_RTC(enablePiCamera=True, camera_num=1)\n

The complete usage example demonstrating the usage of the camera_num parameter is available here \u27b6.

"},{"location":"gears/webgear_rtc/params/#resolution","title":"resolution","text":"

This parameter controls the resolution - a tuple (i.e. (width,height)) of two values giving the width and height of the output frames.

Make sure both width and height values should be at least 64.

When using the Picamera2 backend, the resolution parameter will be OVERRIDDEN, if the user explicitly defines the output_size property of the sensor configurational parameter.

Data-Type: Tuple

Default Value: Its default value is (640,480).

Usage:

WebGear_RTC(enablePiCamera=True, resolution=(1280,720)) # sets 1280x720 resolution\n

"},{"location":"gears/webgear_rtc/params/#framerate","title":"framerate","text":"

This parameter controls the framerate of the source.

Data-Type: integer/float

Default Value: Its default value is 30.

Usage:

WebGear_RTC(enablePiCamera=True, framerate=60) # sets 60fps framerate\n

"},{"location":"gears/webgear_rtc/params/#options_3","title":"options","text":"

This dictionary parameter in the internal PiGear API backend allows you to control various camera settings for both the picamera2 and legacy picamera backends and some internal API tasks. These settings include:

"},{"location":"gears/webgear_rtc/params/#a-configurational-camera-parameters","title":"A. Configurational Camera Parameters","text":""},{"location":"gears/webgear_rtc/params/#b-user-defined-parameters","title":"B. User-defined Parameters","text":"

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

The complete usage example demonstrating the usage of the options parameter is available here \u27b6.

You can format these user-defined and configurational parameters as attributes of this options dictionary parameter as follows:

New Picamera2 backendLegacy Picamera backend
# formulate various Picamera2 API parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"exposure_compensation\": 15,\n    \"sensor\": {\"output_size\": (480, 320)},  # !!! will override `resolution` !!!\n}\n\n# open pi video stream with defined parameters\nstream = WebGear_RTC(enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options).start()\n
# formulate various Picamera API parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = WebGear_RTC(enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options).start()\n

"},{"location":"gears/webgear_rtc/params/#common-parameters","title":"Common Parameters","text":"

These are common parameters that works with every backend in WebGear_RTC.

"},{"location":"gears/webgear_rtc/params/#colorspace","title":"colorspace","text":"

This parameter selects the colorspace of the source stream.

Data-Type: String

Default Value: Its default value is None.

Usage:

All supported colorspace values are given here \u27b6

WebGear_RTC(colorspace=\"COLOR_BGR2HSV\")\n

Its complete usage example is given here \u27b6

"},{"location":"gears/webgear_rtc/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

WebGear_RTC(logging=True)\n

"},{"location":"gears/webgear_rtc/params/#time_delay","title":"time_delay","text":"

This parameter set the time delay (in seconds) before the WebGear_RTC API start reading the frames. This delay is only required if the source required some warm-up delay before starting up.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

WebGear_RTC(time_delay=1)  # set 1 seconds time delay\n

"},{"location":"gears/webgear_rtc/usage/","title":"Usage Examples","text":""},{"location":"gears/webgear_rtc/usage/#webgear_rtc-api-usage-examples","title":"WebGear_RTC API Usage Examples:","text":""},{"location":"gears/webgear_rtc/usage/#requirements","title":"Requirements","text":""},{"location":"gears/webgear_rtc/usage/#installation-with-asyncio-support","title":"Installation with Asyncio Support","text":"

WebGear_RTC API is the part of asyncio package of VidGear, thereby you need to install VidGear with asyncio support as follows:

pip install vidgear[asyncio]\n
"},{"location":"gears/webgear_rtc/usage/#aiortc","title":"Aiortc","text":"

Must Required with WebGear_RTC API. You can easily install it via pip:

Microsoft Visual C++ 14.0 is required.

Installing aiortc on windows requires Microsoft Build Tools for Visual C++ libraries installed. You can easily fix this error by installing any ONE of these choices:

While the error is calling for VC++ 14.0 - but newer versions of Visual C++ libraries works as well.

Afterwards, Select: Workloads \u2192 Desktop development with C++, then for Individual Components, select only:

Finally, proceed installing aiortc via pip.

  pip install aiortc\n
"},{"location":"gears/webgear_rtc/usage/#asgi-server","title":"ASGI Server","text":"

You'll also need to install an ASGI Server to run following WebGear_RTC usage examples, and by default WebGear_RTC ships the state-of-the-art uvicorn Server. But you can also use other ASGI server such as daphne, or hypercorn with it.

"},{"location":"gears/webgear_rtc/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Let's implement a Bare-Minimum usage example:

"},{"location":"gears/webgear_rtc/usage/#running-programmatically","title":"Running Programmatically","text":"

You can access and run WebGear_RTC VideoStreamer Server programmatically in your python script in just a few lines of code, as follows:

For accessing WebGear_RTC on different Client Devices on the network, use \"0.0.0.0\" as host value instead of \"localhost\" on Host Machine. More information can be found here \u27b6

We are using frame_size_reduction attribute for frame size reduction (in percentage) to be streamed with its options dictionary parameter to cope with performance-throttling in this example.

# import required libraries\nimport uvicorn\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# various performance tweaks\noptions = {\n    \"frame_size_reduction\": 25,\n}\n\n# initialize WebGear_RTC app\nweb = WebGear_RTC(source=\"foo.mp4\", logging=True, **options)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

which can be accessed on any browser on your machine at http://localhost:8000/.

"},{"location":"gears/webgear_rtc/usage/#running-from-terminal","title":"Running from Terminal","text":"

You can also access and run WebGear_RTC Server directly from the terminal commandline. The following command will run a WebGear_RTC VideoStreamer server at http://localhost:8000/:

Make sure your PYTHON_PATH is set to python 3.7+ versions only.

If you're using --options/-op flag, then kindly wrap your dictionary value in single '' quotes.

python3 -m vidgear.gears.asyncio --mode webrtc --source test.avi --logging True --options '{\"frame_size_reduction\": 50, \"frame_jpeg_quality\": 80, \"frame_jpeg_optimize\": True, \"frame_jpeg_progressive\": False}'\n

which can also be accessed on any browser on the network at http://localhost:8000/.

Advanced Usage from Terminal

You can run python3 -m vidgear.gears.asyncio -h help command to see all the advanced settings, as follows:

usage: python -m vidgear.gears.asyncio [-h] [-m MODE] [-s SOURCE] [-ep ENABLEPICAMERA] [-S STABILIZE]\n            [-cn CAMERA_NUM] [-yt stream_mode] [-b BACKEND] [-cs COLORSPACE]\n            [-r RESOLUTION] [-f FRAMERATE] [-td TIME_DELAY]\n            [-ip IPADDRESS] [-pt PORT] [-l LOGGING] [-op OPTIONS]\n\nRuns WebGear/WebGear_RTC Video Server through terminal.\n\noptional arguments:\n  -h, --help            show this help message and exit\n  -m {mjpeg,webrtc}, --mode {mjpeg,webrtc}\n                        Whether to use \"MJPEG\" or \"WebRTC\" mode for streaming.\n  -s SOURCE, --source SOURCE\n                        Path to input source for CamGear API.\n  -ep ENABLEPICAMERA, --enablePiCamera ENABLEPICAMERA\n                        Sets the flag to access PiGear(if True) or otherwise\n                        CamGear API respectively.\n  -S STABILIZE, --stabilize STABILIZE\n                        Enables/disables real-time video stabilization.\n  -cn CAMERA_NUM, --camera_num CAMERA_NUM\n                        Sets the camera module index that will be used by\n                        PiGear API.\n  -yt STREAM_MODE, --stream_mode STREAM_MODE\n                        Enables YouTube Mode in CamGear API.\n  -b BACKEND, --backend BACKEND\n                        Sets the backend of the video source in CamGear API.\n  -cs COLORSPACE, --colorspace COLORSPACE\n                        Sets the colorspace of the output video stream.\n  -r RESOLUTION, --resolution RESOLUTION\n                        Sets the resolution (width,height) for camera module\n                        in PiGear API.\n  -f FRAMERATE, --framerate FRAMERATE\n                        Sets the framerate for camera module in PiGear API.\n  -td TIME_DELAY, --time_delay TIME_DELAY\n                        Sets the time delay(in seconds) before start reading\n                        the frames.\n  -ip IPADDRESS, --ipaddress IPADDRESS\n                        Uvicorn binds the socket to this ipaddress.\n  -pt PORT, --port PORT\n                        Uvicorn binds the socket to this port.\n  -l LOGGING, --logging LOGGING\n                        Enables/disables error logging, essential for\n                        debugging.\n  -op OPTIONS, --options OPTIONS\n                        Sets the parameters supported by APIs(whichever being\n                        accessed) to the input videostream, But make sure to\n                        wrap your dict value in single or double quotes.\n

"},{"location":"gears/writegear/introduction/","title":"Introduction","text":""},{"location":"gears/writegear/introduction/#writegear-api","title":"WriteGear API","text":"WriteGear API generalized workflow"},{"location":"gears/writegear/introduction/#overview","title":"Overview","text":"

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 specifications (such asbitrate, codec, framerate, resolution, subtitles, etc.).

WriteGear also supports streaming with traditional protocols such as RTSP/RTP, RTMP. It is powerful enough to perform complex tasks such as Live-Streaming (such as for Twitch, YouTube etc.) and Multiplexing Video-Audio with real-time frames in just few lines of code.

Best of all, WriteGear grants users the complete freedom to play with any FFmpeg parameter with its exclusive Custom Commands function (see this doc) without relying on any third-party API.

In addition to this, WriteGear also provides flexible access to OpenCV's VideoWriter API tools for video-frames encoding without compression.

"},{"location":"gears/writegear/introduction/#modes-of-operation","title":"Modes of Operation","text":"

WriteGear primarily operates in following modes:

Helpful Tips

"},{"location":"gears/writegear/introduction/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/writegear/compression/overview/","title":"Overview","text":""},{"location":"gears/writegear/compression/overview/#writegear-api-compression-mode","title":"WriteGear API: Compression Mode","text":"WriteGear API's Compression Mode generalized workflow"},{"location":"gears/writegear/compression/overview/#overview","title":"Overview","text":"

When compression_mode parameter is enabled (.i.e compression_mode = True), WriteGear API provides a complete, flexible & robust wrapper around FFmpeg to encode lossless & compressed multimedia files.

This mode can process real-time video frames into a lossless compressed format with any suitable setting video/audio properties such as bitrate, codec, framerate, resolution, subtitles, and much more in just a few easy lines of code. It can also perform complex tasks such as Live-Streaming (such as for Twitch), multiplexing video with audio in real-time (see this usage example) while handling all errors robustly.

Important Information

You can speed up the execution time by disabling logging (.i.e logging = False) for production use, and by tweaking FFmpeg parameters in output_params values. Look into FFmpeg docs \u27b6 for such hacks.

"},{"location":"gears/writegear/compression/overview/#custom-ffmpeg-commands-in-writegear-api","title":"Custom FFmpeg Commands in WriteGear API","text":"

WriteGear API now provides the execute_ffmpeg_cmd Function in Compression Mode, that enables the user to pass any custom CLI commands as an input to its internal FFmpeg Pipeline by formating it as a list.

This function opens endless possibilities of exploiting any FFmpeg supported parameter within WriteGear, without relying on a third-party library/API to do the same, and while doing that it robustly handles all errors/warnings quietly.

A complete guide on execute_ffmpeg_cmd Function can be found here \u27b6

"},{"location":"gears/writegear/compression/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through WriteGear Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/writegear/compression/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/writegear/compression/params/","title":"Parameters","text":""},{"location":"gears/writegear/compression/params/#writegear-api-parameters-compression-mode","title":"WriteGear API Parameters: Compression Mode","text":""},{"location":"gears/writegear/compression/params/#output","title":"output","text":"

This parameter sets the valid filename/path/URL for the video output.

Warning

WriteGear API will throw ValueError if output provided is empty or invalid.

Data-Type: String

Usage:

Its valid input can be one of the following:

"},{"location":"gears/writegear/compression/params/#compression_mode","title":"compression_mode","text":"

This parameter selects the WriteGear's Primary Mode of Operation, i.e. if this parameter is enabled (.i.e compression_mode = True) WriteGear will use FFmpeg to encode output video, and if disabled (.i.e compression_mode = False), the OpenCV's VideoWriter API will be used for encoding files/streams.

Data-Type: Boolean

Default Value: Its default value is True.

Usage:

WriteGear(output = 'output.mp4', compression_mode=True)\n

"},{"location":"gears/writegear/compression/params/#custom_ffmpeg","title":"custom_ffmpeg","text":"

This parameter assigns the custom path/directory where the custom FFmpeg executables are located in Compression Mode only.

Compression Mode Behavior on Windows

In Compression Mode, if a custom FFmpeg executable's path | directory is not provided through custom_ffmpeg parameter on Windows machine, then WriteGear API will automatically attempt to download and extract suitable Static FFmpeg binaries at suitable location on your windows machine. More information can be found here \u27b6.

Data-Type: String

Default Value: Its default value is None.

Usage:

# if ffmpeg executables are located at \"/foo/foo1/FFmpeg\"\nWriteGear(output = 'output.mp4', custom_ffmpeg=\"/foo/foo1/FFmpeg\")\n

"},{"location":"gears/writegear/compression/params/#output_params","title":"output_params","text":"

This parameter allows us to exploit almost all FFmpeg supported parameters effortlessly and flexibly for encoding in Compression Mode, by formatting desired FFmpeg Parameters as this parameter's attributes. All supported parameters and encoders for compression mode discussed below:

Kindly read FFmpeg Docs carefully, before passing any values to output_param dictionary parameter. Wrong values may result in undesired Errors or no output at all.

Data-Type: Dictionary

Default Value: Its default value is {}.

"},{"location":"gears/writegear/compression/params/#supported-parameters","title":"Supported Parameters","text":""},{"location":"gears/writegear/compression/params/#supported-encoders","title":"Supported Encoders","text":"

All the encoders that are compiled with FFmpeg in use, are supported by WriteGear API. You can easily check the compiled encoders by running following command in your terminal:

Similarily, supported demuxers and filters depends upons compiled FFmpeg in use.

ffmpeg -encoders           # use `ffmpeg.exe -encoders` on windows\n

"},{"location":"gears/writegear/compression/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

WriteGear(output = 'output.mp4', logging=True)\n

"},{"location":"gears/writegear/compression/usage/","title":"Usage Examples","text":""},{"location":"gears/writegear/compression/usage/#writegear-api-usage-examples-compression-mode","title":"WriteGear API Usage Examples: Compression Mode","text":"

Important Information

After going through WriteGear Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/writegear/compression/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with WriteGear API in Compression Mode:

# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# open any valid video stream(for e.g `myvideo.avi` file)\nstream = CamGear(source=\"myvideo.avi\").start()\n\n# Define writer with default parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\")\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/compression/usage/#using-compression-mode-in-rgb-mode","title":"Using Compression Mode in RGB Mode","text":"

In Compression Mode, WriteGear API contains rgb_mode boolean parameter for RGB Mode, which when enabled (i.e. rgb_mode=True), specifies that incoming frames are of RGB format (instead of default BGR format). This mode makes WriteGear directly compatible with libraries that only supports RGB format.

The complete usage example is as follows:

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = VideoGear(source=0).start()\n\n# Define writer with default parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\")\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # simulating RGB frame for example\n    frame_rgb = frame[:, :, ::-1]\n\n    # writing RGB frame to writer\n    writer.write(frame_rgb, rgb_mode=True)  # activate RGB Mode\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/compression/usage/#using-compression-mode-with-controlled-framerate","title":"Using Compression Mode with controlled FrameRate","text":"

WriteGear API provides -input_framerate attribute for its options dictionary parameter in Compression Mode, which allow us to control/set the constant framerate of the output video.

Advanced Tip for setting constant framerate

If -input_framerate attribute doesn't works for you, then define it in conjunction with another -r FFmpeg parameter as attribute:

# set output constant framerate to (say 60 fps)\noutput_params = {\"-input_framerate\":60, \"-r\":60}\n# assign that to WriteGear\nwriter = WriteGear(output=\"out.mp4\", logging =True, **output_params)\n

But make sure you MUST set value of -r and -input_framerate parameter less than or equal to your input source framerate.

In this code we will retrieve framerate from video stream, and set it as -input_framerate attribute for option parameter in WriteGear API:

# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = CamGear(source=0).start()\n\n# retrieve framerate from CamGear Stream and pass it as `-input_framerate` parameter\noutput_params = {\"-input_framerate\": stream.framerate}\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\", **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if None-type\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/compression/usage/#using-compression-mode-for-live-streaming","title":"Using Compression Mode for live streaming","text":"

In Compression Mode, WriteGear also allows URL strings (as output) for live streaming realtime frames with its output parameter.

In this example, we will stream live camera frames directly to Twitch :

For streaming with traditional protocols such as RTSP/RTP, Checkout this WriteGear's Bonus Examples \u27b6.

YouTube-Live Streaming example code also available in WriteGear's Bonus Examples \u27b6

This example assume you already have a Twitch Account for publishing video.

Make sure to change Twitch Stream Key with yours in following code before running!

# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# Open live webcam video stream on first index(i.e. 0) device\nstream = CamGear(source=0, logging=True).start()\n\n# define required FFmpeg optimizing parameters for your writer\noutput_params = {\n    \"-preset:v\": \"veryfast\",\n    \"-g\": 60,\n    \"-keyint_min\": 60,\n    \"-sc_threshold\": 0,\n    \"-bufsize\": \"2500k\",\n    \"-f\": \"flv\",\n}\n\n# [WARNING] Change your Twitch Stream Key here:\nTWITCH_KEY = \"live_XXXXXXXXXX~XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\"\n\n# Define writer with defined parameters and\nwriter = WriteGear(\n    output=\"rtmp://live.twitch.tv/app/{}\".format(TWITCH_KEY),\n    logging=True,\n    **output_params\n)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/compression/usage/#using-compression-mode-with-hardware-encoders","title":"Using Compression Mode with Hardware encoders","text":"

By default, WriteGear API uses libx264 encoder for encoding output files in Compression Mode. But you can easily change encoder to your suitable supported encoder by passing -vcodec FFmpeg parameter as an attribute with its output_param dictionary parameter. In addition to this, you can also specify the additional properties/features of your system's GPU easily.

User Discretion Advised

This example is just conveying the idea on how to use FFmpeg's hardware encoders with WriteGear API in Compression mode, which MAY/MAY NOT suit your system. Kindly use suitable parameters based your system hardware settings only.

In this example, we will be using h264_vaapi as our hardware encoder and also optionally be specifying our device hardware's location (i.e. '-vaapi_device':'/dev/dri/renderD128') and other features such as '-vf':'format=nv12,hwupload':

Remember to 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:

ffmpeg  -hide_banner -encoders | grep vaapi \n\n V..... h264_vaapi           H.264/AVC (VAAPI) (codec h264)\n V..... hevc_vaapi           H.265/HEVC (VAAPI) (codec hevc)\n V..... mjpeg_vaapi          MJPEG (VAAPI) (codec mjpeg)\n V..... mpeg2_vaapi          MPEG-2 (VAAPI) (codec mpeg2video)\n V..... vp8_vaapi            VP8 (VAAPI) (codec vp8)\n
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# Open live webcam video stream on first index(i.e. 0) device\nstream = CamGear(source=0, logging=True).start()\n\n# define required FFmpeg parameters for your writer\noutput_params = {\n    \"-vcodec\": \"h264_vaapi\",\n    \"-vaapi_device\": \"/dev/dri/renderD128\",\n    \"-vf\": \"format=nv12,hwupload\",\n}\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\", **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/compression/usage/#using-compression-mode-with-opencv","title":"Using Compression Mode with OpenCV","text":"

You can easily use WriterGear API directly with any Video Processing library(For e.g OpenCV itself) in Compression Mode. The complete usage example is as follows:

# import required libraries\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# define suitable (Codec,CRF,preset) FFmpeg parameters for writer\noutput_params = {\"-vcodec\": \"libx264\", \"-crf\": 0, \"-preset\": \"fast\"}\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = cv2.VideoCapture(0)\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n        break\n\n    # {do something with the frame here}\n    # lets convert frame to gray for this example\n    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n\n    # write gray frame to writer\n    writer.write(gray)\n\n    # Show output window\n    cv2.imshow(\"Output Gray Frame\", gray)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.release()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/compression/usage/#using-compression-mode-with-live-audio-input","title":"Using Compression Mode with Live Audio Input","text":"

In Compression Mode, WriteGear API allows us to exploit almost all FFmpeg supported parameters that you can think of in its Compression Mode. Hence, combining audio with live video frames is pretty easy.

In this example code, we will merging the audio from a Audio Device (for e.g. Webcam inbuilt mic) to live frames incoming from the Video Source (for e.g external webcam), and save the output as a compressed video file, all in real time:

Example Assumptions

Identifying and Specifying sound card on different OS platforms Windows Linux MacOS

Windows OS users can use the dshow (DirectShow) to list audio input device which is the preferred option for Windows users. You can refer following steps to identify and specify your sound card:

If audio still doesn't work then checkout this troubleshooting guide \u27b6 or reach us out on Gitter \u27b6 Community channel

Linux OS users can use the alsa to list input device to capture live audio input such as from a webcam. You can refer following steps to identify and specify your sound card:

If audio still doesn't work then reach us out on Gitter \u27b6 Community channel

MAC OS users can use the avfoundation to list input devices for grabbing audio from integrated iSight cameras as well as cameras connected via USB or FireWire. You can refer following steps to identify and specify your sound card on MacOS/OSX machines:

If audio still doesn't work then reach us out on Gitter \u27b6 Community channel

Make sure this -i audio-source it compatible with provided video-source, otherwise you could encounter multiple errors or no output at all.

You MUST use -input_framerate attribute to set exact value of input framerate when using external audio in Real-time Frames mode, otherwise audio delay will occur in output streams.

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = VideoGear(source=0).start()\n\n# change with your webcam soundcard, plus add additional required FFmpeg parameters for your writer\noutput_params = {\n    \"-input_framerate\": stream.framerate,\n    \"-thread_queue_size\": \"512\",\n    \"-ac\": \"2\",\n    \"-ar\": \"48000\",\n    \"-f\": \"alsa\", # !!! warning: always keep this line above \"-i\" parameter !!!\n    \"-i\": \"hw:1\",\n}\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4\nwriter = WriteGear(output=\"Output.mp4\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/compression/advanced/cciw/","title":"Custom FFmpeg Commands","text":""},{"location":"gears/writegear/compression/advanced/cciw/#custom-ffmpeg-commands-in-writegear-api","title":"Custom FFmpeg Commands in WriteGear API","text":"

WriteGear API now provides the execute_ffmpeg_cmd Method in Compression Mode that enables the user to pass any custom FFmpeg CLI (Command Line Interface) commands as input to its internal FFmpeg Pipeline by formating it as a list.

This opens endless possibilities of exploiting every FFmpeg params within WriteGear without relying on a third-party API to do the same and while doing that it robustly handles all errors/warnings quietly.

Important Information

"},{"location":"gears/writegear/compression/advanced/cciw/#features","title":"Features","text":"

"},{"location":"gears/writegear/compression/advanced/cciw/#methods","title":"Methods","text":""},{"location":"gears/writegear/compression/advanced/cciw/#execute_ffmpeg_cmd","title":"execute_ffmpeg_cmd","text":"

This method allows the users to pass the custom FFmpeg terminal commands as a formatted list directly to WriteGear API's FFmpeg pipeline for processing/execution. Its usage is as follows:

# format FFmpeg terminal command `ffmpeg -y -i source_video -acodec copy input_audio.aac` as a list\nffmpeg_command = [\"-y\", \"-i\", source_video, \"-acodec\", \"copy\", \"input_audio.aac\"]\n\n# execute this list using this function\nexecute_ffmpeg_cmd(ffmpeg_command)\n

"},{"location":"gears/writegear/compression/advanced/cciw/#usage-examples","title":"Usage Examples","text":"

Following usage examples is just an idea of what can be done with this powerful function. So just Tinker with various FFmpeg parameters/commands yourself and see it working. Also, if you're unable to run any terminal FFmpeg command, then report an issue.

"},{"location":"gears/writegear/compression/advanced/cciw/#using-writegear-to-separate-audio-from-video","title":"Using WriteGear to separate Audio from Video","text":"

In this example, we will extract and save audio from a URL stream:

# import required libraries\nfrom vidgear.gears import WriteGear\n\n# define a valid url\nurl_to_stream = (\n    \"http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4\"\n)\n\n# Define writer with default parameters\nwriter = WriteGear(output=\"Output.mp4\", logging=True)\n\n# format command to convert stream audio as 'output_audio.aac' as list\nffmpeg_command_to_save_audio = [\n    \"-y\",\n    \"-i\",\n    url_to_stream,\n    \"output_audio.aac\",\n]  # `-y` parameter is to overwrite outputfile if exists\n\n# execute FFmpeg command\nwriter.execute_ffmpeg_cmd(ffmpeg_command_to_save_audio)\n\n# safely close writer\nwriter.close()\n

After running this script, You will get the final 'output_audio.aac' audio file.

"},{"location":"gears/writegear/compression/advanced/cciw/#using-writegear-to-merge-audio-with-video","title":"Using WriteGear to merge Audio with Video","text":"

In this example, we will merge audio with video:

You can also directly add external audio input to video-frames in WriteGear. For more information, See this FAQ example \u27b6

Example Assumptions

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import WriteGear\nimport cv2\nimport time\n\n# Open input video stream\nstream = VideoGear(source=\"input-video.mp4\").start()\n\n# set input audio stream path\ninput_audio = \"input-audio.aac\"\n\n# define your parameters\noutput_params = {\n    \"-input_framerate\": stream.framerate\n}  # output framerate must match source framerate\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\", **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n\n\n# sleep 1 sec as the above video might still be rendering\ntime.sleep(1)\n\n\n# format FFmpeg command to generate `Output_with_audio.mp4` by merging input_audio in above rendered `Output.mp4`\nffmpeg_command = [\n    \"-y\",\n    \"-i\",\n    \"Output.mp4\",\n    \"-i\",\n    input_audio,\n    \"-c:v\",\n    \"copy\",\n    \"-c:a\",\n    \"copy\",\n    \"-map\",\n    \"0:v:0\",\n    \"-map\",\n    \"1:a:0\",\n    \"-shortest\",\n    \"Output_with_audio.mp4\",\n]  # `-y` parameter is to overwrite outputfile if exists\n\n# execute FFmpeg command\nwriter.execute_ffmpeg_cmd(ffmpeg_command)\n

After running this script, You will get the final 'Output_with_audio.mp4' file with both video and audio merged.

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/","title":"FFmpeg Installation","text":""},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#ffmpeg-installation-instructions","title":"FFmpeg Installation Instructions","text":"

WriteGear must requires FFmpeg executables for its Compression capabilities in Compression Mode. You can following machine-specific instructions for its installation:

In case WriteGear API fails to detect valid FFmpeg executables on your system (even if Compression Mode is enabled), it automatically fallbacks to Non-Compression Mode.

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#linux-ffmpeg-installation","title":"Linux FFmpeg Installation","text":"

The WriteGear API supports Auto-Detection and Manual Configuration methods on a Linux machine:

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#a-auto-detection","title":"A. Auto-Detection","text":"

This is a recommended approach on Linux Machines

If WriteGear API not receives any input from the user on custom_ffmpeg parameter, then on Linux system, it tries to auto-detects the required FFmpeg installed binaries through validation test that employs subprocess python module.

Installation: You can install easily install official FFmpeg according to your Linux Distro by following this post \u27b6

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#b-manual-configuration","title":"B. Manual Configuration","text":"

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#windows-ffmpeg-installation","title":"Windows FFmpeg Installation","text":"

The WriteGear API supports Auto-Installation and Manual Configuration methods on Windows systems.

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#a-auto-installation","title":"A. Auto-Installation","text":"

This is a recommended approach on Windows Machines

If WriteGear API not receives any input from the user on custom_ffmpeg parameter, then on Windows system WriteGear API auto-generates the required FFmpeg Static Binaries from a dedicated Github Server into the temporary directory (for e.g. C:\\Temp) of your machine.

Warning

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#b-manual-configuration_1","title":"B. Manual Configuration","text":"

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#macos-ffmpeg-installation","title":"MacOS FFmpeg Installation","text":"

The WriteGear API supports Auto-Detection and Manual Configuration methods on a macOS machine.

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#a-auto-detection_1","title":"A. Auto-Detection","text":"

This is a recommended approach on MacOS Machines

If WriteGear API not receives any input from the user on custom_ffmpeg parameter, then on macOS system, it tries to auto-detects the required FFmpeg installed binaries through validation test that employs subprocess python module.

Installation: You can easily install FFmpeg on your macOS machine by following this tutorial \u27b6

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#b-manual-configuration_2","title":"B. Manual Configuration","text":"

"},{"location":"gears/writegear/non_compression/overview/","title":"Overview","text":""},{"location":"gears/writegear/non_compression/overview/#writegear-api-non-compression-mode","title":"WriteGear API: Non-Compression Mode","text":"WriteGear API's Non-Compression Mode generalized workflow"},{"location":"gears/writegear/non_compression/overview/#overview","title":"Overview","text":"

When compression_mode parameter is disabled (.i.e compression_mode = False), WriteGear API uses basic OpenCV's inbuilt VideoWriter API tools for encoding multimedia files but without compression.

This mode provides flexible access to OpenCV's VideoWriter API,and also supports various parameters available within this API, but lacks the ability to control output quality, compression, and other important features like lossless video compression, audio encoding, etc. which are only available in Compression Mode. Thereby, the resultant output video-file size will be many times larger as compared to Compression Mode.

Important Information

"},{"location":"gears/writegear/non_compression/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through WriteGear Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/writegear/non_compression/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/writegear/non_compression/params/","title":"Parameters","text":""},{"location":"gears/writegear/non_compression/params/#writegear-api-parameters-non-compression-mode","title":"WriteGear API Parameters: Non-Compression Mode","text":""},{"location":"gears/writegear/non_compression/params/#output","title":"output","text":"

This parameter sets the valid output Video filename/path for the output video.

WriteGear API will throw RuntimeError if output provided is empty or invalid.

Data-Type: String

Default Value: Its default value is 0.

Usage:

Make sure to provide valid filename with valid file-extension based on the encoder in use (default is .mp4).

Its valid input can be one of the following:

"},{"location":"gears/writegear/non_compression/params/#compression_mode","title":"compression_mode","text":"

This parameter selects the WriteGear's Primary Mode of Operation, i.e. if this parameter is enabled (.i.e compression_mode = True) WriteGear will use FFmpeg to encode output video, and if disabled (.i.e compression_mode = False), the OpenCV's VideoWriter API will be used for encoding files/streams.

Data-Type: Boolean

Default Value: Its default value is True.

Usage:

WriteGear(output = 'output.mp4', compression_mode=False)\n

"},{"location":"gears/writegear/non_compression/params/#custom_ffmpeg","title":"custom_ffmpeg","text":"

Not supported in Non-Compression Mode!

"},{"location":"gears/writegear/non_compression/params/#output_params","title":"output_params","text":"

This parameter allows us to exploit almost all OpenCV's VideoWriter API supported parameters effortlessly and flexibly for video-encoding in Non-Compression Mode, by formatting desired FFmpeg Parameters as this parameter's attributes. All supported parameters and FOURCC codecs for compression mode discussed below:

Remember, Non-Compression mode lacks the ability to control output quality and other important features like lossless video compression, audio encoding, etc., which are available with WriteGear's Compression Mode only.

Data-Type: Dictionary

Default Value: Its default value is {}.

"},{"location":"gears/writegear/non_compression/params/#supported-attributes","title":"Supported Attributes","text":"

Non-Compression Mode only gives access to a limited number of Parameters through its output_params parameter's attributes, which are as follows:

"},{"location":"gears/writegear/non_compression/params/#a-opencv-parameters","title":"A. OpenCV Parameters","text":"

WriteGear provides access to all available OpenCV's VideoWriter API parameters in Non-Compression Mode.

Parameters Description -fourcc 4-character code of codec used to encode frames -fps controls the framerate of output video(Default value: 25) -backend (optional) In case of multiple backends, this parameter allows us to specify VideoWriter API's backends to use. Its valid values are CAP_FFMPEG or CAP_GSTREAMER(if enabled) -color (optional) If it is not zero(0), the encoder will expect and encode color frames, otherwise it will work with grayscale frames (the flag is currently supported on Windows only)

-height and -width parameter are no longer supported and are automatically derived from the input frames.

"},{"location":"gears/writegear/non_compression/params/#b-exclusive-parameters","title":"B. Exclusive Parameters","text":"

In addition to OpenCV Parameters, WriteGear API also provides few exclusive attribute, which are as follows:

Usage:

To assign desired parameters in Non-Compression Mode, you can format it as dictionary attribute and pass through this(output_params) parameter as follows:

# format parameter as dictionary attribute\noutput_params = {\"-fps\":30} \n# and then, assign it\nWriteGear(output = 'output.mp4', compression_mode=False, **output_params)\n

Its usage example can be found here \u27b6.

"},{"location":"gears/writegear/non_compression/params/#supported-fourcc-codecs","title":"Supported FOURCC Codecs","text":"

FOURCC is a 4-character code of the codec used to encode video in Non-Compression Mode(OpenCV's VideoWriter API) without compression.

List of all supported FOURCC codecs can found here \u27b6

Usage:

To select desired FOURCC codec in Non-Compression Mode, you can format it as dictionary attribute and pass through this(output_params) parameter. For example, using MJPG as codec, we can:

# format codec as dictionary attribute\noutput_params = {\"-fourcc\":\"MJPG\"} \n# and then, assign it\nWriteGear(output = 'output.mp4', compression_mode=False, **output_params)\n

Its usage example can be found here \u27b6.

"},{"location":"gears/writegear/non_compression/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

WriteGear(output = 'output.mp4', compression_mode=False, logging=True)\n

"},{"location":"gears/writegear/non_compression/usage/","title":"Usage Examples","text":""},{"location":"gears/writegear/non_compression/usage/#writegear-api-usage-examples-non-compression-mode","title":"WriteGear API Usage Examples: Non-Compression Mode","text":"

Important Information

After going through WriteGear Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/writegear/non_compression/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with WriteGear API in Non-Compression Mode:

# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# open any valid video stream(for e.g `myvideo.avi` file)\nstream = CamGear(source=\"myvideo.avi\").start()\n\n# Define writer with Non-compression mode and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\", compression_mode=False)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/non_compression/usage/#using-non-compression-mode-with-videocapture-gears","title":"Using Non-Compression Mode with VideoCapture Gears","text":"

In Non-Compression mode, WriteGear API provides flexible control over OpenCV's VideoWriter API parameters through its output_param dictionary parameter by formating them as dictionary attributes. Moreover, WriteGear API can be used in conjunction with any other Gears/APIs effortlessly.

All supported attributes for output_param can be found here \u27b6

The complete usage example is as follows:

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# define suitable tweak parameters for writer\noutput_params = {\"-fourcc\": \"MJPG\", \"-fps\": 30}\n\n# open live video stream on webcam at first index(i.e. 0) device\nstream = VideoGear(source=0, logging=True).start()\n\n# Define writer with defined parameters and suitable output filename \n# for e.g. `Output.mp4`\nwriter = WriteGear(\n    output=\"Output.mp4\", compression_mode=False, logging=True, **output_params\n)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n    # lets convert frame to gray for this example\n    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n\n    # write gray frame to writer\n    writer.write(gray)\n\n    # Show output window\n    cv2.imshow(\"Output Gray Frame\", gray)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/non_compression/usage/#using-non-compression-mode-with-opencv","title":"Using Non-Compression Mode with OpenCV","text":"

You can easily use WriterGear API directly with any Video Processing library(For e.g OpenCV itself) in Non-Compression Mode. The complete usage example is as follows:

# import required libraries\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# define suitable tweak parameters for writer\noutput_params = {\"-fourcc\": \"MJPG\", \"-fps\": 30}\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = cv2.VideoCapture(0)\n\n# Define writer with defined parameters and suitable output filename \n# for e.g. `Output.mp4`\nwriter = WriteGear(\n    output=\"Output.mp4\", compression_mode=False, logging=True, **output_params\n)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n        break\n\n    # {do something with the frame here}\n    # lets convert frame to gray for this example\n    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n\n    # write gray frame to writer\n    writer.write(gray)\n\n    # Show output window\n    cv2.imshow(\"Output Gray Frame\", gray)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.release()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/non_compression/usage/#using-non-compression-mode-with-gstreamer-pipeline","title":"Using Non-Compression Mode with GStreamer Pipeline","text":"

WriteGear API's Non-Compression Mode also supports GStreamer Pipeline as input to its output parameter, when GStreamer Pipeline Mode is enabled. This provides flexible way to write video frames to file or network stream with controlled framerate and bitrate. The complete usage example is as follows:

Requirement for GStreamer Pipelining

GStreamer Pipelining in WriteGear requires your OpenCV to be built with GStreamer support. Checkout this FAQ for compiling OpenCV with GStreamer support.

New in v0.2.5

This example was added in v0.2.5.

In this example we will be constructing GStreamer pipeline to write video-frames into a file(foo.mp4) at 1M video-bitrate.

# import required libraries\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# enable GStreamer Pipeline Mode for writer\noutput_params = {\"-gst_pipeline_mode\": True}\n\n# open live video stream on webcam at first index(i.e. 0) device\nstream = cv2.VideoCapture(0)\n\n# gst pipeline to write to a file `foo.mp4` at 1M video-bitrate\nGSTPipeline = \"appsrc ! videoconvert ! avenc_mpeg4 bitrate=100000 ! mp4mux ! filesink location={}\".format(\n    \"foo.mp4\"\n)\n\n# Define writer with defined parameters and with our Gstreamer pipeline\nwriter = WriteGear(\n    output=GSTPipeline, compression_mode=False, logging=True, **output_params\n)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.release()\n\n# safely close writer\nwriter.close()\n

"},{"location":"help/camgear_ex/","title":"Bonus Examples","text":""},{"location":"help/camgear_ex/#camgear-examples","title":"CamGear Examples","text":""},{"location":"help/camgear_ex/#synchronizing-two-sources-in-camgear","title":"Synchronizing Two Sources in CamGear","text":"

In this example both streams and corresponding frames will be processed synchronously i.e. with no delay:

Using same source with more than one instances of CamGear can lead to Global Interpreter Lock (GIL) that degrades performance even when it is not a bottleneck.

# import required libraries\nfrom vidgear.gears import CamGear\nimport cv2\nimport time\n\n# define and start the stream on first source ( For e.g #0 index device)\nstream1 = CamGear(source=0, logging=True).start() \n\n# define and start the stream on second source ( For e.g #1 index device)\nstream2 = CamGear(source=1, logging=True).start() \n\n# infinite loop\nwhile True:\n\n    frameA = stream1.read()\n    # read frames from stream1\n\n    frameB = stream2.read()\n    # read frames from stream2\n\n    # check if any of two frame is None\n    if frameA is None or frameB is None:\n        #if True break the infinite loop\n        break\n\n    # do something with both frameA and frameB here\n    cv2.imshow(\"Output Frame1\", frameA)\n    cv2.imshow(\"Output Frame2\", frameB)\n    # Show output window of stream1 and stream 2 separately\n\n    key = cv2.waitKey(1) & 0xFF\n    # check for 'q' key-press\n    if key == ord(\"q\"):\n        #if 'q' key-pressed break out\n        break\n\n    if key == ord(\"w\"):\n        #if 'w' key-pressed save both frameA and frameB at same time\n        cv2.imwrite(\"Image-1.jpg\", frameA)\n        cv2.imwrite(\"Image-2.jpg\", frameB)\n        #break   #uncomment this line to break out after taking images\n\ncv2.destroyAllWindows()\n# close output window\n\n# safely close both video streams\nstream1.stop()\nstream2.stop()\n

"},{"location":"help/camgear_ex/#using-variable-yt_dlp-parameters-in-camgear","title":"Using variable yt_dlp parameters in CamGear","text":"

CamGear provides exclusive attributes STREAM_RESOLUTION (for specifying stream resolution) & STREAM_PARAMS (for specifying underlying API(e.g. yt_dlp) parameters) with its options dictionary parameter.

The complete usage example is as follows:

More information on STREAM_RESOLUTION & STREAM_PARAMS attributes can be found here \u27b6

# import required libraries\nfrom vidgear.gears import CamGear\nimport cv2\n\n# specify attributes\noptions = {\"STREAM_RESOLUTION\": \"720p\", \"STREAM_PARAMS\": {\"nocheckcertificate\": True}}\n\n# Add YouTube Video URL as input source (for e.g https://youtu.be/bvetuLwJIkA)\n# and enable Stream Mode (`stream_mode = True`)\nstream = CamGear(\n    source=\"https://youtu.be/bvetuLwJIkA\", stream_mode=True, logging=True, **options\n).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"help/camgear_ex/#using-camgear-for-capturing-rtsprtmp-urls","title":"Using CamGear for capturing RTSP/RTMP URLs","text":"

You can open any network stream (such as RTSP/RTMP) just by providing its URL directly to CamGear's source parameter.

Here's a high-level wrapper code around CamGear API to enable auto-reconnection during capturing:

New in v0.2.2

This example was added in v0.2.2.

Enforcing UDP stream

You can easily enforce UDP for RTSP streams inplace of default TCP, by putting following lines of code on the top of your existing code:

# import required libraries\nimport os\n\n# enforce UDP\nos.environ[\"OPENCV_FFMPEG_CAPTURE_OPTIONS\"] = \"rtsp_transport;udp\"\n

Finally, use backend parameter value as backend=cv2.CAP_FFMPEG in CamGear.

from vidgear.gears import CamGear\nimport cv2\nimport datetime\nimport time\n\n\nclass Reconnecting_CamGear:\n    def __init__(self, cam_address, reset_attempts=50, reset_delay=5):\n        self.cam_address = cam_address\n        self.reset_attempts = reset_attempts\n        self.reset_delay = reset_delay\n        self.source = CamGear(source=self.cam_address).start()\n        self.running = True\n\n    def read(self):\n        if self.source is None:\n            return None\n        if self.running and self.reset_attempts > 0:\n            frame = self.source.read()\n            if frame is None:\n                self.source.stop()\n                self.reset_attempts -= 1\n                print(\n                    \"Re-connection Attempt-{} occured at time:{}\".format(\n                        str(self.reset_attempts),\n                        datetime.datetime.now().strftime(\"%m-%d-%Y %I:%M:%S%p\"),\n                    )\n                )\n                time.sleep(self.reset_delay)\n                self.source = CamGear(source=self.cam_address).start()\n                # return previous frame\n                return self.frame\n            else:\n                self.frame = frame\n                return frame\n        else:\n            return None\n\n    def stop(self):\n        self.running = False\n        self.reset_attempts = 0\n        self.frame = None\n        if not self.source is None:\n            self.source.stop()\n\n\nif __name__ == \"__main__\":\n    # open any valid video stream\n    stream = Reconnecting_CamGear(\n        cam_address=\"rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov\",\n        reset_attempts=20,\n        reset_delay=5,\n    )\n\n    # loop over\n    while True:\n\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if None-type\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # Show output window\n        cv2.imshow(\"Output\", frame)\n\n        # check for 'q' key if pressed\n        key = cv2.waitKey(1) & 0xFF\n        if key == ord(\"q\"):\n            break\n\n    # close output window\n    cv2.destroyAllWindows()\n\n    # safely close video stream\n    stream.stop()\n

"},{"location":"help/camgear_faqs/","title":"FAQs","text":""},{"location":"help/camgear_faqs/#camgear-faqs","title":"CamGear FAQs","text":""},{"location":"help/camgear_faqs/#what-is-camgear-api-and-what-does-it-do","title":"What is CamGear API and what does it do?","text":"

Answer: CamGear supports a diverse range of video streams which can handle/control video stream almost any IP/USB Cameras, multimedia video file format (upto 4k tested), any network stream URL such as http(s), rtp, rtsp, rtmp, mms, etc. In addition to this, it also supports live Gstreamer's RAW pipelines and YouTube video/livestreams URLs. For more info. see CamGear doc \u27b6.

"},{"location":"help/camgear_faqs/#im-only-familiar-with-opencv-how-to-get-started-with-camgear-api","title":"I'm only familiar with OpenCV, how to get started with CamGear API?","text":"

Answer: Answer: First, refer to the Switching from OpenCV guide, then go through CamGear documentation \u27b6. If you still have doubts, ask us on Gitter \u27b6 Community channel.

"},{"location":"help/camgear_faqs/#how-to-change-opencv-source-backend-in-camgear-api","title":"How to change OpenCV source backend in CamGear API?","text":"

Answer: See its Parameters \u27b6. Its, backend(int) parameter sets the backend of the source. Its value can be for e.g. backend = cv2.CAP_DSHOW in case of Direct Show.

"},{"location":"help/camgear_faqs/#how-to-get-framerate-of-the-source-in-camgear-api","title":"How to get framerate of the source in CamGear API?","text":"

Answer: CamGear's framerate global variable can be used to retrieve framerate of the input video stream. See this example \u27b6.

"},{"location":"help/camgear_faqs/#how-to-compile-opencv-with-gstreamer-support","title":"How to compile OpenCV with GStreamer support?","text":"

Answer: For compiling OpenCV with GSstreamer(>=v1.0.0) support:

Linux Windows MacOS

"},{"location":"help/camgear_faqs/#how-to-change-quality-and-parameters-of-youtube-streams-with-camgear","title":"How to change quality and parameters of YouTube Streams with CamGear?","text":"

Answer: CamGear provides exclusive attributes STREAM_RESOLUTION (for specifying stream resolution) & STREAM_PARAMS (for specifying underlying API(e.g. yt_dlp) parameters) with its options dictionary parameter. See this bonus example \u27b6.

"},{"location":"help/camgear_faqs/#how-to-open-rtsp-network-streams-with-camgear","title":"How to open RTSP network streams with CamGear?","text":"

Answer: You can open any local network stream (such as RTSP) just by providing its URL directly to CamGear's source parameter. See this bonus example \u27b6.

"},{"location":"help/camgear_faqs/#how-to-set-camera-settings-with-camgear","title":"How to set Camera Settings with CamGear?","text":"

Answer: See this usage example \u27b6.

"},{"location":"help/camgear_faqs/#can-i-play-4k8k-video-with-camgear-api","title":"Can I play 4K/8k video with CamGear API?","text":"

Answer: Yes, you can if your System Hardware supports it.

"},{"location":"help/camgear_faqs/#how-to-synchronize-between-two-cameras","title":"How to synchronize between two cameras?","text":"

Answer: See this bonus example \u27b6.

"},{"location":"help/camgear_faqs/#can-i-use-gpu-to-decode-the-video-source","title":"Can I use GPU to decode the video source?","text":"

Answer: See this issue comment \u27b6.

"},{"location":"help/camgear_faqs/#why-camgear-is-throwing-warning-that-threaded-queue-mode-is-disabled","title":"Why CamGear is throwing warning that Threaded Queue Mode is disabled?","text":"

Answer: That's a normal behavior. Please read about Threaded Queue Mode \u27b6

"},{"location":"help/general_faqs/","title":"General FAQs","text":""},{"location":"help/general_faqs/#general-faqs","title":"General FAQs","text":""},{"location":"help/general_faqs/#im-new-to-python-programming-or-its-usage-in-opencv-library-how-to-use-vidgear-in-my-projects","title":"\"I'm new to Python Programming or its usage in OpenCV Library\", How to use vidgear in my projects?","text":"

Answer: Before using vidgear, It's recommended to first go through the following dedicated blog sites and learn how OpenCV-Python syntax works (with examples):

Once done, visit Switching from OpenCV \u27b6 to easily replace OpenCV APIs with suitable Gears \u27b6 in your project. All the best!

If you run into any trouble or have any questions, then refer our Help section.

"},{"location":"help/general_faqs/#vidgear-is-using-multi-threading-but-python-is-notorious-for-its-poor-performance-in-multithreading","title":"\"VidGear is using Multi-threading, but Python is notorious for its poor performance in multithreading?\"","text":"

Answer: Refer vidgear's Threaded-Queue-Mode \u27b6

"},{"location":"help/general_faqs/#modulenotfounderror-no-module-named-vidgeargears-vidgear-is-not-a-package","title":"ModuleNotFoundError: No module named 'vidgear.gears'. 'vidgear' is not a package?","text":"

Answer: This error means you either have a file named vidgear.py in your python path or you've named your python script vidgear.py. Replace vidgear name with anything else to fix this error.

"},{"location":"help/general_faqs/#how-to-log-to-a-file-in-vidgear","title":"How to log to a file in VidGear?","text":"

Answer: VidGear provides exclusive VIDGEAR_LOGFILE environment variable to enable logging to a file while logging is enabled (i.e. logging=True) on respective Gear. You just have to set directory pathname (automatically creates vidgear.log file) or a log file pathname itself as value for this environment variable. This can be done on various Operating Systems as follows:

Remember enabling this logging to a file will completely disable any output on the terminal.

Linux Windows (Powershell) MacOS
# path to file\nexport VIDGEAR_LOGFILE=\"$HOME/foo.log\"\n\n# or just directory path \n# !!! Make sure `foo` path already exists !!!\nexport VIDGEAR_LOGFILE=\"$HOME/foo\"\n\n# to remove\nunset VIDGEAR_LOGFILE\n
# path to file\n$Env:VIDGEAR_LOGFILE = \"D:\\foo.log\"\n\n# or just directory path \n# !!! Make sure `foo` path already exists !!!\n$Env:VIDGEAR_LOGFILE = \"D:\\foo\"\n\n# to remove\n$Env:VIDGEAR_LOGFILE = \"\"\n
# path to file\nexport VIDGEAR_LOGFILE=\"$HOME/foo.log\"\n\n# or just directory path \n# !!! Make sure `foo` path already exists !!!\nexport VIDGEAR_LOGFILE=\"$HOME/foo\"\n\n# to remove\nunset VIDGEAR_LOGFILE\n

"},{"location":"help/general_faqs/#can-i-perform-deep-learning-task-with-vidgear","title":"Can I perform Deep Learning task with VidGear?","text":"

Answer: VidGear is a powerful Video Processing library (similar to OpenCV, FFmpeg, etc.) that can read, write, process, send & receive a sequence of video-frames in an optimized manner. But for Deep Learning or Machine Learning tasks, you have to use a third-party library. That being said, all VidGear's APIs can be used with any third-party Library(such as PyTorch, Tensorflow, etc.) that can leverage the overall performance if you're processing video/audio streams/frames in your application with Deep Learning tasks. Also, it eases the workflow since you have to write way fewer lines of code to read/store/process output videos.

"},{"location":"help/general_faqs/#can-i-ask-my-question-directly-without-raising-an-issue","title":"Can I ask my question directly without raising an issue?","text":"

Answer: Yes, please join our Gitter \u27b6 Community channel.

"},{"location":"help/general_faqs/#how-to-contribute-to-vidgear-development","title":"How to contribute to VidGear development?","text":"

Answer: See our Contribution Guidelines \u27b6

"},{"location":"help/general_faqs/#what-oses-are-supported-by-vidgear","title":"What OSes are supported by VidGear?","text":"

Answer: See Supported Systems \u27b6

"},{"location":"help/general_faqs/#what-python-versions-are-supported-by-vidgear","title":"What Python versions are supported by VidGear?","text":"

Answer: See Supported Python legacies \u27b6

"},{"location":"help/general_faqs/#can-i-include-vidgear-in-my-project-commercially-or-not","title":"Can I include VidGear in my project commercially or not?","text":"

Answer: Yes, you can, but strictly under the Terms and Conditions given in VidGear License \u27b6

"},{"location":"help/general_faqs/#i-love-using-vidgear-for-my-projects-how-can-i-support-it","title":"\"I Love using VidGear for my projects\", How can I support it?","text":"

Answer: See Helping VidGear \u27b6

"},{"location":"help/get_help/","title":"Getting Help","text":""},{"location":"help/get_help/#getting-help","title":"Getting Help","text":"Courtesy - Pinterest

Would you like to get help with VidGear?

There are several ways such as:

"},{"location":"help/get_help/#frequently-asked-questions","title":"Frequently Asked Questions","text":"

Got a question related to VidGear Working?

Checkout the Frequently Asked Questions - a curated list of all the questions with adequate answer that we commonly receive for quickly troubleshooting your problems:

"},{"location":"help/get_help/#bonus-examples","title":"Bonus Examples","text":"

How we do this with that API?

Checkout the Bonus Examples - a curated list of all experimental examples with unusual configuration that aren't included in general usage examples:

"},{"location":"help/get_help/#join-our-gitter-community-channel","title":"Join our Gitter Community channel","text":"

Have you come up with some new idea \ud83d\udca1 or looking for the fastest way troubleshoot your problems

Join and chat on our Gitter Community channel:

There you can ask quick questions, swiftly troubleshoot your problems, help others, share ideas & information, etc.

"},{"location":"help/get_help/#this-is-what-you-do-when","title":"This is what you do when...","text":""},{"location":"help/get_help/#reporting-an-issues","title":"Reporting an issues","text":"

Want to report a bug? Suggest a new feature?

Before you do, please read our guidelines \u27b6

"},{"location":"help/get_help/#preparing-a-pull-request","title":"Preparing a Pull Request","text":"

Interested in contributing to VidGear?

Before you do, please read our guidelines \u27b6

"},{"location":"help/netgear_async_ex/","title":"Bonus Examples","text":""},{"location":"help/netgear_async_ex/#netgear_async-examples","title":"NetGear_Async Examples","text":""},{"location":"help/netgear_async_ex/#using-netgear_async-with-webgear","title":"Using NetGear_Async with WebGear","text":"

The complete usage example is as follows:

New in v0.2.2

This example was added in v0.2.2.

"},{"location":"help/netgear_async_ex/#client-webgear-server","title":"Client + WebGear Server","text":"

Open a terminal on Client System where you want to display the input frames (and setup WebGear server) received from the Server and execute the following python code:

After running this code, Make sure to open Browser immediately otherwise NetGear_Async will soon exit with TimeoutError. You can also try setting timeout parameter to a higher value to extend this timeout.

Make sure you use different port value for NetGear_Async and WebGear API.

High CPU utilization may occur on Client's end. User discretion is advised.

Note down the IP-address of this system (required at Server's end) by executing the hostname -I command and also replace it in the following code.\"

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nfrom vidgear.gears.asyncio import WebGear\nfrom vidgear.gears.asyncio.helper import reducer\nimport uvicorn, asyncio, cv2\n\n# Define NetGear_Async Client at given IP address and define parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear_Async(\n    receive_mode=True,\n    pattern=1,\n    logging=True,\n).launch()\n\n# create your own custom frame producer\nasync def my_frame_producer():\n\n    # loop over Client's Asynchronous Frame Generator\n    async for frame in client.recv_generator():\n\n        # {do something with received frames here}\n\n        # reducer frames size if you want more performance otherwise comment this line\n        frame = await reducer(\n            frame, percentage=30, interpolation=cv2.INTER_AREA\n        )  # reduce frame by 30%\n\n        # handle JPEG encoding\n        encodedImage = cv2.imencode(\".jpg\", frame)[1].tobytes()\n        # yield frame in byte format\n        yield (b\"--frame\\r\\nContent-Type:image/jpeg\\r\\n\\r\\n\" + encodedImage + b\"\\r\\n\")\n        await asyncio.sleep(0)\n\n\nif __name__ == \"__main__\":\n    # Set event loop to client's\n    asyncio.set_event_loop(client.loop)\n\n    # initialize WebGear app without any source\n    web = WebGear(logging=True)\n\n    # add your custom frame producer to config with adequate IP address\n    web.config[\"generator\"] = my_frame_producer\n\n    # run this app on Uvicorn server at address http://localhost:8000/\n    uvicorn.run(web(), host=\"localhost\", port=8000)\n\n    # safely close client\n    client.close()\n\n    # close app safely\n    web.shutdown()\n

On successfully running this code, the output stream will be displayed at address http://localhost:8000/ in your Client's Browser.

"},{"location":"help/netgear_async_ex/#server","title":"Server","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

# import library\nfrom vidgear.gears.asyncio import NetGear_Async\nimport cv2, asyncio\n\n# initialize Server without any source\nserver = NetGear_Async(\n    source=None,\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n)\n\n# Create a async frame generator as custom source\nasync def my_frame_generator():\n\n    # !!! define your own video source here !!!\n    # Open any video stream such as live webcam\n    # video stream on first index(i.e. 0) device\n    stream = cv2.VideoCapture(0)\n\n    # loop over stream until its terminated\n    while True:\n\n        # read frames\n        (grabbed, frame) = stream.read()\n\n        # check if frame empty\n        if not grabbed:\n            break\n\n        # do something with the frame to be sent here\n\n        # yield frame\n        yield frame\n        # sleep for sometime\n        await asyncio.sleep(0)\n\n    # close stream\n    stream.release()\n\n\nif __name__ == \"__main__\":\n    # set event loop\n    asyncio.set_event_loop(server.loop)\n    # Add your custom source generator to Server configuration\n    server.config[\"generator\"] = my_frame_generator()\n    # Launch the Server\n    server.launch()\n    try:\n        # run your main function task until it is complete\n        server.loop.run_until_complete(server.task)\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    finally:\n        # finally close the server\n        server.close()\n

"},{"location":"help/netgear_async_faqs/","title":"FAQs","text":""},{"location":"help/netgear_async_faqs/#netgear_async-faqs","title":"NetGear_Async FAQs","text":""},{"location":"help/netgear_async_faqs/#what-is-netgear_async-api-and-what-does-it-do","title":"What is NetGear_Async API and what does it do?","text":"

Answer: NetGear_Async is an asyncio videoframe messaging framework, built on zmq.asyncio, and powered by high-performance asyncio event loop called uvloop to achieve unmatchable high-speed and lag-free video streaming over the network with minimal resource constraints. Basically, this API is able to transfer thousands of frames in just a few seconds without causing any significant load on your system. For more info. see NetGear_Async doc \u27b6

"},{"location":"help/netgear_async_faqs/#how-to-get-started-with-netgear_async-api","title":"How to get started with NetGear_Async API?","text":"

Answer: Answer: Answer: First, refer to the Switching from OpenCV guide, then go through NetGear_Async documentation \u27b6. If you still have doubts, ask us on Gitter \u27b6 Community channel.

See NetGear_Async doc \u27b6. Still in doubt, then ask us on Gitter \u27b6 Community channel.

"},{"location":"help/netgear_async_faqs/#netgear_async-is-throwing-modulenotfounderror-on-importing-why","title":"\"NetGear_Async is throwing ModuleNotFoundError on importing\", Why?","text":"

Answer: This error means, VidGear is installed WITHOUT asyncio package support on your machine. For this support, see Requirements \u27b6.

"},{"location":"help/netgear_async_faqs/#what-is-the-key-difference-between-netgear_async-and-netgear-apis","title":"What is the key difference between NetGear_Async and NetGear APIs?","text":"

Answer:

Key Difference: NetGear_Async is highly memory efficient, but has less features as compared to NetGear API which is marginally faster too.

"},{"location":"help/netgear_async_faqs/#can-i-use-multi-server-bi-directional-like-modes-in-netgear_async","title":"Can I use Multi-Server, Bi-Directional like modes in NetGear_Async?","text":"

Answer: No, NetGear_Async does NOT provide support for any NetGear's Exclusive modes yet.

"},{"location":"help/netgear_async_faqs/#how-to-use-netgear_async-with-custom-server-source-from-opencv","title":"How to use NetGear_Async with custom Server Source from OpenCV?","text":"

Answer: See this usage example \u27b6.

"},{"location":"help/netgear_async_faqs/#why-netgear_async-is-running-slow","title":"Why NetGear_Async is running slow?","text":"

Answer: Checkout tips suggested in this answer \u27b6

"},{"location":"help/netgear_ex/","title":"Bonus Examples","text":""},{"location":"help/netgear_ex/#netgear-examples","title":"NetGear Examples","text":""},{"location":"help/netgear_ex/#using-netgear-with-webgear","title":"Using NetGear with WebGear","text":"

The complete usage example is as follows:

New in v0.2.2

This example was added in v0.2.2.

"},{"location":"help/netgear_ex/#client-webgear-server","title":"Client + WebGear Server","text":"

Open a terminal on Client System where you want to display the input frames (and setup WebGear server) received from the Server and execute the following python code:

After running this code, Make sure to open Browser immediately otherwise NetGear will soon exit with RuntimeError. You can also try setting max_retries and request_timeout like attributes to a higher value to avoid this.

Make sure you use different port value for NetGear and WebGear API.

High CPU utilization may occur on Client's end. User discretion is advised.

Note down the local IP-address of this system (required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

# import necessary libs\nimport uvicorn, asyncio, cv2\nfrom vidgear.gears import NetGear\nfrom vidgear.gears.asyncio import WebGear\nfrom vidgear.gears.asyncio.helper import reducer\n\n# initialize WebGear app without any source\nweb = WebGear(logging=True)\n\n\n# activate jpeg encoding and specify other related parameters\noptions = {\n    \"jpeg_compression\": True,\n    \"jpeg_compression_quality\": 90,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# create your own custom frame producer\nasync def my_frame_producer():\n    # initialize global params\n    # Define NetGear Client at given IP address and define parameters\n    # !!! change following IP address '192.168.x.xxx' with yours !!!\n    client = NetGear(\n        receive_mode=True,\n        address=\"192.168.x.xxx\",\n        port=\"5454\",\n        protocol=\"tcp\",\n        pattern=1,\n        logging=True,\n        **options,\n    )\n\n    # loop over frames\n    while True:\n        # receive frames from network\n        frame = client.recv()\n\n        # if NoneType\n        if frame is None:\n            break\n\n        # do something with your OpenCV frame here\n\n        # reducer frames size if you want more performance otherwise comment this line\n        frame = await reducer(\n            frame, percentage=30, interpolation=cv2.INTER_AREA\n        )  # reduce frame by 30%\n\n        # handle JPEG encoding\n        encodedImage = cv2.imencode(\".jpg\", frame)[1].tobytes()\n        # yield frame in byte format\n        yield (b\"--frame\\r\\nContent-Type:image/jpeg\\r\\n\\r\\n\" + encodedImage + b\"\\r\\n\")\n        await asyncio.sleep(0)\n    # close stream\n    client.close()\n\n\n# add your custom frame producer to config with adequate IP address\nweb.config[\"generator\"] = my_frame_producer\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

On successfully running this code, the output stream will be displayed at address http://localhost:8000/ in your Client's Browser.

"},{"location":"help/netgear_ex/#server","title":"Server","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate jpeg encoding and specify other related parameters\noptions = {\n    \"jpeg_compression\": True,\n    \"jpeg_compression_quality\": 90,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = VideoGear(source=0).start()\n\n# Define NetGear server at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if None-type\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"help/netgear_ex/#using-netgear-with-webgear_rtc","title":"Using NetGear with WebGear_RTC","text":"

The complete usage example is as follows:

New in v0.2.4

This example was added in v0.2.4.

"},{"location":"help/netgear_ex/#client-webgear_rtc-server","title":"Client + WebGear_RTC Server","text":"

Open a terminal on Client System where you want to display the input frames (and setup WebGear_RTC server) received from the Server and execute the following python code:

After running this code, Make sure to open Browser immediately otherwise NetGear will soon exit with RuntimeError. You can also try setting max_retries and request_timeout like attributes to a higher value to avoid this.

Make sure you use different port value for NetGear and WebGear_RTC API.

High CPU utilization may occur on Client's end. User discretion is advised.

Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

For VideoCapture APIs you also need to implement start() in addition to read() and stop() methods in your Custom Streaming Class as shown in following example, otherwise WebGear_RTC will fail to work!

# import necessary libs\nimport uvicorn, cv2\nfrom vidgear.gears import NetGear\nfrom vidgear.gears.helper import reducer\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# create your own custom streaming class\nclass Custom_Stream_Class:\n    \"\"\"\n    Custom Streaming using NetGear Receiver\n    \"\"\"\n\n    def __init__(\n        self,\n        address=None,\n        port=\"5454\",\n        protocol=\"tcp\",\n        pattern=1,\n        logging=True,\n        **options,\n    ):\n        # initialize global params\n        # Define NetGear Client at given IP address and define parameters\n        self.client = NetGear(\n            receive_mode=True,\n            address=address,\n            port=port,\n            protocol=protocol,\n            pattern=pattern,\n            logging=logging,\n            **options\n        )\n        self.running = False\n\n    def start(self):\n\n        # don't forget this function!!!\n        # This function is specific to VideoCapture APIs only\n\n        if not self.source is None:\n            self.source.start()\n\n    def read(self):\n\n        # don't forget this function!!!\n\n        # check if source was initialized or not\n        if self.source is None:\n            return None\n        # check if we're still running\n        if self.running:\n            # receive frames from network\n            frame = self.client.recv()\n            # check if frame is available\n            if not (frame is None):\n\n                # do something with your OpenCV frame here\n\n                # reducer frames size if you want more performance otherwise comment this line\n                frame = reducer(frame, percentage=20)  # reduce frame by 20%\n\n                # return our gray frame\n                return frame\n            else:\n                # signal we're not running now\n                self.running = False\n        # return None-type\n        return None\n\n    def stop(self):\n\n        # don't forget this function!!!\n\n        # flag that we're not running\n        self.running = False\n        # close stream\n        if not (self.client is None):\n            self.client.close()\n            self.client = None\n\n\n# activate jpeg encoding and specify NetGear related parameters\noptions = {\n    \"jpeg_compression\": True,\n    \"jpeg_compression_quality\": 90,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# assign your Custom Streaming Class with adequate NetGear parameters\n# to `custom_stream` attribute in options parameter of WebGear_RTC.\noptions = {\n    \"custom_stream\": Custom_Stream_Class(\n        address=\"192.168.x.xxx\",\n        port=\"5454\",\n        protocol=\"tcp\",\n        pattern=1,\n        logging=True,\n        **options\n    )\n}\n\n# initialize WebGear_RTC app without any source\nweb = WebGear_RTC(logging=True, **options)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

On successfully running this code, the output stream will be displayed at address http://localhost:8000/ in your Client's Browser.

"},{"location":"help/netgear_ex/#server_1","title":"Server","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate jpeg encoding and specify other related parameters\noptions = {\n    \"jpeg_compression\": True,\n    \"jpeg_compression_quality\": 90,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = VideoGear(source=0).start()\n\n# Define NetGear server at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"help/netgear_faqs/","title":"FAQs","text":""},{"location":"help/netgear_faqs/#netgear-faqs","title":"NetGear FAQs","text":""},{"location":"help/netgear_faqs/#what-is-netgear-api-and-what-does-it-do","title":"What is NetGear API and what does it do?","text":"

Answer: NetGear is exclusively designed to transfer video frames & data synchronously (Pair & Request/Reply) as well as asynchronously (Publish/Subscribe) between various interconnecting systems over the network in real-time. For more info. see NetGear doc \u27b6

"},{"location":"help/netgear_faqs/#how-to-get-started-with-netgear-api","title":"How to get started with NetGear API?","text":"

Answer: First, refer to the Switching from OpenCV guide, then go through NetGear documentation \u27b6. If you still have doubts, ask us on Gitter \u27b6 Community channel.

"},{"location":"help/netgear_faqs/#what-exclusive-modes-are-compatible-with-each-other-in-netgear-api","title":"What Exclusive Modes are compatible with each other in NetGear API?","text":"

Here's the compatibility chart for NetGear's Exclusive Modes:

Exclusive Modes Multi-Servers Multi-Clients Secure Bidirectional SSH Tunneling Multi-Servers - No (throws error) Yes Yes No (throws error) Multi-Clients No (throws error) - Yes Yes No (throws error) Secure Yes Yes - Yes Yes Bidirectional Yes Yes Yes - Yes SSH Tunneling No (throws error) No (throws error) Yes Yes -

"},{"location":"help/netgear_faqs/#why-netgear-is-running-slow","title":"Why NetGear is running slow?","text":"

Answer: Here are few tips to troubleshoot performance on your machine:

"},{"location":"help/netgear_faqs/#how-to-find-local-ip-address-on-different-os-platforms","title":"How to find local IP-address on different OS platforms?","text":"

Answer: For finding local IP-address of your machine:

On Linux OSOn Windows OSOn MAC OS

"},{"location":"help/netgear_faqs/#how-to-send-data-along-with-frames-in-multi-servers-and-multi-clients-modes","title":"How to send data along with frames in Multi-Servers and Multi-Clients Modes?","text":"

Answer: See Multi-Servers usage example \u27b6 and Multi-Clients usage example \u27b6

"},{"location":"help/netgear_faqs/#how-to-use-enable-encryption-and-authentication-in-netgear-api","title":"How to use enable Encryption and Authentication in NetGear API?","text":"

Answer: See its Secure Mode doc \u27b6.

"},{"location":"help/netgear_faqs/#how-to-send-custom-data-along-with-frames-bidirectionally-in-netgear-api","title":"How to send custom data along with frames bidirectionally in NetGear API?","text":"

Answer: See its Bidirectional Mode doc \u27b6.

"},{"location":"help/netgear_faqs/#how-to-access-netgear-api-outside-network-or-remotely","title":"How to access NetGear API outside network or remotely?","text":"

Answer: See its SSH Tunneling Mode doc \u27b6.

"},{"location":"help/netgear_faqs/#are-there-any-side-effect-of-sending-data-with-frames","title":"Are there any side-effect of sending data with frames?","text":"

Answer: Yes, it may lead to additional LATENCY depending upon the size/amount of the data being transferred. User discretion is advised.

"},{"location":"help/netgear_faqs/#why-netgear-api-not-working-correctly","title":"Why NetGear API not working correctly?","text":"

Answer: First, carefully go through NetGear doc \u27b6 that contains detailed information. Also, checkout PyZmq Docs \u27b6 for its various settings/parameters. If still it doesn't work for you, then let us know on Gitter \u27b6

"},{"location":"help/netgear_faqs/#how-to-solve-zmqerrorzmqerror-errors","title":"How to solve zmq.error.ZMQError errors?","text":"

Answer: For those used to the idea that a \"server\" provides their address to a client, then you should recheck your preconceptions! Please read the Netgear instructions carefully, and you will note that it is the client device that defines the IP that is provided to the server config. If you get this the wrong way (using the server IP on the client), then you will get a zmq.error.ZMQError error. Make sure it is the client's IP shared across the two systems.

"},{"location":"help/pigear_ex/","title":"Bonus Examples","text":""},{"location":"help/pigear_ex/#pigear-examples","title":"PiGear Examples","text":""},{"location":"help/pigear_ex/#changing-output-pixel-format-in-pigear-api-with-picamera2-backend","title":"Changing Output Pixel Format in PiGear API with Picamera2 Backend","text":"

With the Picamera2 backend, you can also define a custom format (format of output frame pixels) in PiGear API.

Handling output frames with a custom pixel format correctly

While defining custom format as an optional parameter, it is advised to also define the colorspace parameter in the PiGear API. This is required only under TWO conditions:

Otherwise, output frames might NOT be compatible with OpenCV functions, and you need to handle these frames manually!

Picamera2 library has an unconventional naming convention for its pixel formats.

Please note that, Picamera2 takes its pixel format naming from libcamera, which in turn takes them from certain underlying Linux components. The results are not always the most intuitive. For example, OpenCV users will typically want each pixel to be a (B, G, R) triple for which the RGB888 format should be chosen, and not BGR888. Similarly, OpenCV users wanting an alpha channel should select XRGB8888.

For more information, refer Picamera2 docs \u27b6

YUV420/YVU420YUYV

For reducing the size of frames in memory it is advised to use the YUV420 pixels format.

In this example we will be defining custom YUV420 (or YVU420) pixels format of output frame, and converting it back to BGR to be able to display with OpenCV.

You could also instead define colorspace=\"COLOR_YUV420p2RGB\" parameter in PiGear API for converting it back to BGR similarly.

# import required libraries\nfrom vidgear.gears import PiGear\nimport cv2\n\n# formulate `format` Picamera2 API \n# configurational parameters\noptions = {\n    \"format\": \"YUV420\" # or use `YVU420`\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    yuv420_frame = stream.read()\n\n    # check for frame if Nonetype\n    if yuv420_frame is None:\n        break\n\n    # {do something with the `YUV420` frame here}\n\n    # convert `YUV420` to `BGR`\n    bgr = cv2.cvtColor(yuv420_frame, cv2.COLOR_YUV420p2BGR)\n\n    # {do something with the `BGR` frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", bgr)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

YUYV is a one packed 4:2:2 YUV format that is popularly used by USB cameras.

Make sure YUYV pixel format is supported by your USB camera.

In this example we will be defining custom YUYV pixels format of output frame, and converting it back to BGR to be able to display with OpenCV.

You could also instead define colorspace=\"COLOR_YUV2BGR_YUYV\" parameter in PiGear API for converting it back to BGR similarly.

# import required libraries\nfrom vidgear.gears import PiGear\nimport cv2\n\n# formulate `format` Picamera2 API \n# configurational parameters\noptions = {\n    \"format\": \"YUYV\"\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    yuv420_frame = stream.read()\n\n    # check for frame if Nonetype\n    if yuv420_frame is None:\n        break\n\n    # {do something with the `YUV420` frame here}\n\n    # convert `YUV420` to `BGR`\n    bgr = cv2.cvtColor(yuv420_frame, cv2.COLOR_YUV2BGR_YUYV)\n\n    # {do something with the `BGR` frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", bgr)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"help/pigear_ex/#dynamically-adjusting-raspberry-pi-camera-parameters-at-runtime-in-pigear-api","title":"Dynamically Adjusting Raspberry Pi Camera Parameters at Runtime in PiGear API","text":"New Picamera2 backendLegacy Picamera backend

With the picamera2 backend, using stream global parameter in the PiGear API, you can change all camera controls (except output resolution and format) at runtime after the camera has started.

Accessing all available camera controls

A complete list of all the available camera controls can be found in the picamera2 docs \u27b6, and also by inspecting the camera_controls property of the Picamera2 object available with stream global parameter in PiGear API:

# import required libraries\nfrom vidgear.gears import PiGear\n\n# open any pi video stream\nstream = PiGear()\n\n#display all available camera controls\nprint(stream.stream.camera_controls)\n\n# safely close video stream\nstream.stop()\n

This returns a dictionary with the control names as keys, and each value being a tuple of (min, max, default) values for that control. The default value should be interpreted with some caution as in many cases libcamera's default value will be overwritten by the camera tuning as soon as the camera is started.

In this example, we will set the initial Camera Module's brightness value to -0.5 (dark), and will change it to 0.5 (bright) when the Z key is pressed at runtime:

Delay in setting runtime controls

There will be a delay of several frames before the controls take effect. This is because there is perhaps quite a large number of requests for camera frames already in flight, and for some controls (exposure time and analogue gain specifically), the camera may actually take several frames to apply the updates.

Using with construct for Guaranteed Camera Control Updates at Runtime

While directly modifying using set_controls method might seem convenient, it doesn't guarantee that all camera control settings are applied within the same frame at runtime. The with construct provides a structured approach to managing camera control updates in real-time. Here's how to use it:

# import required libraries\nfrom vidgear.gears import PiGear\n\n# formulate initial configurational parameters\noptions = \"controls\": {\"ExposureTime\": 5000, \"AnalogueGain\": 0.5}\n\n# open pi video stream with these parameters\nstream = PiGear(logging=True, **options).start() \n\n# Enter context manager and set runtime controls\n# Within this block, the controls are guaranteed to be applied atomically\nwith stream.stream.controls as controls:  \n    controls.ExposureTime = 10000  # Set new exposure time\n    controls.AnalogueGain = 1.0     # Set new analogue gain\n\n# ...rest of code goes here...\n\n# safely close video stream\nstream.stop()\n
# import required libraries\nfrom vidgear.gears import PiGear\nimport cv2\n\n# formulate initial configurational parameters\n# set brightness to -0.5 (dark)\noptions = {\"controls\": {\"Brightness\": -0.5}}\n\n# open pi video stream with these parameters\nstream = PiGear(logging=True, **options).start() \n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n    # check for 'z' key if pressed\n    if key == ord(\"z\"):\n        # change brightness to 0.5 (bright)\n        stream.stream.set_controls({\"Brightness\": 0.5})\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

You can also use the stream global parameter in PiGear with thepicamera backend to feed any picamera parameters at runtime after the camera has started.

PiGear API switches to the legacy picamerabackend if the picamera2 library is unavailable.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera optional parameter boolean attribute.

In this example we will set initial Camera Module's brightness value 80 (brighter), and will change it 30 (darker) when Z key is pressed at runtime:

# import required libraries\nfrom vidgear.gears import PiGear\nimport cv2\n\n# formulate initial configurational parameters \n# set brightness to `80` (bright)\noptions = {\"brightness\": 80} \n\n# open pi video stream with these parameters\nstream = PiGear(logging=True, **options).start() \n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n    # check for 'z' key if pressed\n    if key == ord(\"z\"):\n        # change brightness to `30` (darker)\n        stream.stream.brightness = 30\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n
"},{"location":"help/pigear_ex/#accessing-multiple-camera-through-its-index-in-pigear-api","title":"Accessing Multiple Camera through its Index in PiGear API","text":"

With the camera_num parameter in the PiGear API, you can easily select the camera index to be used as the source, allowing you to drive these multiple cameras simultaneously from within a single Python session.

The camera_num value can only be zero or greater, otherwise, PiGear API will throw ValueError for any negative value.

New Picamera2 backendLegacy Picamera backend

With the picamera2 backend, you can use the camera_num parameter in PiGear to select the camera index to be used as the source if you have multiple Raspberry Pi camera modules (such as CM4) and/or USB cameras connected simultaneously to your Raspberry Pi.

Accessing metadata about connected cameras.

You can call the global_camera_info() method of the Picamera2 object available with stream global parameter in PiGear API to find out what cameras are attached. This returns a list containing one dictionary for each camera, ordered according the camera number you would pass to the camera_num parameter in PiGear API to open that device. The dictionary contains:

You should always check this list to discover which camera is which as the order can change when the system boots or USB cameras are re-connected as follows:

# import required libraries\nfrom vidgear.gears import PiGear\n\n# open any pi video stream\nstream = PiGear()\n\n#display all available cameras metadata\nprint(stream.stream.global_camera_info())\n\n# safely close video stream\nstream.stop()\n

The PiGear API can accurately differentiate between USB and Raspberry Pi camera modules by utilizing the camera's metadata.

In this example, we will select the USB Camera connected at index 1 on the Raspberry Pi as the primary source for extracting frames in PiGear API:

Limited support for USB Cameras

This example also works with USB Cameras, However:

This example assumes a USB Camera is connected at index 1, and some other camera connected at index 0 on your Raspberry Pi.

# import required libraries\nfrom vidgear.gears import PiGear\nfrom libcamera import Transform\nimport cv2\n\n# formulate various Picamera2 API \n# configurational parameters for USB camera\noptions = {\n    \"sensor\": {\"output_size\": (480, 320)},  # will override `resolution`\n    \"format\": \"RGB888\" # BGR format for this example\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream at index `1` with defined parameters\nstream = PiGear(camera_num=1, resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

With the Picamera backend, you should not change the camera_num parameter unless you are using the Raspberry Pi 3/3+/4 Compute Module IO Boards or third party Arducam Camarray Multiple Camera Solutions, which supports attaching multiple camera modules to the same Raspberry Pi board using appropriate I/O connections.

You can use the camera_num parameter in PiGear with the picamera backend to select the camera index to be used as the source if you have multiple Raspberry Pi camera modules connected.

PiGear API switches to the legacy picamerabackend if the picamera2 library is unavailable.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera optional parameter boolean attribute.

In this example, we will select the Camera Module connected at index 1 on the Raspberry Pi as the primary source for extracting frames in PiGear API:

This example assumes a Camera Module is connected at index 1 on your Raspberry Pi.

# import required libraries\nfrom vidgear.gears import PiGear\nimport cv2\n\n# formulate various Picamera API \n# configurational parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream at index `1` with defined parameters\nstream = PiGear(camera_num=1, resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"help/pigear_faqs/","title":"FAQs","text":""},{"location":"help/pigear_faqs/#pigear-faqs","title":"PiGear FAQs","text":""},{"location":"help/pigear_faqs/#what-is-pigear-api-and-what-does-it-do","title":"What is PiGear API and what does it do?","text":"

Answer: PiGear is a specialized API similar to the CamGear API but optimized for Raspberry Pi Boards, offering comprehensive support for camera modules (e.g., OmniVision OV5647, Sony IMX219), along with limited compatibility for USB cameras. For more info. see PiGear doc \u27b6

"},{"location":"help/pigear_faqs/#im-only-familiar-with-opencv-how-to-get-started-with-pigear-api","title":"I'm only familiar with OpenCV, how to get started with PiGear API?","text":"

Answer: First, refer to the Switching from OpenCV guide, then go through PiGear documentation. If you still have doubts, ask us on Gitter \u27b6 Community channel.

"},{"location":"help/pigear_faqs/#why-my-camera-module-is-not-detected-by-pigear","title":"Why my camera module is not detected by PiGear?","text":"

Answer: Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using PiGear API. Also, recheck/change your Camera Module's ribbon-cable and Camera Module itself, if it damaged or got broken somehow.

"},{"location":"help/pigear_faqs/#how-to-select-camera-index-on-pi-compute-io-board-with-two-cameras-attached","title":"How to select camera index on Pi Compute IO board with two Cameras attached?","text":"

Answer: Refer this bonus example \u27b6

"},{"location":"help/pigear_faqs/#why-pigear-is-throwing-systemerror","title":"Why PiGear is throwing SystemError?","text":"

Answer: This means your Raspberry Pi CSI ribbon-cable is not connected properly to your Camera Module, or damaged, or even both.

"},{"location":"help/pigear_faqs/#how-to-assign-various-configurational-settings-for-camera-module-with-pigear","title":"How to assign various configurational settings for Camera Module with PiGear?","text":"

Answer: See this usage example \u27b6

"},{"location":"help/pigear_faqs/#video-output-is-too-dark-with-pigear-why","title":"\"Video output is too dark with PiGear\", Why?","text":"

Answer: The camera configuration settings might be incorrect. Check this usage example \u27b6 and try tinkering parameters like sensor_mode, shutter_speed, and exposure_mode. Additionally, if your framerate parameter value is too high, try lowering it.

"},{"location":"help/pigear_faqs/#how-to-dynamically-adjust-raspberry-pi-camera-parameters-at-runtime-with-pigear","title":"How to dynamically adjust Raspberry Pi Camera Parameters at runtime with PiGear?","text":"

Answer: See this bonus example \u27b6

"},{"location":"help/pigear_faqs/#is-it-possible-to-change-output-frames-pixel-format-in-pigear-api","title":"Is it possible to change output frames Pixel Format in PiGear API?","text":"

Answer: Yes it is possible with Picamera2 Backend. See this bonus example \u27b6

"},{"location":"help/screengear_ex/","title":"Bonus Examples","text":""},{"location":"help/screengear_ex/#screengear-examples","title":"ScreenGear Examples","text":""},{"location":"help/screengear_ex/#using-screengear-with-netgear-and-writegear","title":"Using ScreenGear with NetGear and WriteGear","text":"

The complete usage example is as follows:

New in v0.2.2

This example was added in v0.2.2.

"},{"location":"help/screengear_ex/#client-writegear","title":"Client + WriteGear","text":"

Open a terminal on Client System (where you want to save the input frames received from the Server) and execute the following python code:

Note down the IP-address of this system(required at Server's end) by executing the command: hostname -I and also replace it in the following code.

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# define various tweak flags\noptions = {\"flag\": 0, \"copy\": True, \"track\": False}\n\n# Define Netgear Client at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# Define writer with default parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\")\n\n# loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n\n# safely close writer\nwriter.close()\n
"},{"location":"help/screengear_ex/#server-screengear","title":"Server + ScreenGear","text":"

Now, Open the terminal on another Server System (with a montior/display attached to it), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import ScreenGear\nfrom vidgear.gears import NetGear\n\n# define dimensions of screen w.r.t to given monitor to be captured\noptions = {\"top\": 40, \"left\": 0, \"width\": 100, \"height\": 100}\n\n# open stream with defined parameters\nstream = ScreenGear(logging=True, **options).start()\n\n# define various netgear tweak flags\noptions = {\"flag\": 0, \"copy\": True, \"track\": False}\n\n# Define Netgear server at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"help/screengear_ex/#using-screengear-with-webgear_rtc","title":"Using ScreenGear with WebGear_RTC","text":"

The complete usage example is as follows:

New in v0.2.4

This example was added in v0.2.4.

Bare-MinimumAdvanced
# import necessary libs\nimport uvicorn, cv2\nfrom vidgear.gears import ScreenGear\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# assign your ScreenGear class with adequate parameters \n# to `custom_stream` attribute in options parameter\noptions = {\"custom_stream\": ScreenGear(logging=True)}\n\n# initialize WebGear_RTC app without any source\nweb = WebGear_RTC(logging=True, **options)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

For VideoCapture APIs you also need to implement start() in addition to read() and stop() methods in your Custom Streaming Class as shown in following example, otherwise WebGear_RTC will fail to work!

# import necessary libs\nimport uvicorn, cv2\nfrom vidgear.gears import ScreenGear\nfrom vidgear.gears.helper import reducer\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# create your own custom streaming class\nclass Custom_Stream_Class:\n    \"\"\"\n    Custom Streaming using ScreenGear\n    \"\"\"\n\n    def __init__(self, backend=\"mss\", logging=False):\n\n        # !!! define your own video source here !!!\n        self.source = ScreenGear(backend=backend, logging=logging)\n\n        # define running flag\n        self.running = True\n\n    def start(self):\n\n        # don't forget this function!!!\n        # This function is specific to VideoCapture APIs only\n\n        if not self.source is None:\n            self.source.start()\n\n    def read(self):\n\n        # don't forget this function!!!\n\n        # check if source was initialized or not\n        if self.source is None:\n            return None\n        # check if we're still running\n        if self.running:\n            # read frame from provided source\n            frame = self.source.read()\n            # check if frame is available\n            if not(frame is None):\n\n                # do something with your OpenCV frame here\n\n                # reducer frames size if you want more performance otherwise comment this line\n                frame = reducer(frame, percentage=20)  # reduce frame by 20%\n\n                # return our gray frame\n                return frame\n            else:\n                # signal we're not running now\n                self.running = False\n        # return None-type\n        return None\n\n    def stop(self):\n\n        # don't forget this function!!!\n\n        # flag that we're not running\n        self.running = False\n        # close stream\n        if not self.source is None:\n            self.source.stop()\n\n\n# assign your Custom Streaming Class with adequate ScreenGear parameters\n# to `custom_stream` attribute in options parameter\noptions = {\"custom_stream\": Custom_Stream_Class(backend=\"pil\", logging=True)}\n\n# initialize WebGear_RTC app without any source\nweb = WebGear_RTC(logging=True, **options)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

"},{"location":"help/screengear_faqs/","title":"FAQs","text":""},{"location":"help/screengear_faqs/#screengear-faqs","title":"ScreenGear FAQs","text":""},{"location":"help/screengear_faqs/#what-is-screengear-api-and-what-does-it-do","title":"What is ScreenGear API and what does it do?","text":"

Answer: ScreenGear is designed exclusively for targeting rapid Screencasting Capabilities, which means it can grab frames from your monitor in real-time, either by defining an area on the computer screen or full-screen, at the expense of inconsiderable latency. ScreenGear also seamlessly support frame capturing from multiple monitors as well as supports multiple backends. For more info. see ScreenGear doc \u27b6

"},{"location":"help/screengear_faqs/#im-only-familiar-with-opencv-how-to-get-started-with-screengear-api","title":"I'm only familiar with OpenCV, how to get started with ScreenGear API?","text":"

Answer: First, refer to the Switching from OpenCV guide, then go through ScreenGear documentation. If you still have doubts, ask us on Gitter \u27b6 Community channel.

"},{"location":"help/screengear_faqs/#screengear-is-slow","title":"ScreenGear is Slow?","text":"

Answer: This maybe due to selected backend for ScreenGear API is not compatible with your machine. See this usage example to change backend \u27b6. Try different backends, and select which works the best for your machine.

"},{"location":"help/screengear_faqs/#how-to-define-area-on-screen-to-record-with-screengear","title":"How to define area on screen to record with ScreenGear?","text":"

Answer: See this usage example \u27b6

"},{"location":"help/screengear_faqs/#how-to-record-video-from-all-connected-screens","title":"How to record video from all connected screens?","text":"

Answer: With mss backend, see ScreenGear's monitor parameter that sets the index of the monitor to grab a frame from. If its value is -1, it will record from all monitors. More information can be found here \u27b6

"},{"location":"help/screengear_faqs/#im-getting-attributeerror-dxcamera-object-has-no-attribute-is_capturing-error","title":"I'm getting \"AttributeError: 'DXCamera' object has no attribute 'is_capturing'\" Error?","text":"

Answer: This is a well-known error in backend dxcam library which occurs when you've multiple GPUs on your Windows machine. To workaround this, you need select Internal GPU in settings as follows:

On Windows 11On Windows 10

In Settings, go to System > Display > Graphics and add your Python.exe as \"Desktop App\", then select \"Power saving\" as follows:

And finally press Save button.

In Settings, go to Graphics Settings and add your Python.exe as \"Desktop App\", then select \"Power saving\" as follows:

And finally press Save button.

"},{"location":"help/stabilizer_ex/","title":"Bonus Examples","text":""},{"location":"help/stabilizer_ex/#stabilizer-class-examples","title":"Stabilizer Class Examples","text":""},{"location":"help/stabilizer_ex/#saving-stabilizer-class-output-with-live-audio-input","title":"Saving Stabilizer Class output with Live Audio Input","text":"

In this example code, we will merging the audio from a Audio Device (for e.g. Webcam inbuilt mic input) with Stabilized frames incoming from the Stabilizer Class (which is also using same Webcam video input through OpenCV), and save the final output as a compressed video file, all in real time:

New in v0.2.2

This example was added in v0.2.2.

Example Assumptions

Identifying and Specifying sound card on different OS platforms Windows Linux MacOS

Windows OS users can use the dshow (DirectShow) to list audio input device which is the preferred option for Windows users. You can refer following steps to identify and specify your sound card:

If audio still doesn't work then checkout this troubleshooting guide \u27b6 or reach us out on Gitter \u27b6 Community channel

Linux OS users can use the alsa to list input device to capture live audio input such as from a webcam. You can refer following steps to identify and specify your sound card:

If audio still doesn't work then reach us out on Gitter \u27b6 Community channel

MAC OS users can use the avfoundation to list input devices for grabbing audio from integrated iSight cameras as well as cameras connected via USB or FireWire. You can refer following steps to identify and specify your sound card on MacOS/OSX machines:

If audio still doesn't work then reach us out on Gitter \u27b6 Community channel

Make sure this -i audio-source it compatible with provided video-source, otherwise you could encounter multiple errors or no output at all.

You MUST use -input_framerate attribute to set exact value of input framerate when using external audio in Real-time Frames mode, otherwise audio delay will occur in output streams.

# import required libraries\nfrom vidgear.gears import WriteGear\nfrom vidgear.gears.stabilizer import Stabilizer\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = cv2.VideoCapture(0)\n\n# initiate stabilizer object with defined parameters\nstab = Stabilizer(smoothing_radius=30, crop_n_zoom=True, border_size=5, logging=True)\n\n# change with your webcam soundcard, plus add additional required FFmpeg parameters for your writer\noutput_params = {\n    \"-input_framerate\": stream.get(cv2.CAP_PROP_FPS),\n    \"-thread_queue_size\": \"512\",\n    \"-ac\": \"2\",\n    \"-ar\": \"48000\",\n    \"-f\": \"alsa\", # (1)\n    \"-i\": \"hw:1\",\n}\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4\nwriter = WriteGear(output=\"Output.mp4\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n        break\n\n    # send current frame to stabilizer for processing\n    stabilized_frame = stab.stabilize(frame)\n\n    # wait for stabilizer which still be initializing\n    if stabilized_frame is None:\n        continue\n\n    # {do something with the stabilized frame here}\n\n    # write stabilized frame to writer\n    writer.write(stabilized_frame)\n\n\n# clear stabilizer resources\nstab.clean()\n\n# safely close video stream\nstream.release()\n\n# safely close writer\nwriter.close()\n
  1. Always keep this line above -i parameter!

"},{"location":"help/stabilizer_ex/#saving-stabilizer-class-output-with-file-audio-input","title":"Saving Stabilizer Class output with File Audio Input","text":"

In this example code, we will be directly merging the audio from a Video-File (to be stabilized) with its processed stabilized frames into a compressed video output in real time:

New in v0.2.4

This example was added in v0.2.4.

Make sure this input video-file (to be stabilized) contains valid audio source, otherwise you could encounter multiple errors or no output at all.

You MUST use -input_framerate attribute to set exact value of input framerate when using external audio in Real-time Frames mode, otherwise audio delay will occur in output streams.

Use -disable_force_termination flag when video duration is too short(<60sec), otherwise WriteGear will not produce any valid output.

# import required libraries\nfrom vidgear.gears import WriteGear\nfrom vidgear.gears.stabilizer import Stabilizer\nimport cv2\n\n# Give suitable video file path to be stabilized\nunstabilized_videofile = \"test.mp4\"\n\n# open stream on given path\nstream = cv2.VideoCapture(unstabilized_videofile)\n\n# initiate stabilizer object with defined parameters\nstab = Stabilizer(smoothing_radius=30, crop_n_zoom=True, border_size=5, logging=True)\n\n# define required FFmpeg optimizing parameters for your writer\noutput_params = {\n    \"-i\": unstabilized_videofile,\n    \"-c:a\": \"aac\",\n    \"-input_framerate\": stream.get(cv2.CAP_PROP_FPS),\n    \"-clones\": [\"-shortest\"],\n    # !!! Uncomment following line if video duration is too short(<60sec). !!!\n    #\"-disable_force_termination\": True,\n}\n\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4\nwriter = WriteGear(output=\"Output.mp4\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n        break\n\n    # send current frame to stabilizer for processing\n    stabilized_frame = stab.stabilize(frame)\n\n    # wait for stabilizer which still be initializing\n    if stabilized_frame is None:\n        continue\n\n    # {do something with the stabilized frame here}\n\n    # write stabilized frame to writer\n    writer.write(stabilized_frame)\n\n\n# clear stabilizer resources\nstab.clean()\n\n# safely close video stream\nstream.release()\n\n# safely close writer\nwriter.close()\n

"},{"location":"help/stabilizer_faqs/","title":"FAQs","text":""},{"location":"help/stabilizer_faqs/#stabilizer-class-faqs","title":"Stabilizer Class FAQs","text":""},{"location":"help/stabilizer_faqs/#what-is-stabilizer-class-and-what-does-it-do","title":"What is Stabilizer Class and what does it do?","text":"

Answer: Stabilizer Class is an auxiliary class that enables Video Stabilization for vidgear with minimalistic latency, and at the expense of little to no additional computational requirements. For more info. see Stabilizer Class doc \u27b6

"},{"location":"help/stabilizer_faqs/#how-much-latency-you-would-typically-expect-with-stabilizer-class","title":"How much latency you would typically expect with Stabilizer Class?","text":"

Answer: The stabilizer will be Slower for High-Quality videos-frames. Try reducing frames size (Use reducer() method) before feeding them for reducing latency. Also, see smoothing_radius parameter of Stabilizer class that handles the quality of stabilization at the expense of latency and sudden panning. The larger its value, the less will be panning, more will be latency, and vice-versa.

"},{"location":"help/stabilizer_faqs/#how-to-remove-black-borders-in-output-video-after-stabilizing-it","title":"How to remove black borders in output video after stabilizing it?","text":"

Answer: See crop_n_zoom parameter of Stabilizer class, that enables the feature, where it crops and zooms frames(to original size) to reduce the black borders from stabilization being too noticeable (similar to the feature available in Adobe AfterEffects). It works in conjunction with the border_size parameter, i.e. when this parameter is enabled border_size will be used for cropping border instead of making them. Its default value is False.

"},{"location":"help/stabilizer_faqs/#can-i-use-stabilizer-directly-with-opencv","title":"Can I use Stabilizer directly with OpenCV?","text":"

Answer: Yes, see this usage example \u27b6.

"},{"location":"help/stabilizer_faqs/#why-stabilization-is-not-working-properly-for-my-video","title":"Why stabilization is not working properly for my video?","text":"

Answer: The Stabilizer may not perform well against High-frequency jitter in video. But,you can check if increasing smoothing_radius parameter value helps but it will add latency too.

"},{"location":"help/streamgear_ex/","title":"Bonus Examples","text":""},{"location":"help/streamgear_ex/#streamgear-examples","title":"StreamGear Examples","text":""},{"location":"help/streamgear_ex/#streamgear-live-streaming-usage-with-pigear","title":"StreamGear Live-Streaming Usage with PiGear","text":"

In this example, we will be Live-Streaming video-frames from Raspberry Pi (with Camera Module connected) using PiGear API and StreamGear API's Real-time Frames Mode:

Use -window_size & -extra_window_size FFmpeg parameters for controlling number of frames to be kept in Chunks. Less these value, less will be latency.

After every few chunks (equal to the sum of -window_size & -extra_window_size values), all chunks will be overwritten in Live-Streaming. Thereby, since newer chunks in manifest/playlist will contain NO information of any older ones, and therefore resultant DASH/HLS stream will play only the most recent frames.

In this mode, StreamGear DOES NOT automatically maps video-source audio to generated streams. You need to manually assign separate audio-source through -audio attribute of stream_params dictionary parameter.

PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using the PiGear API, otherwise nothing will work.

DASHHLS New Picamera2 backendLegacy Picamera backend
# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import StreamGear\nfrom libcamera import Transform\nimport cv2\n\n# formulate various Picamera2 API \n# configurational parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# enable livestreaming and retrieve framerate from CamGear Stream and\n# pass it as `-input_framerate` parameter for controlled framerate\nstream_params = {\"-input_framerate\": stream.framerate, \"-livestream\": True}\n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n
Under the hood, PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, PiGear API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# formulate various Picamera API \n# configurational parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# enable livestreaming and retrieve framerate from CamGear Stream and\n# pass it as `-input_framerate` parameter for controlled framerate\nstream_params = {\"-input_framerate\": stream.framerate, \"-livestream\": True}\n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n
New Picamera2 backendLegacy Picamera backend
# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import StreamGear\nfrom libcamera import Transform\nimport cv2\n\n# formulate various Picamera2 API \n# configurational parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# enable livestreaming and retrieve framerate from CamGear Stream and\n# pass it as `-input_framerate` parameter for controlled framerate\nstream_params = {\"-input_framerate\": stream.framerate, \"-livestream\": True}\n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n
Under the hood, PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, PiGear API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# formulate various Picamera API \n# configurational parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# enable livestreaming and retrieve framerate from CamGear Stream and\n# pass it as `-input_framerate` parameter for controlled framerate\nstream_params = {\"-input_framerate\": stream.framerate, \"-livestream\": True}\n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n

"},{"location":"help/streamgear_faqs/","title":"FAQs","text":""},{"location":"help/streamgear_faqs/#streamgear-faqs","title":"StreamGear FAQs","text":""},{"location":"help/streamgear_faqs/#what-is-streamgear-api-and-what-does-it-do","title":"What is StreamGear API and what does it do?","text":"

Answer: StreamGear automates transcoding workflow for generating Ultra-Low Latency, High-Quality, Dynamic & Adaptive Streaming Formats (such as MPEG-DASH) in just few lines of python code. For more info. see StreamGear doc \u27b6

"},{"location":"help/streamgear_faqs/#how-to-get-started-with-streamgear-api","title":"How to get started with StreamGear API?","text":"

Answer: First, refer to the Switching from OpenCV guide, then go through StreamGear documentation. If you still have doubts, ask us on Gitter \u27b6 Community channel.

"},{"location":"help/streamgear_faqs/#what-is-mpd-file-created-with-streamgear","title":"What is .mpd file created with StreamGear?","text":"

Answer: SteamGear also creates a Manifest file (such as MPD in-case of DASH) besides segments that describe these segment information (timing, URL, media characteristics like video resolution and bit rates) and is provided to the client before the streaming session.

"},{"location":"help/streamgear_faqs/#how-to-play-streaming-assets-created-with-streamgear-api","title":"How to play Streaming Assets created with StreamGear API?","text":"

Answer: You can easily feed Manifest file(.mpd) to DASH Supported Players Input but sure encoded chunks are present along with it. See this list of recommended players \u27b6

"},{"location":"help/streamgear_faqs/#what-adaptive-streaming-formats-are-supported-yet","title":"What Adaptive Streaming Formats are supported yet?","text":"

Answer: SteamGear currently only supports MPEG-DASH (Dynamic Adaptive Streaming over HTTP, ISO/IEC 23009-1) , but other adaptive streaming technologies such as Apple HLS, Microsoft Smooth Streaming, will be added soon.

"},{"location":"help/streamgear_faqs/#is-drm-encryption-supported-in-streamgear-api","title":"Is DRM Encryption supported in StreamGear API?","text":"

Answer: No, DRM Encryption is NOT supported yet.

"},{"location":"help/streamgear_faqs/#how-to-create-additional-streams-in-streamgear-api","title":"How to create additional streams in StreamGear API?","text":"

Answer: See this example \u27b6

"},{"location":"help/streamgear_faqs/#how-to-use-streamgear-api-with-opencv","title":"How to use StreamGear API with OpenCV?","text":"

Answer: See this example \u27b6

"},{"location":"help/streamgear_faqs/#how-to-use-streamgear-api-with-real-time-frames","title":"How to use StreamGear API with real-time frames?","text":"

Answer: See Real-time Frames Mode \u27b6

"},{"location":"help/streamgear_faqs/#how-to-use-hardwaregpu-encoder-for-transcoding-in-streamgear-api","title":"How to use Hardware/GPU encoder for transcoding in StreamGear API?","text":"

Answer: See this example \u27b6

"},{"location":"help/videogear_ex/","title":"Bonus Examples","text":""},{"location":"help/videogear_ex/#videogear-examples","title":"VideoGear Examples","text":""},{"location":"help/videogear_ex/#using-videogear-with-rosrobot-operating-system","title":"Using VideoGear with ROS(Robot Operating System)","text":"

We will be using cv_bridge to convert OpenCV frames to ROS image messages and vice-versa.

In this example, we'll create a node that convert OpenCV frames into ROS image messages, and then publishes them over ROS.

New in v0.2.2

This example was added in v0.2.2.

This example is vidgear implementation of this wiki example.

# import roslib\nimport roslib\n\nroslib.load_manifest(\"my_package\")\n\n# import other required libraries\nimport sys\nimport rospy\nimport cv2\nfrom std_msgs.msg import String\nfrom sensor_msgs.msg import Image\nfrom cv_bridge import CvBridge, CvBridgeError\nfrom vidgear.gears import VideoGear\n\n# custom publisher class\nclass image_publisher:\n    def __init__(self, source=0, logging=False):\n        # create CV bridge\n        self.bridge = CvBridge()\n        # define publisher topic\n        self.image_pub = rospy.Publisher(\"image_topic_pub\", Image)\n        # open stream with given parameters\n        self.stream = VideoGear(source=source, logging=logging).start()\n        # define publisher topic\n        rospy.Subscriber(\"image_topic_sub\", Image, self.callback)\n\n    def callback(self, data):\n\n        # {do something with received ROS node data here}\n\n        # read frames\n        frame = self.stream.read()\n        # check for frame if None-type\n        if not (frame is None):\n\n            # {do something with the frame here}\n\n            # publish our frame\n            try:\n                self.image_pub.publish(self.bridge.cv2_to_imgmsg(frame, \"bgr8\"))\n            except CvBridgeError as e:\n                # catch any errors\n                print(e)\n\n    def close(self):\n        # stop stream\n        self.stream.stop()\n\n\ndef main(args):\n    # !!! define your own video source here !!!\n    # Open any video stream such as live webcam\n    # video stream on first index(i.e. 0) device\n\n    # define publisher\n    ic = image_publisher(source=0, logging=True)\n    # initiate ROS node on publisher\n    rospy.init_node(\"image_publisher\", anonymous=True)\n    try:\n        # run node\n        rospy.spin()\n    except KeyboardInterrupt:\n        print(\"Shutting down\")\n    finally:\n        # close publisher\n        ic.close()\n\n\nif __name__ == \"__main__\":\n    main(sys.argv)\n

"},{"location":"help/videogear_ex/#using-videogear-for-capturing-rtsprtmp-urls","title":"Using VideoGear for capturing RTSP/RTMP URLs","text":"

Here's a high-level wrapper code around VideoGear API to enable auto-reconnection during capturing, plus stabilization is enabled (stabilize=True) in order to stabilize captured frames on-the-go:

New in v0.2.2

This example was added in v0.2.2.

Enforcing UDP stream

You can easily enforce UDP for RTSP streams inplace of default TCP, by putting following lines of code on the top of your existing code:

# import required libraries\nimport os\n\n# enforce UDP\nos.environ[\"OPENCV_FFMPEG_CAPTURE_OPTIONS\"] = \"rtsp_transport;udp\"\n

Finally, use backend parameter value as backend=cv2.CAP_FFMPEG in VideoGear.

from vidgear.gears import VideoGear\nimport cv2\nimport datetime\nimport time\n\n\nclass Reconnecting_VideoGear:\n    def __init__(self, cam_address, stabilize=False, reset_attempts=50, reset_delay=5):\n        self.cam_address = cam_address\n        self.stabilize = stabilize\n        self.reset_attempts = reset_attempts\n        self.reset_delay = reset_delay\n        self.source = VideoGear(\n            source=self.cam_address, stabilize=self.stabilize\n        ).start()\n        self.running = True\n\n    def read(self):\n        if self.source is None:\n            return None\n        if self.running and self.reset_attempts > 0:\n            frame = self.source.read()\n            if frame is None:\n                self.source.stop()\n                self.reset_attempts -= 1\n                print(\n                    \"Re-connection Attempt-{} occured at time:{}\".format(\n                        str(self.reset_attempts),\n                        datetime.datetime.now().strftime(\"%m-%d-%Y %I:%M:%S%p\"),\n                    )\n                )\n                time.sleep(self.reset_delay)\n                self.source = VideoGear(\n                    source=self.cam_address, stabilize=self.stabilize\n                ).start()\n                # return previous frame\n                return self.frame\n            else:\n                self.frame = frame\n                return frame\n        else:\n            return None\n\n    def stop(self):\n        self.running = False\n        self.reset_attempts = 0\n        self.frame = None\n        if not self.source is None:\n            self.source.stop()\n\n\nif __name__ == \"__main__\":\n    # open any valid video stream\n    stream = Reconnecting_VideoGear(\n        cam_address=\"rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov\",\n        reset_attempts=20,\n        reset_delay=5,\n    )\n\n    # loop over\n    while True:\n\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if None-type\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # Show output window\n        cv2.imshow(\"Output\", frame)\n\n        # check for 'q' key if pressed\n        key = cv2.waitKey(1) & 0xFF\n        if key == ord(\"q\"):\n            break\n\n    # close output window\n    cv2.destroyAllWindows()\n\n    # safely close video stream\n    stream.stop()\n

"},{"location":"help/videogear_ex/#using-videogear-for-real-time-stabilization-with-audio-encoding","title":"Using VideoGear for Real-time Stabilization with Audio Encoding","text":"

In this example code, we will be directly merging the audio from a Video-File (to be stabilized) with its processed stabilized frames into a compressed video output in real time:

New in v0.2.4

This example was added in v0.2.4.

Make sure this input video-file (to be stabilized) contains valid audio source, otherwise you could encounter multiple errors or no output at all.

You MUST use -input_framerate attribute to set exact value of input framerate when using external audio in Real-time Frames mode, otherwise audio delay will occur in output streams.

Use -disable_force_termination flag when video duration is too short(<60sec), otherwise WriteGear will not produce any valid output.

# import required libraries\nfrom vidgear.gears import WriteGear\nfrom vidgear.gears import VideoGear\nimport cv2\n\n# Give suitable video file path to be stabilized\nunstabilized_videofile = \"test.mp4\"\n\n# open any valid video path with stabilization enabled(`stabilize = True`)\nstream_stab = VideoGear(source=unstabilized_videofile, stabilize=True, logging=True).start()\n\n# define required FFmpeg optimizing parameters for your writer\noutput_params = {\n    \"-i\": unstabilized_videofile,\n    \"-c:a\": \"aac\",\n    \"-input_framerate\": stream_stab.framerate,\n    \"-clones\": [\"-shortest\"],\n    # !!! Uncomment following line if video duration is too short(<60sec). !!!\n    #\"-disable_force_termination\": True,\n}\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4\nwriter = WriteGear(output=\"Output.mp4\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame_stab = stream_stab.read()\n\n    # check for frame if not grabbed\n    if frame_stab is None:\n        break\n\n    # {do something with the stabilized frame here}\n\n    # write stabilized frame to writer\n    writer.write(frame_stab)\n\n# safely close streams\nstream_stab.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"help/videogear_faqs/","title":"FAQs","text":""},{"location":"help/videogear_faqs/#videogear-faqs","title":"VideoGear FAQs","text":""},{"location":"help/videogear_faqs/#what-is-videogear-api-and-what-does-it-do","title":"What is VideoGear API and what does it do?","text":"

Answer: VideoGear provides a special internal wrapper around VidGear's exclusive Video Stabilizer class. It also act as a Common API, that provided an internal access to both CamGear and PiGear APIs and their parameters, with a special enablePiCamera boolean flag. For more info. see VideoGear doc \u27b6

"},{"location":"help/videogear_faqs/#whats-the-need-of-videogear-api","title":"What's the need of VideoGear API?","text":"

Answer: VideoGear is basically ideal when you need to switch to different video sources without changing your code much. Also, it enables easy stabilization for various video-streams (real-time or not) with minimum efforts and using way fewer lines of code. It also serve as backend for other powerful APIs, such WebGear and NetGear_Async.

"},{"location":"help/videogear_faqs/#which-apis-are-accessible-with-videogear-api","title":"Which APIs are accessible with VideoGear API?","text":"

Answer: VideoGear provided an internal access to both CamGear and PiGear APIs and their parameters, also it contains wrapper around Video Stabilizer class.

"},{"location":"help/videogear_faqs/#can-we-access-writegear-api-or-netgear-api-too-with-videogear","title":"Can we access WriteGear API or NetGear API too with VideoGear?","text":"

Answer: No, only selected VideoCapture APIs (anwsered above) are accessible.

"},{"location":"help/videogear_faqs/#does-using-videogear-instead-of-camgear-api-directly-affects-performance","title":"Does using VideoGear instead of CamGear API directly, affects performance?","text":"

Answer: No, there's no difference, as VideoGear just a high-level wrapper around CamGear API and without any modifications in-between.

"},{"location":"help/webgear_ex/","title":"Bonus Examples","text":""},{"location":"help/webgear_ex/#webgear-examples","title":"WebGear Examples","text":""},{"location":"help/webgear_ex/#using-webgear-with-raspberrypi-camera-module","title":"Using WebGear with RaspberryPi Camera Module","text":"

Because of WebGear API's flexible internal wapper around VideoGear, it can easily access any parameter of CamGear and PiGear videocapture APIs.

Following usage examples are just an idea of what can be done with WebGear API, you can try various VideoGear, CamGear and PiGear parameters directly in WebGear API in the similar manner.

Here's a bare-minimum example of using WebGear API with the Raspberry Pi camera module while tweaking its various properties in few lines of python code:

Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this backend, otherwise nothing will work.

New Picamera2 backendLegacy Picamera backend
# import libs\nimport uvicorn\nfrom libcamera import Transform\nfrom vidgear.gears.asyncio import WebGear\n\n# various WebGear_RTC performance \n# and Picamera2 API tweaks\noptions = {\n    \"frame_size_reduction\": 40,\n    \"jpeg_compression_quality\": 80,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": False,\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# initialize WebGear app\nweb = WebGear(\n    enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options\n)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import libs\nimport uvicorn\nfrom vidgear.gears.asyncio import WebGear\n\n# various webgear performance and Picamera API tweaks\noptions = {\n    \"frame_size_reduction\": 40,\n    \"jpeg_compression_quality\": 80,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": False,\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# initialize WebGear app\nweb = WebGear(\n    enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options\n)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

"},{"location":"help/webgear_ex/#using-webgear-with-real-time-video-stabilization-enabled","title":"Using WebGear with real-time Video Stabilization enabled","text":"

Here's an example of using WebGear API with real-time Video Stabilization enabled:

# import libs\nimport uvicorn\nfrom vidgear.gears.asyncio import WebGear\n\n# various webgear performance tweaks\noptions = {\n    \"frame_size_reduction\": 40,\n    \"jpeg_compression_quality\": 80,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": False,\n}\n\n# initialize WebGear app  with a raw source and enable video stabilization(`stabilize=True`)\nweb = WebGear(source=\"foo.mp4\", stabilize=True, logging=True, **options)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

"},{"location":"help/webgear_ex/#display-two-sources-simultaneously-in-webgear","title":"Display Two Sources Simultaneously in WebGear","text":"

In this example, we'll be displaying two video feeds side-by-side simultaneously on browser using WebGear API by defining two separate frame generators:

New in v0.2.2

This example was added in v0.2.2.

Step-1 (Trigger Auto-Generation Process): Firstly, run this bare-minimum code to trigger the Auto-generation process, this will create .vidgear directory at current location (directory where you'll run this code):

# import required libraries\nimport uvicorn\nfrom vidgear.gears.asyncio import WebGear\n\n# provide current directory to save data files\noptions = {\"custom_data_location\": \"./\"}\n\n# initialize WebGear app\nweb = WebGear(source=0, logging=True, **options)\n\n# close app safely\nweb.shutdown()\n

Step-2 (Replace HTML file): Now, go inside .vidgear webgear templates directory at current location of your machine, and there replace content of index.html file with following:

{% extends \"base.html\" %}\n{% block content %}\n  <h1 class=\"glow\">WebGear Video Feed</h1>\n   <div class=\"rows\">\n     <img src=\"/video\" alt=\"Feed\"/>\n     <img src=\"/video2\" alt=\"Feed\"/>\n   </div>\n{% endblock %}\n

Step-3 (Build your own Frame Producers): Now, create a python script code with OpenCV source, as follows:

# import necessary libs\nimport uvicorn, asyncio, cv2\nfrom vidgear.gears.asyncio import WebGear\nfrom vidgear.gears.asyncio.helper import reducer\nfrom starlette.responses import StreamingResponse\nfrom starlette.routing import Route\n\n# provide current directory to load data files\noptions = {\"custom_data_location\": \"./\"}\n\n# initialize WebGear app without any source\nweb = WebGear(logging=True, **options)\n\n# create your own custom frame producer\nasync def my_frame_producer1():\n\n   # !!! define your first video source here !!!\n   # Open any video stream such as \"foo1.mp4\"\n   stream = cv2.VideoCapture(\"foo1.mp4\")\n   # loop over frames\n   while True:\n       # read frame from provided source\n       (grabbed, frame) = stream.read()\n       # break if NoneType\n       if not grabbed:\n           break\n\n       # do something with your OpenCV frame here\n\n       # reducer frames size if you want more performance otherwise comment this line\n       frame = await reducer(frame, percentage=30)  # reduce frame by 30%\n       # handle JPEG encoding\n       encodedImage = cv2.imencode(\".jpg\", frame)[1].tobytes()\n       # yield frame in byte format\n       yield (b\"--frame\\r\\nContent-Type:video/jpeg2000\\r\\n\\r\\n\" + encodedImage + b\"\\r\\n\")\n       await asyncio.sleep(0.00001)\n   # close stream\n   stream.release()\n\n\n# create your own custom frame producer\nasync def my_frame_producer2():\n\n   # !!! define your second video source here !!!\n   # Open any video stream such as \"foo2.mp4\"\n   stream = cv2.VideoCapture(\"foo2.mp4\")\n   # loop over frames\n   while True:\n       # read frame from provided source\n       (grabbed, frame) = stream.read()\n       # break if NoneType\n       if not grabbed:\n           break\n\n       # do something with your OpenCV frame here\n\n       # reducer frames size if you want more performance otherwise comment this line\n       frame = await reducer(frame, percentage=30)  # reduce frame by 30%\n       # handle JPEG encoding\n       encodedImage = cv2.imencode(\".jpg\", frame)[1].tobytes()\n       # yield frame in byte format\n       yield (b\"--frame\\r\\nContent-Type:video/jpeg2000\\r\\n\\r\\n\" + encodedImage + b\"\\r\\n\")\n       await asyncio.sleep(0.00001)\n   # close stream\n   stream.release()\n\n\nasync def custom_video_response(scope):\n   \"\"\"\n   Return a async video streaming response for `my_frame_producer2` generator\n   \"\"\"\n   assert scope[\"type\"] in [\"http\", \"https\"]\n   await asyncio.sleep(0.00001)\n   return StreamingResponse(\n       my_frame_producer2(),\n       media_type=\"multipart/x-mixed-replace; boundary=frame\",\n   )\n\n\n# add your custom frame producer to config\nweb.config[\"generator\"] = my_frame_producer1\n\n# append new route i.e. new custom route with custom response\nweb.routes.append(\n    Route(\"/video2\", endpoint=custom_video_response)\n    )\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

On successfully running this code, the output stream will be displayed at address http://localhost:8000/ in Browser.

"},{"location":"help/webgear_faqs/","title":"FAQs","text":""},{"location":"help/webgear_faqs/#webgear-faqs","title":"WebGear FAQs","text":""},{"location":"help/webgear_faqs/#what-is-webgear-api-and-what-does-it-do","title":"What is WebGear API and what does it do?","text":"

Answer: WebGear is a powerful ASGI Video-Broadcaster API ideal for transmitting Motion-JPEG-frames from a single source to multiple recipients via the browser. For more info. see WebGear doc \u27b6

"},{"location":"help/webgear_faqs/#how-to-get-started-with-webgear-api","title":"How to get started with WebGear API?","text":"

Answer: First, refer to the Switching from OpenCV guide, then go through WebGear documentation. If you still have doubts, ask us on Gitter \u27b6 Community channel.

"},{"location":"help/webgear_faqs/#webgear-is-throwing-modulenotfounderror-on-importing-why","title":"\"WebGear is throwing ModuleNotFoundError on importing\", Why?","text":"

Answer: This error means, VidGear is installed WITHOUT asyncio package support on your machine. For this support, see Requirements \u27b6.

"},{"location":"help/webgear_faqs/#can-webgear-always-need-active-internet-connection","title":"Can WebGear always need Active Internet Connection?","text":"

Answer: No, it just need internet only once during its Auto-Generation Process \u27b6 to download default data-files and it takes few seconds. You can also download files manually from Github Server, otherwise you can also add your own custom files. For more information see Data-Files Auto-Generation WorkFlow \u27b6

"},{"location":"help/webgear_faqs/#is-it-possible-to-stream-on-a-different-device-on-the-network-with-webgear","title":"Is it possible to stream on a different device on the network with WebGear?","text":"

If you set \"0.0.0.0\" as host value instead of \"localhost\" on Host Machine, then you must still use http://localhost:8000/ to access stream on that same host machine browser.

For accessing WebGear on different Client Devices on the network, use \"0.0.0.0\" as host value instead of \"localhost\" on Host Machine. Then type the IP-address of source machine followed by the defined port value in your desired Client Device's browser (for e.g. http://192.27.0.101:8000) to access the stream.

"},{"location":"help/webgear_faqs/#can-i-manually-place-default-files-for-webgear","title":"Can I manually place default files for WebGear?","text":"

Answer: Yes, you can either download default files from Github Server, and manually place at default location, OR, you can yourself create the require three critical files (i.e index.html, 404.html & 500.html) inside templates folder at the default location, thereby you don't need any internet connection at all. For more information see Data-Files Auto-Generation WorkFlow \u27b6

"},{"location":"help/webgear_faqs/#how-to-send-opencv-frames-directly-to-webgear-server","title":"How to send OpenCV frames directly to Webgear Server?","text":"

Answer: See this usage example \u27b6.

"},{"location":"help/webgear_faqs/#how-can-i-add-my-custom-webpage-to-webgear","title":"How can I add my custom WebPage to WebGear?","text":"

Answer: See this usage example \u27b6.

"},{"location":"help/webgear_faqs/#how-can-to-add-cors-headers-to-webgear","title":"How can to add CORS headers to WebGear?","text":"

Answer: See this usage example \u27b6.

"},{"location":"help/webgear_faqs/#can-i-change-the-default-location","title":"Can I change the default location?","text":"

Answer: Yes, you can use WebGear's custom_data_location attribute of option parameter in WebGear API, to change default location to somewhere else.

"},{"location":"help/webgear_faqs/#can-i-deleterename-the-webgear-default-data","title":"Can I delete/rename the WebGear default data?","text":"

Answer: Yes, but you've to follow these rules \u27b6

"},{"location":"help/webgear_faqs/#what-web-browser-are-supported-by-webgear-api","title":"What Web browser are supported by WebGear API?","text":"

Answer: All modern browser with Javascript support are supported by WebGear. If not, then discuss with us on Gitter \u27b6 Community channel.

"},{"location":"help/webgear_rtc_ex/","title":"Bonus Examples","text":""},{"location":"help/webgear_rtc_ex/#webgear_rtc_rtc-examples","title":"WebGear_RTC_RTC Examples","text":""},{"location":"help/webgear_rtc_ex/#using-webgear_rtc-with-raspberrypi-camera-module","title":"Using WebGear_RTC with RaspberryPi Camera Module","text":"

Because of WebGear_RTC API's flexible internal wapper around VideoGear, it can easily access any parameter of CamGear and PiGear videocapture APIs.

Following usage examples are just an idea of what can be done with WebGear_RTC API, you can try various VideoGear, CamGear and PiGear parameters directly in WebGear_RTC API in the similar manner.

Here's a bare-minimum example of using WebGear_RTC API with the Raspberry Pi camera module while tweaking its various properties in just one-liner:

Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this backend, otherwise nothing will work.

New Picamera2 backendLegacy Picamera backend
# import libs\nimport uvicorn\nfrom libcamera import Transform\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# various WebGear_RTC performance \n# and Picamera2 API tweaks\noptions = {\n    \"frame_size_reduction\": 25,\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# initialize WebGear app\nweb = WebGear_RTC(\n    enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options\n)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import libs\nimport uvicorn\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# various WebGear_RTC performance and Picamera API tweaks\noptions = {\n    \"frame_size_reduction\": 25,\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# initialize WebGear app\nweb = WebGear_RTC(\n    enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options\n)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

"},{"location":"help/webgear_rtc_ex/#using-webgear_rtc-with-real-time-video-stabilization-enabled","title":"Using WebGear_RTC with real-time Video Stabilization enabled","text":"

Here's an example of using WebGear_RTC API with real-time Video Stabilization enabled:

# import libs\nimport uvicorn\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# various webgear_rtc performance tweaks\noptions = {\n    \"frame_size_reduction\": 25,\n}\n\n# initialize WebGear_RTC app  with a raw source and enable video stabilization(`stabilize=True`)\nweb = WebGear_RTC(source=\"foo.mp4\", stabilize=True, logging=True, **options)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

"},{"location":"help/webgear_rtc_ex/#display-two-sources-simultaneously-in-webgear_rtc","title":"Display Two Sources Simultaneously in WebGear_RTC","text":"

In this example, we'll be displaying two video feeds side-by-side simultaneously on browser using WebGear_RTC API by simply concatenating frames in real-time:

New in v0.2.4

This example was added in v0.2.4.

# import necessary libs\nimport uvicorn, cv2\nimport numpy as np\nfrom vidgear.gears.helper import reducer\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# initialize WebGear_RTC app without any source\nweb = WebGear_RTC(logging=True)\n\n# frame concatenator\ndef get_conc_frame(frame1, frame2):\n    h1, w1 = frame1.shape[:2]\n    h2, w2 = frame2.shape[:2]\n\n    # create empty matrix\n    vis = np.zeros((max(h1, h2), w1 + w2, 3), np.uint8)\n\n    # combine 2 frames\n    vis[:h1, :w1, :3] = frame1\n    vis[:h2, w1 : w1 + w2, :3] = frame2\n\n    return vis\n\n\n# create your own custom streaming class\nclass Custom_Stream_Class:\n    \"\"\"\n    Custom Streaming using two OpenCV sources\n    \"\"\"\n\n    def __init__(self, source1=None, source2=None):\n\n        # !!! define your own video source here !!!\n        # check is source are provided\n        if source1 is None or source2 is None:\n            raise ValueError(\"Provide both source\")\n\n        # initialize global params\n        # define both source here\n        self.stream1 = cv2.VideoCapture(source1)\n        self.stream2 = cv2.VideoCapture(source2)\n\n        # define running flag\n        self.running = True\n\n    def read(self):\n\n        # don't forget this function!!!\n\n        # check if sources were initialized or not\n        if self.stream1 is None or self.stream2 is None:\n            return None\n\n        # check if we're still running\n        if self.running:\n            # read video frame\n            (grabbed1, frame1) = self.stream1.read()\n            (grabbed2, frame2) = self.stream2.read()\n\n            # if NoneType\n            if not grabbed1 or not grabbed2:\n\n                # do something with your OpenCV frame here\n\n                # concatenate frame\n                frame = get_conc_frame(frame1, frame2)\n\n                # reducer frames size if you want more performance otherwise comment this line\n                # frame = await reducer(frame, percentage=30)  # reduce frame by 30%\n\n                # return our gray frame\n                return frame\n            else:\n                # signal we're not running now\n                self.running = False\n        # return None-type\n        return None\n\n    def stop(self):\n\n        # don't forget this function!!!\n\n        # flag that we're not running\n        self.running = False\n        # close stream\n        if not (self.stream1 is None):\n            self.stream1.release()\n            self.stream1 = None\n\n        if not (self.stream2 is None):\n            self.stream2.release()\n            self.stream2 = None\n\n\n# assign your Custom Streaming Class with adequate two sources\n# to `custom_stream` attribute in options parameter\noptions = {\n    \"custom_stream\": Custom_Stream_Class(\n        source1=\"foo1.mp4\", source2=\"foo2.mp4\"\n    )\n}\n\n# initialize WebGear_RTC app without any source\nweb = WebGear_RTC(logging=True, **options)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

On successfully running this code, the output stream will be displayed at address http://localhost:8000/ in Browser.

"},{"location":"help/webgear_rtc_faqs/","title":"FAQs","text":""},{"location":"help/webgear_rtc_faqs/#webgear_rtc-faqs","title":"WebGear_RTC FAQs","text":""},{"location":"help/webgear_rtc_faqs/#what-is-webgear_rtc-api-and-what-does-it-do","title":"What is WebGear_RTC API and what does it do?","text":"

Answer: WebGear_RTC utilizes WebRTC technology under the hood, which makes it suitable for building powerful video-streaming solutions for all modern browsers as well as native clients available on all major platforms. For more info. see WebGear_RTC doc \u27b6

"},{"location":"help/webgear_rtc_faqs/#how-to-get-started-with-webgear_rtc-api","title":"How to get started with WebGear_RTC API?","text":"

Answer: First, refer to the Switching from OpenCV guide, then go through WebGear_RTC documentation. If you still have doubts, ask us on Gitter \u27b6 Community channel.

"},{"location":"help/webgear_rtc_faqs/#how-webgear_rtc-is-different-to-webgear-api-which-should-i-choose","title":"How WebGear_RTC is different to WebGear API, which should I choose?","text":"

Answer: WebGear_RTC is similar to WeGear API in many aspects but utilizes WebRTC technology under the hood instead of Motion JPEG. You can choose any API according to your application, but the quality would be better on WebGear API, on-the-other-hand latency would be better on WebGear_RTC API. Also, WebRTC protocol accepts a wide range of devices, whereas WebGear is limited only to modern browsers.

"},{"location":"help/webgear_rtc_faqs/#webgear_rtc-is-throwing-modulenotfounderror-on-importing-why","title":"\"WebGear_RTC is throwing ModuleNotFoundError on importing\", Why?","text":"

Answer: This error means, VidGear is installed WITHOUT asyncio package support on your machine. For this support, see Requirements \u27b6.

"},{"location":"help/webgear_rtc_faqs/#can-webgear_rtc-always-need-active-internet-connection","title":"Can WebGear_RTC always need Active Internet Connection?","text":"

Answer: No, it just need internet only once during its Auto-Generation Process \u27b6 to download default data-files and it takes few seconds. You can also download files manually from Github Server, otherwise you can also add your own custom files. For more information see Data-Files Auto-Generation WorkFlow \u27b6

"},{"location":"help/webgear_rtc_faqs/#is-it-possible-to-stream-on-a-different-device-on-the-network-with-webgear_rtc","title":"Is it possible to stream on a different device on the network with WebGear_RTC?","text":"

If you set \"0.0.0.0\" as host value instead of \"localhost\" on Host Machine, then you must still use http://localhost:8000/ to access stream on your host machine browser.

For accessing WebGear_RTC on different Client Devices on the network, use \"0.0.0.0\" as host value instead of \"localhost\" on Host Machine. Then type the IP-address of source machine followed by the defined port value in your desired Client Device's browser (for e.g. http://192.27.0.101:8000) to access the stream.

"},{"location":"help/webgear_rtc_faqs/#can-i-manually-place-default-files-for-webgear_rtc","title":"Can I manually place default files for WebGear_RTC?","text":"

Answer: Yes, you can either download default files from Github Server, and manually place at default location, OR, you can yourself create the require three critical files (i.e index.html, 404.html & 500.html) inside templates folder at the default location, thereby you don't need any internet connection at all. For more information see Data-Files Auto-Generation WorkFlow \u27b6

"},{"location":"help/webgear_rtc_faqs/#how-to-stream-webgear_rtc-server-output-to-multiple-clients","title":"How to stream Webgear_RTC Server output to multiple clients?","text":"

Answer: See this usage example \u27b6.

"},{"location":"help/webgear_rtc_faqs/#how-to-send-opencv-frames-directly-to-webgear_rtc-server","title":"How to send OpenCV frames directly to Webgear_RTC Server?","text":"

Answer: See this usage example \u27b6.

"},{"location":"help/webgear_rtc_faqs/#how-can-i-add-my-custom-webpage-to-webgear_rtc","title":"How can I add my custom WebPage to WebGear_RTC?","text":"

Answer: See this usage example \u27b6.

"},{"location":"help/webgear_rtc_faqs/#how-can-to-add-cors-headers-to-webgear_rtc","title":"How can to add CORS headers to WebGear_RTC?","text":"

Answer: See this usage example \u27b6.

"},{"location":"help/webgear_rtc_faqs/#can-i-change-the-default-location","title":"Can I change the default location?","text":"

Answer: Yes, you can use WebGear_RTC's custom_data_location attribute of option parameter in WebGear_RTC API, to change default location to somewhere else.

"},{"location":"help/webgear_rtc_faqs/#can-i-deleterename-the-webgear_rtc-default-data","title":"Can I delete/rename the WebGear_RTC default data?","text":"

Answer: Yes, but you've to follow these rules \u27b6

"},{"location":"help/writegear_ex/","title":"Bonus Examples","text":""},{"location":"help/writegear_ex/#writegear-examples","title":"WriteGear Examples","text":""},{"location":"help/writegear_ex/#using-writegears-compression-mode-for-rtsprtp-live-streaming","title":"Using WriteGear's Compression Mode for RTSP/RTP Live-Streaming","text":"

In Compression Mode, you can use WriteGear for livestreaming with traditional protocols such as RTSP/RTP. The example to achieve that is as follows:

New in v0.2.6

This example was added in v0.2.6.

Creating your own RTSP Server locally

If you want to create your RTSP Server locally, then checkout MediaMTX (formerly rtsp-simple-server) - ready-to-use and zero-dependency real-time media server and media proxy that allows to publish, read, proxy, record and playback video and audio streams.

This example assume you already have a RTSP Server running at specified RTSP address with format rtsp://[RTSP_ADDRESS]:[RTSP_PORT]/[RTSP_PATH] for publishing video frames.

Make sure to change RTSP address rtsp://localhost:8554/mystream with yours in following code before running!

# import required libraries\nimport cv2\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\n\n# open any valid video stream(for e.g `foo.mp4` file)\nstream = CamGear(source=\"foo.mp4\").start()\n\n# define required FFmpeg parameters for your writer\noutput_params = {\"-f\": \"rtsp\", \"-rtsp_transport\": \"tcp\"}\n\n# Define writer with defined parameters and RTSP address\n# [WARNING] Change your RTSP address `rtsp://localhost:8554/mystream` with yours!\nwriter = WriteGear(\n    output=\"rtsp://localhost:8554/mystream\", logging=True, **output_params\n)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"help/writegear_ex/#using-writegears-compression-mode-for-youtube-live-streaming","title":"Using WriteGear's Compression Mode for YouTube-Live Streaming","text":"

In Compression Mode, you can also use WriteGear for Youtube-Livestreaming. The example is as follows:

New in v0.2.1

This example was added in v0.2.1.

This example assume you already have a YouTube Account with Live-Streaming enabled for publishing video.

Make sure to change YouTube-Live Stream Key with yours in following code before running!

Without AudioWith Audio
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# define and open video source\nstream = CamGear(source=\"/home/foo/foo.mp4\", logging=True).start()\n\n# define required FFmpeg parameters for your writer\noutput_params = {\n    \"-clones\": [\"-f\", \"lavfi\", \"-i\", \"anullsrc\"],\n    \"-vcodec\": \"libx264\",\n    \"-preset\": \"medium\",\n    \"-b:v\": \"4500k\",\n    \"-bufsize\": \"512k\",\n    \"-pix_fmt\": \"yuv420p\",\n    \"-f\": \"flv\",\n}\n\n# [WARNING] Change your YouTube-Live Stream Key here:\nYOUTUBE_STREAM_KEY = \"xxxx-xxxx-xxxx-xxxx-xxxx\"\n\n# Define writer with defined parameters\nwriter = WriteGear(\n    output=\"rtmp://a.rtmp.youtube.com/live2/{}\".format(YOUTUBE_STREAM_KEY),\n    logging=True,\n    **output_params\n)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

This code assume given input video source contains valid audio stream.

# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# define video source(with audio) here\nVIDEO_SOURCE = \"/home/foo/foo.mp4\"\n\n# Open stream\nstream = CamGear(source=VIDEO_SOURCE, logging=True).start()\n\n# define required FFmpeg parameters for your writer\n# [NOTE]: Added VIDEO_SOURCE as audio-source\noutput_params = {\n    \"-i\": VIDEO_SOURCE,\n    \"-acodec\": \"aac\",\n    \"-ar\": 44100,\n    \"-b:a\": 712000,\n    \"-vcodec\": \"libx264\",\n    \"-preset\": \"medium\",\n    \"-b:v\": \"4500k\",\n    \"-bufsize\": \"512k\",\n    \"-pix_fmt\": \"yuv420p\",\n    \"-f\": \"flv\",\n}\n\n# [WARNING] Change your YouTube-Live Stream Key here:\nYOUTUBE_STREAM_KEY = \"xxxx-xxxx-xxxx-xxxx-xxxx\"\n\n# Define writer with defined parameters\nwriter = WriteGear(\n    output=\"rtmp://a.rtmp.youtube.com/live2/{}\".format(YOUTUBE_STREAM_KEY),\n    logging=True,\n    **output_params\n)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"help/writegear_ex/#using-writegears-compression-mode-with-v4l2loopback-virtual-cameras","title":"Using WriteGear's Compression Mode with v4l2loopback Virtual Cameras","text":"

With WriteGear's Compression Mode, you can directly feed video-frames to v4l2loopback generated Virtual Camera devices on Linux Machines. The complete usage example is as follows:

New in v0.3.0

This example was added in v0.3.0.

Example Assumptions Creating your own Virtual Camera device with v4l2loopback module.

To install and create a v4l2loopback virtual camera device on Linux Mint OS/Ubuntu (may slightly differ for other distros), run following two terminal commands:

$ sudo apt-get install v4l2loopback-dkms v4l2loopback-utils linux-modules-extra-$(uname -r)\n\n$ sudo modprobe v4l2loopback devices=1 video_nr=0 exclusive_caps=1 card_label='VCamera'\n

For further information on parameters used, checkout v4l2loopback docs

Finally, You can check the loopback device you just created by listing contents of /sys/devices/virtual/video4linux directory with terminal command:

$ sudo ls -1 /sys/devices/virtual/video4linux\n\nvideo0 \n

Now you can use /dev/video0 Virtual Camera device path in WriteGear API.

v4l2: open /dev/videoX: Permission denied

If you got this error, then you must add your username to the video group by running following commands:

$ sudo adduser $(whoami) video\n$ sudo usermod -a -G video $(whoami)\n
Afterwards, restart your computer to finialize these changes.

Note: If the problem still persists, then try to run your python script as superuser with sudo command.

Default libx264 encoder is incompatible with v4l2loopback module.

Kindly use other encoders such as libxvid, mpeg4 etc.

# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# open any valid video stream(for e.g `foo.mp4` file)\nstream = CamGear(source=\"foo.mp4\").start()\n\n# define required FFmpeg parameters for your writer\n# also retrieve framerate from CamGear Stream and pass it as `-input_framerate` parameter\noutput_params = {\n    \"-input_framerate\": stream.framerate,\n    \"-vcodec\": \"libxvid\",\n    \"-f\": \"v4l2\",\n    \"-pix_fmt\": \"yuv420p\",\n}\n\n# Define writer with \"/dev/video0\" as source and user-defined parameters \nwriter = WriteGear(output=\"/dev/video0\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if None-type\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

The data sent to the v4l2loopback device /dev/video0 in this example with WriteGear API, can then be read by any v4l2-capable application (such as OpenCV, VLC, ffplay etc.)

"},{"location":"help/writegear_ex/#using-writegears-compression-mode-for-creating-mp4-segments","title":"Using WriteGear's Compression Mode for creating MP4 segments","text":"

In Compression Mode, you can also use WriteGear for creating MP4 segments from almost any video source. The example is as follows:

New in v0.2.1

This example was added in v0.2.1.

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# Open any video source `foo.mp4`\nstream = VideoGear(\n    source=\"foo.mp4\", logging=True\n).start()\n\n# define required FFmpeg optimizing parameters for your writer\noutput_params = {\n    \"-c:v\": \"libx264\",\n    \"-crf\": 22,\n    \"-map\": 0,\n    \"-segment_time\": 9,\n    \"-g\": 9,\n    \"-sc_threshold\": 0,\n    \"-force_key_frames\": \"expr:gte(t,n_forced*9)\",\n    \"-clones\": [\"-f\", \"segment\"],\n}\n\n# Define writer with defined parameters\nwriter = WriteGear(output=\"output%03d.mp4\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"help/writegear_ex/#using-writegears-compression-mode-to-add-external-audio-file-input-to-video-frames","title":"Using WriteGear's Compression Mode to add external audio file input to video frames","text":"

You can also use WriteGear for merging external audio with live video-source:

New in v0.2.1

This example was added in v0.2.1.

Make sure this -i audio-source it compatible with provided video-source, otherwise you could encounter multiple errors or no output at all.

# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# open any valid video stream(for e.g `foo_video.mp4` file)\nstream = CamGear(source=\"foo_video.mp4\").start()\n\n# add various parameters, along with custom audio\nstream_params = {\n    \"-input_framerate\": stream.framerate,  # controlled framerate for audio-video sync !!! don't forget this line !!!\n    \"-i\": \"foo_audio.aac\",  # assigns input audio-source: \"foo_audio.aac\"\n}\n\n# Define writer with defined parameters\nwriter = WriteGear(output=\"Output.mp4\", logging=True, **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"help/writegear_ex/#using-writegears-compression-mode-for-generating-timely-accurate-video","title":"Using WriteGear's Compression Mode for generating Timely Accurate Video","text":"

If you need timely accurate video with exactly same speed as real-time input, then you need to use FFmpeg directly through its execute_ffmpeg_cmd method:

New in v0.2.4

This example was added in v0.2.4.

In this example we are capturing video from desktop screen in a Timely Accurate manner.

Windows Linux MacOS
# import required libraries\nfrom vidgear.gears import WriteGear\n\n# Define writer with defined parameters and with some dummy name\nwriter = WriteGear(output=\"Output.mp4\", logging=True)\n\n# format FFmpeg command to generate time accurate video\nffmpeg_command = [\n    \"-y\",\n    \"-f\",\n    \"gdigrab\",\n    \"-framerate\",\n    \"30\",\n    \"-i\",\n    \"desktop\",\n    \"Output.mkv\",\n]  # `-y` parameter is to overwrite outputfile if exists\n\n# execute FFmpeg command\nwriter.execute_ffmpeg_cmd(ffmpeg_command)\n\n# safely close writer\nwriter.close()\n
# import required libraries\nfrom vidgear.gears import WriteGear\n\n# Define writer with defined parameters and with some dummy name\nwriter = WriteGear(output=\"Output.mp4\", logging=True)\n\n# format FFmpeg command to generate time accurate video\nffmpeg_command = [\n    \"-y\",\n    \"-f\",\n    \"x11grab\",\n    \"-framerate\",\n    \"30\",\n    \"-i\",\n    \"default\",\n    \"Output.mkv\",\n]  # `-y` parameter is to overwrite outputfile if exists\n\n# execute FFmpeg command\nwriter.execute_ffmpeg_cmd(ffmpeg_command)\n\n# safely close writer\nwriter.close()\n
# import required libraries\nfrom vidgear.gears import WriteGear\n\n# Define writer with defined parameters and with some dummy name\nwriter = WriteGear(output=\"Output.mp4\", logging=True)\n\n# format FFmpeg command to generate time accurate video\nffmpeg_command = [\n    \"-y\",\n    \"-f\",\n    \"avfoundation\",\n    \"-framerate\",\n    \"30\",\n    \"-i\",\n    \"default\",\n    \"Output.mkv\",\n]  # `-y` parameter is to overwrite outputfile if exists\n\n# execute FFmpeg command\nwriter.execute_ffmpeg_cmd(ffmpeg_command)\n\n# safely close writer\nwriter.close()\n

"},{"location":"help/writegear_ex/#using-writegear-with-rosrobot-operating-system","title":"Using WriteGear with ROS(Robot Operating System)","text":"

We will be using cv_bridge to convert OpenCV frames to ROS image messages and vice-versa.

In this example, we'll create a node that listens to a ROS image message topic, converts the received images messages into OpenCV frames, draws a circle on it, and then process these frames into a lossless compressed file format in real-time.

New in v0.2.2

This example was added in v0.2.2.

This example is vidgear implementation of this wiki example.

# import roslib\nimport roslib\n\nroslib.load_manifest(\"my_package\")\n\n# import other required libraries\nimport sys\nimport rospy\nimport cv2\nfrom std_msgs.msg import String\nfrom sensor_msgs.msg import Image\nfrom cv_bridge import CvBridge, CvBridgeError\nfrom vidgear.gears import WriteGear\n\n# custom publisher class\nclass image_subscriber:\n    def __init__(self, output=\"Output.mp4\"):\n        # create CV bridge\n        self.bridge = CvBridge()\n        # define publisher topic\n        self.image_pub = rospy.Subscriber(\"image_topic_sub\", Image, self.callback)\n        # Define writer with default parameters\n        self.writer = WriteGear(output=output)\n\n    def callback(self, data):\n        # convert received data to frame\n        try:\n            cv_image = self.bridge.imgmsg_to_cv2(data, \"bgr8\")\n        except CvBridgeError as e:\n            print(e)\n\n        # check if frame is valid\n        if cv_image:\n\n            # {do something with the frame here}\n            # let's add a circle\n            (rows, cols, channels) = cv_image.shape\n            if cols > 60 and rows > 60:\n                cv2.circle(cv_image, (50, 50), 10, 255)\n\n            # write frame to writer\n            self.writer.write(cv_image)\n\n        def close(self):\n            # safely close video stream\n            self.writer.close()\n\n\ndef main(args):\n    # define publisher with suitable output filename\n    # such as `Output.mp4` for saving output\n    ic = image_subscriber(output=\"Output.mp4\")\n    # initiate ROS node on publisher\n    rospy.init_node(\"image_subscriber\", anonymous=True)\n    try:\n        # run node\n        rospy.spin()\n    except KeyboardInterrupt:\n        print(\"Shutting down\")\n    finally:\n        # close publisher\n        ic.close()\n\n\nif __name__ == \"__main__\":\n    main(sys.argv)\n

"},{"location":"help/writegear_faqs/","title":"FAQs","text":""},{"location":"help/writegear_faqs/#writegear-faqs","title":"WriteGear FAQs","text":""},{"location":"help/writegear_faqs/#what-is-writegear-api-and-what-does-it-do","title":"What is WriteGear API and what does it do?","text":"

Answer: WriteGear handles various powerful Writer Tools that provide us the freedom to do almost anything imagine with multimedia files. For more info. see WriteGear doc \u27b6

"},{"location":"help/writegear_faqs/#im-only-familiar-with-opencv-how-to-get-started-with-writegear-api","title":"I'm only familiar with OpenCV, how to get started with WriteGear API?","text":"

Answer: Answer: First, refer to the Switching from OpenCV guide, then go through WriteGear documentation. If you still have doubts, ask us on Gitter \u27b6 Community channel.

"},{"location":"help/writegear_faqs/#why-writegear-is-throwing-valueerror","title":"Why WriteGear is throwing ValueError?","text":"

Answer: WriteGear will exit with ValueError if you feed frames of different dimensions or channels.

"},{"location":"help/writegear_faqs/#how-to-install-and-configure-ffmpeg-correctly-for-writegear-on-my-machine","title":"How to install and configure FFmpeg correctly for WriteGear on my machine?","text":"

Answer: Follow these Installation Instructions \u27b6 for its installation.

"},{"location":"help/writegear_faqs/#can-i-use-writegear-directly-with-opencv","title":"Can I use WriteGear directly with OpenCV?","text":"

Answer: Yes,

"},{"location":"help/writegear_faqs/#what-ffmpegs-encoders-and-parameters-are-supported-by-writegear-in-compression-mode","title":"What FFmpeg's encoders and parameters are supported by WriteGear in compression mode?","text":"

Answer: See Supported Parameters \u27b6 and Supported encoders \u27b6

"},{"location":"help/writegear_faqs/#what-opencvs-fourcc-and-parameters-are-supported-by-writegear-in-non-compression-mode","title":"What OpenCV's FOURCC and parameters are supported by WriteGear in non-compression mode?","text":"

Answer: See Supported Parameters \u27b6 and Supported FOURCC \u27b6.

"},{"location":"help/writegear_faqs/#why-this-fourcc-is-not-working-for-me","title":"Why this FOURCC is not working for me?","text":"

Answer: Remember not all the FOURCC and Video extensions are compatible and supported by OpenCV VideoWriter Class. You\u2019ll need to try different combinations of FourCC and file extensions. Furthermore, OpenCV does not return any helpful error messages regarding this problem, so it\u2019s pretty much based on trial and error.

"},{"location":"help/writegear_faqs/#can-i-pass-my-custom-ffmpeg-commands-directly-in-writegear-api","title":"Can I pass my custom FFmpeg commands directly in WriteGear API?","text":"

Answer: Yes, See Custom FFmpeg Commands in WriteGear API \u27b6.

"},{"location":"help/writegear_faqs/#how-to-use-specific-hardware-encoder-in-writegear","title":"How to use specific Hardware Encoder in WriteGear?","text":"

Answer: See this usage example \u27b6

"},{"location":"help/writegear_faqs/#how-to-add-live-audio-to-writegear","title":"How to add live audio to WriteGear?","text":"

Answer: See this doc \u27b6

"},{"location":"help/writegear_faqs/#how-to-separate-and-merge-audio-fromto-video","title":"How to separate and merge audio from/to video?","text":"

Answer: See these usage examples \u27b6

"},{"location":"help/writegear_faqs/#can-i-live-stream-to-twitch-with-writegear-api","title":"Can I live stream to Twitch with WriteGear API?","text":"

Answer: Yes, See this usage example \u27b6

"},{"location":"help/writegear_faqs/#is-youtube-live-streaming-possible-with-writegear","title":"Is YouTube-Live Streaming possible with WriteGear?","text":"

Answer: Yes, See this bonus example \u27b6.

"},{"location":"help/writegear_faqs/#how-to-live-streaming-using-rtsprtp-protocol-with-writegear","title":"How to Live-Streaming using RTSP/RTP protocol with WriteGear?","text":"

Answer: See this bonus example \u27b6.

"},{"location":"help/writegear_faqs/#how-to-create-mp4-segments-from-a-video-stream-with-writegear","title":"How to create MP4 segments from a video stream with WriteGear?","text":"

Answer: See this bonus example \u27b6.

"},{"location":"help/writegear_faqs/#how-add-external-audio-file-input-to-video-frames","title":"How add external audio file input to video frames?","text":"

Answer: See this bonus example \u27b6.

"},{"location":"help/writegear_faqs/#why-this-ffmpeg-parameter-is-not-working-for-me-in-compression-mode","title":"Why this FFmpeg parameter is not working for me in compression mode?","text":"

Answer: If some FFmpeg parameter doesn't work for you, then tell us on Gitter \u27b6, and if that doesn't help, then finally report an issue \u27b6

"},{"location":"help/writegear_faqs/#why-writegear-is-switching-to-non-compression-mode-even-if-it-is-not-enable","title":"Why WriteGear is switching to Non-compression Mode, even if it is not enable?","text":"

Answer: In case WriteGear API fails to detect valid FFmpeg executables on your system (even if Compression Mode is enabled), it will automatically fallback to Non-Compression Mode. Follow Installation Instructions \u27b6 for FFmpeg installation.

"},{"location":"installation/pip_install/","title":"Install using pip","text":""},{"location":"installation/pip_install/#install-using-pip","title":"Install using pip","text":"

Best option for easily getting stable VidGear installed.

"},{"location":"installation/pip_install/#prerequisites","title":"Prerequisites","text":"

When installing VidGear with pip, you need to manually install following prerequisites:

Upgrade your pip

It strongly advised to upgrade to latest pip before installing vidgear to avoid any undesired installation error(s).

There are two mechanisms to upgrade pip:

pipensurepip

You can use existing pip to upgrade itself:

Install pip if not present Linux / MacOS Windows
python get-pip.py\n
py get-pip.py\n

More details about this script can be found in pypa/get-pip\u2019s README.

Linux / MacOS Windows
python -m pip install pip --upgrade\n
py -m pip install pip --upgrade\n

Python also comes with an ensurepip module1, which can easily upgrade/install pip in any Python environment.

Linux / MacOS Windows
python -m ensurepip --upgrade\n
py -m ensurepip --upgrade\n

"},{"location":"installation/pip_install/#critical-prerequisites","title":"Critical Prerequisites","text":""},{"location":"installation/pip_install/#opencv","title":"OpenCV","text":"

Must require OpenCV(3.0+) python binaries installed for all core functions. You easily install it directly via pip:

OpenCV installation from source

You can also follow online tutorials for building & installing OpenCV on Windows, Linux, MacOS and Raspberry Pi machines manually from its source.

Make sure not to install both pip and source version together. Otherwise installation will fail to work!

Other OpenCV binaries

OpenCV maintainers also provide additional binaries via pip that contains both main modules and contrib/extra modules opencv-contrib-python, and for server (headless) environments like opencv-python-headless and opencv-contrib-python-headless. You can also install any one of them in similar manner. More information can be found here.

pip install opencv-python       \n
"},{"location":"installation/pip_install/#api-specific-prerequisites","title":"API Specific Prerequisites","text":""},{"location":"installation/pip_install/#ffmpeg","title":"FFmpeg","text":"

Require only for the video compression and encoding compatibility within StreamGear API and WriteGear API's Compression Mode.

FFmpeg Installation

"},{"location":"installation/pip_install/#picamera2","title":"Picamera2","text":"

Required only if you're using Raspberry Pi Camera Modules (or USB webcams) with the PiGear API. Here's how to install Picamera2 python library:

Using Legacy picamera library with PiGear (v0.3.3 and above)

PiGear API (version 0.3.3 onwards) prioritizes the newer Picamera2 library under the hood for Raspberry Pi camera modules. However, if your operating system doesn't support Picamera2, you can still use the legacy picamera library. Here's how to easily install it using pip:

pip install picamera\n

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

Picamera2 is only supported on Raspberry Pi OS Bullseye (or later) images, both 32 and 64-bit.

Picamera2 is NOT supported on:

Installation using apt (Recommended)Installation using pip As of September 2022, Picamera2 is pre-installed on images downloaded from Raspberry Pi. So you don't have to install it manually.

If Picamera2 is not already installed, then your image is presumably older and you should start with system upgrade:

sudo apt update && upgrade\n

If you have installed Picamera2 previously using pip, then you should also uninstall this (pip3 uninstall picamera2).

Thereafter, you can install Picamera2 with all the GUI (Qt and OpenGL) dependencies using:

sudo apt install -y python3-picamera2\n

Or, If you DON'T want the GUI dependencies, use:

sudo apt install -y python3-picamera2 --no-install-recommends\n

This is NOT the recommended way to install Picamera2.

However, if you wish to install Picamera2 with all the GUI (Qt and OpenGL) dependencies with pip, use:

sudo apt install -y python3-libcamera python3-kms++\nsudo apt install -y python3-pyqt5 python3-prctl \nsudo apt install -y libatlas-base-dev ffmpeg python3-pip\npip3 install numpy --upgrade\npip3 install picamera2[gui]\n

Or, If you DON'T want the GUI dependencies, use:

sudo apt install -y python3-libcamera python3-kms++\nsudo apt install -y python3-prctl libatlas-base-dev\nsudo apt install -y ffmpeg libopenjp2-7 python3-pip\npip3 install numpy --upgrade\npip3 install picamera2\n
"},{"location":"installation/pip_install/#uvloop","title":"Uvloop","text":"

Required only if you're using the NetGear_Async API on UNIX machines for maximum performance. You can easily install it via pip:

uvloop is NOT yet supported on Windows Machines.

pip install uvloop\n
"},{"location":"installation/pip_install/#dxcam","title":"DXcam","text":"

Required only if you're using the ScreenGear API on Windows machines for better FPS performance. You can easily install it via pip:

FYI, DXcam is ONLY supported on Windows Machines.

pip install dxcam\n
"},{"location":"installation/pip_install/#installation","title":"Installation","text":"Installation command with pip has been changed in v0.2.4

The legacy pip install vidgear command now installs critical bare-minimum dependencies only. Therefore in order to automatically install all the API specific dependencies as previous versions, use pip install vidgear[core] command instead.

v0.2.4 and newerOlder
# Install latest stable release with all Core dependencies\npip install -U vidgear[core]\n

[core] keyword isn't available in versions older than v0.2.4

# Install older stable release with all Core dependencies\npip install vidgear<0.2.4\n

Similarly in your python project files like setup.py or requirements.txt or setup.cfg, use vidgear dependency as vidgear[core]>=0.2.4 instead.

This change does not affects pip install vidgear[asyncio] command.

Installation is as simple as:

Installing vidgear with only selective dependencies

Starting with version v0.2.2, you can now run any VidGear API by installing only just specific dependencies required by the API in use(except for some Core dependencies).

This is useful when you want to manually review, select and install minimal API-specific dependencies on bare-minimum vidgear from scratch on your system:

Windows Installation

If you are using Windows, some of the commands given below, may not work out-of-the-box.

A quick solution may be to preface every Python command with python -m like this:

# Install latest stable release with all Core dependencies\npython -m pip install -U vidgear[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npython -m pip install -U vidgear[asyncio]\n

And, If you don't have the privileges to the directory you're installing package. Then use --user flag, that makes pip install packages in your home directory instead:

# Install latest stable release with all Core dependencies\npython -m pip install --upgrade --user vidgear[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npython -m pip install --upgrade --user vidgear[asyncio]\n

Or, If you're using py as alias for installed python, then:

# Install latest stable release with all Core dependencies\npy -m pip install --upgrade --user vidgear[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npy -m pip install --upgrade --user vidgear[asyncio]\n
# Install latest stable release with all Core dependencies\npip install -U vidgear[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npip install -U vidgear[asyncio]\n

And if you prefer to install VidGear directly from the repository:

# Install latest stable release with all Core dependencies\npip install git+git://github.com/abhiTronix/vidgear@master#egg=vidgear[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npip install git+git://github.com/abhiTronix/vidgear@master#egg=vidgear[asyncio]\n

Or you can also download its wheel (.whl) package from our repository's releases section, and thereby can be installed as follows:

# Install latest stable release with all Core dependencies\npip install vidgear-0.3.3-py3-none-any.whl[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npip install vidgear-0.3.3-py3-none-any.whl[asyncio]\n

  1. The ensurepip module is missing/disabled on Ubuntu. Use pip method only.\u00a0\u21a9

"},{"location":"installation/source_install/","title":"Install from source","text":""},{"location":"installation/source_install/#install-from-source","title":"Install from source","text":"

Best option for trying latest patches(maybe experimental), forking for Pull Requests, or automatically installing all prerequisites(with a few exceptions).

"},{"location":"installation/source_install/#prerequisites","title":"Prerequisites","text":"

When installing VidGear from source, following are some API specific prerequisites you may need to install manually:

What about rest of the prerequisites?

Any other python prerequisites (Critical/API specific) will be automatically installed based on your OS/System specifications.

Upgrade your pip

It strongly advised to upgrade to latest pip before installing vidgear to avoid any undesired installation error(s).

There are two mechanisms to upgrade pip:

pipensurepip

You can use existing pip to upgrade itself:

Install pip if not present Linux/MacOSWindows
python get-pip.py\n
py get-pip.py\n

More details about this script can be found in pypa/get-pip\u2019s README.

Linux/MacOSWindows
python -m pip install pip --upgrade\n
py -m pip install pip --upgrade\n

Python also comes with an ensurepip module1, which can easily upgrade/install pip in any Python environment.

Linux/MacOSWindows
python -m ensurepip --upgrade\n
py -m ensurepip --upgrade\n
"},{"location":"installation/source_install/#api-specific-prerequisites","title":"API Specific Prerequisites","text":""},{"location":"installation/source_install/#ffmpeg","title":"FFmpeg","text":"

Require only for the video compression and encoding compatibility within StreamGear API and WriteGear API's Compression Mode.

FFmpeg Installation

"},{"location":"installation/source_install/#picamera2","title":"Picamera2","text":"

Required only if you're using Raspberry Pi Camera Modules (or USB webcams) with the PiGear API. Here's how to install Picamera2 python library:

Using Legacy picamera library with PiGear (v0.3.3 and above)

PiGear API (version 0.3.3 onwards) prioritizes the newer Picamera2 library under the hood for Raspberry Pi camera modules. However, if your operating system doesn't support Picamera2, you can still use the legacy picamera library. Here's how to easily install it using pip:

pip install picamera\n

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

Picamera2 is only supported on Raspberry Pi OS Bullseye (or later) images, both 32 and 64-bit.

Picamera2 is NOT supported on:

Installation using apt (Recommended)Installation using pip As of September 2022, Picamera2 is pre-installed on images downloaded from Raspberry Pi. So you don't have to install it manually.

If Picamera2 is not already installed, then your image is presumably older and you should start with system upgrade:

sudo apt update && upgrade\n

If you have installed Picamera2 previously using pip, then you should also uninstall this (pip3 uninstall picamera2).

Thereafter, you can install Picamera2 with all the GUI (Qt and OpenGL) dependencies using:

sudo apt install -y python3-picamera2\n

Or, If you DON'T want the GUI dependencies, use:

sudo apt install -y python3-picamera2 --no-install-recommends\n

This is NOT the recommended way to install Picamera2.

However, if you wish to install Picamera2 with all the GUI (Qt and OpenGL) dependencies with pip, use:

sudo apt install -y python3-libcamera python3-kms++\nsudo apt install -y python3-pyqt5 python3-prctl \nsudo apt install -y libatlas-base-dev ffmpeg python3-pip\npip3 install numpy --upgrade\npip3 install picamera2[gui]\n

Or, If you DON'T want the GUI dependencies, use:

sudo apt install -y python3-libcamera python3-kms++\nsudo apt install -y python3-prctl libatlas-base-dev\nsudo apt install -y ffmpeg libopenjp2-7 python3-pip\npip3 install numpy --upgrade\npip3 install picamera2\n
"},{"location":"installation/source_install/#installation","title":"Installation","text":"

If you want to checkout the latest beta testing branch , you can do so with the following commands:

This can be useful if you want to provide feedback for a new feature or bug fix in the testing branch.

DO NOT clone or install any other branch other than testing unless advised, as it is not tested with CI environments and possibly very unstable or unusable.

Installing vidgear with only selective dependencies

Starting with version v0.2.2, you can now run any VidGear API by installing only just specific dependencies required by the API in use(except for some Core dependencies).

This is useful when you want to manually review, select and install minimal API-specific dependencies on bare-minimum vidgear from scratch on your system:

Windows Installation

If you are using Windows, some of the commands given below, may not work out-of-the-box.

A quick solution may be to preface every Python command with python -m like this:

# Install latest stable release with all Core dependencies\npython -m pip install -U .[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npython -m pip install -U .[asyncio]\n

And, If you don't have the privileges to the directory you're installing package. Then use --user flag, that makes pip install packages in your home directory instead:

# Install latest stable release with all Core dependencies\npython -m pip install --upgrade --user .[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npython -m pip install --upgrade --user .[asyncio]\n

Or, If you're using py as alias for installed python, then:

# Install latest stable release with all Core dependencies\npy -m pip install --upgrade --user .[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npy -m pip install --upgrade --user .[asyncio]\n
# clone the repository and get inside\ngit clone https://github.com/abhiTronix/vidgear.git && cd vidgear\n\n# checkout the latest testing branch\ngit checkout testing\n\n# Install latest stable release with all Core dependencies\npip install -U .[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npip install -U .[asyncio]\n

  1. The ensurepip module is missing/disabled on Ubuntu. Use pip method only.\u00a0\u21a9

"}]} \ No newline at end of file diff --git a/dev/sitemap.xml b/dev/sitemap.xml index 149a49c1..141ace12 100644 --- a/dev/sitemap.xml +++ b/dev/sitemap.xml @@ -2,522 +2,522 @@ https://abhitronix.github.io/vidgear/v0.3.3-dev/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/changelog/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/contribution/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/installation/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/license/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/switch_from_cv/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/bonus/TQM/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/bonus/colorspace_manipulation/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/bonus/reference/camgear/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/bonus/reference/helper/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/bonus/reference/helper_async/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/bonus/reference/netgear/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/bonus/reference/netgear_async/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/bonus/reference/pigear/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/bonus/reference/screengear/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/bonus/reference/stabilizer/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/bonus/reference/streamgear/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/bonus/reference/videogear/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/bonus/reference/webgear/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/bonus/reference/webgear_rtc/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/bonus/reference/writegear/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/contribution/PR/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/contribution/issue/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/camgear/overview/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/camgear/params/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/camgear/usage/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/camgear/advanced/source_params/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/netgear/overview/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/netgear/params/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/netgear/usage/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/netgear/advanced/bidirectional_mode/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/netgear/advanced/compression/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/netgear/advanced/multi_client/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/netgear/advanced/multi_server/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/netgear/advanced/secure_mode/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/netgear/advanced/ssh_tunnel/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/netgear_async/overview/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/netgear_async/params/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/netgear_async/usage/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/netgear_async/advanced/bidirectional_mode/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/pigear/overview/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/pigear/params/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/pigear/usage/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/screengear/overview/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/screengear/params/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/screengear/usage/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/stabilizer/overview/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/stabilizer/params/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/stabilizer/usage/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/streamgear/ffmpeg_install/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/streamgear/introduction/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/streamgear/params/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/streamgear/rtfm/overview/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/streamgear/rtfm/usage/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/streamgear/ssm/overview/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/streamgear/ssm/usage/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/videogear/overview/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/videogear/params/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/videogear/usage/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/webgear/advanced/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/webgear/overview/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/webgear/params/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/webgear/usage/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/webgear_rtc/advanced/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/webgear_rtc/overview/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/webgear_rtc/params/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/webgear_rtc/usage/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/writegear/introduction/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/writegear/compression/overview/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/writegear/compression/params/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/writegear/compression/usage/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/writegear/compression/advanced/cciw/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/writegear/compression/advanced/ffmpeg_install/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/writegear/non_compression/overview/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/writegear/non_compression/params/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/writegear/non_compression/usage/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/camgear_ex/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/camgear_faqs/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/general_faqs/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/get_help/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/netgear_async_ex/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/netgear_async_faqs/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/netgear_ex/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/netgear_faqs/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/pigear_ex/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/pigear_faqs/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/screengear_ex/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/screengear_faqs/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/stabilizer_ex/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/stabilizer_faqs/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/streamgear_ex/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/streamgear_faqs/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/videogear_ex/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/videogear_faqs/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/webgear_ex/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/webgear_faqs/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/webgear_rtc_ex/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/webgear_rtc_faqs/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/writegear_ex/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/writegear_faqs/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/installation/pip_install/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/installation/source_install/ - 2024-06-20 + 2024-06-22 daily \ No newline at end of file diff --git a/dev/sitemap.xml.gz b/dev/sitemap.xml.gz index 827eb2b4..2378bff5 100644 Binary files a/dev/sitemap.xml.gz and b/dev/sitemap.xml.gz differ diff --git a/v0.3.3-dev/changelog/index.html b/v0.3.3-dev/changelog/index.html index 48d6ebd8..4d56caf1 100644 --- a/v0.3.3-dev/changelog/index.html +++ b/v0.3.3-dev/changelog/index.html @@ -1,4 +1,4 @@ - Release Notes - VidGear
Skip to content
We're excited to announce our new Deffcode library, which will be integrated with VidGear soon. We value your feedback and would love to hear your thoughts!

Release Notes

v0.3.2 (2023-09-10)

New Features
  • 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.
  • 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.
  • Setup.py
    • Added official support for python 3.11.x legacies.
    • Bumped version to 0.3.1.
  • 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.
  • CI
    • Added python 3.11 legacy support for MacOS, Windows and Linux environments.
    • Added kill argument to close() method in various NetGear tests.
Updates/Improvements
  • Asyncio:
    • Formatted TemplateResponse class parameters w.r.t new changes in backend Starlette API.
  • Setup.py:
    • Readded latest patch to uvicorn, starlette, pyzmq dependencies.
    • Removed 3.7 legacy from Programming Language metadata.
  • Maintenance:
    • Added GitHub sponsors and dropped liberapay from Funding.yml.
    • Removed redundant code.
  • 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
  • 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.
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-fixes
  • ScreenGear:
    • Fixed swapped region dimensions bug with dxcam backend.
    • Fixed "mss" backend disabled when monitor parameter is not defined.
  • Docs:
    • Fixed missing compression_mode flags in WriteGear API docs.
    • Fixed missing hyperlinks.
    • Fixed typos and context.
  • 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.
Pull Requests
New Contributors

 

 

v0.3.1 (2023-07-22)

New Features
  • WebGear:
    • Added an option to add a custom video endpoint path.
      • Users can now change the video endpoint path from "/video" to any alphanumeric string.
      • Added the custom_video_endpoint optional string attribute for this purpose.
      • Only alphanumeric strings with no spaces in between are allowed as its value.
  • ScreenGear:
    • Added dxcam support for Windows machines.
      • Implemented a complete end-to-end workflow for the dxcam backend.
      • dxcam is now the default backend for Windows machines when no backend is defined.
      • Added support for variable screen dimensions to capture an area from the screen.
      • Added the optional flag dxcam_target_fps to control the target fps in dxcam. Defaults to 0 (disabled).
      • RGB frames from dxcam are automatically converted into BGR.
      • For better performance, video_mode is enabled by default in dxcam backend.
      • Added necessary imports.
    • Added support for tuple values in the monitor parameter to specify device and output indexes as (int[device_idx], int[output_idx]) in the dxcam backend only.
      • Default int index is also allowed as a value for selecting device index.
  • Helper
    • Added multiple servers support for downloading assets.
      • Added GitHub server to the generate_webdata method to make it more robust for rate limits and other shortcomings.
      • Now, the generate_webdata method will retry a different server when one fails.
  • Setup.py
    • Added dxcam dependency in core and asyncio extra requires.
    • Bumped version to 0.3.1.
  • Docs
    • Added dxcam API specific prerequisites for ScreenGear API when installing on Windows via pip.
    • Added documentation for the custom_video_endpoint optional string attribute.
    • Added documentation for controlling Chunk size in HLS stream.
    • Added new hyperlinks for dxcam dependency.
  • CI
    • Added a test case for ndim==3 grayscale frames.
      • Added the Custom_Grayscale_class to generate ndim==3 grayscale frames.
    • Added test cases for the custom_video_endpoint optional string attribute.
Updates/Improvements
  • WebGear:
    • Improved the conditions logic to check if non-empty values are assigned to optional parameters.
  • WebGear_RTC:
    • Improved the handling of the format parameter when constructing a VideoFrame from ndarray frames.
  • ScreenGear:
    • Enforced dxcam backend (if installed) when monitor is defined on Windows machines.
    • Refactored code blocks to ensure backward compatibility.
  • Maintenance:
    • Cleaned up unused imports and code blocks.
    • Cleaned redundant code.
    • Improved logging.
    • Implemented short-circuiting.
    • Fixed comment typos.
    • Updated comments.
  • Docs:
    • Updated ScreenGear API usage example docs, added new relevant information, updated requirements for dxcam support in Windows machines.
    • Refactored monitor and backend parameters docs of ScreenGear.
    • Updated class and class parameters descriptions in ScreenGear docs.
    • Updated a new description for ScreenGear API.
    • Updated Zenodo badge and the BibTeX entry.
    • Relocated some docs for a better context.
    • Removed ScreenGear name from Threaded Queue Mode doc.
    • Updated ScreenGear FAQs.
    • Updated changelog.md
  • CI:
    • Updated the test_webgear_rtc_custom_stream_class method.
    • Updated the test_webgear_options method.
    • Updated the test_webgear_routes test to validate the new custom endpoint.
    • Increased code coverage by updating tests.
Breaking Updates/Changes
  • ScreenGear:
    • Previously enforced threaded queue mode is now completely removed, resulting in a potential performance boost.
      • 💬 Reason: The IO is automatically blocked by the screen refresh rate, so adding the overhead of maintaining a separate queue is pointless.
    • Removed the THREAD_TIMEOUT optional flag.
Bug-fixes
  • WebGear_RTC:
    • Fixed a bug caused by PyAV's error when ndim==3 grayscale frames are encountered.
      • The API will now drop the third dimension if ndim==3 grayscale frames are detected.
  • ScreenGear:
    • Fixed backend not defined while logging.
  • 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.
  • Docs:
    • Fixed context and added separate code for controlling chunk size in HLS and DASH streams in StreamGear docs.
    • Fixed naming conventions for the recently added DXcam backend in ScreenGear docs.
    • Fixed missing hyperlinks.
  • CI:
    • Fixed m3u8 module failing to recognize Windows paths in ScreenGear tests.
    • Fixed a path bug by replacing the absolute file path with the decoded file content as a string in its loads()
Pull Requests

 

 

v0.3.0 (2023-01-26)

New Features
  • WriteGear:
    • Added support for user-defined and higher than 8-bit depth input frames pixel-format.
      • Added support for higher than 8-bit depth frames with datatypes of unsigned integer(uint) kind and element size 2.
      • Added dtype parameter to internal Preprocess method for passing input frames datatype.
      • Implemented auto-calculation of input pixel-format based on number of channels in higher than 8-bit depth frames.
      • Added various known working pixel-formats(based on number of channels), supported by all prominent computer vision libraries.
      • Added support for up to 1-channel(gray16-le/be) to all the way up to 4-channels(bgra64-le/be) in input frames.
      • Added endianness little(le) or big(be) at the suffix of pixel-format based on byte-order of input frames datatypes.
      • Extended support for higher RGB 8-bit depth frames through RGB mode.
    • Added support for user-defined custom input pixel-format.
      • Added new -input_pixfmt attribute to output_params dictionary parameter for easily specifying custom input pixel-format.
      • Added newly implemented get_supported_pixfmts method import for verifying user-defined input pixel-format against Installed FFmpeg supported pixel-formats. Unsupported values will be discarded.
      • Implemented runtime datatype validation check, such that all input frames must have same datatype.
    • Added support for Context Managers for proper handling of resources via with statement for allocating and releasing resources precisely. (Suggested by @sueskind)
      • Implement the __enter__() and __exit__() methods.
      • Added __enter__ method that returns reference to the WriteGear Class.
      • Added __exit__ method that automatically executes close() for performing the cleanup operations and handling exception gracefully.
  • StreamGear:
    • Added support for Context Managers for proper handling of resources via with statement for allocating and releasing resources precisely. (Suggested by @sueskind)
      • Implement the __enter__() and __exit__() methods.
      • Added __enter__ method that returns reference to the StreamGear Class.
      • Added __exit__ method that automatically executes close() for performing the cleanup operations and handling exception gracefully.
  • WebGear:
    • Added way to completely disable Data-Files Auto-Generation WorkFlow.
      • Added new skip_generate_webdata boolean optional attribute(False by default) to completely disable Data-Files Auto-Generation WorkFlow.
      • This flag enables only /video route for disabled Data-Files Auto-Generation WorkFlow.
      • Implemented JSONResponse as placeholder response instead of Index, 404 and 500 HTML pages, when workflow is disabled. (Note: Index HTML page will throw 404 status code.)
      • Added necessary imports.
  • Helper:
    • Added more robust implementation of validate_audio method.
      • Added new more robust regex pattern for extracting audio-samplerate.
      • Added new validate_audio method for calculating accurate bitrate(in kbps) from audio samplerate, channels, bit-depth values.
      • Implemented new patterns and logic for accurately extracting audio channels and bit-depth from given metadata.
    • Added support for Linux video device path (such as /dev/video0).
  • Maintenance:
    • Logging current vidgear version when vidgear APIs are called, not at import.
      • Added logcurr_vidgear_ver helper function to facilitate logging current vidgear version, when called within a API.
      • Implemented ver_is_logged global variable in helper to log version only once, which can modifiable with logcurr_vidgear_ver method only. Followed recommendation given in official python docs: https://docs.python.org/3/faq/programming.html#how-do-i-share-global-variables-across-modules
      • Current version can only be logged by VidGear APIs with the logging turned on (i.e. logging=True).
  • Docs:
    • Added new WriteGear Bonus Example:
      • Added "Using WriteGear's Compression Mode with v4l2loopback Virtual Cameras bonus python example.
      • Added related prerequisites and dependencies for creating v4l2loopback Virtual Cameras on Linux machines.
      • Added both With/Without-Audio cases for "Using WriteGear's Compression Mode for YouTube-Live Streaming".
    • Added content.code.copy and content.tabs.link features.
    • Added docs related to skip_generate_webdata optional attribute.
    • Added feedback features to mkdocs.yml.
    • Added 404.html static template to mkdocs.yml.
  • CI:
    • Added v4l2loopback support for testing /dev/video0 device on Linux machines.
    • Added test cases for newer implementation of validate_audio method.
    • Added test_skip_generate_webdata to test skip_generate_webdata optional attribute.
    • Added tests for user-defined and higher than 8-bit depth input frames pixel-format.
Updates/Improvements
  • WriteGear:
    • Completely revamped code structure and comments.
      • Updated comments, description, and logging messages to more sensible and developer friendly.
      • Implemented operator short-circuiting to cleanup code as much as possible.
      • Renamed startFFmpeg_Process internal class method to start_FFProcess.
      • Renamed Preprocess internal class method to PreprocessFFParams.
      • Renamed startCV_Process internal class method to start_CVProcess.
      • Renamed initiate internal class parameter to initiate_process.
      • Renamed force_termination internal class parameter to forced_termination.
      • Enabled output_params parameters logging in both modes.
      • Improved compression and logging parameters boolean value handling.
      • Implemented stdout closing to cleanup pipeline before terminating.
  • Helper:
    • Updated validate_audio method with improved and more robust regex patterns for identifying audio bitrate in ay audio file.
  • Setup.py:
    • Bumped version to 0.3.0.
    • Replaced >= comparison operator with more flexible ~=.
    • Replaced distutils.version.LooseVersion with pkg_resources.parse_version.
  • Maintenance:
    • Replaced depreciated LooseVersion with parse_version.
    • Updated Retry package to be imported from requests.adapters.
    • Moved terminal and python code text area to Question GitHub Form Schema.
    • Removed unnecessary imports.
    • Removed redundant code.
    • Improved logging messages.
    • Updated code comments.
    • Updated method descriptions.
    • Refactored code.
    • Increased coverage.
  • Bash Script:
    • Updated FFmpeg Static Binaries links to latest date/version tag to 12-07-2022.
    • Removed depreciated binaries download links and code.
  • Docs:
    • Replaced all raw.githubusercontent.com GIF URLs with user-images.githubusercontent.com.
    • Reformatted custom.css and added missing comments.
    • Updated sponsor block.
    • Enabled Code Highlights.
    • Updated announcement bar.
    • Updated changelog.md.
    • Reduced webgear_rtc.gif size.
    • Updated Zenodo badge and the BibTeX entry.
  • CI:
    • Added more flexible formats to return_testvideo_path function.
    • Updated test_write test for higher than 8-bit depth input frames pixel-format in WriteGear's Compression Mode.
    • Updated actions/checkout to v3.
    • Updated actions/setup-python to v4.
    • Updated codecov/codecov-action to v3.
    • Moved test_colorspaces test to CamGear tests.
    • Added deffcode library import.
  • Re-stuctured yaml code.
Breaking Updates/Changes
  • WriteGear:
    • Renamed output_filename string parameter to output.
      • Since WriteGear API accepts all sorts of streams (such as valid filename/path/URL) for encoding, thereby changing parameter name to output will be more true to its purpose.
      • Renaming output_filename to output in WriteGear API will also help user to not accidentally assume WriteGear supports only encoding of video files.
      • It matches the output parameter in StreamGear which basically does the same thing.
    • Renamed cmd parameter in execute_ffmpeg_cmd() class method to more sensible command.
    • ValueError will be raised if datatype of input frames mismatches Writegear API
Bug-fixes
  • Camgear:
    • Fixed CamGear.read() blocked unnecessarily.
      • 💬 When THREADED_QUEUE_MODE is enabled CamGear.read() blocks for an excessive duration when attempting to read past the end of a stream.
      • Added None frame to the queue at the end to signal we're done.
      • Added terminate Event check before continuing.
    • Fixed deadlock on exit.
      • 💬 The deadlock is due to self.__queue.get(timeout=self.__thread_timeout) line in read() method, which still waits for timeout(thread_timeout) to happen when main update() thread was already terminated on exit and queue was empty. Since there was no way to signal queue that stream is already ended, the blocking queue.get() keeps on waiting until timeout occurs.
      • The solution was to signal queue.get() that stream is already ended by putting None in queue on exiting the main update() thread.
  • ScreenGear:
    • Fixed ScreenGear.read() blocked during cold startup.
    • 💬 During startup, ScreenGear.read() doesn't checks if queue is empty before continuing.
  • WriteGear:
    • Fixed gstpipeline_mode not activating when wrongly assuming output value as valid path.
    • Fixed name 'compression' is not defined bug.
    • Fixed AttributeError.
  • Helper:
    • Fixed fltp keyword in regex pattern causing non-ftlp streams to be not recognized.
    • Fixed response.headers returning content-length as Nonetype since it may not necessarily have the Content-Legth header set.
    • Fixed Linux video device paths still not working.
      • Moved helper.py logic to WriteGear and StreamGear APIs resp.
    • Fixed KeyError for empty metadata.
  • Setup:
    • Pinned pyzmq==24.0.1 working version.
    • Removed redundant patch for the issue.
  • Maintaince:
    • Fixed missing pkg name import_dependency_safe functions calls.
  • Bash Script:
    • Fixed gstreamer installation.
  • CI:
    • Fixed missing v4l2loopback apt dependency on Linux envs.
    • Added fix for RTCPeerConnection fails to create RTCDtlsTransport (Related issue: aiortc/aiortc#804)
      • Pinned cryptography==38.0.4 in dependencies.
    • Pinned Linux image to ubuntu-20.04 in github actions.
    • Fixed No module named 'httpx' bug.
      • Added httpx library import.
    • Fixed F821 undefined name bug.
    • Fixed Gstreamer bug.
  • Docs:
    • Fixed hyperlinks to new GitHub's form schemas.
    • Fixed non-rendering images in README.md
      • Replaced all relative image/gifs paths with absolute URLs in README.md.
    • Fixed badges/shields#8671 badge issue in README.md
    • Fixed GitLab CDN links throwing blocked by CORS policy bug.
      • Replaced gitlab GitHack CDN links with with bitbucket.
    • Fixed DASH playback failing by setting the maxAttempts to Infinity.
    • Removed x-sign glow-text effect CSS.
    • Fixed several typos (suggested by @timgates42)
    • Fixed coverage badge.
Pull Requests
New Contributors

 

 

v0.2.6 (2022-07-05)

New Features
  • Docs:
    • Added new bonus example for RTSP/RTP Live-Streaming using WriteGear's Compression Mode.
    • Added "How to resolve zmq.error.ZMQError" FAQ for NetGear API.(PR by @iandol)
    • Added new ko-fi button to README.md
    • Added new contributors block to changelog.md
  • Maintenance:
    • Added new patterns to .gitignore to ignore pypi's build directory and egg-info files.
  • CI:
    • Switched to new Issue GitHub's form schema using YAML
      • Added new bug_report.yaml.
      • Added new question.yaml.
      • Added new proposal.yaml.
      • Deleted depreciated markdown files.
      • Polished forms.
Updates/Improvements
  • Setup.py:
    • Bumped version to 0.2.6.
    • Updated logic operators and dependency.
      • Replaced >= comparsion operator with more flexible ~=.
      • Replaced distutils.version.LooseVersion with pkg_resources.parse_version.
  • Docs:
    • Updated Site Navigation.
      • Added new notices to inform users more effectively about bonus examples.
      • Added new Bonus section to navigation and moved suitable pages under it.
      • Updated headings and URLs.
    • Redesigned and Rewritten Donation and Contribution section to README.md
    • Updated Zenodo badge and Bibtex entry.
    • Updated Admonition Icon, FAQs and site-links.
    • Reformatted code and its comments.
    • Updated changelog.md.
  • API:
  • CI:
    • Added more paths and files to skip commits.
Breaking Updates/Changes
  • -input_framerate parameter now accepts any positive value for WriteGear and StreamGear APIs.
Bug-fixes
  • API:
    • Fixed -input_framerate less than 5 does not get used in WriteGear and StreamGear APIs.(PR by @freol35241)
  • CamGear: Fixed Yt-dlp generated HTTP DASH Segments URLs not supported by OpenCV's VideoCapture(PR by @DynamiteC)
  • StreamGear:
    • Fixed hls_segment_type not working bug. (PR by @enarche-ahn)
    • Fixed critical logging parameter bug
      • Fixed debug logs even when logging=False in StreamGear's Real-time Mode. (patch suggested by @enarche-ahn)
      • Added length check to -video_source attribute to correctly infers it as empty(or invalid).
  • CI:
    • Xfailed RTSP CamGear CI test.
    • Fixed pinned version syntax bug in docs_deployer workflow.
    • Fixed typos in Github forms and its context.
    • Added missing dependency.
  • Docs:
    • Fixed jinja2 3.1.0 or above breaks mkdocs.
      • jinja2>=3.1.0 breaks mkdocs (mkdocs/mkdocs#2799), therefore pinned jinja2 version to <3.1.0.
    • Fixed support for new mkdocstring versions
      • Replaced rendering sub-value with options.
      • Removed pinned mkdocstrings==0.17.0 version.
    • Fixed Netgear+Webgear bonus example code bugs.(PR by @iandol)
      • Added a missing import.
      • Removed self. typo.
      • Replaced the return value with break in the async as it triggers an error.
    • Fixed external bug that causing "Home" tab to irresponsive randomly when accessed from other tabs.
    • Fixed indentation and spacing.
    • Fixed typos and updated context.
    • Removed dead code.
Pull Requests
New Contributors

 

 

v0.2.5 (2021-02-11)

New Features
  • WriteGear:
    • Add support for GStreamer pipeline in WriteGear API's Non-Compression mode:
      • Implemented GStreamer Pipeline Mode to accept GStreamer pipeline as string to its output_filename parameter.
      • Added new special -gst_pipeline_mode attribute for its output_params parameter.
      • This feature provides flexible way to directly write video frames into GStreamer Pipeline with controlled bitrate.
      • Added new docs and updated existing docs with related changes.
    • Added new -ffpreheaders special attribute to WriteGear's options parameter:
      • This attribute is specifically required to set special FFmpeg parameters in Compression Mode that are present at the starting of command(such as -re).
      • This attribute only accepts list datatype as value.
      • Added related docs.
  • NetGear:
    • Added bidirectional data transfer support by extending Bidirectional mode support to exclusive Multi-Clients and Multi-Servers modes:
      • Users will now able to send data bidirectionally in both Multi-Clients and Multi-Servers exclusive modes.
      • Bidirectional mode will no longer disables automatically when Multi-Clients and Multi-Servers modes already enabled.
      • Added new docs and updated existing docs with related changes.
  • Maintenance:
    • Added official support for Python-3.10 legacies.
    • Added float value support to THREAD_TIMEOUT optional parameter.
    • Added info about dropped support for Python-3.6 legacies through announcement bar.
    • Added config.md file for Issue templates.
    • Added title to Issue templates.
  • Docs:
    • Added new Code Annotations
    • Added new icons to headings.
    • Added Advanced VideoGear usage example with CamGear backend.
Updates/Improvements
  • Setup.py:
    • Dropped support for Python-3.6 and below legacies.
    • Updated logging formatting.
    • Updated python_requires to >=3.7.
    • Bumped version to 0.2.5.
  • Helper:
    • Vidgear will now report current version on every run.
  • Docs:
    • Updated SSH tunneling docs context.
    • Excluded docs directory from CI envs.
    • Updated Zenodo badge and BibTeX entry.
    • Updated dark theme hue to 260.
    • Updated Admonitions.
    • Additional warnings against pushing PR against VidGear's testing branch only.
    • Updated code comments.
  • CI:
    • Removed support for Python-3.6 legacies from all workflows.
    • Updated NetGear's Exclusive Mode tests.
    • Added GStreamer Pipeline Mode tests.
  • Maintenance:
    • Updated Issue and PR templates.
    • Updated metadata.
Breaking Updates/Changes
  • Dropped support for Python-3.6 legacies from vidgear.
Bug-fixes
  • NetGear: Fixed bidirectional mode overriding multi-clients mode's data.
  • WriteGear:
    • Fixed wrongly defined ffmpeg_preheaders.
    • Fixed condition logic bugs.
    • Fixed UnboundLocalError bug.
  • Setup: Fixed uvicorn and aiortc dropped support for Python-3.6 legacies.
  • CI:
    • Fixed GitHub Actions interprets 3.10 as 3.1 if used without strings.
    • Fixed naming error in azure YAML.
  • Docs:
    • Fixed codecov badge URL in README.md
    • Fixed hyperlinks in README.
    • Fixed indentation and spacing.
    • Fixed typos and updated context.
    • Removed dead code.
  • Maintenance:
    • Removed depreciated condition checks.
Pull Requests

 

 

v0.2.4 (2021-12-05)

New Features
  • CamGear:
    • Added a new YT_backend Internal Class with YT-DLP backend:
      • Implemented YT_backend a new CamGear's Internal YT-DLP backend class for extracting metadata from Streaming URLs.
      • Added support for pipeling (live) video-frames from all yt-dlp supported streaming sites: https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md#supported-sites
      • Implemented algorithm from scratch for auto-extracting resolution specific streamable URLs for pipelineing.
      • Implemented logic for auto-calculating best and worst resolutions.
      • Added new ytv_metadata global parameter to CamGear for accessing video's metadata(such as duration, title, description) on-the-go.
      • ⚠️ Playlists are still unsupported.
  • WebGear_RTC:
    • Implemented a new easy way of defining Custom Streaming Class with suitable source(such as OpenCV):
      • Added new custom_stream attribute with WebGear_RTC options parameter that allows you to easily define your own Custom Streaming Class with suitable source(such as OpenCV).
      • This implementation supports repeated Auto-Reconnection or Auto-Refresh out-of-the-box.
      • This implementation is more user-friendly and easy to integrate within complex APIs.
      • This implementation requires at-least read() and stop() methods implemented within Custom Streaming Class, otherwise WebGear_RTC will throw ValueError.
      • This implementation supports all vidgear's VideoCapture APIs readily as input.
  • Maintenance:
    • Added new .gitignore for specifying intentionally untracked files to ignore
      • Added more files entries to .gitignore.
    • Added new .gitattributes to manage how Git reads line endings.
      • Enabled auto default behavior, in case people don't have core.autocrlf set.
      • Enforced LF line-endings for selective files types.
      • Added Binary data files that specifies they are not text, and git should not try to change them.
      • Added Language aware diff headers.
      • Added Linguist language overrides.
  • Docs:
    • Added bonus example to add real-time file audio encoding with VideoGear and Stabilizer class.
    • Added complete usage docs with new CamGear's Internal Class with YT-DLP backend.
    • Added instructions to extract video's metadata in CamGear.
    • Added donation link in page footer with bouncing heart animation through pure CSS.
    • Added info about critical changes in v0.2.4 and above installation through new announcement bar.
    • Added related usage docs for new WebGear_RTC custom streaming class.
    • Added changes for upgrading mkdocs-material from v7.x to newer v8.x.
    • Added outdated version warning block.
Updates/Improvements
  • CamGear:
    • Added is_livestream global YT_backend parameters.
    • Added default options for yt-dlp for extracting info_dict(metadata) of the video as a single JSON line.
    • Completely removed old logic for extracting streams using pafy.
    • Removed all dead code related to streamlink backend.
  • Setup.py:
    • Moved all API specific dependencies to extra_requires under the name "core". [PR #268 by @zpapakipos]
    • Added rule to replace GitHub heading links in description.
    • Updated extra_require dependencies.
    • Removed streamlink dependency.
    • Removed pafy dependency.
    • Removed pyzmq from latest_version group.
    • Updated SEO Keywords.
  • Docs:
    • Re-written pip and source installation docs.
    • Added warning for using -disable_force_termination flag for short duration videos.
    • Added permalink_title entry to mkdocs.yml.
    • Updated CamGear parameters.
    • Updated Admonitions with related information.
    • Updated Functional Block Diagram(gears_fbd.png) image.
    • Updated installation instructions.
    • Updated Advanced examples using WebGear_RTC's custom streaming class.
    • Updated code highlighting.
    • Updated zenodo badge.
    • Updated BibTex for project citation.
    • Replaced incorrect API parameter docs.
    • Updated WebGear_RTC parameters.
  • CI:
    • Updated CI tests for new WebGear_RTC custom streaming class.
    • Restored test_stream_mode CamGear test.
    • Updated Streaming Sites test links.
    • Added more tests cases.
  • Maintenance:
    • Updated spacing in logger formatting.
    • Renamed Asyncio Helper logger name.
    • Changed logging colors.
    • Updated logging messages.
Breaking Updates/Changes
  • Installation command with pip has been changed in v0.2.4:
    • The legacy pip install vidgear command now installs critical bare-minimum dependencies only. Therefore in order to automatically install all the API specific dependencies as previous versions, use pip install vidgear[core] command instead.
  • CamGear:
    • Removed streamlink backend support from stream_mode in favor of more reliable CamGear's Internal YT-DLP backend class for extracting metadata from Streaming URLs.
      • CamGear will raise ValueError if streaming site URL is unsupported by yt-dlp backend.
      • CamGear will raise ValueError if yt-dlp isn't installed and stream_mode is enabled.
    • Removed automatic enforcing of GStreamer backend for YouTube-livestreams and made it optional.
      • The CamGear will not raise ValueError if GStreamer support is missing in OpenCV backends.
  • WebGear_RTC:
    • Removed support for assigning Custom Media Server Class(inherited from aiortc's VideoStreamTrack) in WebGear_RTC through its config global parameter.
    • WebGear_RTC API will now throws ValueError if source parameter is NoneType as well as custom_stream attribute is undefined.
  • Helper:
    • Removed restore_levelnames method.
    • Removed youtube_url_validator helper method.
Bug-fixes
  • CamGear:
    • Fixed KeyError Bug for missing attributed in meta_data json in some streaming sites.
  • Helper:
    • Removed unused imports.
  • Docs:
    • Removed slugify from mkdocs which was causing invalid hyperlinks in docs.
    • Fixed GitHub hyperlinks in README.md.
    • Fixed hyperlink in announcement bar.
    • Fixed content tabs failing to work.
    • Fixed line-endings and usage example code.
    • Removed any pafy and streamlink references.
    • Fixed context and typos.
  • CI:
    • Fixed NameError bugs in WebGear_RTC CI test.
  • Maintenance:
    • Removed dead logger code causing Python's Built-in logging module to hide logs.
    • Removed unused logging import.
    • Updated code comments.
Pull Requests
New Contributors

 

 

v0.2.3 (2021-10-27)

New Features
  • CamGear:
    • Added support for 4K Streaming URLs.
  • Helper:
    • Implemented logging ColorFormatter string alignment.
      • Center aligned logging Level-name and Class-name.
      • Changed % formatting style with modern {.
      • Re-added asctime value to Formatter string.
      • Re-arranged parameter positions in Formatter string.
  • Maintenance:
    • Added new .gitignore for specifying intentionally untracked files to ignore
      • Added more files entries to .gitignore.
    • Added new .gitattributes to manage how Git reads line endings.
      • Enabled auto default behavior, in case people don't have core.autocrlf set.
      • Enforced LF line-endings for selective files types.
      • Added Binary data files that specifies they are not text, and git should not try to change them.
      • Added Language aware diff headers.
      • Added Linguist language overrides.
  • Docs:
    • Added new ScreenGear with WebGear_RTC API bonus example.
    • Added support for hl_lines argument for highlighting specific code lines.
    • Added drop-shadow effects for its slate theme to improve visibility.
Updates/Improvements
  • CamGear:
    • Replaced youtube-dl with yt-dlp as pafy backend for YouTube videos pipelining.
      • Implemented hack to trick pafy into assuming yt-dlp as youtube-dl.
      • Using sys.modules to present yt-dlp as youtube-dl.
      • yt-dlp python API functions exactly similar to youtube-dl.
      • Replaced youtube-dl dependency with yt-dlp.
      • Replaced youtube-dl imports with yt-dlp.
  • StreamGear:
    • Updated default stream_count internal dict key value to 1.
  • Maintenance:
    • Introduced python short-circuiting for handling logging logic.
    • Enabled logging for check_WriteAccess method in WriteGear, StreamGear and NetGear APIs.
  • Docs:
    • Added warning for ScreenGear outputting RGBA frames instead of default BGR frames with mss backend.
    • Added warnings for properly formatting output_params when assigning external audio-source in WriteGear.
    • Added depreciation notice for Python 3.6 legacies.
    • Restructured docs to make it more user-friendly.
    • Updated, Extended and Improved context.
    • Improved code comments.
    • Updated docs admonitions.
    • Updated Zenodo badge.
  • CI:
    • Migrated to new Codecov Uploader in Azure Pipelines.
    • Replaced env with export in ci_linux.yml.
    • Replaced bubkoo/needs-more-info@v1 with wow-actions/needs-more-info@v1.
    • Added codecov secret token through env variable.
    • Added wildcard to skip CI tests for doc(.md) files.
    • Added .md files to Codecov ignore list.
    • Update vidgear's banner image.
Breaking Updates/Changes
  • check_WriteAccess will now return as invalid path if writing directory does not exists. This will effect output file handling in WriteGear and StreamGear APIs.
Bug-fixes
  • StreamGear:
    • Fixed StreamGear Malformed URI Error with HLS Segments [PR #243 by @Vboivin]
      • Removed the extra '%' character from the naming convention for segment files.
      • Used stream_count internal dict variable to alter template for HLS segment filenames.
  • WriteGear:
    • Fixed bug in disable_force_termination logic which accidentally disables force termination.
  • WebGear_RTC:
    • Fixed name 'VideoStreamTrack' is not defined bug.
  • Setup.py:
    • Fixed TypeError bug.
    • Fixed invalid latest_version retrieval.
  • Helper:
    • Fixed check_WriteAccess failing to recognize correct permission for writing the output file on windows platform.
      • Implemented separate logic for Windows and *nix platforms.
      • Added new stat import.
      • Improved warnings and error handling.
      • Added logging parameter to check_WriteAccess.
    • Fixed bug in check_WriteAccess that throws OSError while handling URLs.
  • Docs:
    • Fixed bugs in WriteGear's Compression Mode with Live Audio Input example.
    • Fixed "drop-shadow" property via filter function conflicting with sidecard button.
      • Added new CSS classes for image, admonitions and code highlight in dark theme.
    • Several internal and external webpage links typos fixed.
    • Fixed several language typos.
  • CI:
    • Fixed Azure Pipeline coverage upload bugs.
    • Fixed random errors in CamGear stream_mode test.
  • Bash:
    • Removed the Windows carriage returns from the shell scripts to be able to execute them on Linux.
  • Fixed logging comments.
Pull Requests
New Contributors

 

 

v0.2.2 (2021-09-02)

New Features
  • StreamGear:
    • Native Support for Apple HLS Multi-Bitrate Streaming format:
      • Added support for new Apple HLS (HTTP Live Streaming) HTTP streaming format in StreamGear.
      • Implemented default workflow for auto-generating primary HLS stream of same resolution and framerate as source.
      • Added HLS support in Single-Source and Real-time Frames Modes.
      • Implemented inherit support for fmp4 and mpegts HLS segment types.
      • Added adequate default parameters required for trans-coding HLS streams.
      • Added native support for HLS live-streaming.
      • Added "hls" value to format parameter for easily selecting HLS format.
      • Added HLS support in -streams attribute for transcoding additional streams.
      • Added support for .m3u8 and .ts extensions in clear_prev_assets workflow.
      • Added validity check for .m3u8 extension in output when HLS format is used.
      • Separated DASH and HLS command handlers.
      • Created HLS format exclusive parameters.
      • Implemented -hls_base_url FFMpeg parameter support.
    • Added support for audio input from external device:
      • Implemented support for audio input from external device.
      • Users can now easily add audio device and decoder by formatting them as python list.
      • Modified -audio parameter to support list data type as value.
      • Modified validate_audio helper function to validate external audio devices.
    • Added -seg_duration to control segment duration.
  • NetGear:
    • New SSH Tunneling Mode for remote connection:
      • New SSH Tunneling Mode for connecting ZMQ sockets across machines via SSH tunneling.
      • Added new ssh_tunnel_mode attribute to enable ssh tunneling at provide address at server end only.
      • Implemented new check_open_port helper method to validate availability of host at given open port.
      • Added new attributes ssh_tunnel_keyfile and ssh_tunnel_pwd to easily validate ssh connection.
      • Extended this feature to be compatible with bi-directional mode and auto-reconnection.
      • Disabled support for exclusive Multi-Server and Multi-Clients modes.
      • Implemented logic to automatically enable paramiko support if installed.
      • Reserved port-47 for testing.
    • Additional colorspace support for input frames with Frame-Compression enabled:
      • Allowed to manually select colorspace on-the-fly with JPEG frame compression.
      • Updated jpeg_compression dict parameter to support colorspace string values.
      • Added all supported colorspace values by underline simplejpeg library.
      • Server enforced frame-compression colorspace on client(s).
      • Enable "BGR" colorspace by default.
      • Added Example for changing incoming frames colorspace with NetGear's Frame Compression.
      • Updated Frame Compression parameters in NetGear docs.
      • Updated existing CI tests to cover new frame compression functionality.
  • NetGear_Async:
    • New exclusive Bidirectional Mode for bidirectional data transfer:
      • NetGear_Async's first-ever exclusive Bidirectional mode with pure asyncio implementation.
      • Bidirectional mode is only available with User-defined Custom Source(i.e. source=None)
      • Added support for PAIR & REQ/REP bidirectional patterns for this mode.
      • Added powerful asyncio.Queues for handling user data and frames in real-time.
      • Implemented new transceive_data method to Transmit (in Recieve mode) and Receive (in Send mode) data in real-time.
      • Implemented terminate_connection internal asyncio method to safely terminate ZMQ connection and queues.
      • Added msgpack automatic compression encoding and decoding of data and frames in bidirectional mode.
      • Added support for np.ndarray video frames.
      • Added new bidirectional_mode attribute for enabling this mode.
      • Added 8-digit random alphanumeric id generator for each device.
      • NetGear_Async will throw RuntimeError if bidirectional mode is disabled at server or client but not both.
    • Added new disable_confirmation used to force disable termination confirmation from client in terminate_connection.
    • Added task_done() method after every get() call to gracefully terminate queues.
    • Added new secrets and string imports.
  • WebGear:
    • Updated JPEG Frame compression with simplejpeg:
      • Implemented JPEG compression algorithm for 4-5% performance boost at cost of minor loss in quality.
      • Utilized encode_jpeg and decode_jpeg methods to implement turbo-JPEG transcoding with simplejpeg.
      • Added new options to control JPEG frames quality, enable fastest dct, fast upsampling to boost performance.
      • Added new jpeg_compression, jpeg_compression_quality, jpeg_compression_fastdct, jpeg_compression_fastupsample attributes.
      • Enabled fast dct by default with JPEG frames at 90%.
      • Incremented default frame reduction to 25%.
      • Implemented automated grayscale colorspace frames handling.
      • Updated old and added new usage examples.
      • Dropped support for depreciated attributes from WebGear and added new attributes.
    • Added new WebGear Theme: (Checkout at https://github.com/abhiTronix/vidgear-vitals)
      • Added responsive image scaling according to screen aspect ratios.
      • Added responsive text scaling.
      • Added rounded border and auto-center to image tag.
      • Added bootstrap css properties to implement auto-scaling.
      • Removed old resize() hack.
      • Improved text spacing and weight.
      • Integrated toggle full-screen to new implementation.
      • Hide Scrollbar both in WebGear_RTC and WebGear Themes.
      • Beautify files syntax and updated files checksum.
      • Refactor files and removed redundant code.
      • Bumped theme version to v0.1.2.
  • WebGear_RTC:
    • Added native support for middlewares:
      • Added new global middleware variable for easily defining Middlewares as list.
      • Added validity check for Middlewares.
      • Added tests for middlewares support.
      • Added example for middlewares support.
      • Extended middlewares support to WebGear API too.
      • Added related imports.
    • Added new WebGear_RTC Theme: (Checkout at https://github.com/abhiTronix/vidgear-vitals)
      • Implemented new responsive video scaling according to screen aspect ratios.
      • Added bootstrap CSS properties to implement auto-scaling.
      • Removed old resize() hack.
      • Beautify files syntax and updated files checksum.
      • Refactored files and removed redundant code.
      • Bumped theme version to v0.1.2
  • Helper:
    • New automated interpolation selection for gears:
      • Implemented retrieve_best_interpolation method to automatically select best available interpolation within OpenCV.
      • Added support for this method in WebGear, WebGear_RTC and Stabilizer Classes/APIs.
      • Added new CI tests for this feature.
    • Implemented get_supported_demuxers method to get list of supported demuxers.
  • CI:
    • Added new no-response work-flow for stale issues.
    • Added new CI tests for SSH Tunneling Mode.
    • Added paramiko to CI dependencies.
    • Added support for "hls" format in existing CI tests.
    • Added new functions check_valid_m3u8 and extract_meta_video for validating HLS files.
    • Added new m3u8 dependency to CI workflows.
    • Added complete CI tests for NetGear_Async's new Bidirectional Mode:
      • Implemented new exclusive Custom_Generator class for testing bidirectional data dynamically on server-end.
      • Implemented new exclusive client_dataframe_iterator method for testing bidirectional data on client-end.
      • Implemented test_netgear_async_options and test_netgear_async_bidirectionalmode two new tests.
      • Added timeout value on server end in CI tests.
  • Setup.py:
    • Added new cython and msgpack dependency.
    • Added msgpack and msgpack_numpy to auto-install latest.
  • BASH:
    • Added new temp_m3u8 folder for generating M3U8 assets in CI tests.
  • Docs:
    • Added docs for new Apple HLS StreamGear format:
      • Added StreamGear HLS transcoding examples for both StreamGear modes.
      • Updated StreamGear parameters to w.r.t new HLS configurations.
      • Added open-sourced "Sintel" - project Durian Teaser Demo with StreamGear's HLS stream using Clappr and raw.githack.com.
      • Added new HLS chunks at https://github.com/abhiTronix/vidgear-docs-additionals for StreamGear
      • Added support for HLS video in Clappr within custom.js using HlsjsPlayback plugin.
      • Added support for Video Thumbnail preview for HLS video in Clappr within custom.js
      • Added hlsjs-playback.min.js JS script and suitable configuration for HlsjsPlayback plugin.
      • Added custom labels for quality levels selector in custom.js.
      • Added new docs content related to new Apple HLS format.
      • Updated DASH chunk folder at https://github.com/abhiTronix/vidgear-docs-additionals.
      • Added example for audio input support from external device in StreamGear.
      • Added steps for using -audio attribute on different OS platforms in StreamGear.
    • Added usage examples for NetGear_Async's Bidirectional Mode:
      • Added new Usage examples and Reference doc for NetGear_Async's Bidirectional Mode.
      • Added new image asset for NetGear_Async's Bidirectional Mode.
      • Added NetGear_Async's option parameter reference.
      • Updated NetGear_Async definition in docs.
      • Changed font size for Helper methods.
      • Renamed Bonus section to References in mkdocs.yml.
    • Added Gitter sidecard embed widget:
      • Imported gitter-sidecar script to main.html.
      • Updated custom.js to set global window option.
      • Updated Sidecard UI in custom.css.
    • Added bonus examples to help section:
      • Implemented a curated list of more advanced examples with unusual configuration for each API.
    • Added several new contents and updated context.
    • Added support for search suggestions, search highlighting and search sharing (i.e. deep linking)
    • Added more content to docs to make it more user-friendly.
    • Added warning that JPEG Frame-Compression is disabled with Custom Source in WebGear.
    • Added steps for identifying and specifying sound card on different OS platforms in WriteGear.
    • Added Zenodo DOI badge and its reference in BibTex citations.
    • Added extra.homepage parameter, which allows for setting a dedicated URL for site_url.
    • Added pymdownx.striphtml plugin for stripping comments.
    • Added complete docs for SSH Tunneling Mode.
    • Added complete docs for NetGear's SSH Tunneling Mode.
    • Added pip upgrade related docs.
    • Added docs for installing vidgear with only selective dependencies
    • Added new advance/experiment admonition with new background color.
    • Added new icons SVGs for advance and warning admonition.
    • Added new usage example and related information.
    • Added new image assets for ssh tunneling example.
    • Added new admonitions
    • Added new FAQs.
Updates/Improvements
  • VidGear Core:
    • New behavior to virtually isolate optional API specific dependencies by silencing ImportError on all VidGear's APIs import.
    • Implemented algorithm to cache all imports on startup but silence any ImportError on missing optional dependency.
    • Now ImportError will be raised only any certain API specific dependency is missing during given API's initialization.
    • New import_dependency_safe to imports specified dependency safely with importlib module.
    • Replaced all APIs imports with import_dependency_safe.
    • Added support for relative imports in import_dependency_safe.
    • Implemented error parameter to by default ImportError with a meaningful message if a dependency is missing, Otherwise if error = log a warning will be logged and on error = silent everything will be quit. But If a dependency is present, but older than specified, an error is raised if specified.
    • Implemented behavior that if a dependency is present, but older than min_version specified, an error is raised always.
    • Implemented custom_message to display custom message on error instead of default one.
    • Implemented separate import_core_dependency function to import and check for specified core dependency.
    • ImportError will be raised immediately if core dependency not found.
  • StreamGear:
    • Replaced depreciated -min_seg_duration flag with -seg_duration.
    • Removed redundant -re flag from RTFM.
    • Improved Live-Streaming performance by disabling SegmentTimline
    • Improved DASH assets detection for removal by using filename prefixes.
  • NetGear:
    • Replaced np.newaxis with np.expand_dims.
    • Replaced random module with secrets while generating system ID.
    • Update array indexing with np.copy.
  • NetGear_Async:
    • Improved custom source handling.
    • Removed deprecated loop parameter from asyncio methods.
    • Re-implemented skip_loop parameter in close() method.
    • run_until_complete will not used if skip_loop is enabled.
    • skip_loop now will create asyncio task instead and will enable disable_confirmation by default.
    • Replaced create_task with ensure_future to ensure backward compatibility with python-3.6 legacies.
    • Simplified code for transceive_data method.
  • WebGear_RTC:
    • Improved handling of failed ICE connection.
    • Made is_running variable globally available for internal use.
  • Helper:
    • Added 4320p resolution support to dimensions_to_resolutions method.
    • Implemented new delete_file_safe to safely delete files at given path.
    • Replaced os.remove calls with delete_file_safe.
    • Added support for filename prefixes in delete_ext_safe method.
    • Improved and simplified create_blank_frame functions frame channels detection.
    • Added logging parameter to capPropId function to forcefully discard any error(if required).
  • Setup.py:
  • Asyncio:
    • Changed asyncio.sleep value to 0.
      • The amount of time sleep is irrelevant; the only purpose await asyncio.sleep() serves is to force asyncio to suspend execution to the event loop, and give other tasks a chance to run. Also, await asyncio.sleep(0) will achieve the same effect. https://stackoverflow.com/a/55782965/10158117
  • License:
    • Dropped publication year range to avoid confusion. (Signed and Approved by @abhiTronix)
    • Updated Vidgear license's year of first publication of the work in accordance with US copyright notices defined by Title 17, Chapter 4(Visually perceptible copies): https://www.copyright.gov/title17/92chap4.html
    • Reflected changes in all copyright notices.
  • CI:
    • Updated macOS VM Image to latest in azure devops.
    • Updated VidGear Docs Deployer Workflow.
    • Updated WebGear_RTC CI tests.
    • Removed redundant code from CI tests.
    • Updated tests to increase coverage.
    • Enabled Helper tests for python 3.8+ legacies.
    • Enabled logging in validate_video method.
    • Added -hls_base_url to streamgear tests.
    • Update mpegdash dependency to 0.3.0-dev2 version in Appveyor.
    • Updated CI tests for new HLS support
    • Updated CI tests from scratch for new native HLS support in StreamGear.
    • Updated test patch for StreamGear.
    • Added exception for RunTimeErrors in NetGear CI tests.
    • Added more directories to Codecov ignore list.
    • Imported relative logger_handler for asyncio tests.
  • Docs:
    • Re-positioned few docs comments at bottom for easier detection during stripping.
    • Updated to new extra analytics parameter in Material Mkdocs.
    • Updated dark theme to dark orange.
    • Changed fonts => text: Muli & code: Fira Code
    • Updated fonts to Source Sans Pro.
    • Updated setup.py update-link for modules.
    • Re-added missing StreamGear Code docs.
    • Several minor tweaks and typos fixed.
    • Updated 404.html page.
    • Updated admonitions colors and beautified custom.css.
    • Replaced VideoGear & CamGear with OpenCV in CPU intensive examples.
    • Updated mkdocs.yml with new changes and URLs.
    • Moved FAQ examples to bonus examples.
    • Moved StreamGear primary modes to separate sections for better readability.
    • Implemented separate overview and usage example pages for StreamGear primary modes.
    • Improved StreamGear docs context and simplified language.
    • Renamed StreamGear overview page to introduction.
    • Re-written Threaded-Queue-Mode from scratch with elaborated functioning.
    • Replace Paypal with Liberpay in FUNDING.yml.
    • Updated FFmpeg Download links.
    • Reverted UI change in CSS.
    • Updated changelog.md and fixed clutter.
    • Updated README.md and mkdocs.yml with new additions
    • Updated context for CamGear example.
    • Restructured and added more content to docs.
    • Updated comments in source code.
    • Removed redundant data table tweaks from custom.css.
    • Re-aligned badges in README.md.
    • Beautify custom.css.
    • Updated mkdocs.yml.
    • Updated context and fixed typos.
    • Added missing helper methods in Reference.
    • Updated Admonitions.
    • Updates images assets.
    • Bumped CodeCov.
  • Logging:
    • Improved logging level-names.
    • Updated logging messages.
  • Minor tweaks to needs-more-info template.
  • Updated issue templates and labels.
  • Removed redundant imports.
Breaking Updates/Changes
  • Virtually isolated all API specific dependencies, Now ImportError for API-specific dependencies will be raised only when any of them is missing at API's initialization.
  • Renamed delete_safe to delete_ext_safe.
  • Dropped support for frame_jpeg_quality, frame_jpeg_optimize, frame_jpeg_progressive attributes from WebGear.
Bug-fixes
  • CamGear:
    • Hot-fix for Live Camera Streams:
      • Added new event flag to keep check on stream read.
      • Implemented event wait for read() to block it when source stream is busy.
      • Added and Linked THREAD_TIMEOUT with event wait timout.
      • Improved backward compatibility of new additions.
    • Enforced logging for YouTube live.
  • NetGear:
    • Fixed Bidirectional Video-Frame Transfer broken with frame-compression:
      • Fixed return_data interfering with return JSON-data in receive mode.
      • Fixed logic.
    • Fixed color-subsampling interfering with colorspace.
    • Patched external simplejpeg bug. Issue: https://gitlab.com/jfolz/simplejpeg/-/issues/11
      • Added np.squeeze to drop grayscale frame's 3rd dimension on Client's end.
    • Fixed bug that cause server end frame dimensions differ from client's end when frame compression enabled.
  • NetGear_Async:
    • Fixed bug related asyncio queue freezing on calling join().
    • Fixed ZMQ connection bugs in bidirectional mode.
    • Fixed several critical bugs in event loop handling.
    • Fixed several bugs in bidirectional mode implementation.
    • Fixed missing socket termination in both server and client end.
    • Fixed timeout parameter logic.
    • Fixed typos in error messages.
  • WebGear_RTC:
    • Fixed stream freezes after web-page reloading:
      • Implemented new algorithm to continue stream even when webpage is reloaded.
      • Inherit and modified next_timestamp VideoStreamTrack method for generating accurate timestamps.
      • Implemented reset_connections callable to reset all peer connections and recreate Video-Server timestamps. (Implemented by @kpetrykin)
      • Added close_connection endpoint in JavaScript to inform server page refreshing.(Thanks to @kpetrykin)
      • Added exclusive reset connection node /close_connection in routes.
      • Added reset() method to Video-Server class for manually resetting timestamp clock.
      • Added reset_enabled flag to keep check on reloads.
      • Fixed premature webpage auto-reloading.
      • Added additional related imports.
    • Fixed web-page reloading bug after stream ended:
      • Disable webpage reload behavior handling for Live broadcasting.
      • Disable reload CI test on Windows machines due to random failures.
      • Improved handling of failed ICE connection.
    • Fixed Assertion error bug:
      • Source must raise MediaStreamError when stream ends instead of returning None-type.
  • WebGear
    • Removed format specific OpenCV decoding and encoding support for WebGear.
  • Helper:
    • Regex bugs fixed:
      • New improved regex for discovering supported encoders in get_supported_vencoders.
      • Re-implemented check for extracting only valid output protocols in is_valid_url.
      • Minor tweaks for better regex compatibility.
    • Bugfix related to OpenCV import:
      • Bug fixed for OpenCV import comparison test failing with Legacy versions and throwing ImportError.
      • Replaced packaging.parse_version with more robust distutils.version.
    • Fixed bug with create_blank_frame that throws error with gray frames:
      • Implemented automatic output channel correction inside create_blank_frame function.
      • Extended automatic output channel correction support to asyncio package.
    • Implemented RTSP protocol validation as demuxer, since it's not a protocol but a demuxer.
    • Removed redundant logger_handler, mkdir_safe, retrieve_best_interpolation, capPropId helper functions from asyncio package. Relatively imported helper functions from non-asyncio package.
    • Removed unused aiohttp dependency.
    • Removed asctime formatting from logging.
  • StreamGear:
    • Fixed Multi-Bitrate HLS VOD streams:
      • Re-implemented complete workflow for Multi-Bitrate HLS VOD streams.
      • Extended support to both Single-Source and Real-time Frames Modes.
    • Fixed bugs with audio-video mapping.
    • Fixed master playlist not generating in output.
    • Fixed improper -seg_duration value resulting in broken pipeline.
    • Fixed expected aspect ratio not calculated correctly for additional streams.
    • Fixed stream not terminating when provided input from external audio device.
    • Fixed bugs related to external audio not mapped correctly in HLS format.
    • Fixed OPUS audio fragments not supported with MP4 video in HLS.
    • Fixed unsupported high audio bit-rate bug.
  • Setup.py:
    • Fixed latest_version returning incorrect version for some PYPI packages.
    • Removed latest_version variable support from simplejpeg.
    • Fixed streamlink only supporting requests==2.25.1 on Windows.
    • Removed all redundant dependencies like colorama, aiofiles, aiohttp.
    • Fixed typos in dependencies.
  • Setup.cfg:
    • Replaced dashes with underscores to remove warnings.
  • CI:
    • Replaced buggy starlette.TestClient with async-asgi-testclient in WebGear_RTC
    • Removed run() method and replaced with pure asyncio implementation.
    • Added new async-asgi-testclient CI dependency.
    • Fixed fake_picamera class logger calling vidgear imports prematurely before importing picamera class in tests.
      • Implemented new fake_picamera class logger inherently with logging module.
      • Moved sys.module logic for faking to init.py.
      • Added __init__.py to ignore in Codecov.
    • Fixed event loop closing prematurely while reloading:
      • Internally disabled suspending event loop while reloading.
    • Event Policy Loop patcher added for WebGear_RTC tests.
    • Fixed return_assets_path path bug.
    • Fixed typo in TimeoutError exception import.
    • Fixed eventloop is already closed bug.
    • Fixed eventloop bugs in Helper CI tests.
    • Fixed several minor bugs related to new CI tests.
    • Fixed bug in PiGear tests.
  • Docs:
    • Fixed 404 page does not work outside the site root with mkdocs.
    • Fixed markdown files comments not stripped when converted to HTML.
    • Fixed missing heading in VideoGear.
    • Typos in links and code comments fixed.
    • Several minor tweaks and typos fixed.
    • Fixed improper URLs/Hyperlinks and related typos.
    • Fixed typos in usage examples.
    • Fixed redundant properties in CSS.
    • Fixed bugs in mkdocs.yml.
    • Fixed docs contexts and typos.
    • Fixed stream.release() missing in docs.
    • Fixed several typos in code comments.
    • Removed dead code from docs.
  • Refactored Code and reduced redundancy.
  • Fixed shutdown in main.py.
  • Fixed logging comments.
Pull Requests
New Contributors

 

 

v0.2.1 (2021-04-25)

New Features
  • WebGear_RTC:
    • A new API that is similar to WeGear API in all aspects but utilizes WebRTC standard instead of Motion JPEG for streaming.
    • Now it is possible to share data and perform teleconferencing peer-to-peer, without requiring that the user install plugins or any other third-party software.
    • Added a flexible backend for aiortc - a python library for Web Real-Time Communication (WebRTC).
    • Integrated all functionality and parameters of WebGear into WebGear_RTC API.
    • Implemented JSON Response with a WebRTC Peer Connection of Video Server.
    • Added a internal RTC_VideoServer server on WebGear_RTC, a inherit-class to aiortc's VideoStreamTrack API.
    • New Standalone UI Default theme v0.1.1 for WebGear_RTC from scratch without using 3rd-party assets. (by @abhiTronix)
    • New custom.js and custom.css for custom responsive behavior.
    • Added WebRTC support to custom.js and ensured compatibility with WebGear_RTC.
    • Added example support for ICE framework and STUN protocol like WebRTC features to custom.js.
    • Added resize() function to custom.js to automatically adjust video & img tags for smaller screens.
    • Added WebGear_RTC support in main.py for easy access through terminal using --mode flag.
    • Integrated all WebGear_RTC enhancements to WebGear Themes.
    • Added CI test for WebGear_RTC.
    • Added complete docs for WebGear_RTC API.
    • Added bare-minimum as well as advanced examples usage code.
    • Added new theme images.
    • Added Reference and FAQs.
  • CamGear API:
    • New Improved Pure-Python Multiple-Threaded Implementation:
      • Optimized Threaded-Queue-Mode Performance. (PR by @bml1g12)
      • Replaced regular queue.full checks followed by sleep with implicit sleep with blocking queue.put.
      • Replaced regular queue.empty checks followed by queue.
      • Replaced nowait_get with a blocking queue.get natural empty check.
      • Up-to 2x performance boost than previous implementations.
    • New THREAD_TIMEOUT attribute to prevent deadlocks:
      • Added support for THREAD_TIMEOUT attribute to its options parameter.
      • Updated CI Tests and docs.
  • WriteGear API:
    • New more robust handling of default video-encoder in compression mode:
      • Implemented auto-switching of default video-encoder automatically based on availability.
      • API now selects Default encoder based on priority: "libx264" > "libx265" > "libxvid" > "mpeg4".
      • Added get_supported_vencoders Helper method to enumerate Supported Video Encoders.
      • Added common handler for -c:v and -vcodec flags.
  • NetGear API:
    • New Turbo-JPEG compression with simplejpeg
      • Implemented JPEG compression algorithm for 4-5% performance boost at cost of minor loss in quality.
      • Utilized encode_jpeg and decode_jpeg methods to implement turbo-JPEG transcoding with simplejpeg.
      • Added options to control JPEG frames quality, enable fastest dct, fast upsampling to boost performance.
      • Added new jpeg_compression, jpeg_compression_quality, jpeg_compression_fastdct, jpeg_compression_fastupsample attributes.
      • Enabled fast dct by default with JPEG frames at 90%.
      • Added Docs for JPEG Frame Compression.
  • WebGear API:
    • New modular and flexible configuration for Custom Sources:
      • Implemented more convenient approach for handling custom source configuration.
      • Added new config global variable for this new behavior.
      • Now None-type source parameter value is allowed for defining own custom sources.
      • Added new Example case and Updates Docs for this feature.
      • Added new CI Tests.
    • New Browser UI Updates:
      • New Standalone UI Default theme v0.1.0 for browser (by @abhiTronix)
      • Completely rewritten theme from scratch with only local resources.
      • New custom.js and custom.css for custom responsive behavior.
      • New sample glow effect with css.
      • New sample click to full-screen behavior with javascript.
      • Removed all third-party theme dependencies.
      • Update links to new github server abhiTronix/vidgear-vitals
      • Updated docs with new theme's screenshots.
    • Added enable_infinite_frames attribute for enabling infinite frames.
    • Added New modular and flexible configuration for Custom Sources.
    • Bumped WebGear Theme Version to v0.1.1.
    • Updated Docs and CI tests.
  • ScreenGear API:
    • Implemented Improved Pure-Python Multiple-Threaded like CamGear.
    • Added support for THREAD_TIMEOUT attribute to its options parameter.
  • StreamGear API:
    • Enabled pseudo live-streaming flag re for live content.
  • Docs:
    • Added new native docs versioning to mkdocs-material.
    • Added new examples and few visual tweaks.
    • Updated Stylesheet for versioning.
    • Added new DASH video chunks at https://github.com/abhiTronix/vidgear-docs-additionals for StreamGear and Stabilizer streams.
    • Added open-sourced "Tears of Steel" * project Mango Teaser video chunks.
    • Added open-sourced "Subspace Video Stabilization" http://web.cecs.pdx.edu/~fliu/project/subspace_stabilization/ video chunks.
    • Added support for DASH Video Thumbnail preview in Clappr within custom.js.
    • Added responsive clappr DASH player with bootstrap's embed-responsive.
    • Added new permalink icon and slugify to toc.
    • Added "back-to-top" button for easy navigation.
  • Helper:
    • New GitHub Mirror with latest Auto-built FFmpeg Static Binaries:
      • Replaced new GitHub Mirror abhiTronix/FFmpeg-Builds in helper.py
      • New CI maintained Auto-built FFmpeg Static Binaries.
      • Removed all 3rd-party and old links for better compatibility and Open-Source reliability.
      • Updated Related CI tests.
    • Added auto-font-scaling for create_blank_frame method.
    • Added c_name parameter to generate_webdata and download_webdata to specify class.
    • A more robust Implementation of Downloading Artifacts:
      • Added a custom HTTP TimeoutHTTPAdapter Adapter with a default timeout for all HTTP calls based on this GitHub comment.
      • Implemented http client and the send() method to ensure that the default timeout is used if a timeout argument isn't provided.
      • Implemented Requests sessionwith block to exit properly even if there are unhandled exceptions.
      • Add a retry strategy to custom TimeoutHTTPAdapter Adapter with max 3 retries and sleep(backoff_factor=1) between failed requests.
    • Added create_blank_frame method to create bland frames with suitable text.
  • [CI] Continuous Integration:
    • Added new fake frame generated for fake picamera class with numpy.
    • Added new create_bug parameter to fake picamera class for emulating various artificial bugs.
    • Added float/int instance check on time_delay for camgear and pigear.
    • Added EXIT_CODE to new timeout implementation for pytests to upload codecov report when no timeout.
    • Added auxiliary classes to fake picamera for facilitating the emulation.
    • Added new CI tests for PiGear Class for testing on all platforms.
    • Added shutdown() function to gracefully terminate WebGear_RTC API.
    • Added new coreutils brew dependency.
    • Added handler for variable check on exit and codecov upload.
    • Added is_running flag to WebGear_RTC to exit safely.
  • Setup:
    • New automated latest version retriever for packages:
      • Implemented new latest_version method to automatically retrieve latest version for packages.
      • Added Some Dependencies.
    • Added simplejpeg package for all platforms.
Updates/Improvements
  • Added exception for RunTimeErrors in NetGear CI tests.
  • WriteGear: Critical file write access checking method:
    • Added new check_WriteAccess Helper method.
    • Implemented a new robust algorithm to check if given directory has write-access.
    • Removed old behavior which gives irregular results.
  • Helper: Maintenance Updates
    • Added workaround for Python bug.
    • Added safe_mkdir to check_WriteAccess to automatically create non-existential parent folder in path.
    • Extended check_WriteAccess Patch to StreamGear.
    • Simplified check_WriteAccess to handle Windows envs easily.
    • Updated FFmpeg Static Download URL for WriteGear.
    • Implemented fallback option for auto-calculating bitrate from extracted audio sample-rate in validate_audio method.
  • Docs: General UI Updates
    • Updated Meta tags for og site and twitter cards.
    • Replaced Custom dark theme toggle with mkdocs-material's official Color palette toggle
    • Added example for external audio input and creating segmented MP4 video in WriteGear FAQ.
    • Added example for YouTube streaming with WriteGear.
    • Removed custom dark-material.js and header.html files from theme.
    • Added blogpost link for detailed information on Stabilizer Working.
    • Updated mkdocs.yml and custom.css configuration.
    • Remove old hack to resize clappr DASH player with css.
    • Updated Admonitions.
    • Improved docs contexts.
    • Updated CSS for version-selector-button.
    • Adjusted files to match new themes.
    • Updated welcome-bot message for typos.
    • Removed redundant FAQs from NetGear Docs.
    • Updated Assets Images.
    • Updated spacing.
  • CI:
    • Removed unused github.ref from yaml.
    • Updated OpenCV Bash Script for Linux envs.
    • Added timeout-minutes flag to github-actions workflow.
    • Added timeout flag to pytest.
    • Replaced Threaded Gears with OpenCV VideoCapture API.
    • Moved files and Removed redundant code.
    • Replaced grayscale frames with color frames for WebGear tests.
    • Updated pytest timeout value to 15mins.
    • Removed aiortc automated install on Windows platform within setup.py.
    • Added new timeout logic to continue to run on external timeout for GitHub Actions Workflows.
    • Removed unreliable old timeout solution from WebGear_RTC.
    • Removed timeout_decorator and asyncio_timeout dependencies for CI.
    • Removed WebGear_RTC API exception from codecov.
    • Implemented new fake picamera class to CI utils for emulating RPi Camera-Module Real-time capabilities.
    • Implemented new get_RTCPeer_payload method to receive WebGear_RTC peer payload.
    • Removed PiGear from Codecov exceptions.
    • Disable Frame Compression in few NetGear tests failing on frame matching.
    • Updated NetGear CI tests to support new attributes
    • Removed warnings and updated yaml
      • Added pytest.ini to address multiple warnings.
      • Updated azure workflow condition syntax.
    • Update mike settings for mkdocs versioning.
    • Updated codecov configurations.
    • Minor logging and docs updates.
    • Implemented pytest timeout for azure pipelines for macOS envs.
    • Added aiortc as external dependency in appveyor.yml.
    • Re-implemented WebGear_RTC improper offer-answer handshake in CI tests.
    • WebGear_RTC CI Updated with VideoTransformTrack to test stream play.
    • Implemented fake AttributeError for fake picamera class.
    • Updated PiGear CI tests to increment codecov.
    • Update Tests docs and other minor tweaks to increase overall coverage.
    • Enabled debugging and disabled exit 1 on error in azure pipeline.
    • Removed redundant benchmark tests.
  • Helper: Added missing RTSP URL scheme to is_valid_url method.
  • NetGear_Async: Added fix for uvloop only supporting python>=3.7 legacies.
  • Extended WebGear's Video-Handler scope to https.
  • CI: Remove all redundant 32-bit Tests from Appveyor:
  • Setup: Removed latest_version behavior from some packages.
  • NetGear_Async: Revised logic for handling uvloop for all platforms and legacies.
  • Setup: Updated logic to install uvloop-"v0.14.0" for python-3.6 legacies.
  • Removed any redundant code from webgear.
  • StreamGear:
    • Replaced Ordinary dict with Ordered Dict to use move_to_end method.
    • Moved external audio input to output parameters dict.
    • Added additional imports.
    • Updated docs to reflect changes.
  • Numerous Updates to Readme and mkdocs.yml.
  • Updated font to FONT_HERSHEY_SCRIPT_COMPLEX and enabled logging in create_blank_frame.
  • Separated channels for downloading and storing theme files for WebGear and WebGear_RTC APIs.
  • Removed logging condition to always inform user in a event of FFmpeg binary download failure.
  • WebGear_RTC:
    • Improved auto internal termination.
    • More Performance updates through setCodecPreferences.
    • Moved default Video RTC video launcher to __offer.
  • NetGear_Async: Added timeout to client in CI tests.
  • Reimplemented and updated changelog.md.
  • Updated code comments.
  • Setup: Updated keywords and classifiers.
  • Bumped codecov.
Breaking Updates/Changes
  • WriteGear will automatically switch video encoder to default if specified encoder not found.
  • WriteGear will throw RuntimeError if no suitable default encoder found!
  • Removed format specific OpenCV decoding and encoding support for NetGear.
  • Dropped support for compression_format, compression_param attributes from NetGear.
  • Non-existent parent folder in output_filename value will no longer be considered as invalid in StreamGear and WriteGear APIs.
  • None-type source parameter value is allowed for WebGear and NetGear_Async for defining custom sources.
Bug-fixes
  • CamGear: Fixed F821 undefined name 'queue' bug.
  • NetGear_Async: Fixed source parameter missing None as default value.
  • Fixed uvloops only supporting python>=3.7 in NetGear_Async.
  • Helper:
  • Docs:
    • Invalid site_url bug patched in mkdocs.yml
    • Remove redundant mike theme support and its files.
    • Fixed video not centered when DASH video in fullscreen mode with clappr.
    • Fixed Incompatible new mkdocs-docs theme.
    • Fixed missing hyperlinks.
  • CI:
    • Fixed NetGear Address bug
    • Fixed bugs related to termination in WebGear_RTC.
    • Fixed random CI test failures and code cleanup.
    • Fixed string formating bug in Helper.py.
    • Fixed F821 undefined name bugs in WebGear_RTC tests.
    • NetGear_Async Tests fixes.
    • Fixed F821 undefined name bugs.
    • Fixed typo bugs in main.py.
    • Fixed Relative import bug in PiGear.
    • Fixed regex bug in warning filter.
    • Fixed WebGear_RTC frozen threads on exit.
    • Fixed bugs in codecov bash uploader setting for azure pipelines.
    • Fixed False-positive picamera import due to improper sys.module settings.
    • Fixed Frozen Threads on exit in WebGear_RTC API.
    • Fixed deploy error in VidGear Docs Deployer workflow
    • Fixed low timeout bug.
    • Fixed bugs in PiGear tests.
    • Patched F821 undefined name bug.
  • StreamGear:
    • Fixed StreamGear throwing Picture size 0x0 is invalid bug with external audio.
    • Fixed default input framerate value getting discarded in Real-time Frame Mode.
    • Fixed internal list-formatting bug.
  • Fixed E999 SyntaxError bug in main.py.
  • Fixed Typo in bash script.
  • Fixed WebGear freeze on reloading bug.
  • Fixed anomalies in install_opencv bash script.
  • Helper: Bug Fixed in download_ffmpeg_binaries method.
  • Helper: Fixed OSError bug in check_WriteAccess method.
  • Helper: Fixed Input Audio stream bitrate test failing to detect audio-bitrate in certain videos with validate_audio method.
  • Fixed bugs in requests module's function arguments.
  • Fixed None-type stream bug in WebGear.
  • Fixed random crashes in WebGear.
  • Fixed numerous CI test bugs.
  • Fixed several typos.
Pull Requests
New Contributors

 

 

v0.2.0 (2021-01-01)

New Features
  • CamGear API:
    • Support for various Live-Video-Streaming services:
      • Added seamless support for live video streaming sites like Twitch, LiveStream, Dailymotion etc.
      • Implemented flexible framework around streamlink python library with easy control over parameters and quality.
      • Stream Mode can now automatically detects whether source belong to YouTube or elsewhere, and handles it with appropriate API.
    • Re-implemented YouTube URLs Handler:
      • Re-implemented CamGear's YouTube URLs Handler completely from scratch.
      • New Robust Logic to flexibly handing video and video-audio streams.
      • Intelligent stream selector for selecting best possible stream compatible with OpenCV.
      • Added support for selecting stream qualities and parameters.
      • Implemented new get_supported_quality helper method for handling specified qualities
      • Fixed Live-Stream URLs not supported by OpenCV's Videocapture and its FFmpeg.
    • Added additional STREAM_QUALITY and STREAM_PARAMS attributes.
  • ScreenGear API:
    • Multiple Backends Support:
      • Added new multiple backend support with new pyscreenshot python library.
      • Made pyscreenshot the default API for ScreenGear, replaces mss.
      • Added new backend parameter for this feature while retaining previous behavior.
      • Added native automated RGB to BGR conversion for default PIL backend.
      • Kept support for old mss for old compatibility and multi-screen support.
      • Added native dimensional support for multi-screen.
      • Added support all input from all multiple screens.
      • Updated ScreenGear Docs.
      • Updated ScreenGear CI tests.
  • StreamGear API:
    • Changed default behaviour to support complete video transcoding.
    • Added -livestream attribute to support live-streaming.
    • Added additional parameters for -livestream attribute functionality.
    • Updated StreamGear Tests.
    • Updated StreamGear docs.
  • Stabilizer Class:
    • New Robust Error Handling with Blank Frames:
      • Elegantly handles all crashes due to Empty/Blank/Dark frames.
      • Stabilizer throws Warning with this new behavior instead of crashing.
      • Updated CI test for this feature.
  • Docs:
    • Automated Docs Versioning:
      • Implemented Docs versioning through mike API.
      • Separate new workflow steps to handle different versions.
      • Updated docs deploy worflow to support release and dev builds.
      • Added automatic version extraction from github events.
      • Added version-select.js and version-select.css files.
    • Toggleable Dark-White Docs Support:
      • Toggle-button to easily switch dark, white and preferred theme.
      • New Updated Assets for dark backgrounds
      • New css, js files/content to implement this behavior.
      • New material icons for button.
      • Updated scheme to slate in mkdocs.yml.
    • New Theme and assets:
      • New purple theme with dark-purple accent color.
      • New images assets with updated transparent background.
      • Support for both dark and white theme.
      • Increased rebufferingGoal for dash videos.
      • New updated custom 404 page for docs.
    • Issue and PR automated-bots changes
      • New need_info.yml YAML Workflow.
      • New needs-more-info.yml Request-Info template.
      • Replaced Request-Info templates.
      • Improved PR and Issue welcome formatting.
    • Added custom HTML pages.
    • Added show_root_heading flag to disable headings in References.
    • Added new inserAfter function to version-select.js.
    • Adjusted hue for dark-theme for better contrast.
    • New usage examples and FAQs.
    • Added gitmoji for commits.
  • Continuous Integration:
    • Maintenance Updates:
      • Added support for new VIDGEAR_LOGFILE environment variable in Travis CI.
      • Added missing CI tests.
      • Added logging for helper functions.
    • Azure-Pipeline workflow for MacOS envs
      • Added Azure-Pipeline Workflow for testing MacOS environment.
      • Added codecov support.
    • GitHub Actions workflow for Linux envs
      • Added GitHub Action work-flow for testing Linux environment.
    • New YAML to implement GitHub Action workflow for python 3.6, 3.7, 3,8 & 3.9 matrices.
    • Added Upload coverage to Codecov GitHub Action workflow.
    • New codecov-bash uploader for Azure Pipelines.
  • Logging:
    • Added file support
      • Added VIDGEAR_LOGFILE environment variable to manually add file/dir path.
      • Reworked logger_handler() Helper methods (in asyncio too).
      • Added new formatter and Filehandler for handling logger files.
    • Added restore_levelnames auxiliary method for restoring logging levelnames.
  • Added auto version extraction from package version.py in setup.py.
Updates/Improvements
  • Added missing Lazy-pirate auto-reconnection support for Multi-Servers and Multi-Clients Mode in NetGear API.
  • Added new FFmpeg test path to Bash-Script and updated README broken links.
  • Asset Cleanup:
    • Removed all third-party javascripts from projects.
    • Linked all third-party javascript directly.
    • Cleaned up necessary code from CSS and JS files.
    • Removed any copyrighted material or links.
  • Rewritten Docs from scratch:
    • Improved complete docs formatting.
    • Simplified language for easier understanding.
    • Fixed mkdocstrings showing root headings.
    • Included all APIs methods to mkdocstrings docs.
    • Removed unnecessary information from docs.
    • Corrected Spelling and typos.
    • Fixed context and grammar.
    • Removed motivation.md.
    • Renamed many terms.
    • Fixed hyper-links.
    • Reformatted missing or improper information.
    • Fixed context and spellings in Docs files.
    • Simplified language for easy understanding.
    • Updated image sizes for better visibility.
  • Bash Script: Updated to Latest OpenCV Binaries version and related changes
  • Docs: Moved version-selector to header and changed default to alias.
  • Docs: Updated deploy_docs.yml for releasing dev, stable, and release versions.
  • Re-implemented overridden material theme.
  • Updated docs with all new additions and examples.
  • CamGear: CI Stream Mode test updated.
  • Updated ReadMe.md badges.
  • Updated CI tests.
  • Updated setup.py with new features.
  • Updated contributing.md and ReadMe.md.
  • Updated OpenCV version to 4.5.1-dev in bash scripts
  • Updated changelog.md.
  • Moved WebGear API to Streaming Gears.
  • Bumped Codecov.
  • UI changes to version-select.js
  • Docs: Retitle the versions and mkdocs.yml formatting updated.
  • Docs: Version Selector UI reworked and other minor changes.
Breaking Updates/Changes
  • y_tube parameter renamed as stream_mode in CamGear API!
  • Removed Travis support and travis.yml deleted.
Bug-fixes
  • Fixed StreamGear API Limited Segments Bug
  • Fixed Missing links in docs and bump up version.
  • CI: Fixed Appveyor need newer VM image to support Python 3.9.x matrix.
  • ScreenGear BugFix: Fixed Error Handling and updated CI Tests.
  • Fixed improper mkdocs.yml variables.
  • Fixed GStreamer plugin support in bash scripts.
  • Fixed typos in YAMLs and docs.
  • Docs: Fixed Docs Deployer YAML bug for CI envs.
  • Fixed wrong import in YAML.
  • Fixed visible hyperlink on hover in dark-toggle button.
  • Docs: Deployer YAML bug fixed.
  • Docs YAML: issue jimporter/mike#33 patched and fixed fetch-depth=0.
  • Docs: version-select.js bug fixed.
  • Docs: UI Bugs Fixed.
  • CI: Codecov bugfixes.
  • Azure-Pipelines Codecov BugFixes.
  • Fixed version.json not detecting properly in version-select.js.
  • Fixed images not centered inside <figure> tag.
  • Fixed Asset Colors.
  • Fixed failing CI tests.
  • Fixed Several logging bugs.
Pull Requests

 

 

v0.1.9 (2020-08-31)

New Features
  • StreamGear API:
    • New API that automates transcoding workflow for generating Ultra-Low Latency, High-Quality, Dynamic & Adaptive Streaming Formats.
    • Implemented multi-platform , standalone, highly extensible and flexible wrapper around FFmpeg for generating chunked-encoded media segments of the media, and easily accessing almost all of its parameters.
    • API automatically transcodes videos/audio files & real-time frames into a sequence of multiple smaller chunks/segments and also creates a Manifest file.
    • Added initial support for MPEG-DASH (Dynamic Adaptive Streaming over HTTP, ISO/IEC 23009-1).
    • Constructed default behavior in StreamGear, for auto-creating a Primary Stream of same resolution and framerate as source.
    • Added TQDM progress bar in non-debugged output for visual representation of internal processes.
    • Implemented several internal methods for preprocessing FFmpeg and internal parameters for producing streams.
    • Several standalone internal checks to ensure robust performance.
    • New terminate() function to terminate StremGear Safely.
    • New StreamGear Dual Modes of Operation:
      • Implemented Single-Source and Real-time Frames like independent Transcoding Modes.
      • Linked -video_source attribute for activating these modes
      • Single-Source Mode, transcodes entire video/audio file (as opposed to frames by frame) into a sequence of multiple smaller segments for streaming
      • Real-time Frames Mode, directly transcodes video-frames (as opposed to a entire file), into a sequence of multiple smaller segments for streaming
      • Added separate functions, stream() for Real-time Frame Mode and transcode_source() for Single-Source Mode for easy transcoding.
      • Included auto-colorspace detection and RGB Mode like features (extracted from WriteGear), into StreamGear.
    • New StreamGear Parameters:
      • Developed several new parameters such as:
        • output: handles assets directory
        • formats: handles adaptive HTTP streaming format.
        • custom_ffmpeg: handles custom FFmpeg location.
        • stream_params: handles internal and FFmpeg parameter seamlessly.
        • logging: turns logging on or off.
      • New stream_params parameter allows us to exploit almost all FFmpeg parameters and flexibly change its internal settings, and seamlessly generating high-quality streams with its attributes:
        • -streams (list of dictionaries) for building additional streams with -resolution, -video_bitrate & -framerate like sub-attributes.
        • -audio for specifying external audio.
        • -video_source for specifying Single-Source Mode source.
        • -input_framerate for handling input framerate in Real-time Frames Mode.
        • -bpp attribute for handling bits-per-pixels used to auto-calculate video-bitrate.
        • -gop to manually specify GOP length.
        • -ffmpeg_download_path to handle custom FFmpeg download path on windows.
        • -clear_prev_assets to remove any previous copies of SteamGear Assets.
    • New StreamGear docs, MPEG-DASH demo, and recommended DASH players list:
      • Added new StreamGear docs, usage examples, parameters, references, new FAQs.
      • Added Several StreamGear usage examples w.r.t Mode of Operation.
      • Implemented Clappr based on Shaka-Player, as Demo Player.
      • Added Adaptive-dimensional behavior for Demo-player, purely in css.
      • Hosted StreamGear generated DASH chunks on GitHub and served with raw.githack.com.
      • Introduced variable quality level-selector plugin for Clapper Player.
      • Provide various required javascripts and implemented additional functionality for player in extra.js.
      • Recommended tested Online, Command-line and GUI Adaptive Stream players.
      • Implemented separate FFmpeg installation doc for StreamGear API.
      • Reduced rebufferingGoal for faster response.
    • New StreamGear CI tests:
      • Added IO and API initialization CI tests for its Modes.
      • Added various mode Streaming check CI tests.
  • NetGear_Async API:
    • Added new send_terminate_signal internal method.
    • Added WindowsSelectorEventLoopPolicy() for windows 3.8+ envs.
    • Moved Client auto-termination to separate method.
    • Implemented graceful termination with signal API on UNIX machines.
    • Added new timeout attribute for controlling Timeout in Connections.
    • Added missing termination optimizer (linger=0) flag.
    • Several ZMQ Optimizer Flags added to boost performance.
  • WriteGear API:
    • Added support for adding duplicate FFmpeg parameters to output_params:
      • Added new -clones attribute in output_params parameter for handing this behavior..
      • Support to pass FFmpeg parameters as list, while maintaining the exact order it was specified.
      • Built support for zmq.REQ/zmq.REP and zmq.PUB/zmq.SUB patterns in this mode.
      • Added new CI tests debugging this behavior.
      • Updated docs accordingly.
    • Added support for Networks URLs in Compression Mode:
      • output_filename parameter supports Networks URLs in compression modes only
      • Added automated handling of non path/file Networks URLs as input.
      • Implemented new is_valid_url helper method to easily validate assigned URLs value.
      • Validates whether the given URL value has scheme/protocol supported by assigned/installed ffmpeg or not.
      • WriteGear will throw ValueError if -output_filename is not supported.
      • Added related CI tests and docs.
    • Added disable_force_termination attribute in WriteGear to disable force-termination.
  • NetGear API:
    • Added option to completely disable Native Frame-Compression:
      • Checks if any Incorrect/Invalid value is assigned on compression_format attribute.
      • Completely disables Native Frame-Compression.
      • Updated docs accordingly.
  • CamGear API:
    • Added new and robust regex for identifying YouTube URLs.
    • Moved youtube_url_validator to Helper.
  • New helper.py methods:
    • Added validate_video function to validate video_source.
    • Added extract_time Extract time from give string value.
    • Added get_video_bitrate to calculate video birate from resolution, framerate, bits-per-pixels values.
    • Added delete_safe to safely delete files of given extension.
    • Added validate_audio to validate audio source.
    • Added new Helper CI tests.
      • Added new check_valid_mpd function to test MPD files validity.
      • Added mpegdash library to CI requirements.
  • Deployed New Docs Upgrades:
    • Added new assets like images, gifs, custom scripts, javascripts fonts etc. for achieving better visual graphics in docs.
    • Added clappr.min.js, dash-shaka-playback.js, clappr-level-selector.min.js third-party javascripts locally.
    • Extended Overview docs Hyperlinks to include all major sub-pages (such as Usage Examples, Reference, FAQs etc.).
    • Replaced GIF with interactive MPEG-DASH Video Example in Stabilizer Docs.
    • Added new pymdownx.keys to replace [Ctrl+C]/[⌘+C] formats.
    • Added new custom.css stylescripts variables for fluid animations in docs.
    • Overridden announce bar and added donation button.
    • Lossless WEBP compressed all PNG assets for faster loading.
    • Enabled lazy-loading for GIFS and Images for performance.
    • Reimplemented Admonitions contexts and added new ones.
    • Added StreamGear and its different modes Docs Assets.
    • Added patch for images & unicodes for PiP flavored markdown in setup.py.
  • Added Request Info and Welcome GitHub Apps to automate PR and issue workflow
    • Added new config.yml for customizations.
    • Added various suitable configurations.
  • Added new -clones attribute to handle FFmpeg parameter clones in StreamGear and WriteGear API.
  • Added new Video-only and Audio-Only sources in bash script.
  • Added new paths in bash script for storing StreamGear & WriteGear assets temporarily.
Updates/Improvements
  • Added patch for NotImplementedError in NetGear_Async API on Windows 3.8+ envs.
  • Check for valid output file extension according to format selected in StreamGear.
  • Completed migration to travis.com.
  • Created new temp_write temp directory for WriteGear Assets in bash script.
  • Deleted old Redundant assets and added new ones.
  • Employed isort library to sort and group imports in Vidgear APIs.
  • Enabled exception for list, tuple, int, float in WriteGear API's output_params dict.
  • Enabled missing support for frame-compression in its primary Receive Mode.
  • Enforced pixel formats for streams.
  • Improved check for valid system path detection in WriteGear API.
  • Overrided pytest-asyncio fixture in NetGear_Async API.
  • Quoted Gear Headline for understanding each gear easily.
  • Re-Positioned Gear's banner images in overview for better readability.
  • Reduced redundant try-except blocks in NetGear Async.
  • Reformatted and Simplified Docs context.
  • Reimplemented return_testvideo_path CI function with variable streams.
  • Reimplemented skip_loop in NetGear_Async to fix asyncio.CancelledError.
  • Reimplemented buggy audio handler in StreamGear.
  • Reimplemented images with <figure> and <figurecaption> like tags.
  • Removed Python < 3.8 condition from all CI tests.
  • Removed or Grouped redundant code for increasing codecov.
  • Removed redundant code and simplified algorithmic complexities in Gears.
  • Replaced ;nbsp with ;thinsp and ;emsp.
  • Replaced IOError with more reliable RuntimeError in StreamGear Pipelines.
  • Replaced del with pop in dicts.
  • Replaced all Netgear CI tests with more reliable try-except-final blocks.
  • Replaced simple lists with pymdownx.tasklist.
  • Replaced subprocess call() with run() for better error handling in execute_ffmpeg_cmd function.
  • Resized over-sized docs images.
  • Simplified delete_safe Helper function.
  • Simplified default audio-bitrate logic in StreamGear
  • Updated CI tests and cleared redundant code from NetGear_Async API.
  • Updated CI with new tests and Bumped Codecov.
  • Updated Issue and PR templates.
  • Updated Licenses for new files and shrink images dimensions.
  • Updated Missing Helpful tips and increased logging.
  • Updated PR guidelines for more clarity.
  • Updated WebGear examples addresses from 0.0.0.0 to localhost.
  • Updated WriteGear and StreamGear CI tests for not supporting temp directory.
  • Updated README.md and changelog.md with new changes.
  • Updated check_output and added force_retrieve_stderr support to **kwargs to extract stderr output even on FFmpeg error.
  • Updated dicts2args to support internal repeated coreX FFmpeg parameters for StreamGear.
  • Updated mkdocs.yml, changelog.md and README.md with latest changes.
  • Updated validate_audio Helper function will now retrieve audio-bitrate for validation.
  • Updated buggy mpegdash dependency with custom dev fork for Windows machines.
  • Updated core parameters for audio handling.
  • Updated logging for debugging selected eventloops in NetGear_Async API.
  • Updated termination linger to zero at Server's end.
Breaking Updates/Changes
  • Changed Webgear API default address to localhost for cross-compatibility between different platforms.
  • In Netgear_Async API, source value can now be NoneType for a custom frame-generator at Server-end only.
  • Temp (such as /tmp in linux) is now not a valid directory for WriteGear & StreamGear API outputs.
  • Moved vidgear docs assets (i.e images, gifs, javascripts and stylescripts) to override directory.
Bug-fixes
  • Added workaround for system path not handle correctly.
  • Fixed Bug: URL Audio format not being handled properly.
  • Fixed Critical Bug in NetGear_Async throwing ValueError with None-type Source.
  • Fixed Critical StreamGear Bug: FFmpeg pipeline terminating prematurely in Single-Source Mode.
  • Fixed Critical external audio handler bug: moved audio-input to input_parameters.
  • Fixed Frozen-threads bug in CI tests.
  • Fixed Mkdocs only accepting Relative paths.
  • Fixed OSError in WriteGear's compression mode.
  • Fixed StreamGear CI bugs for Windows and CI envs.
  • Fixed Typos and Indentation bugs in NetGear API.
  • Fixed ZMQ throwing error on termination if all max-tries exhausted.
  • Fixed NameError bug in NetGear API and CI tests.
  • Fixed TimeoutError bug in NetGear_Async CI tests.
  • Fixed get_valid_ffmpeg_path throwing TypeError with non-string values.
  • Fixed broken links in docs.
  • Fixed critical duplicate logging bug.
  • Fixed default gop value not handle correctly.
  • Fixed handling of incorrect paths detection.
  • Fixed incorrect definitions in NetGear_Async.
  • Fixed left-over attribute bug in WriteGear.
  • Fixed logic and indentation bugs in CI tests.
  • Fixed logic for handling output parameters in WriteGear API.
  • Fixed missing definitions and logic bug in StreamGear.
  • Fixed missing import and incorrect CI definitions.
  • Fixed missing source dimensions from extract_resolutions output in StreamGear API.
  • Fixed missing support for compression parameters in Multi-Clients Mode.
  • Fixed round off error in FPS.
  • Fixed several CI bugs and updated extract_resolutions method.
  • Fixed several bugs from CI Bidirectional Mode tests.
  • Fixed several typos in docs usage examples.
  • Fixed various AttributeError with wrong attribute names and definition in CI Helper functions.
  • Fixed wrong and missing definitions in docs.
  • Fixed wrong logic for extracting OpenCV frames.
  • Fixed wrong type bug in StreamGear API.
  • Fixed wrong type error bug in WriteGear API.
  • Fixed wrong variable assignments bug in WriteGear API.
  • Fixes to CLI tests and missing docs imports.
  • Many minor typos and wrong definitions.
Pull Requests

 

 

v0.1.8 (2020-06-12)

New Features
  • NetGear API:
    • Multiple Clients support:
      • Implemented support for handling any number of Clients simultaneously with a single Server in this mode.
      • Added new multiclient_mode attribute for enabling this mode easily.
      • Built support for zmq.REQ/zmq.REP and zmq.PUB/zmq.SUB patterns in this mode.
      • Implemented ability to receive data from all Client(s) along with frames with zmq.REQ/zmq.REP pattern only.
      • Updated related CI tests
    • Support for robust Lazy Pirate pattern(auto-reconnection) in NetGear API for both server and client ends:
      • Implemented a algorithm where NetGear rather than doing a blocking receive, will now:
        • Poll the socket and receive from it only when it's sure a reply has arrived.
        • Attempt to reconnect, if no reply has arrived within a timeout period.
        • Abandon the connection if there is still no reply after several requests.
      • Implemented its default support for REQ/REP and PAIR messaging patterns internally.
      • Added new max_retries and request_timeout(in seconds) for handling polling.
      • Added DONTWAIT flag for interruption-free data receiving.
      • Both Server and Client can now reconnect even after a premature termination.
    • Performance Updates:
      • Added default Frame Compression support for Bidirectional frame transmission in Bidirectional mode.
      • Added support for Reducer() function in Helper.py to aid reducing frame-size on-the-go for more performance.
      • Added small delay in recv() function at client's end to reduce system load.
      • Reworked and Optimized NetGear termination, and also removed/changed redundant definitions and flags.
  • Docs: Migration to Mkdocs
    • Implemented a beautiful, static documentation site based on MkDocs which will then be hosted on GitHub Pages.
    • Crafted base mkdocs with third-party elegant & simplistic mkdocs-material theme.
    • Implemented new mkdocs.yml for Mkdocs with relevant data.
    • Added new docs folder to handle markdown pages and its assets.
    • Added new Markdown pages(.md) to docs folder, which are carefully crafted documents - [x] based on previous Wiki's docs, and some completely new additions.
    • Added navigation under tabs for easily accessing each document.
    • New Assets:
    • Added Required Plugins and Extensions:
      • Added support for all pymarkdown-extensions.
      • Added support for some important admonition, attr_list, codehilite, def_list, footnotes, meta, and toc like Mkdocs extensions.
      • Enabled search, minify and git-revision-date-localized plugins support.
      • Added various VidGear's social links to yaml.
      • Added support for en (English) language.
    • Auto-Build API Reference with mkdocstrings:
      • Added support for mkdocstrings plugin for auto-building each VidGear's API references.
      • Added python handler for parsing python source-code to mkdocstrings.
    • Auto-Deploy Docs with GitHub Actions:
      • Implemented Automated Docs Deployment on gh-pages through GitHub Actions workflow.
      • Added new workflow yaml with minimal configuration for automated docs deployment.
      • Added all required python dependencies and environment for this workflow.
      • Added master branch on Ubuntu machine to build matrix.
Updates/Improvements
  • Added in-built support for bidirectional frames(NDarray) transfer in Bidirectional mode.
  • Added support for User-Defined compression params in Bidirectional frames transfer.
  • Added workaround for address already in use bug at client's end.
  • Unified Bidirectional and Multi-Clients mode for client's return data transmission.
  • Replaced ValueError with more suitable RuntimeError.
  • Updated logging for better readability.
  • Added CI test for Multi-Clients mode.
  • Reformatted and grouped imports in VidGear.
  • Added Reducer Helper function CI test.
  • Added Reliability tests for both Server and Client end.
  • Disabled reliable reconnection for Multi-Clients mode.
  • Replaced os.devnull with suprocess's inbuilt function.
  • Updated README.md, Issue and PR templates with new information and updates.
  • Moved changelog.md to /docs and updated contribution guidelines.
  • Improved source-code docs for compatibility with mkdocstrings.
  • Added additional dependency mkdocs-exclude, for excluding files from Mkdocs builds.
  • Updated license and compressed images/diagrams.
  • Added new CI tests and Bumped Codecov.
  • Changed YouTube video URL for CI tests to Creative Commons(CC) video.
  • Removed redundant code.
Breaking Updates/Changes
  • VidGear Docs moved to GitHub Pages, Now Available at https://abhitronix.github.io/vidgear.
  • Removed filter attribute from options parameter in NetGear API.
  • Removed force_terminate parameter support from NetGear API.
  • Disabled additional data of datatype numpy.ndarray for Server end in Bidirectional Mode.
Bug-fixes
  • Fixed 'NoneType' object is not subscriptable bug.
  • Fixed bugs related to delayed termination in NetGear API.
  • Reduced default request_timeout value to 4 and also lowered cut-off limit for the same.
  • Removed redundant ZMQ context termination and similar variables.
  • Added missing VidGear installation in workflow.
  • Excluded conflicting assets README.md from Mkdocs builds.
  • Fixed pattern value check bypassed if wrong value is assigned.
  • Fixed incorrect handling of additional data transferred in synchronous mode at both Server and Client end.
  • Replaced Netgear CI test with more reliable try-except-final blocks.
  • Updated termination linger to zero at Server's end.
  • Fixed NameError bug in NetGear API.
  • Fixed missing support for compression parameters in Multi-Clients Mode.
  • Fixed ZMQ throwing error on termination if all max-tries exhausted.
  • Enabled missing support for frame compression in its primary receive mode.
  • Fixed several bugs from CI Bidirectional Mode tests.
  • Removed or Grouped redundant code for increasing codecov.
  • Fixed Mkdocs only accepting Relative paths.
  • Fixed broken links in docs.
  • Fixed round off error in FPS.
  • Many small typos and bugs fixes.
Pull Requests

 

 

v0.1.7 (2020-04-29)

New Features
  • WebGear API:
    • Added a robust Live Video Server API that can transfer live video frames to any web browser on the network in real-time.
    • Implemented a flexible asyncio wrapper around starlette ASGI Application Server.
    • Added seamless access to various starlette's Response classes, Routing tables, Static Files, Template engine(with Jinja2), etc.
    • Added a special internal access to VideoGear API and all its parameters.
    • Implemented a new Auto-Generation Work-flow to generate/download & thereby validate WebGear API data files from its GitHub server automatically.
    • Added on-the-go dictionary parameter in WebGear to tweak performance, Route Tables and other internal properties easily.
    • Added new simple & elegant default Bootstrap Cover Template for WebGear Server.
    • Added __main__.py to directly run WebGear Server through the terminal.
    • Added new gif and related docs for WebGear API.
    • Added and Updated various CI tests for this API.
  • NetGear_Async API:
    • Designed NetGear_Async asynchronous network API built upon ZeroMQ's asyncio API.
    • Implemented support for state-of-the-art asyncio event loop uvloop at its backend.
    • Achieved Unmatchable high-speed and lag-free video streaming over the network with minimal resource constraint.
    • Added exclusive internal wrapper around VideoGear API for this API.
    • Implemented complete server-client handling and options to use variable protocols/patterns for this API.
    • Implemented support for all four ZeroMQ messaging patterns: i.e zmq.PAIR, zmq.REQ/zmq.REP, zmq.PUB/zmq.SUB, and zmq.PUSH/zmq.PULL.
    • Implemented initial support for tcp and ipc protocols.
    • Added new Coverage CI tests for NetGear_Async Network Gear.
    • Added new Benchmark tests for benchmarking NetGear_Async against NetGear.
  • Asynchronous Enhancements:
    • Added asyncio package to for handling asynchronous APIs.
    • Moved WebGear API(webgear.py) to asyncio and created separate asyncio helper.py for it.
    • Various Performance tweaks for Asyncio APIs with concurrency within a single thread.
    • Moved __main__.py to asyncio for easier access to WebGear API through the terminal.
    • Updated setup.py with new dependencies and separated asyncio dependencies.
  • General Enhancements:
    • Added new highly-precise Threaded FPS class for accurate benchmarking with time.perf_counter python module.
    • Added a new Gitter community channel.
    • Added a new Reducer function to reduce the frame size on-the-go.
    • Add Flake8 tests to Travis CI to find undefined names. (PR by @cclauss)
    • Added a new unified logging handler helper function for vidgear.
Updates/Improvements
  • Re-implemented and simplified logic for NetGear Async server-end.
  • Added new dependencies for upcoming asyncio updates to setup.py.
  • Added retry function and replaced wget with curl for Linux test envs.
  • Bumped OpenCV to latest 4.2.0-dev for Linux test envs.
  • Updated YAML files to reflect new changes to different CI envs.
  • Separated each API logger with a common helper method to avoid multiple copies.
  • Limited Importing OpenCV API version check's scope to helper.py only.
  • Implemented case for incorrect color_space value in ScreenGear API.
  • Removed old conflicting logging formatter with a common method and expanded logging.
  • Improved and added shutdown function for safely stopping frame producer threads in WebGear API.
  • Re-implemented and simplified all CI tests with maximum code-coverage in mind.
  • Replaced old mkdir function with new mkdir_safe helper function for creating directories safely.
  • Updated ReadMe.md with updated diagrams, gifs and information.
  • Improve, structured and Simplified the Contribution Guidelines.
  • Bundled CI requirements in a single command.(Suggested by @cclauss)
  • Replaced line endings CRLF with LF endings.
  • Added dos2unix for Travis OSX envs.
  • Bumped Codecov to maximum.
Breaking Updates/Changes
  • Dropped support for Python 3.5 and below legacies. (See issue #99)
  • Dropped and replaced Python 3.5 matrices with new Python 3.8 matrices in all CI environments.
  • Implemented PEP-8 Styled Black formatting throughout the source-code.
  • Limited protocols support to tcp and ipc only, in NetGear API.
Bug-fixes
  • Fixed Major NetGear_Async bug where __address and __port are not set in async mode.(PR by @otter-in-a-suit)
  • Fixed Major PiGear Color-space Conversion logic bug.
  • Workaround for CAP_IMAGES error in YouTube Mode.
  • Replaced incorrect terminate() with join() in PiGear.
  • Removed uvloop for windows as still NOT yet supported.
  • Refactored Asynchronous Package name async to asyncio, since it is used as Keyword in python>=3.7 (raises SyntaxError).
  • Fixed unfinished close of event loops bug in WebGear API.
  • Fixed NameError in helper.py.
  • Added fix for OpenCV installer failure on Linux test envs.
  • Fixed undefined NameError in helper.py context. (@cclauss)
  • Fixed incorrect logic while pulling frames from ScreenGear API.
  • Fixed missing functions in __main__.py.
  • Fixed Typos and definitions in docs.
  • Added missing camera_num parameter to VideoGear.
  • Added OpenSSL's [SSL: CERTIFICATE_VERIFY_FAILED] bug workaround for macOS envs.
  • Removed download_url meta from setup.py.
  • Removed PiGear from CI completely due to hardware emulation limitation.
  • Removed VideoCapture benchmark tests for macOS envs.
  • Removed trivial __main__.py from codecov.
  • Removed several redundant try-catch loops.
  • Renamed youtube_url_validation as youtube_url_validator.
  • Several minor wrong/duplicate variable definitions and various bugs fixed.
  • Fixed, Improved & removed many Redundant CI tests for various APIs.
Pull Requests
New Contributors

 

 

v0.1.6 (2020-01-01)

New Features
  • NetGear API:
    • Added powerful ZMQ Authentication & Data Encryption features for NetGear API:
      • Added exclusive secure_mode param for enabling it.
      • Added support for two most powerful Stonehouse & Ironhouse ZMQ security mechanisms.
      • Added smart auth-certificates/key generation and validation features.
    • Implemented Robust Multi-Servers support for NetGear API:
      • Enables Multiple Servers messaging support with a single client.
      • Added exclusive multiserver_mode param for enabling it.
      • Added support for REQ/REP & PUB/SUB patterns for this mode.
      • Added ability to send additional data of any datatype along with the frame in realtime in this mode.
    • Introducing exclusive Bidirectional Mode for bidirectional data transmission:
      • Added new return_data parameter to recv() function.
      • Added new bidirectional_mode attribute for enabling this mode.
      • Added support for PAIR & REQ/REP patterns for this mode
      • Added support for sending data of any python datatype.
      • Added support for message parameter for non-exclusive primary modes for this mode.
    • Implemented compression support with on-the-fly flexible frame encoding for the Server-end:
      • Added initial support for JPEG, PNG & BMP encoding formats .
      • Added exclusive options attribute compression_format & compression_param to tweak this feature.
      • Client-end will now decode frame automatically based on the encoding as well as support decoding flags.
    • Added force_terminate attribute flag for handling force socket termination at the Server-end if there's latency in the network.
    • Implemented new Publish/Subscribe(zmq.PUB/zmq.SUB) pattern for seamless Live Streaming in NetGear API.
  • PiGear API:
    • Added new threaded internal timing function for PiGear to handle any hardware failures/frozen threads.
    • PiGear will not exit safely with SystemError if Picamera ribbon cable is pulled out to save resources.
    • Added support for new user-defined HWFAILURE_TIMEOUT options attribute to alter timeout.
  • VideoGear API:
    • Added framerate global variable and removed redundant function.
    • Added CROP_N_ZOOM attribute in Videogear API for supporting Crop and Zoom stabilizer feature.
  • WriteGear API:
    • Added new execute_ffmpeg_cmd function to pass a custom command to its FFmpeg pipeline.
  • Stabilizer class:
    • Added new Crop and Zoom feature.
      • Added crop_n_zoom param for enabling this feature.
    • Updated docs.
  • CI & Tests updates:
    • Replaced python 3.5 matrices with latest python 3.8 matrices in Linux environment.
    • Added full support for Codecov in all CI environments.
    • Updated OpenCV to v4.2.0-pre(master branch).
    • Added various Netgear API tests.
    • Added initial Screengear API test.
    • More test RTSP feeds added with better error handling in CamGear network test.
    • Added tests for ZMQ authentication certificate generation.
    • Added badge and Minor doc updates.
  • Added VidGear's official native support for MacOS environments.
Updates/Improvements
  • Replace print logging commands with python's logging module completely.
  • Implemented encapsulation for class functions and variables on all gears.
  • Updated support for screen casting from multiple/all monitors in ScreenGear API.
  • Updated ScreenGear API to use Threaded Queue Mode by default, thereby removed redundant THREADED_QUEUE_MODE param.
  • Updated bash script path to download test dataset in $TMPDIR rather than $HOME directory for downloading testdata.
  • Implemented better error handling of colorspace in various videocapture APIs.
  • Updated bash scripts, Moved FFmpeg static binaries to github.com.
  • Updated bash scripts, Added additional flag to support un-secure apt sources.
  • CamGear API will now throw RuntimeError if source provided is invalid.
  • Updated threaded Queue mode in CamGear API for more robust performance.
  • Added new camera_num to support multiple Picameras.
  • Moved thread exceptions to the main thread and then re-raised.
  • Added alternate github mirror for FFmpeg static binaries auto-installation on windows oses.
  • Added colorlog python module for presentable colored logging.
  • Replaced traceback with sys.exc_info.
  • Overall APIs Code and Docs optimizations.
  • Updated Code Readability and Wiki Docs.
  • Updated ReadMe & Changelog with the latest changes.
  • Updated Travis CI Tests with support for macOS environment.
  • Reformatted & implemented necessary MacOS related changes and dependencies in travis.yml.
Breaking Updates/Changes
  • Python 2.7 legacy support dropped completely.
  • Source-code Relicensed to Apache 2.0 License.
  • Python 3+ are only supported legacies for installing v0.1.6 and above.
  • Python 2.7 and 3.4 legacies support dropped from CI tests.
Bug-fixes
  • Reimplemented Pub/Sub pattern for smoother performance on various networks.
  • Fixed Assertion error in CamGear API during colorspace manipulation.
  • Fixed random freezing in Secure Mode and several related performance updates
  • Fixed multiserver_mode not working properly over some networks.
  • Fixed assigned Port address ignored bug (commit 073bca1).
  • Fixed several wrong definition bugs from NetGear API(commit 8f7153c).
  • Fixed unreliable dataset video URL(rehosted file on github.com).
  • Disabled overwrite_cert for client-end in NetGear API.
  • Disabled Universal Python wheel builds in setup.cfgfile.
  • Removed duplicate code to import MSS(@BoboTiG) from ScreenGear API.
  • Eliminated unused redundant code blocks from library.
  • Fixed Code indentation in setup.py and updated new release information.
  • Fixed code definitions & Typos.
  • Fixed several bugs related to secure_mode & multiserver_mode Modes.
  • Fixed various macOS environment bugs.
Pull Requests
New Contributors

 

 

v0.1.5 (2019-07-24)

New Features
  • Added new ScreenGear API, supports Live ScreenCasting.
  • Added new NetGear API, aids real-time frame transfer through messaging(ZmQ) over network.
  • Added new new Stabilizer Class, for minimum latency Video Stabilization with OpenCV.
  • Added Option to use API's standalone.
  • Added Option to use VideoGear API as internal wrapper around Stabilizer Class.
  • Added new parameter stabilize to API, to enable or disable Video Stabilization.
  • Added support for **option dict attributes to update VidGear's video stabilizer parameters directly.
  • Added brand new logo and functional block diagram (.svg) in readme.md
  • Added new pictures and GIFs for improving readme.md readability
  • Added new contributing.md and changelog.md for reference.
  • Added collections.deque import in Threaded Queue Mode for performance consideration
  • Added new install_opencv.sh bash scripts for Travis cli, to handle OpenCV installation.
  • Added new Project Issue & PR Templates
  • Added new Sponsor Button(FUNDING.yml)
Updates/Improvements
  • Updated New dependencies: mss, pyzmq and rejected redundant ones.
  • Revamped and refreshed look for readme.md and added new badges.
  • Updated Releases Documentation completely.
  • Updated CI tests for new changes
  • Updated Code Documentation.
  • Updated bash scripts and removed redundant information
  • Updated Youtube video URL in tests
  • Completely Reformatted and Updated Wiki Docs with new changes.
Breaking Updates/Changes
  • Implemented experimental Threaded Queue Mode(a.k.a Blocking Mode) for fast, synchronized, error-free multi-threading.
  • Renamed bash script pre-install.sh to prepare_dataset.sh - [x] downloads opensourced test datasets and static FFmpeg binaries for debugging.
  • Changed script folder location to bash/script.
  • Python 3.4 removed from Travis CI tests.
Bug-fixes
  • Temporarily fixed Travis CI bug: Replaced opencv-contrib-python with OpenCV built from scratch as dependency.
  • Fixed CI Timeout Bug: Disable Threaded Queue Mode for CI Tests
  • Fixes** sys.stderr.close() throws ValueError bug: Replaced sys.close() with DEVNULL.close()
  • Fixed Youtube Live Stream bug that return NonType frames in CamGear API.
  • Fixed NoneType frames bug in PiGear class on initialization.
  • Fixed Wrong function definitions
  • Removed /xe2 unicode bug from Stabilizer class.
  • Fixed **output_params KeyError bug in WriteGear API
  • Fixed subprocess not closing properly on exit in WriteGear API.
  • Fixed bugs in ScreenGear: Non-negative monitor values
  • Fixed missing import, typos, wrong variable definitions
  • Removed redundant hack from setup.py
  • Fixed Minor YouTube playback Test CI Bug
  • Fixed new Twitter Intent
  • Fixed bug in bash script that not working properly due to changes at server end.
Pull Requests

 

 

v0.1.4 (2019-05-11)

New Features
  • Added new WriteGear API: for enabling lossless video encoding and compression(built around FFmpeg and OpenCV Video Writer)
  • Added YouTube Mode for direct Video Pipelining from YouTube in CamGear API
  • Added new y_tube to access YouTube Mode in CamGear API.
  • Added flexible Output file Compression control capabilities in compression-mode(WriteGear).
  • Added -output_dimensions special parameter to WriteGear API.
  • Added new helper.py to handle special helper functions.
  • Added feature to auto-download and configure FFmpeg Static binaries(if not found) on Windows platforms.
  • Added -input_framerate special parameter to WriteGear class to change/control output constant framerate in compression mode(WriteGear).
  • Added new Direct Video colorspace Conversion capabilities in CamGear and PiGear API.
  • Added new framerate class variable for CamGear API, to retrieve input framerate.
  • Added new parameter backend - [x] changes the backend of CamGear's API
  • Added automatic required prerequisites installation ability, when installation from source.
  • Added Travis CI Complete Integration for Linux-based Testing for VidGear.
  • Added and configured travis.yml
  • Added Appveyor CI Complete Integration for Windows-based Testing in VidGear.
  • Added and configured new appveyor.yml
  • Added new bash script pre-install.sh to download opensourced test datasets and static FFmpeg binaries for debugging.
  • Added several new Tests(including Benchmarking Tests) for each API for testing with pytest.
  • Added license to code docs.
  • Added Say Thank you! badge to Readme.md.
Updates/Improvements
  • Removed redundant dependencies
  • Updated youtube-dl as a dependency, as required by pafy's backend.
  • Updated common VideoGear API with new parameter.
  • Update robust algorithm to auto-detect FFmpeg executables and test them, if failed, auto fallback to OpenCV's VideoWriter API.
  • Improved system previously installed OpenCV detection in setup.py.
  • Updated setup.py with hack to remove bullets from pypi description.
  • Updated Code Documentation
  • Reformatted & Modernized readme.md with new badges.
  • Reformatted and Updated Wiki Docs.
Breaking Updates/Changes
  • Removed -height and -width parameter from CamGear API.
  • Replaced dependency opencv-python with opencv-contrib-python completely
Bug-fixes
  • Windows Cross-Platform fix: replaced dependency os with platform in setup.py.
  • Fixed Bug: Arises due to spaces in input **options/**output_param dictionary keys.
  • Fixed several wrong/missing variable & function definitions.
  • Fixed code uneven indentation.
  • Fixed several typos in docs.
Pull Requests

 

 

v0.1.3 (2019-04-07)

Bug-fixes
  • Patched Major PiGear Bug: Incorrect import of PiRGBArray function in PiGear Class
  • Several Fixes for backend picamera API handling during frame capture(PiGear)
  • Fixed missing frame variable initialization.
  • Fixed minor typos
Pull Requests

 

 

v0.1.2 (2019-03-27)

New Features
  • Added easy Source manipulation feature in CamGear API, to control features like resolution, brightness, framerate etc.
  • Added new **option parameter to CamGear API, provides the flexibility to manipulate input stream directly.
  • Added new parameters for Camgear API for time delay and logging.
  • Added new Logo to readme.md
  • Added new Wiki Documentation.
Updates/Improvements
  • Reformatted readme.md.
  • Updated Wiki Docs with new changes.
Bug-fixes
  • Improved Error Handling in CamGear & PiGear API.
  • Fixed minor typos in docs.
Pull Requests

 

 

v0.1.1 (2019-03-24)

New Features
  • Release ViGear binaries on the Python Package Index (PyPI)
  • Added new and configured setup.py & setup.cfg
Bug-fixes
  • Fixed PEP bugs: added and configured properly __init__.py in each folder
  • Fixed PEP bugs: improved code Indentation
  • Fixed wrong imports: replaced distutils.core with setuptools
  • Fixed readme.md

 

 

v0.1.0 (2019-03-17)

New Features
  • Initial Release
  • Converted my imutils PR into Python Project.
  • Renamed conventions and reformatted complete source-code from scratch.
  • Added support for both python 2.7 and 3 legacies
  • Added new multi-threaded CamGear, PiGear, and VideoGear APIs
  • Added multi-platform compatibility
  • Added robust & flexible control over the source in PiGear API.
\ No newline at end of file +-->

Release Notes

v0.3.3 (2024-06-22)

New Features
  • PiGear:
    • ⚡️ Official Support for Picamera2 API backend. (Fixes #342)
      • This massive update brings official support for the new Picamera2 API, unlocking powerful features for Raspberry Pi Camera Modules and limited USB camera support.
      • Seamless Python wrapper: A robust wrapper around Picamera2 API library simplifies integration with minimal code changes for existing PiGear users.
      • Enhanced camera control: Leverages libcamera API under the hood for Raspberry Pi Camera Modules.
      • Existing compatibility: Maintains compatibility with PiGear's existing super-charged multi-threaded and colorspace manipulation framework.
      • Proper Resource management: Ensures proper resource release during PiGear termination.
      • USB camera support (limited): Provides basic functionality for USB webcams. PiGear could accurately differentiates between USB and Raspberry Pi cameras using metadata.
      • Backward compatibility: Seamlessly switches to the legacy Picamera library backend if Picamera2 is unavailable.
      • Standalone functionalities: Standalone functionalities for both legacy picamera and newer picamera2 backends for clarity.
      • Advanced optional parameters handling: Handles camera configurational parameters and user-defined settings for various camera types.
        • New optional configurational parameters: Currently Supports sensor, format, controls, transform, stride, buffer_count, and queue with sanity checks.
        • New user-defined optional parameters: Such as auto_align_output_config, enable_verbose_logs, and more.
  • StreamGear:
    • Introduced new -enable_force_termination attribute for immediate FFmpeg process termination.
  • Helper:
    • Added support for SRTP/RTSPS in is_valid_url function (Fixes #410)
      • Enhanced is_valid_url in helper.py to recognize and support both rtsp and rtsps protocols. (Suggested by @jonra1993)
      • SRTP/RTSPS extends RTSP/RTP to encrypt video and audio data using the same ciphers as HTTPS, typically AES with a 128-bit key length.
    • Added a custom deprecated decorator to mark deprecated functions and parameters to display a warning message when a deprecated one is used.
  • Docs:
    • Overhauled mkdocs material theme:
      • Added unrecognized_links: ignore to mkdocs.yml for validations.
      • Added custom admonition icons.
      • Added new git-authors plugin.
      • Added new tables markdown extension.
      • Added custom fences to pymdownx.superfences markdown extension.
      • Added line_spans: __span and pygments_lang_class: true parameters to pymdownx.highlight markdown extension.
      • Added normalize_issue_symbols: true to pymdownx.magiclink markdown extension.
      • Added new mkdocs feature dependency mkdocs-git-authors-plugin.
    • Added the use of new -enable_force_termination parameter.
    • Added a new FAQ entry about the deprecated rgb_mode parameter.
    • Added new screengear_error11.png asset.
  • CI:
    • Added test cases for import_dependency_safe function to validate different scenarios and error handling in import_dependency_safe.
Updates/Improvements
  • Core:
    • Improved exception handling for module imports:
      • Updated import_dependency_safe in helper.py:
        • Added 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.
    • Improved 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.
  • Asyncio:
    • Replaced deprecated Starlette's on_shutdown parameter with an async context manager lifespan in WebGear and WebGear_RTC APIs. (Fixes #397)
      • Moved shutdown logic for VideoGear and peer RTC connections to this new lifespan context manager.
      • Added new contextlib import for using asynccontextmanager.
  • NetGear_Async API:
    • Modified __init__ method to handle event loop more robustly:
      • Try to get the running event loop using asyncio.get_running_loop()
      • If no running event loop found, create a new one with asyncio.new_event_loop()
      • Log if creating a new event loop
    • Changed launch method to use self.loop.create_task() instead of asyncio.ensure_future()
      • Ensures the task is created using the correct event loop instance.
    • Moved the event loop initialization code to an earlier point before setting event loop policy to ensure it is set up correctly before selecting WindowsSelectorEventLoop policy.
      • On Windows, vidgear requires the WindowsSelectorEventLoop, but Python 3.8 and above defaults to the ProactorEventLoop which is not compatible.
    • Removed redundant python version check to set WindowsSelectorEventLoop policy, as minimum supported version is already 3.8.
    • Move event loop setup and policy assignment to the beginning of __init__ before zmq Context creation.
    • Refactored return data handling.
  • StreamGear:
    • Updated close() methods for handling gracefully signal interruptions based on different operating systems with device audio streams.
    • Deprecated terminate() method, introducing close() for safer process termination.
    • Enhanced stream copy support in Single Source mode (Fixes #396).
      • Moved settings for "-vf" and "-aspect" inside conditional blocks.
      • Added warnings and discarded these parameters in stream copy mode.
      • 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.
    • Restricted -livestream parameter to Real-time Frames Mode only.
      • Disabled live streaming for video files and updated relevant logging.
    • Enhanced warning messages and clarified description.
  • PiGear:
    • Logging optimization with warning for common libcamera messages.
    • Lowered framerate minimum value to 0.0.
    • Moved sensor optional parameter to commonly supported picamera2 configurational parameters.
    • Removed unsupported bit_depth optional parameters.
    • Updated PiGear API tagline and introduction.
  • NetGear:
    • Enhanced logging and error handling for secure mode.
    • Logged Authenticator start/stop events.
    • Handled socket session expiration more gracefully in recv_handler.
    • Ensured proper termination of the ZMQ context and socket when closing the NetGear instance.
  • WebGear:
    • Enhanced error messages for WebGear auto-generation workflow (Fixes #403)
      • Updated homepage, not_found, and server_error methods to include more detailed JSON error messages.
      • Added specific error and message prefixes to improve clarity.
  • 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.
  • WriteGear:
    • Simplified the logic for formatting output parameters.
    • 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.
  • CamGear:
    • Removed GStreamer support check.
    • Improved readability of livestream warning logs.
  • Setup.py:
    • Dropped legacy picamera dependency in setup.py.
    • Updated setup.py to use the latest pyzmq version to address installation issues (Fixes #399).
  • Helper:
    • Added patch for substring index bug in get_supported_demuxers helper method.
    • Updated extract_time helper function regex to handle milliseconds.
  • Docs:
    • Update StreamGear documentation:
      • Updated documentation to deprecated terminate() method, and introducing close() for safer process termination.
      • Improved the overview section's description and wording.
      • Updated usage examples for both Single-Source Mode and Real-time Frames Mode.
      • Updated StreamGear usage examples for device audio input.
      • Refactored sections for Live Streaming usage.
        • Added warning for unsupported -livestream parameter in Single-Source Mode.
      • 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.
      • Updated usage example for device video source.
      • Addressed deprecation of the terminate() method in favor of the new close() method.
      • Updated respective notices for the deprecated terminate() method and rgb_mode parameter.
      • Added a deprecation warning admonition for the rgb_mode parameter in the stream() method.
      • Removed the obsolete usage example for deprecation RGB mode with StreamGear.
      • Added documentation and usage of the new -enable_force_termination parameter.
      • Modified the warning message to mention that forced termination can cause corrupted output in certain scenarios.
      • Updated the docstring for the stream() method and transcode_source() method.
      • Refactored the StreamGear API Parameters documentation to enhance clarity and readability.
      • Refined the description of the -streams attribute of the StreamGear API.
    • Update PiGear documentation:
      • Added a warning advising users to disable common libcamera messages when logging is disabled.
      • Updated Picamera2 installation instructions (including apt, pip, pre-installation on Raspberry Pi images, and compatibility warnings)
      • Moved legacy Picamera library installation instructions to an admonition.
      • Removed Importing section from overview to avoid confusion.
    • Update NetGear documentation:
      • Added Admonition for warning users about the Client's end must run before the Server's end to establish a secure connection in Secure Mode.
      • Added warning log for potential issues with flag=1 (NOBLOCK). (Fixes #390)
        • Changed default value of copy to True in NetGear API documentation.
      • Noted that track option is ignored when copy=True.
    • Update WriteGear documentation:
      • Updated the documentation for the -disable_force_termination parameter.
    • Update README.md:
      • Replaced deprecated options (frame_jpeg_quality, frame_jpeg_optimize, frame_jpeg_progressive) with their newer equivalents (jpeg_compression_quality, jpeg_compression_fastdct, jpeg_compression_fastupsample) in WebGear usage example.
    • Update mkdocs.yml:
      • Set edit_uri for GitHub edit links.
      • Add new theme features like content actions, tooltips, etc.
      • Update palette settings for light/dark mode.
      • Enable new markdown extensions.
      • Add custom javascript hook support.
      • Migrated to new Google Analytics 4.
      • Replaced depreciated materialxwith supported emoji extension.
      • Replaced permalink icon with default one.
      • Change system mode toggle icon and name in mkdocs.yml.
    • Improved overall documentation quality by added detailed explanations, practical examples, following best practices, and clearer usage patterns.
    • Updated sections, code examples, admonitions, and comments for better readability, consistency, and precision.
    • Added missing version contributors to changelog.md.
    • Added new icons to make headings more readable.
    • Replaced unsupported admonitions with supported ones.
    • Removed all custom admonition icons and color CSS from custom.css.
    • Removed Twitter section from help and docs site metadata.
    • Updated Zenodo badge and BibTeX entry.
    • Added workaround for 'AttributeError: 'DXCamera' object has no attribute 'is_capturing'' error on Windows.
    • Remove script tags from main.html and use a custom hook for adding javascripts on certain pages.
    • Refactored all APIs and bonus examples to use linenums and hl_lines which makes it easier to highlight specific lines in code blocks.
    • Removed Gitter community chat sidecard javascript file.
    • Redefined spacing between sections.
    • Add failure warning in various docs about picamera incompatibility on 64-bit OS.
    • Update announcement icon in main.html.
    • Remove site.webmanifest file.
  • Maintenance:
    • Improved logging, parameter validation, and added descriptive dialogs across various APIs.
    • Moved logging enablement before version logging for consistency in vidgear APIs.
    • Removed redundant boolean assignment for various APIs internal logging.
    • Simplified conditional statements and assignments using short-circuiting, Boolean operations, and ternary operators across various APIs and tests.
    • Refactored vidgear code to improve readability, maintainability, and performance.
    • Added .cache directory to .gitignore,
    • Updated vidgear library version to v0.3.3.
    • Improved code efficiency with short-circuiting and formatting.
    • Updated logging practices to be more developer-friendly.
    • Removed unnecessary parentheses and type checks.
    • Removed unused imports.
    • Updated code comments.
  • CI:
    • Temporarily removed PiGear API from code coverage due to hardware limitations.
    • Deprecated custom event_loop fixture overrides in WebGear_RTC and NetGear_Async tests
      • Removed redundant pytest.mark.asyncio decorators from several test functions.
    • Add a new event_loop_policy fixture for pytest to override the event loop policy:
      • Added new recommended approach of using pytest.mark.asyncio(scope="module") to mark all WebGear_RTC and NetGear_Async tests as asynchronous and utilize the same event loop throughout the module.
      • Log the event loop being used for debugging.
    • Updated NetGear unit tests to reflect the new default for copy.
    • Ensured coverage for raise, log, silent, and unknown error types.
    • Improved parameterized test cases to support floating point values.
    • Updated StreamGear tests to use the new close() method instead of the deprecated terminate() method.
    • Updated tests of various APIs for better coverage and reliability.
    • Enabled kill=True in close() in NetGear Tests.
    • Removed pinned cryptography==38.0.4 dependency.
    • Remove unused imports and code cleanup.
    • Rearranged the dependencies.
Breaking Updates/Changes
  • StreamGear:
    • Deprecated terminate() method and introduce close() method.
      • The terminate() method in StreamGear is now deprecated and will be removed in a future release. Developers should use the new close() method instead, which provides a more descriptive name like in WriteGear API for terminating StreamGear processes safely.
    • Deprecated rgb_mode parameter in stream() method.
      • This parameter will be removed in a future version, and only BGR format frames will be supported.
    • Restricted -livestream parameter to Real-time Frames Mode only.
      • Live streaming is intended for low-latency streaming of real-time frames, where chunks contain only the most recent frames. It doesn't make sense when streaming from a video file, as the entire file can be streamed normally without the need for live streaming.
Bug-fixes
  • PiGear:
    • Modify PiGear class behavior when enforce_legacy_picamera=True on unsupported system
      • Instead of silently disabling picamera2 API directly, PiGear now raises an error if picamera is unavailable or unsupported
      • Prevented incorrect initialization of PiGear class on unsupported 64-bit OS systems.
    • Fixed UnboundLocalError bug for 'picamera2' variable assigment.
    • Fixed UnboundLocalError bug for 'queue' variable assignment.
    • Fixed colorspace typo bug.
  • StreamGear:
    • Fixed incompatibility of stream copy with Real-time Frames Mode.
      • Added warnings and discarded -vcodec copy when using this mode.
    • Removed non-essential aspect ratio parameter to prevent unwanted distortions (Fixes #385).
    • Moved handle streaming format to beginning to fix 'StreamGear' object has no attribute '_StreamGear__format' bug.
  • NetGear:
    • Fixed Secure Mode failing to work on conflicting ZMQ Contexts:
      • Handled "Address in use" error more gracefully and disabled secure mode if errors occur.
      • Improved handling of ZMQ Authenticator and Certificates.
    • Fixed msg_json undefined when terminating context in the recv_handler method.
  • CamGear:
    • Fixed logging condition for yt-dlp (Fixes #394)
      • Updated no_warnings parameter in CamGear to be False when logging is enabled and True otherwise.
  • Docs:
    • Replaced buggy kofi widget with a button image in index.md.
    • Fixed Ko-fi sponsorship heart hover effect in footer
      • Replaced twemoji heart emoji with heart-pulse fontawesome SVG
    • Fixed titles and indentations in various admonitions.
    • Fixed various issues in code comments, and hyperlinks URLs.
    • Fixed typos, formatting, code highlighting, and grammar inconsistencies.
    • Fixed minor typo in js_hook.py.
  • CI:
    • Fixed simplejpeg and opencv not compatible with numpy==2.x.x versions.
      • Pinned numpy<2.0.0 in all CI envs.
    • Fixed expected duration value in parameterized test case from 8 to 8.44 since test_extract_time function now supports floating point values.
    • Fixed test_secure_mode NetGear test:
      • Added "127.0.0.1" address to allow common endpoint for connection.
      • Added "jpeg_compression":False to disable frame compression, allowing frame to be the same while assertion.
    • Fixed pip install hash bug in Azure Pipelines CI.
    • Fixed various typos and code issues in tests.
    • Fixed invalid escape sequence in test case string.
    • Fixed python environment bugs in appveyor.yml.
Pull Requests
New Contributors

 

 

v0.3.2 (2023-09-10)

New Features
  • 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.
  • 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.
  • Setup.py
    • Added official support for python 3.11.x legacies.
    • Bumped version to 0.3.1.
  • 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.
  • CI
    • Added python 3.11 legacy support for MacOS, Windows and Linux environments.
    • Added kill argument to close() method in various NetGear tests.
Updates/Improvements
  • Asyncio:
    • Formatted TemplateResponse class parameters w.r.t new changes in backend Starlette API.
  • Setup.py:
    • Readded latest patch to uvicorn, starlette, pyzmq dependencies.
    • Removed 3.7 legacy from Programming Language metadata.
  • Maintenance:
    • Added GitHub sponsors and dropped liberapay from Funding.yml.
    • Removed redundant code.
  • 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
  • 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.
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-fixes
  • ScreenGear:
    • Fixed swapped region dimensions bug with dxcam backend.
    • Fixed "mss" backend disabled when monitor parameter is not defined.
  • Docs:
    • Fixed missing compression_mode flags in WriteGear API docs.
    • Fixed missing hyperlinks.
    • Fixed typos and context.
  • 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.
Pull Requests
New Contributors

 

 

v0.3.1 (2023-07-22)

New Features
  • WebGear:
    • Added an option to add a custom video endpoint path.
      • Users can now change the video endpoint path from "/video" to any alphanumeric string.
      • Added the custom_video_endpoint optional string attribute for this purpose.
      • Only alphanumeric strings with no spaces in between are allowed as its value.
  • ScreenGear:
    • Added dxcam support for Windows machines.
      • Implemented a complete end-to-end workflow for the dxcam backend.
      • dxcam is now the default backend for Windows machines when no backend is defined.
      • Added support for variable screen dimensions to capture an area from the screen.
      • Added the optional flag dxcam_target_fps to control the target fps in dxcam. Defaults to 0 (disabled).
      • RGB frames from dxcam are automatically converted into BGR.
      • For better performance, video_mode is enabled by default in dxcam backend.
      • Added necessary imports.
    • Added support for tuple values in the monitor parameter to specify device and output indexes as (int[device_idx], int[output_idx]) in the dxcam backend only.
      • Default int index is also allowed as a value for selecting device index.
  • Helper
    • Added multiple servers support for downloading assets.
      • Added GitHub server to the generate_webdata method to make it more robust for rate limits and other shortcomings.
      • Now, the generate_webdata method will retry a different server when one fails.
  • Setup.py
    • Added dxcam dependency in core and asyncio extra requires.
    • Bumped version to 0.3.1.
  • Docs
    • Added dxcam API specific prerequisites for ScreenGear API when installing on Windows via pip.
    • Added documentation for the custom_video_endpoint optional string attribute.
    • Added documentation for controlling Chunk size in HLS stream.
    • Added new hyperlinks for dxcam dependency.
  • CI
    • Added a test case for ndim==3 grayscale frames.
      • Added the Custom_Grayscale_class to generate ndim==3 grayscale frames.
    • Added test cases for the custom_video_endpoint optional string attribute.
Updates/Improvements
  • WebGear:
    • Improved the conditions logic to check if non-empty values are assigned to optional parameters.
  • WebGear_RTC:
    • Improved the handling of the format parameter when constructing a VideoFrame from ndarray frames.
  • ScreenGear:
    • Enforced dxcam backend (if installed) when monitor is defined on Windows machines.
    • Refactored code blocks to ensure backward compatibility.
  • Maintenance:
    • Cleaned up unused imports and code blocks.
    • Cleaned redundant code.
    • Improved logging.
    • Implemented short-circuiting.
    • Fixed comment typos.
    • Updated comments.
  • Docs:
    • Updated ScreenGear API usage example docs, added new relevant information, updated requirements for dxcam support in Windows machines.
    • Refactored monitor and backend parameters docs of ScreenGear.
    • Updated class and class parameters descriptions in ScreenGear docs.
    • Updated a new description for ScreenGear API.
    • Updated Zenodo badge and the BibTeX entry.
    • Relocated some docs for a better context.
    • Removed ScreenGear name from Threaded Queue Mode doc.
    • Updated ScreenGear FAQs.
    • Updated changelog.md
  • CI:
    • Updated the test_webgear_rtc_custom_stream_class method.
    • Updated the test_webgear_options method.
    • Updated the test_webgear_routes test to validate the new custom endpoint.
    • Increased code coverage by updating tests.
Breaking Updates/Changes
  • ScreenGear:
    • Previously enforced threaded queue mode is now completely removed, resulting in a potential performance boost.
      • 💬 Reason: The IO is automatically blocked by the screen refresh rate, so adding the overhead of maintaining a separate queue is pointless.
    • Removed the THREAD_TIMEOUT optional flag.
Bug-fixes
  • WebGear_RTC:
    • Fixed a bug caused by PyAV's error when ndim==3 grayscale frames are encountered.
      • The API will now drop the third dimension if ndim==3 grayscale frames are detected.
  • ScreenGear:
    • Fixed backend not defined while logging.
  • 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.
  • Docs:
    • Fixed context and added separate code for controlling chunk size in HLS and DASH streams in StreamGear docs.
    • Fixed naming conventions for the recently added DXcam backend in ScreenGear docs.
    • Fixed missing hyperlinks.
  • CI:
    • Fixed m3u8 module failing to recognize Windows paths in ScreenGear tests.
    • Fixed a path bug by replacing the absolute file path with the decoded file content as a string in its loads()
Pull Requests

 

 

v0.3.0 (2023-01-26)

New Features
  • WriteGear:
    • Added support for user-defined and higher than 8-bit depth input frames pixel-format.
      • Added support for higher than 8-bit depth frames with datatypes of unsigned integer(uint) kind and element size 2.
      • Added dtype parameter to internal Preprocess method for passing input frames datatype.
      • Implemented auto-calculation of input pixel-format based on number of channels in higher than 8-bit depth frames.
      • Added various known working pixel-formats(based on number of channels), supported by all prominent computer vision libraries.
      • Added support for up to 1-channel(gray16-le/be) to all the way up to 4-channels(bgra64-le/be) in input frames.
      • Added endianness little(le) or big(be) at the suffix of pixel-format based on byte-order of input frames datatypes.
      • Extended support for higher RGB 8-bit depth frames through RGB mode.
    • Added support for user-defined custom input pixel-format.
      • Added new -input_pixfmt attribute to output_params dictionary parameter for easily specifying custom input pixel-format.
      • Added newly implemented get_supported_pixfmts method import for verifying user-defined input pixel-format against Installed FFmpeg supported pixel-formats. Unsupported values will be discarded.
      • Implemented runtime datatype validation check, such that all input frames must have same datatype.
    • Added support for Context Managers for proper handling of resources via with statement for allocating and releasing resources precisely. (Suggested by @sueskind)
      • Implement the __enter__() and __exit__() methods.
      • Added __enter__ method that returns reference to the WriteGear Class.
      • Added __exit__ method that automatically executes close() for performing the cleanup operations and handling exception gracefully.
  • StreamGear:
    • Added support for Context Managers for proper handling of resources via with statement for allocating and releasing resources precisely. (Suggested by @sueskind)
      • Implement the __enter__() and __exit__() methods.
      • Added __enter__ method that returns reference to the StreamGear Class.
      • Added __exit__ method that automatically executes close() for performing the cleanup operations and handling exception gracefully.
  • WebGear:
    • Added way to completely disable Data-Files Auto-Generation WorkFlow.
      • Added new skip_generate_webdata boolean optional attribute(False by default) to completely disable Data-Files Auto-Generation WorkFlow.
      • This flag enables only /video route for disabled Data-Files Auto-Generation WorkFlow.
      • Implemented JSONResponse as placeholder response instead of Index, 404 and 500 HTML pages, when workflow is disabled. (Note: Index HTML page will throw 404 status code.)
      • Added necessary imports.
  • Helper:
    • Added more robust implementation of validate_audio method.
      • Added new more robust regex pattern for extracting audio-samplerate.
      • Added new validate_audio method for calculating accurate bitrate(in kbps) from audio samplerate, channels, bit-depth values.
      • Implemented new patterns and logic for accurately extracting audio channels and bit-depth from given metadata.
    • Added support for Linux video device path (such as /dev/video0).
  • Maintenance:
    • Logging current vidgear version when vidgear APIs are called, not at import.
      • Added logcurr_vidgear_ver helper function to facilitate logging current vidgear version, when called within a API.
      • Implemented ver_is_logged global variable in helper to log version only once, which can modifiable with logcurr_vidgear_ver method only. Followed recommendation given in official python docs: https://docs.python.org/3/faq/programming.html#how-do-i-share-global-variables-across-modules
      • Current version can only be logged by VidGear APIs with the logging turned on (i.e. logging=True).
  • Docs:
    • Added new WriteGear Bonus Example:
      • Added "Using WriteGear's Compression Mode with v4l2loopback Virtual Cameras bonus python example.
      • Added related prerequisites and dependencies for creating v4l2loopback Virtual Cameras on Linux machines.
      • Added both With/Without-Audio cases for "Using WriteGear's Compression Mode for YouTube-Live Streaming".
    • Added content.code.copy and content.tabs.link features.
    • Added docs related to skip_generate_webdata optional attribute.
    • Added feedback features to mkdocs.yml.
    • Added 404.html static template to mkdocs.yml.
  • CI:
    • Added v4l2loopback support for testing /dev/video0 device on Linux machines.
    • Added test cases for newer implementation of validate_audio method.
    • Added test_skip_generate_webdata to test skip_generate_webdata optional attribute.
    • Added tests for user-defined and higher than 8-bit depth input frames pixel-format.
Updates/Improvements
  • WriteGear:
    • Completely revamped code structure and comments.
      • Updated comments, description, and logging messages to more sensible and developer friendly.
      • Implemented operator short-circuiting to cleanup code as much as possible.
      • Renamed startFFmpeg_Process internal class method to start_FFProcess.
      • Renamed Preprocess internal class method to PreprocessFFParams.
      • Renamed startCV_Process internal class method to start_CVProcess.
      • Renamed initiate internal class parameter to initiate_process.
      • Renamed force_termination internal class parameter to forced_termination.
      • Enabled output_params parameters logging in both modes.
      • Improved compression and logging parameters boolean value handling.
      • Implemented stdout closing to cleanup pipeline before terminating.
  • Helper:
    • Updated validate_audio method with improved and more robust regex patterns for identifying audio bitrate in ay audio file.
  • Setup.py:
    • Bumped version to 0.3.0.
    • Replaced >= comparison operator with more flexible ~=.
    • Replaced distutils.version.LooseVersion with pkg_resources.parse_version.
  • Maintenance:
    • Replaced depreciated LooseVersion with parse_version.
    • Updated Retry package to be imported from requests.adapters.
    • Moved terminal and python code text area to Question GitHub Form Schema.
    • Removed unnecessary imports.
    • Removed redundant code.
    • Improved logging messages.
    • Updated code comments.
    • Updated method descriptions.
    • Refactored code.
    • Increased coverage.
  • Bash Script:
    • Updated FFmpeg Static Binaries links to latest date/version tag to 12-07-2022.
    • Removed depreciated binaries download links and code.
  • Docs:
    • Replaced all raw.githubusercontent.com GIF URLs with user-images.githubusercontent.com.
    • Reformatted custom.css and added missing comments.
    • Updated sponsor block.
    • Enabled Code Highlights.
    • Updated announcement bar.
    • Updated changelog.md.
    • Reduced webgear_rtc.gif size.
    • Updated Zenodo badge and the BibTeX entry.
  • CI:
    • Added more flexible formats to return_testvideo_path function.
    • Updated test_write test for higher than 8-bit depth input frames pixel-format in WriteGear's Compression Mode.
    • Updated actions/checkout to v3.
    • Updated actions/setup-python to v4.
    • Updated codecov/codecov-action to v3.
    • Moved test_colorspaces test to CamGear tests.
    • Added deffcode library import.
  • Re-stuctured yaml code.
Breaking Updates/Changes
  • WriteGear:
    • Renamed output_filename string parameter to output.
      • Since WriteGear API accepts all sorts of streams (such as valid filename/path/URL) for encoding, thereby changing parameter name to output will be more true to its purpose.
      • Renaming output_filename to output in WriteGear API will also help user to not accidentally assume WriteGear supports only encoding of video files.
      • It matches the output parameter in StreamGear which basically does the same thing.
    • Renamed cmd parameter in execute_ffmpeg_cmd() class method to more sensible command.
    • ValueError will be raised if datatype of input frames mismatches Writegear API
Bug-fixes
  • Camgear:
    • Fixed CamGear.read() blocked unnecessarily.
      • 💬 When THREADED_QUEUE_MODE is enabled CamGear.read() blocks for an excessive duration when attempting to read past the end of a stream.
      • Added None frame to the queue at the end to signal we're done.
      • Added terminate Event check before continuing.
    • Fixed deadlock on exit.
      • 💬 The deadlock is due to self.__queue.get(timeout=self.__thread_timeout) line in read() method, which still waits for timeout(thread_timeout) to happen when main update() thread was already terminated on exit and queue was empty. Since there was no way to signal queue that stream is already ended, the blocking queue.get() keeps on waiting until timeout occurs.
      • The solution was to signal queue.get() that stream is already ended by putting None in queue on exiting the main update() thread.
  • ScreenGear:
    • Fixed ScreenGear.read() blocked during cold startup.
    • 💬 During startup, ScreenGear.read() doesn't checks if queue is empty before continuing.
  • WriteGear:
    • Fixed gstpipeline_mode not activating when wrongly assuming output value as valid path.
    • Fixed name 'compression' is not defined bug.
    • Fixed AttributeError.
  • Helper:
    • Fixed fltp keyword in regex pattern causing non-ftlp streams to be not recognized.
    • Fixed response.headers returning content-length as Nonetype since it may not necessarily have the Content-Legth header set.
    • Fixed Linux video device paths still not working.
      • Moved helper.py logic to WriteGear and StreamGear APIs resp.
    • Fixed KeyError for empty metadata.
  • Setup:
    • Pinned pyzmq==24.0.1 working version.
    • Removed redundant patch for the issue.
  • Maintaince:
    • Fixed missing pkg name import_dependency_safe functions calls.
  • Bash Script:
    • Fixed gstreamer installation.
  • CI:
    • Fixed missing v4l2loopback apt dependency on Linux envs.
    • Added fix for RTCPeerConnection fails to create RTCDtlsTransport (Related issue: aiortc/aiortc#804)
      • Pinned cryptography==38.0.4 in dependencies.
    • Pinned Linux image to ubuntu-20.04 in github actions.
    • Fixed No module named 'httpx' bug.
      • Added httpx library import.
    • Fixed F821 undefined name bug.
    • Fixed Gstreamer bug.
  • Docs:
    • Fixed hyperlinks to new GitHub's form schemas.
    • Fixed non-rendering images in README.md
      • Replaced all relative image/gifs paths with absolute URLs in README.md.
    • Fixed badges/shields#8671 badge issue in README.md
    • Fixed GitLab CDN links throwing blocked by CORS policy bug.
      • Replaced gitlab GitHack CDN links with with bitbucket.
    • Fixed DASH playback failing by setting the maxAttempts to Infinity.
    • Removed x-sign glow-text effect CSS.
    • Fixed several typos (suggested by @timgates42)
    • Fixed coverage badge.
Pull Requests
New Contributors

 

 

v0.2.6 (2022-07-05)

New Features
  • Docs:
    • Added new bonus example for RTSP/RTP Live-Streaming using WriteGear's Compression Mode.
    • Added "How to resolve zmq.error.ZMQError" FAQ for NetGear API.(PR by @iandol)
    • Added new ko-fi button to README.md
    • Added new contributors block to changelog.md
  • Maintenance:
    • Added new patterns to .gitignore to ignore pypi's build directory and egg-info files.
  • CI:
    • Switched to new Issue GitHub's form schema using YAML
      • Added new bug_report.yaml.
      • Added new question.yaml.
      • Added new proposal.yaml.
      • Deleted depreciated markdown files.
      • Polished forms.
Updates/Improvements
  • Setup.py:
    • Bumped version to 0.2.6.
    • Updated logic operators and dependency.
      • Replaced >= comparsion operator with more flexible ~=.
      • Replaced distutils.version.LooseVersion with pkg_resources.parse_version.
  • Docs:
    • Updated Site Navigation.
      • Added new notices to inform users more effectively about bonus examples.
      • Added new Bonus section to navigation and moved suitable pages under it.
      • Updated headings and URLs.
    • Redesigned and Rewritten Donation and Contribution section to README.md
    • Updated Zenodo badge and Bibtex entry.
    • Updated Admonition Icon, FAQs and site-links.
    • Reformatted code and its comments.
    • Updated changelog.md.
  • API:
  • CI:
    • Added more paths and files to skip commits.
Breaking Updates/Changes
  • -input_framerate parameter now accepts any positive value for WriteGear and StreamGear APIs.
Bug-fixes
  • API:
    • Fixed -input_framerate less than 5 does not get used in WriteGear and StreamGear APIs.(PR by @freol35241)
  • CamGear: Fixed Yt-dlp generated HTTP DASH Segments URLs not supported by OpenCV's VideoCapture(PR by @DynamiteC)
  • StreamGear:
    • Fixed hls_segment_type not working bug. (PR by @enarche-ahn)
    • Fixed critical logging parameter bug
      • Fixed debug logs even when logging=False in StreamGear's Real-time Mode. (patch suggested by @enarche-ahn)
      • Added length check to -video_source attribute to correctly infers it as empty(or invalid).
  • CI:
    • Xfailed RTSP CamGear CI test.
    • Fixed pinned version syntax bug in docs_deployer workflow.
    • Fixed typos in Github forms and its context.
    • Added missing dependency.
  • Docs:
    • Fixed jinja2 3.1.0 or above breaks mkdocs.
      • jinja2>=3.1.0 breaks mkdocs (mkdocs/mkdocs#2799), therefore pinned jinja2 version to <3.1.0.
    • Fixed support for new mkdocstring versions
      • Replaced rendering sub-value with options.
      • Removed pinned mkdocstrings==0.17.0 version.
    • Fixed Netgear+Webgear bonus example code bugs.(PR by @iandol)
      • Added a missing import.
      • Removed self. typo.
      • Replaced the return value with break in the async as it triggers an error.
    • Fixed external bug that causing "Home" tab to irresponsive randomly when accessed from other tabs.
    • Fixed indentation and spacing.
    • Fixed typos and updated context.
    • Removed dead code.
Pull Requests
New Contributors

 

 

v0.2.5 (2021-02-11)

New Features
  • WriteGear:
    • Add support for GStreamer pipeline in WriteGear API's Non-Compression mode:
      • Implemented GStreamer Pipeline Mode to accept GStreamer pipeline as string to its output_filename parameter.
      • Added new special -gst_pipeline_mode attribute for its output_params parameter.
      • This feature provides flexible way to directly write video frames into GStreamer Pipeline with controlled bitrate.
      • Added new docs and updated existing docs with related changes.
    • Added new -ffpreheaders special attribute to WriteGear's options parameter:
      • This attribute is specifically required to set special FFmpeg parameters in Compression Mode that are present at the starting of command(such as -re).
      • This attribute only accepts list datatype as value.
      • Added related docs.
  • NetGear:
    • Added bidirectional data transfer support by extending Bidirectional mode support to exclusive Multi-Clients and Multi-Servers modes:
      • Users will now able to send data bidirectionally in both Multi-Clients and Multi-Servers exclusive modes.
      • Bidirectional mode will no longer disables automatically when Multi-Clients and Multi-Servers modes already enabled.
      • Added new docs and updated existing docs with related changes.
  • Maintenance:
    • Added official support for Python-3.10 legacies.
    • Added float value support to THREAD_TIMEOUT optional parameter.
    • Added info about dropped support for Python-3.6 legacies through announcement bar.
    • Added config.md file for Issue templates.
    • Added title to Issue templates.
  • Docs:
    • Added new Code Annotations
    • Added new icons to headings.
    • Added Advanced VideoGear usage example with CamGear backend.
Updates/Improvements
  • Setup.py:
    • Dropped support for Python-3.6 and below legacies.
    • Updated logging formatting.
    • Updated python_requires to >=3.7.
    • Bumped version to 0.2.5.
  • Helper:
    • Vidgear will now report current version on every run.
  • Docs:
    • Updated SSH tunneling docs context.
    • Excluded docs directory from CI envs.
    • Updated Zenodo badge and BibTeX entry.
    • Updated dark theme hue to 260.
    • Updated Admonitions.
    • Additional warnings against pushing PR against VidGear's testing branch only.
    • Updated code comments.
  • CI:
    • Removed support for Python-3.6 legacies from all workflows.
    • Updated NetGear's Exclusive Mode tests.
    • Added GStreamer Pipeline Mode tests.
  • Maintenance:
    • Updated Issue and PR templates.
    • Updated metadata.
Breaking Updates/Changes
  • Dropped support for Python-3.6 legacies from vidgear.
Bug-fixes
  • NetGear: Fixed bidirectional mode overriding multi-clients mode's data.
  • WriteGear:
    • Fixed wrongly defined ffmpeg_preheaders.
    • Fixed condition logic bugs.
    • Fixed UnboundLocalError bug.
  • Setup: Fixed uvicorn and aiortc dropped support for Python-3.6 legacies.
  • CI:
    • Fixed GitHub Actions interprets 3.10 as 3.1 if used without strings.
    • Fixed naming error in azure YAML.
  • Docs:
    • Fixed codecov badge URL in README.md
    • Fixed hyperlinks in README.
    • Fixed indentation and spacing.
    • Fixed typos and updated context.
    • Removed dead code.
  • Maintenance:
    • Removed depreciated condition checks.
Pull Requests

 

 

v0.2.4 (2021-12-05)

New Features
  • CamGear:
    • Added a new YT_backend Internal Class with YT-DLP backend:
      • Implemented YT_backend a new CamGear's Internal YT-DLP backend class for extracting metadata from Streaming URLs.
      • Added support for pipeling (live) video-frames from all yt-dlp supported streaming sites: https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md#supported-sites
      • Implemented algorithm from scratch for auto-extracting resolution specific streamable URLs for pipelineing.
      • Implemented logic for auto-calculating best and worst resolutions.
      • Added new ytv_metadata global parameter to CamGear for accessing video's metadata(such as duration, title, description) on-the-go.
      • ⚠️ Playlists are still unsupported.
  • WebGear_RTC:
    • Implemented a new easy way of defining Custom Streaming Class with suitable source(such as OpenCV):
      • Added new custom_stream attribute with WebGear_RTC options parameter that allows you to easily define your own Custom Streaming Class with suitable source(such as OpenCV).
      • This implementation supports repeated Auto-Reconnection or Auto-Refresh out-of-the-box.
      • This implementation is more user-friendly and easy to integrate within complex APIs.
      • This implementation requires at-least read() and stop() methods implemented within Custom Streaming Class, otherwise WebGear_RTC will throw ValueError.
      • This implementation supports all vidgear's VideoCapture APIs readily as input.
  • Maintenance:
    • Added new .gitignore for specifying intentionally untracked files to ignore
      • Added more files entries to .gitignore.
    • Added new .gitattributes to manage how Git reads line endings.
      • Enabled auto default behavior, in case people don't have core.autocrlf set.
      • Enforced LF line-endings for selective files types.
      • Added Binary data files that specifies they are not text, and git should not try to change them.
      • Added Language aware diff headers.
      • Added Linguist language overrides.
  • Docs:
    • Added bonus example to add real-time file audio encoding with VideoGear and Stabilizer class.
    • Added complete usage docs with new CamGear's Internal Class with YT-DLP backend.
    • Added instructions to extract video's metadata in CamGear.
    • Added donation link in page footer with bouncing heart animation through pure CSS.
    • Added info about critical changes in v0.2.4 and above installation through new announcement bar.
    • Added related usage docs for new WebGear_RTC custom streaming class.
    • Added changes for upgrading mkdocs-material from v7.x to newer v8.x.
    • Added outdated version warning block.
Updates/Improvements
  • CamGear:
    • Added is_livestream global YT_backend parameters.
    • Added default options for yt-dlp for extracting info_dict(metadata) of the video as a single JSON line.
    • Completely removed old logic for extracting streams using pafy.
    • Removed all dead code related to streamlink backend.
  • Setup.py:
    • Moved all API specific dependencies to extra_requires under the name "core". [PR #268 by @zpapakipos]
    • Added rule to replace GitHub heading links in description.
    • Updated extra_require dependencies.
    • Removed streamlink dependency.
    • Removed pafy dependency.
    • Removed pyzmq from latest_version group.
    • Updated SEO Keywords.
  • Docs:
    • Re-written pip and source installation docs.
    • Added warning for using -disable_force_termination flag for short duration videos.
    • Added permalink_title entry to mkdocs.yml.
    • Updated CamGear parameters.
    • Updated Admonitions with related information.
    • Updated Functional Block Diagram(gears_fbd.png) image.
    • Updated installation instructions.
    • Updated Advanced examples using WebGear_RTC's custom streaming class.
    • Updated code highlighting.
    • Updated zenodo badge.
    • Updated BibTex for project citation.
    • Replaced incorrect API parameter docs.
    • Updated WebGear_RTC parameters.
  • CI:
    • Updated CI tests for new WebGear_RTC custom streaming class.
    • Restored test_stream_mode CamGear test.
    • Updated Streaming Sites test links.
    • Added more tests cases.
  • Maintenance:
    • Updated spacing in logger formatting.
    • Renamed Asyncio Helper logger name.
    • Changed logging colors.
    • Updated logging messages.
Breaking Updates/Changes
  • Installation command with pip has been changed in v0.2.4:
    • The legacy pip install vidgear command now installs critical bare-minimum dependencies only. Therefore in order to automatically install all the API specific dependencies as previous versions, use pip install vidgear[core] command instead.
  • CamGear:
    • Removed streamlink backend support from stream_mode in favor of more reliable CamGear's Internal YT-DLP backend class for extracting metadata from Streaming URLs.
      • CamGear will raise ValueError if streaming site URL is unsupported by yt-dlp backend.
      • CamGear will raise ValueError if yt-dlp isn't installed and stream_mode is enabled.
    • Removed automatic enforcing of GStreamer backend for YouTube-livestreams and made it optional.
      • The CamGear will not raise ValueError if GStreamer support is missing in OpenCV backends.
  • WebGear_RTC:
    • Removed support for assigning Custom Media Server Class(inherited from aiortc's VideoStreamTrack) in WebGear_RTC through its config global parameter.
    • WebGear_RTC API will now throws ValueError if source parameter is NoneType as well as custom_stream attribute is undefined.
  • Helper:
    • Removed restore_levelnames method.
    • Removed youtube_url_validator helper method.
Bug-fixes
  • CamGear:
    • Fixed KeyError Bug for missing attributed in meta_data json in some streaming sites.
  • Helper:
    • Removed unused imports.
  • Docs:
    • Removed slugify from mkdocs which was causing invalid hyperlinks in docs.
    • Fixed GitHub hyperlinks in README.md.
    • Fixed hyperlink in announcement bar.
    • Fixed content tabs failing to work.
    • Fixed line-endings and usage example code.
    • Removed any pafy and streamlink references.
    • Fixed context and typos.
  • CI:
    • Fixed NameError bugs in WebGear_RTC CI test.
  • Maintenance:
    • Removed dead logger code causing Python's Built-in logging module to hide logs.
    • Removed unused logging import.
    • Updated code comments.
Pull Requests
New Contributors

 

 

v0.2.3 (2021-10-27)

New Features
  • CamGear:
    • Added support for 4K Streaming URLs.
  • Helper:
    • Implemented logging ColorFormatter string alignment.
      • Center aligned logging Level-name and Class-name.
      • Changed % formatting style with modern {.
      • Re-added asctime value to Formatter string.
      • Re-arranged parameter positions in Formatter string.
  • Maintenance:
    • Added new .gitignore for specifying intentionally untracked files to ignore
      • Added more files entries to .gitignore.
    • Added new .gitattributes to manage how Git reads line endings.
      • Enabled auto default behavior, in case people don't have core.autocrlf set.
      • Enforced LF line-endings for selective files types.
      • Added Binary data files that specifies they are not text, and git should not try to change them.
      • Added Language aware diff headers.
      • Added Linguist language overrides.
  • Docs:
    • Added new ScreenGear with WebGear_RTC API bonus example.
    • Added support for hl_lines argument for highlighting specific code lines.
    • Added drop-shadow effects for its slate theme to improve visibility.
Updates/Improvements
  • CamGear:
    • Replaced youtube-dl with yt-dlp as pafy backend for YouTube videos pipelining.
      • Implemented hack to trick pafy into assuming yt-dlp as youtube-dl.
      • Using sys.modules to present yt-dlp as youtube-dl.
      • yt-dlp python API functions exactly similar to youtube-dl.
      • Replaced youtube-dl dependency with yt-dlp.
      • Replaced youtube-dl imports with yt-dlp.
  • StreamGear:
    • Updated default stream_count internal dict key value to 1.
  • Maintenance:
    • Introduced python short-circuiting for handling logging logic.
    • Enabled logging for check_WriteAccess method in WriteGear, StreamGear and NetGear APIs.
  • Docs:
    • Added warning for ScreenGear outputting RGBA frames instead of default BGR frames with mss backend.
    • Added warnings for properly formatting output_params when assigning external audio-source in WriteGear.
    • Added depreciation notice for Python 3.6 legacies.
    • Restructured docs to make it more user-friendly.
    • Updated, Extended and Improved context.
    • Improved code comments.
    • Updated docs admonitions.
    • Updated Zenodo badge.
  • CI:
    • Migrated to new Codecov Uploader in Azure Pipelines.
    • Replaced env with export in ci_linux.yml.
    • Replaced bubkoo/needs-more-info@v1 with wow-actions/needs-more-info@v1.
    • Added codecov secret token through env variable.
    • Added wildcard to skip CI tests for doc(.md) files.
    • Added .md files to Codecov ignore list.
    • Update vidgear's banner image.
Breaking Updates/Changes
  • check_WriteAccess will now return as invalid path if writing directory does not exists. This will effect output file handling in WriteGear and StreamGear APIs.
Bug-fixes
  • StreamGear:
    • Fixed StreamGear Malformed URI Error with HLS Segments [PR #243 by @Vboivin]
      • Removed the extra '%' character from the naming convention for segment files.
      • Used stream_count internal dict variable to alter template for HLS segment filenames.
  • WriteGear:
    • Fixed bug in disable_force_termination logic which accidentally disables force termination.
  • WebGear_RTC:
    • Fixed name 'VideoStreamTrack' is not defined bug.
  • Setup.py:
    • Fixed TypeError bug.
    • Fixed invalid latest_version retrieval.
  • Helper:
    • Fixed check_WriteAccess failing to recognize correct permission for writing the output file on windows platform.
      • Implemented separate logic for Windows and *nix platforms.
      • Added new stat import.
      • Improved warnings and error handling.
      • Added logging parameter to check_WriteAccess.
    • Fixed bug in check_WriteAccess that throws OSError while handling URLs.
  • Docs:
    • Fixed bugs in WriteGear's Compression Mode with Live Audio Input example.
    • Fixed "drop-shadow" property via filter function conflicting with sidecard button.
      • Added new CSS classes for image, admonitions and code highlight in dark theme.
    • Several internal and external webpage links typos fixed.
    • Fixed several language typos.
  • CI:
    • Fixed Azure Pipeline coverage upload bugs.
    • Fixed random errors in CamGear stream_mode test.
  • Bash:
    • Removed the Windows carriage returns from the shell scripts to be able to execute them on Linux.
  • Fixed logging comments.
Pull Requests
New Contributors

 

 

v0.2.2 (2021-09-02)

New Features
  • StreamGear:
    • Native Support for Apple HLS Multi-Bitrate Streaming format:
      • Added support for new Apple HLS (HTTP Live Streaming) HTTP streaming format in StreamGear.
      • Implemented default workflow for auto-generating primary HLS stream of same resolution and framerate as source.
      • Added HLS support in Single-Source and Real-time Frames Modes.
      • Implemented inherit support for fmp4 and mpegts HLS segment types.
      • Added adequate default parameters required for trans-coding HLS streams.
      • Added native support for HLS live-streaming.
      • Added "hls" value to format parameter for easily selecting HLS format.
      • Added HLS support in -streams attribute for transcoding additional streams.
      • Added support for .m3u8 and .ts extensions in clear_prev_assets workflow.
      • Added validity check for .m3u8 extension in output when HLS format is used.
      • Separated DASH and HLS command handlers.
      • Created HLS format exclusive parameters.
      • Implemented -hls_base_url FFMpeg parameter support.
    • Added support for audio input from external device:
      • Implemented support for audio input from external device.
      • Users can now easily add audio device and decoder by formatting them as python list.
      • Modified -audio parameter to support list data type as value.
      • Modified validate_audio helper function to validate external audio devices.
    • Added -seg_duration to control segment duration.
  • NetGear:
    • New SSH Tunneling Mode for remote connection:
      • New SSH Tunneling Mode for connecting ZMQ sockets across machines via SSH tunneling.
      • Added new ssh_tunnel_mode attribute to enable ssh tunneling at provide address at server end only.
      • Implemented new check_open_port helper method to validate availability of host at given open port.
      • Added new attributes ssh_tunnel_keyfile and ssh_tunnel_pwd to easily validate ssh connection.
      • Extended this feature to be compatible with bi-directional mode and auto-reconnection.
      • Disabled support for exclusive Multi-Server and Multi-Clients modes.
      • Implemented logic to automatically enable paramiko support if installed.
      • Reserved port-47 for testing.
    • Additional colorspace support for input frames with Frame-Compression enabled:
      • Allowed to manually select colorspace on-the-fly with JPEG frame compression.
      • Updated jpeg_compression dict parameter to support colorspace string values.
      • Added all supported colorspace values by underline simplejpeg library.
      • Server enforced frame-compression colorspace on client(s).
      • Enable "BGR" colorspace by default.
      • Added Example for changing incoming frames colorspace with NetGear's Frame Compression.
      • Updated Frame Compression parameters in NetGear docs.
      • Updated existing CI tests to cover new frame compression functionality.
  • NetGear_Async:
    • New exclusive Bidirectional Mode for bidirectional data transfer:
      • NetGear_Async's first-ever exclusive Bidirectional mode with pure asyncio implementation.
      • Bidirectional mode is only available with User-defined Custom Source(i.e. source=None)
      • Added support for PAIR & REQ/REP bidirectional patterns for this mode.
      • Added powerful asyncio.Queues for handling user data and frames in real-time.
      • Implemented new transceive_data method to Transmit (in Recieve mode) and Receive (in Send mode) data in real-time.
      • Implemented terminate_connection internal asyncio method to safely terminate ZMQ connection and queues.
      • Added msgpack automatic compression encoding and decoding of data and frames in bidirectional mode.
      • Added support for np.ndarray video frames.
      • Added new bidirectional_mode attribute for enabling this mode.
      • Added 8-digit random alphanumeric id generator for each device.
      • NetGear_Async will throw RuntimeError if bidirectional mode is disabled at server or client but not both.
    • Added new disable_confirmation used to force disable termination confirmation from client in terminate_connection.
    • Added task_done() method after every get() call to gracefully terminate queues.
    • Added new secrets and string imports.
  • WebGear:
    • Updated JPEG Frame compression with simplejpeg:
      • Implemented JPEG compression algorithm for 4-5% performance boost at cost of minor loss in quality.
      • Utilized encode_jpeg and decode_jpeg methods to implement turbo-JPEG transcoding with simplejpeg.
      • Added new options to control JPEG frames quality, enable fastest dct, fast upsampling to boost performance.
      • Added new jpeg_compression, jpeg_compression_quality, jpeg_compression_fastdct, jpeg_compression_fastupsample attributes.
      • Enabled fast dct by default with JPEG frames at 90%.
      • Incremented default frame reduction to 25%.
      • Implemented automated grayscale colorspace frames handling.
      • Updated old and added new usage examples.
      • Dropped support for depreciated attributes from WebGear and added new attributes.
    • Added new WebGear Theme: (Checkout at https://github.com/abhiTronix/vidgear-vitals)
      • Added responsive image scaling according to screen aspect ratios.
      • Added responsive text scaling.
      • Added rounded border and auto-center to image tag.
      • Added bootstrap css properties to implement auto-scaling.
      • Removed old resize() hack.
      • Improved text spacing and weight.
      • Integrated toggle full-screen to new implementation.
      • Hide Scrollbar both in WebGear_RTC and WebGear Themes.
      • Beautify files syntax and updated files checksum.
      • Refactor files and removed redundant code.
      • Bumped theme version to v0.1.2.
  • WebGear_RTC:
    • Added native support for middlewares:
      • Added new global middleware variable for easily defining Middlewares as list.
      • Added validity check for Middlewares.
      • Added tests for middlewares support.
      • Added example for middlewares support.
      • Extended middlewares support to WebGear API too.
      • Added related imports.
    • Added new WebGear_RTC Theme: (Checkout at https://github.com/abhiTronix/vidgear-vitals)
      • Implemented new responsive video scaling according to screen aspect ratios.
      • Added bootstrap CSS properties to implement auto-scaling.
      • Removed old resize() hack.
      • Beautify files syntax and updated files checksum.
      • Refactored files and removed redundant code.
      • Bumped theme version to v0.1.2
  • Helper:
    • New automated interpolation selection for gears:
      • Implemented retrieve_best_interpolation method to automatically select best available interpolation within OpenCV.
      • Added support for this method in WebGear, WebGear_RTC and Stabilizer Classes/APIs.
      • Added new CI tests for this feature.
    • Implemented get_supported_demuxers method to get list of supported demuxers.
  • CI:
    • Added new no-response work-flow for stale issues.
    • Added new CI tests for SSH Tunneling Mode.
    • Added paramiko to CI dependencies.
    • Added support for "hls" format in existing CI tests.
    • Added new functions check_valid_m3u8 and extract_meta_video for validating HLS files.
    • Added new m3u8 dependency to CI workflows.
    • Added complete CI tests for NetGear_Async's new Bidirectional Mode:
      • Implemented new exclusive Custom_Generator class for testing bidirectional data dynamically on server-end.
      • Implemented new exclusive client_dataframe_iterator method for testing bidirectional data on client-end.
      • Implemented test_netgear_async_options and test_netgear_async_bidirectionalmode two new tests.
      • Added timeout value on server end in CI tests.
  • Setup.py:
    • Added new cython and msgpack dependency.
    • Added msgpack and msgpack_numpy to auto-install latest.
  • BASH:
    • Added new temp_m3u8 folder for generating M3U8 assets in CI tests.
  • Docs:
    • Added docs for new Apple HLS StreamGear format:
      • Added StreamGear HLS transcoding examples for both StreamGear modes.
      • Updated StreamGear parameters to w.r.t new HLS configurations.
      • Added open-sourced "Sintel" - project Durian Teaser Demo with StreamGear's HLS stream using Clappr and raw.githack.com.
      • Added new HLS chunks at https://github.com/abhiTronix/vidgear-docs-additionals for StreamGear
      • Added support for HLS video in Clappr within custom.js using HlsjsPlayback plugin.
      • Added support for Video Thumbnail preview for HLS video in Clappr within custom.js
      • Added hlsjs-playback.min.js JS script and suitable configuration for HlsjsPlayback plugin.
      • Added custom labels for quality levels selector in custom.js.
      • Added new docs content related to new Apple HLS format.
      • Updated DASH chunk folder at https://github.com/abhiTronix/vidgear-docs-additionals.
      • Added example for audio input support from external device in StreamGear.
      • Added steps for using -audio attribute on different OS platforms in StreamGear.
    • Added usage examples for NetGear_Async's Bidirectional Mode:
      • Added new Usage examples and Reference doc for NetGear_Async's Bidirectional Mode.
      • Added new image asset for NetGear_Async's Bidirectional Mode.
      • Added NetGear_Async's option parameter reference.
      • Updated NetGear_Async definition in docs.
      • Changed font size for Helper methods.
      • Renamed Bonus section to References in mkdocs.yml.
    • Added Gitter sidecard embed widget:
      • Imported gitter-sidecar script to main.html.
      • Updated custom.js to set global window option.
      • Updated Sidecard UI in custom.css.
    • Added bonus examples to help section:
      • Implemented a curated list of more advanced examples with unusual configuration for each API.
    • Added several new contents and updated context.
    • Added support for search suggestions, search highlighting and search sharing (i.e. deep linking)
    • Added more content to docs to make it more user-friendly.
    • Added warning that JPEG Frame-Compression is disabled with Custom Source in WebGear.
    • Added steps for identifying and specifying sound card on different OS platforms in WriteGear.
    • Added Zenodo DOI badge and its reference in BibTex citations.
    • Added extra.homepage parameter, which allows for setting a dedicated URL for site_url.
    • Added pymdownx.striphtml plugin for stripping comments.
    • Added complete docs for SSH Tunneling Mode.
    • Added complete docs for NetGear's SSH Tunneling Mode.
    • Added pip upgrade related docs.
    • Added docs for installing vidgear with only selective dependencies
    • Added new advance/experiment admonition with new background color.
    • Added new icons SVGs for advance and warning admonition.
    • Added new usage example and related information.
    • Added new image assets for ssh tunneling example.
    • Added new admonitions
    • Added new FAQs.
Updates/Improvements
  • VidGear Core:
    • New behavior to virtually isolate optional API specific dependencies by silencing ImportError on all VidGear's APIs import.
    • Implemented algorithm to cache all imports on startup but silence any ImportError on missing optional dependency.
    • Now ImportError will be raised only any certain API specific dependency is missing during given API's initialization.
    • New import_dependency_safe to imports specified dependency safely with importlib module.
    • Replaced all APIs imports with import_dependency_safe.
    • Added support for relative imports in import_dependency_safe.
    • Implemented error parameter to by default ImportError with a meaningful message if a dependency is missing, Otherwise if error = log a warning will be logged and on error = silent everything will be quit. But If a dependency is present, but older than specified, an error is raised if specified.
    • Implemented behavior that if a dependency is present, but older than min_version specified, an error is raised always.
    • Implemented custom_message to display custom message on error instead of default one.
    • Implemented separate import_core_dependency function to import and check for specified core dependency.
    • ImportError will be raised immediately if core dependency not found.
  • StreamGear:
    • Replaced depreciated -min_seg_duration flag with -seg_duration.
    • Removed redundant -re flag from RTFM.
    • Improved Live-Streaming performance by disabling SegmentTimline
    • Improved DASH assets detection for removal by using filename prefixes.
  • NetGear:
    • Replaced np.newaxis with np.expand_dims.
    • Replaced random module with secrets while generating system ID.
    • Update array indexing with np.copy.
  • NetGear_Async:
    • Improved custom source handling.
    • Removed deprecated loop parameter from asyncio methods.
    • Re-implemented skip_loop parameter in close() method.
    • run_until_complete will not used if skip_loop is enabled.
    • skip_loop now will create asyncio task instead and will enable disable_confirmation by default.
    • Replaced create_task with ensure_future to ensure backward compatibility with python-3.6 legacies.
    • Simplified code for transceive_data method.
  • WebGear_RTC:
    • Improved handling of failed ICE connection.
    • Made is_running variable globally available for internal use.
  • Helper:
    • Added 4320p resolution support to dimensions_to_resolutions method.
    • Implemented new delete_file_safe to safely delete files at given path.
    • Replaced os.remove calls with delete_file_safe.
    • Added support for filename prefixes in delete_ext_safe method.
    • Improved and simplified create_blank_frame functions frame channels detection.
    • Added logging parameter to capPropId function to forcefully discard any error(if required).
  • Setup.py:
  • Asyncio:
    • Changed asyncio.sleep value to 0.
      • The amount of time sleep is irrelevant; the only purpose await asyncio.sleep() serves is to force asyncio to suspend execution to the event loop, and give other tasks a chance to run. Also, await asyncio.sleep(0) will achieve the same effect. https://stackoverflow.com/a/55782965/10158117
  • License:
    • Dropped publication year range to avoid confusion. (Signed and Approved by @abhiTronix)
    • Updated Vidgear license's year of first publication of the work in accordance with US copyright notices defined by Title 17, Chapter 4(Visually perceptible copies): https://www.copyright.gov/title17/92chap4.html
    • Reflected changes in all copyright notices.
  • CI:
    • Updated macOS VM Image to latest in azure devops.
    • Updated VidGear Docs Deployer Workflow.
    • Updated WebGear_RTC CI tests.
    • Removed redundant code from CI tests.
    • Updated tests to increase coverage.
    • Enabled Helper tests for python 3.8+ legacies.
    • Enabled logging in validate_video method.
    • Added -hls_base_url to streamgear tests.
    • Update mpegdash dependency to 0.3.0-dev2 version in Appveyor.
    • Updated CI tests for new HLS support
    • Updated CI tests from scratch for new native HLS support in StreamGear.
    • Updated test patch for StreamGear.
    • Added exception for RunTimeErrors in NetGear CI tests.
    • Added more directories to Codecov ignore list.
    • Imported relative logger_handler for asyncio tests.
  • Docs:
    • Re-positioned few docs comments at bottom for easier detection during stripping.
    • Updated to new extra analytics parameter in Material Mkdocs.
    • Updated dark theme to dark orange.
    • Changed fonts => text: Muli & code: Fira Code
    • Updated fonts to Source Sans Pro.
    • Updated setup.py update-link for modules.
    • Re-added missing StreamGear Code docs.
    • Several minor tweaks and typos fixed.
    • Updated 404.html page.
    • Updated admonitions colors and beautified custom.css.
    • Replaced VideoGear & CamGear with OpenCV in CPU intensive examples.
    • Updated mkdocs.yml with new changes and URLs.
    • Moved FAQ examples to bonus examples.
    • Moved StreamGear primary modes to separate sections for better readability.
    • Implemented separate overview and usage example pages for StreamGear primary modes.
    • Improved StreamGear docs context and simplified language.
    • Renamed StreamGear overview page to introduction.
    • Re-written Threaded-Queue-Mode from scratch with elaborated functioning.
    • Replace Paypal with Liberpay in FUNDING.yml.
    • Updated FFmpeg Download links.
    • Reverted UI change in CSS.
    • Updated changelog.md and fixed clutter.
    • Updated README.md and mkdocs.yml with new additions
    • Updated context for CamGear example.
    • Restructured and added more content to docs.
    • Updated comments in source code.
    • Removed redundant data table tweaks from custom.css.
    • Re-aligned badges in README.md.
    • Beautify custom.css.
    • Updated mkdocs.yml.
    • Updated context and fixed typos.
    • Added missing helper methods in Reference.
    • Updated Admonitions.
    • Updates images assets.
    • Bumped CodeCov.
  • Logging:
    • Improved logging level-names.
    • Updated logging messages.
  • Minor tweaks to needs-more-info template.
  • Updated issue templates and labels.
  • Removed redundant imports.
Breaking Updates/Changes
  • Virtually isolated all API specific dependencies, Now ImportError for API-specific dependencies will be raised only when any of them is missing at API's initialization.
  • Renamed delete_safe to delete_ext_safe.
  • Dropped support for frame_jpeg_quality, frame_jpeg_optimize, frame_jpeg_progressive attributes from WebGear.
Bug-fixes
  • CamGear:
    • Hot-fix for Live Camera Streams:
      • Added new event flag to keep check on stream read.
      • Implemented event wait for read() to block it when source stream is busy.
      • Added and Linked THREAD_TIMEOUT with event wait timout.
      • Improved backward compatibility of new additions.
    • Enforced logging for YouTube live.
  • NetGear:
    • Fixed Bidirectional Video-Frame Transfer broken with frame-compression:
      • Fixed return_data interfering with return JSON-data in receive mode.
      • Fixed logic.
    • Fixed color-subsampling interfering with colorspace.
    • Patched external simplejpeg bug. Issue: https://gitlab.com/jfolz/simplejpeg/-/issues/11
      • Added np.squeeze to drop grayscale frame's 3rd dimension on Client's end.
    • Fixed bug that cause server end frame dimensions differ from client's end when frame compression enabled.
  • NetGear_Async:
    • Fixed bug related asyncio queue freezing on calling join().
    • Fixed ZMQ connection bugs in bidirectional mode.
    • Fixed several critical bugs in event loop handling.
    • Fixed several bugs in bidirectional mode implementation.
    • Fixed missing socket termination in both server and client end.
    • Fixed timeout parameter logic.
    • Fixed typos in error messages.
  • WebGear_RTC:
    • Fixed stream freezes after web-page reloading:
      • Implemented new algorithm to continue stream even when webpage is reloaded.
      • Inherit and modified next_timestamp VideoStreamTrack method for generating accurate timestamps.
      • Implemented reset_connections callable to reset all peer connections and recreate Video-Server timestamps. (Implemented by @kpetrykin)
      • Added close_connection endpoint in JavaScript to inform server page refreshing.(Thanks to @kpetrykin)
      • Added exclusive reset connection node /close_connection in routes.
      • Added reset() method to Video-Server class for manually resetting timestamp clock.
      • Added reset_enabled flag to keep check on reloads.
      • Fixed premature webpage auto-reloading.
      • Added additional related imports.
    • Fixed web-page reloading bug after stream ended:
      • Disable webpage reload behavior handling for Live broadcasting.
      • Disable reload CI test on Windows machines due to random failures.
      • Improved handling of failed ICE connection.
    • Fixed Assertion error bug:
      • Source must raise MediaStreamError when stream ends instead of returning None-type.
  • WebGear
    • Removed format specific OpenCV decoding and encoding support for WebGear.
  • Helper:
    • Regex bugs fixed:
      • New improved regex for discovering supported encoders in get_supported_vencoders.
      • Re-implemented check for extracting only valid output protocols in is_valid_url.
      • Minor tweaks for better regex compatibility.
    • Bugfix related to OpenCV import:
      • Bug fixed for OpenCV import comparison test failing with Legacy versions and throwing ImportError.
      • Replaced packaging.parse_version with more robust distutils.version.
    • Fixed bug with create_blank_frame that throws error with gray frames:
      • Implemented automatic output channel correction inside create_blank_frame function.
      • Extended automatic output channel correction support to asyncio package.
    • Implemented RTSP protocol validation as demuxer, since it's not a protocol but a demuxer.
    • Removed redundant logger_handler, mkdir_safe, retrieve_best_interpolation, capPropId helper functions from asyncio package. Relatively imported helper functions from non-asyncio package.
    • Removed unused aiohttp dependency.
    • Removed asctime formatting from logging.
  • StreamGear:
    • Fixed Multi-Bitrate HLS VOD streams:
      • Re-implemented complete workflow for Multi-Bitrate HLS VOD streams.
      • Extended support to both Single-Source and Real-time Frames Modes.
    • Fixed bugs with audio-video mapping.
    • Fixed master playlist not generating in output.
    • Fixed improper -seg_duration value resulting in broken pipeline.
    • Fixed expected aspect ratio not calculated correctly for additional streams.
    • Fixed stream not terminating when provided input from external audio device.
    • Fixed bugs related to external audio not mapped correctly in HLS format.
    • Fixed OPUS audio fragments not supported with MP4 video in HLS.
    • Fixed unsupported high audio bit-rate bug.
  • Setup.py:
    • Fixed latest_version returning incorrect version for some PYPI packages.
    • Removed latest_version variable support from simplejpeg.
    • Fixed streamlink only supporting requests==2.25.1 on Windows.
    • Removed all redundant dependencies like colorama, aiofiles, aiohttp.
    • Fixed typos in dependencies.
  • Setup.cfg:
    • Replaced dashes with underscores to remove warnings.
  • CI:
    • Replaced buggy starlette.TestClient with async-asgi-testclient in WebGear_RTC
    • Removed run() method and replaced with pure asyncio implementation.
    • Added new async-asgi-testclient CI dependency.
    • Fixed fake_picamera class logger calling vidgear imports prematurely before importing picamera class in tests.
      • Implemented new fake_picamera class logger inherently with logging module.
      • Moved sys.module logic for faking to init.py.
      • Added __init__.py to ignore in Codecov.
    • Fixed event loop closing prematurely while reloading:
      • Internally disabled suspending event loop while reloading.
    • Event Policy Loop patcher added for WebGear_RTC tests.
    • Fixed return_assets_path path bug.
    • Fixed typo in TimeoutError exception import.
    • Fixed eventloop is already closed bug.
    • Fixed eventloop bugs in Helper CI tests.
    • Fixed several minor bugs related to new CI tests.
    • Fixed bug in PiGear tests.
  • Docs:
    • Fixed 404 page does not work outside the site root with mkdocs.
    • Fixed markdown files comments not stripped when converted to HTML.
    • Fixed missing heading in VideoGear.
    • Typos in links and code comments fixed.
    • Several minor tweaks and typos fixed.
    • Fixed improper URLs/Hyperlinks and related typos.
    • Fixed typos in usage examples.
    • Fixed redundant properties in CSS.
    • Fixed bugs in mkdocs.yml.
    • Fixed docs contexts and typos.
    • Fixed stream.release() missing in docs.
    • Fixed several typos in code comments.
    • Removed dead code from docs.
  • Refactored Code and reduced redundancy.
  • Fixed shutdown in main.py.
  • Fixed logging comments.
Pull Requests
New Contributors

 

 

v0.2.1 (2021-04-25)

New Features
  • WebGear_RTC:
    • A new API that is similar to WeGear API in all aspects but utilizes WebRTC standard instead of Motion JPEG for streaming.
    • Now it is possible to share data and perform teleconferencing peer-to-peer, without requiring that the user install plugins or any other third-party software.
    • Added a flexible backend for aiortc - a python library for Web Real-Time Communication (WebRTC).
    • Integrated all functionality and parameters of WebGear into WebGear_RTC API.
    • Implemented JSON Response with a WebRTC Peer Connection of Video Server.
    • Added a internal RTC_VideoServer server on WebGear_RTC, a inherit-class to aiortc's VideoStreamTrack API.
    • New Standalone UI Default theme v0.1.1 for WebGear_RTC from scratch without using 3rd-party assets. (by @abhiTronix)
    • New custom.js and custom.css for custom responsive behavior.
    • Added WebRTC support to custom.js and ensured compatibility with WebGear_RTC.
    • Added example support for ICE framework and STUN protocol like WebRTC features to custom.js.
    • Added resize() function to custom.js to automatically adjust video & img tags for smaller screens.
    • Added WebGear_RTC support in main.py for easy access through terminal using --mode flag.
    • Integrated all WebGear_RTC enhancements to WebGear Themes.
    • Added CI test for WebGear_RTC.
    • Added complete docs for WebGear_RTC API.
    • Added bare-minimum as well as advanced examples usage code.
    • Added new theme images.
    • Added Reference and FAQs.
  • CamGear API:
    • New Improved Pure-Python Multiple-Threaded Implementation:
      • Optimized Threaded-Queue-Mode Performance. (PR by @bml1g12)
      • Replaced regular queue.full checks followed by sleep with implicit sleep with blocking queue.put.
      • Replaced regular queue.empty checks followed by queue.
      • Replaced nowait_get with a blocking queue.get natural empty check.
      • Up-to 2x performance boost than previous implementations.
    • New THREAD_TIMEOUT attribute to prevent deadlocks:
      • Added support for THREAD_TIMEOUT attribute to its options parameter.
      • Updated CI Tests and docs.
  • WriteGear API:
    • New more robust handling of default video-encoder in compression mode:
      • Implemented auto-switching of default video-encoder automatically based on availability.
      • API now selects Default encoder based on priority: "libx264" > "libx265" > "libxvid" > "mpeg4".
      • Added get_supported_vencoders Helper method to enumerate Supported Video Encoders.
      • Added common handler for -c:v and -vcodec flags.
  • NetGear API:
    • New Turbo-JPEG compression with simplejpeg
      • Implemented JPEG compression algorithm for 4-5% performance boost at cost of minor loss in quality.
      • Utilized encode_jpeg and decode_jpeg methods to implement turbo-JPEG transcoding with simplejpeg.
      • Added options to control JPEG frames quality, enable fastest dct, fast upsampling to boost performance.
      • Added new jpeg_compression, jpeg_compression_quality, jpeg_compression_fastdct, jpeg_compression_fastupsample attributes.
      • Enabled fast dct by default with JPEG frames at 90%.
      • Added Docs for JPEG Frame Compression.
  • WebGear API:
    • New modular and flexible configuration for Custom Sources:
      • Implemented more convenient approach for handling custom source configuration.
      • Added new config global variable for this new behavior.
      • Now None-type source parameter value is allowed for defining own custom sources.
      • Added new Example case and Updates Docs for this feature.
      • Added new CI Tests.
    • New Browser UI Updates:
      • New Standalone UI Default theme v0.1.0 for browser (by @abhiTronix)
      • Completely rewritten theme from scratch with only local resources.
      • New custom.js and custom.css for custom responsive behavior.
      • New sample glow effect with css.
      • New sample click to full-screen behavior with javascript.
      • Removed all third-party theme dependencies.
      • Update links to new github server abhiTronix/vidgear-vitals
      • Updated docs with new theme's screenshots.
    • Added enable_infinite_frames attribute for enabling infinite frames.
    • Added New modular and flexible configuration for Custom Sources.
    • Bumped WebGear Theme Version to v0.1.1.
    • Updated Docs and CI tests.
  • ScreenGear API:
    • Implemented Improved Pure-Python Multiple-Threaded like CamGear.
    • Added support for THREAD_TIMEOUT attribute to its options parameter.
  • StreamGear API:
    • Enabled pseudo live-streaming flag re for live content.
  • Docs:
    • Added new native docs versioning to mkdocs-material.
    • Added new examples and few visual tweaks.
    • Updated Stylesheet for versioning.
    • Added new DASH video chunks at https://github.com/abhiTronix/vidgear-docs-additionals for StreamGear and Stabilizer streams.
    • Added open-sourced "Tears of Steel" * project Mango Teaser video chunks.
    • Added open-sourced "Subspace Video Stabilization" http://web.cecs.pdx.edu/~fliu/project/subspace_stabilization/ video chunks.
    • Added support for DASH Video Thumbnail preview in Clappr within custom.js.
    • Added responsive clappr DASH player with bootstrap's embed-responsive.
    • Added new permalink icon and slugify to toc.
    • Added "back-to-top" button for easy navigation.
  • Helper:
    • New GitHub Mirror with latest Auto-built FFmpeg Static Binaries:
      • Replaced new GitHub Mirror abhiTronix/FFmpeg-Builds in helper.py
      • New CI maintained Auto-built FFmpeg Static Binaries.
      • Removed all 3rd-party and old links for better compatibility and Open-Source reliability.
      • Updated Related CI tests.
    • Added auto-font-scaling for create_blank_frame method.
    • Added c_name parameter to generate_webdata and download_webdata to specify class.
    • A more robust Implementation of Downloading Artifacts:
      • Added a custom HTTP TimeoutHTTPAdapter Adapter with a default timeout for all HTTP calls based on this GitHub comment.
      • Implemented http client and the send() method to ensure that the default timeout is used if a timeout argument isn't provided.
      • Implemented Requests sessionwith block to exit properly even if there are unhandled exceptions.
      • Add a retry strategy to custom TimeoutHTTPAdapter Adapter with max 3 retries and sleep(backoff_factor=1) between failed requests.
    • Added create_blank_frame method to create bland frames with suitable text.
  • [CI] Continuous Integration:
    • Added new fake frame generated for fake picamera class with numpy.
    • Added new create_bug parameter to fake picamera class for emulating various artificial bugs.
    • Added float/int instance check on time_delay for camgear and pigear.
    • Added EXIT_CODE to new timeout implementation for pytests to upload codecov report when no timeout.
    • Added auxiliary classes to fake picamera for facilitating the emulation.
    • Added new CI tests for PiGear Class for testing on all platforms.
    • Added shutdown() function to gracefully terminate WebGear_RTC API.
    • Added new coreutils brew dependency.
    • Added handler for variable check on exit and codecov upload.
    • Added is_running flag to WebGear_RTC to exit safely.
  • Setup:
    • New automated latest version retriever for packages:
      • Implemented new latest_version method to automatically retrieve latest version for packages.
      • Added Some Dependencies.
    • Added simplejpeg package for all platforms.
Updates/Improvements
  • Added exception for RunTimeErrors in NetGear CI tests.
  • WriteGear: Critical file write access checking method:
    • Added new check_WriteAccess Helper method.
    • Implemented a new robust algorithm to check if given directory has write-access.
    • Removed old behavior which gives irregular results.
  • Helper: Maintenance Updates
    • Added workaround for Python bug.
    • Added safe_mkdir to check_WriteAccess to automatically create non-existential parent folder in path.
    • Extended check_WriteAccess Patch to StreamGear.
    • Simplified check_WriteAccess to handle Windows envs easily.
    • Updated FFmpeg Static Download URL for WriteGear.
    • Implemented fallback option for auto-calculating bitrate from extracted audio sample-rate in validate_audio method.
  • Docs: General UI Updates
    • Updated Meta tags for og site and twitter cards.
    • Replaced Custom dark theme toggle with mkdocs-material's official Color palette toggle
    • Added example for external audio input and creating segmented MP4 video in WriteGear FAQ.
    • Added example for YouTube streaming with WriteGear.
    • Removed custom dark-material.js and header.html files from theme.
    • Added blogpost link for detailed information on Stabilizer Working.
    • Updated mkdocs.yml and custom.css configuration.
    • Remove old hack to resize clappr DASH player with css.
    • Updated Admonitions.
    • Improved docs contexts.
    • Updated CSS for version-selector-button.
    • Adjusted files to match new themes.
    • Updated welcome-bot message for typos.
    • Removed redundant FAQs from NetGear Docs.
    • Updated Assets Images.
    • Updated spacing.
  • CI:
    • Removed unused github.ref from yaml.
    • Updated OpenCV Bash Script for Linux envs.
    • Added timeout-minutes flag to github-actions workflow.
    • Added timeout flag to pytest.
    • Replaced Threaded Gears with OpenCV VideoCapture API.
    • Moved files and Removed redundant code.
    • Replaced grayscale frames with color frames for WebGear tests.
    • Updated pytest timeout value to 15mins.
    • Removed aiortc automated install on Windows platform within setup.py.
    • Added new timeout logic to continue to run on external timeout for GitHub Actions Workflows.
    • Removed unreliable old timeout solution from WebGear_RTC.
    • Removed timeout_decorator and asyncio_timeout dependencies for CI.
    • Removed WebGear_RTC API exception from codecov.
    • Implemented new fake picamera class to CI utils for emulating RPi Camera-Module Real-time capabilities.
    • Implemented new get_RTCPeer_payload method to receive WebGear_RTC peer payload.
    • Removed PiGear from Codecov exceptions.
    • Disable Frame Compression in few NetGear tests failing on frame matching.
    • Updated NetGear CI tests to support new attributes
    • Removed warnings and updated yaml
      • Added pytest.ini to address multiple warnings.
      • Updated azure workflow condition syntax.
    • Update mike settings for mkdocs versioning.
    • Updated codecov configurations.
    • Minor logging and docs updates.
    • Implemented pytest timeout for azure pipelines for macOS envs.
    • Added aiortc as external dependency in appveyor.yml.
    • Re-implemented WebGear_RTC improper offer-answer handshake in CI tests.
    • WebGear_RTC CI Updated with VideoTransformTrack to test stream play.
    • Implemented fake AttributeError for fake picamera class.
    • Updated PiGear CI tests to increment codecov.
    • Update Tests docs and other minor tweaks to increase overall coverage.
    • Enabled debugging and disabled exit 1 on error in azure pipeline.
    • Removed redundant benchmark tests.
  • Helper: Added missing RTSP URL scheme to is_valid_url method.
  • NetGear_Async: Added fix for uvloop only supporting python>=3.7 legacies.
  • Extended WebGear's Video-Handler scope to https.
  • CI: Remove all redundant 32-bit Tests from Appveyor:
  • Setup: Removed latest_version behavior from some packages.
  • NetGear_Async: Revised logic for handling uvloop for all platforms and legacies.
  • Setup: Updated logic to install uvloop-"v0.14.0" for python-3.6 legacies.
  • Removed any redundant code from webgear.
  • StreamGear:
    • Replaced Ordinary dict with Ordered Dict to use move_to_end method.
    • Moved external audio input to output parameters dict.
    • Added additional imports.
    • Updated docs to reflect changes.
  • Numerous Updates to Readme and mkdocs.yml.
  • Updated font to FONT_HERSHEY_SCRIPT_COMPLEX and enabled logging in create_blank_frame.
  • Separated channels for downloading and storing theme files for WebGear and WebGear_RTC APIs.
  • Removed logging condition to always inform user in a event of FFmpeg binary download failure.
  • WebGear_RTC:
    • Improved auto internal termination.
    • More Performance updates through setCodecPreferences.
    • Moved default Video RTC video launcher to __offer.
  • NetGear_Async: Added timeout to client in CI tests.
  • Reimplemented and updated changelog.md.
  • Updated code comments.
  • Setup: Updated keywords and classifiers.
  • Bumped codecov.
Breaking Updates/Changes
  • WriteGear will automatically switch video encoder to default if specified encoder not found.
  • WriteGear will throw RuntimeError if no suitable default encoder found!
  • Removed format specific OpenCV decoding and encoding support for NetGear.
  • Dropped support for compression_format, compression_param attributes from NetGear.
  • Non-existent parent folder in output_filename value will no longer be considered as invalid in StreamGear and WriteGear APIs.
  • None-type source parameter value is allowed for WebGear and NetGear_Async for defining custom sources.
Bug-fixes
  • CamGear: Fixed F821 undefined name 'queue' bug.
  • NetGear_Async: Fixed source parameter missing None as default value.
  • Fixed uvloops only supporting python>=3.7 in NetGear_Async.
  • Helper:
  • Docs:
    • Invalid site_url bug patched in mkdocs.yml
    • Remove redundant mike theme support and its files.
    • Fixed video not centered when DASH video in fullscreen mode with clappr.
    • Fixed Incompatible new mkdocs-docs theme.
    • Fixed missing hyperlinks.
  • CI:
    • Fixed NetGear Address bug
    • Fixed bugs related to termination in WebGear_RTC.
    • Fixed random CI test failures and code cleanup.
    • Fixed string formating bug in Helper.py.
    • Fixed F821 undefined name bugs in WebGear_RTC tests.
    • NetGear_Async Tests fixes.
    • Fixed F821 undefined name bugs.
    • Fixed typo bugs in main.py.
    • Fixed Relative import bug in PiGear.
    • Fixed regex bug in warning filter.
    • Fixed WebGear_RTC frozen threads on exit.
    • Fixed bugs in codecov bash uploader setting for azure pipelines.
    • Fixed False-positive picamera import due to improper sys.module settings.
    • Fixed Frozen Threads on exit in WebGear_RTC API.
    • Fixed deploy error in VidGear Docs Deployer workflow
    • Fixed low timeout bug.
    • Fixed bugs in PiGear tests.
    • Patched F821 undefined name bug.
  • StreamGear:
    • Fixed StreamGear throwing Picture size 0x0 is invalid bug with external audio.
    • Fixed default input framerate value getting discarded in Real-time Frame Mode.
    • Fixed internal list-formatting bug.
  • Fixed E999 SyntaxError bug in main.py.
  • Fixed Typo in bash script.
  • Fixed WebGear freeze on reloading bug.
  • Fixed anomalies in install_opencv bash script.
  • Helper: Bug Fixed in download_ffmpeg_binaries method.
  • Helper: Fixed OSError bug in check_WriteAccess method.
  • Helper: Fixed Input Audio stream bitrate test failing to detect audio-bitrate in certain videos with validate_audio method.
  • Fixed bugs in requests module's function arguments.
  • Fixed None-type stream bug in WebGear.
  • Fixed random crashes in WebGear.
  • Fixed numerous CI test bugs.
  • Fixed several typos.
Pull Requests
New Contributors

 

 

v0.2.0 (2021-01-01)

New Features
  • CamGear API:
    • Support for various Live-Video-Streaming services:
      • Added seamless support for live video streaming sites like Twitch, LiveStream, Dailymotion etc.
      • Implemented flexible framework around streamlink python library with easy control over parameters and quality.
      • Stream Mode can now automatically detects whether source belong to YouTube or elsewhere, and handles it with appropriate API.
    • Re-implemented YouTube URLs Handler:
      • Re-implemented CamGear's YouTube URLs Handler completely from scratch.
      • New Robust Logic to flexibly handing video and video-audio streams.
      • Intelligent stream selector for selecting best possible stream compatible with OpenCV.
      • Added support for selecting stream qualities and parameters.
      • Implemented new get_supported_quality helper method for handling specified qualities
      • Fixed Live-Stream URLs not supported by OpenCV's Videocapture and its FFmpeg.
    • Added additional STREAM_QUALITY and STREAM_PARAMS attributes.
  • ScreenGear API:
    • Multiple Backends Support:
      • Added new multiple backend support with new pyscreenshot python library.
      • Made pyscreenshot the default API for ScreenGear, replaces mss.
      • Added new backend parameter for this feature while retaining previous behavior.
      • Added native automated RGB to BGR conversion for default PIL backend.
      • Kept support for old mss for old compatibility and multi-screen support.
      • Added native dimensional support for multi-screen.
      • Added support all input from all multiple screens.
      • Updated ScreenGear Docs.
      • Updated ScreenGear CI tests.
  • StreamGear API:
    • Changed default behaviour to support complete video transcoding.
    • Added -livestream attribute to support live-streaming.
    • Added additional parameters for -livestream attribute functionality.
    • Updated StreamGear Tests.
    • Updated StreamGear docs.
  • Stabilizer Class:
    • New Robust Error Handling with Blank Frames:
      • Elegantly handles all crashes due to Empty/Blank/Dark frames.
      • Stabilizer throws Warning with this new behavior instead of crashing.
      • Updated CI test for this feature.
  • Docs:
    • Automated Docs Versioning:
      • Implemented Docs versioning through mike API.
      • Separate new workflow steps to handle different versions.
      • Updated docs deploy worflow to support release and dev builds.
      • Added automatic version extraction from github events.
      • Added version-select.js and version-select.css files.
    • Toggleable Dark-White Docs Support:
      • Toggle-button to easily switch dark, white and preferred theme.
      • New Updated Assets for dark backgrounds
      • New css, js files/content to implement this behavior.
      • New material icons for button.
      • Updated scheme to slate in mkdocs.yml.
    • New Theme and assets:
      • New purple theme with dark-purple accent color.
      • New images assets with updated transparent background.
      • Support for both dark and white theme.
      • Increased rebufferingGoal for dash videos.
      • New updated custom 404 page for docs.
    • Issue and PR automated-bots changes
      • New need_info.yml YAML Workflow.
      • New needs-more-info.yml Request-Info template.
      • Replaced Request-Info templates.
      • Improved PR and Issue welcome formatting.
    • Added custom HTML pages.
    • Added show_root_heading flag to disable headings in References.
    • Added new inserAfter function to version-select.js.
    • Adjusted hue for dark-theme for better contrast.
    • New usage examples and FAQs.
    • Added gitmoji for commits.
  • Continuous Integration:
    • Maintenance Updates:
      • Added support for new VIDGEAR_LOGFILE environment variable in Travis CI.
      • Added missing CI tests.
      • Added logging for helper functions.
    • Azure-Pipeline workflow for MacOS envs
      • Added Azure-Pipeline Workflow for testing MacOS environment.
      • Added codecov support.
    • GitHub Actions workflow for Linux envs
      • Added GitHub Action work-flow for testing Linux environment.
    • New YAML to implement GitHub Action workflow for python 3.6, 3.7, 3,8 & 3.9 matrices.
    • Added Upload coverage to Codecov GitHub Action workflow.
    • New codecov-bash uploader for Azure Pipelines.
  • Logging:
    • Added file support
      • Added VIDGEAR_LOGFILE environment variable to manually add file/dir path.
      • Reworked logger_handler() Helper methods (in asyncio too).
      • Added new formatter and Filehandler for handling logger files.
    • Added restore_levelnames auxiliary method for restoring logging levelnames.
  • Added auto version extraction from package version.py in setup.py.
Updates/Improvements
  • Added missing Lazy-pirate auto-reconnection support for Multi-Servers and Multi-Clients Mode in NetGear API.
  • Added new FFmpeg test path to Bash-Script and updated README broken links.
  • Asset Cleanup:
    • Removed all third-party javascripts from projects.
    • Linked all third-party javascript directly.
    • Cleaned up necessary code from CSS and JS files.
    • Removed any copyrighted material or links.
  • Rewritten Docs from scratch:
    • Improved complete docs formatting.
    • Simplified language for easier understanding.
    • Fixed mkdocstrings showing root headings.
    • Included all APIs methods to mkdocstrings docs.
    • Removed unnecessary information from docs.
    • Corrected Spelling and typos.
    • Fixed context and grammar.
    • Removed motivation.md.
    • Renamed many terms.
    • Fixed hyper-links.
    • Reformatted missing or improper information.
    • Fixed context and spellings in Docs files.
    • Simplified language for easy understanding.
    • Updated image sizes for better visibility.
  • Bash Script: Updated to Latest OpenCV Binaries version and related changes
  • Docs: Moved version-selector to header and changed default to alias.
  • Docs: Updated deploy_docs.yml for releasing dev, stable, and release versions.
  • Re-implemented overridden material theme.
  • Updated docs with all new additions and examples.
  • CamGear: CI Stream Mode test updated.
  • Updated ReadMe.md badges.
  • Updated CI tests.
  • Updated setup.py with new features.
  • Updated contributing.md and ReadMe.md.
  • Updated OpenCV version to 4.5.1-dev in bash scripts
  • Updated changelog.md.
  • Moved WebGear API to Streaming Gears.
  • Bumped Codecov.
  • UI changes to version-select.js
  • Docs: Retitle the versions and mkdocs.yml formatting updated.
  • Docs: Version Selector UI reworked and other minor changes.
Breaking Updates/Changes
  • y_tube parameter renamed as stream_mode in CamGear API!
  • Removed Travis support and travis.yml deleted.
Bug-fixes
  • Fixed StreamGear API Limited Segments Bug
  • Fixed Missing links in docs and bump up version.
  • CI: Fixed Appveyor need newer VM image to support Python 3.9.x matrix.
  • ScreenGear BugFix: Fixed Error Handling and updated CI Tests.
  • Fixed improper mkdocs.yml variables.
  • Fixed GStreamer plugin support in bash scripts.
  • Fixed typos in YAMLs and docs.
  • Docs: Fixed Docs Deployer YAML bug for CI envs.
  • Fixed wrong import in YAML.
  • Fixed visible hyperlink on hover in dark-toggle button.
  • Docs: Deployer YAML bug fixed.
  • Docs YAML: issue jimporter/mike#33 patched and fixed fetch-depth=0.
  • Docs: version-select.js bug fixed.
  • Docs: UI Bugs Fixed.
  • CI: Codecov bugfixes.
  • Azure-Pipelines Codecov BugFixes.
  • Fixed version.json not detecting properly in version-select.js.
  • Fixed images not centered inside <figure> tag.
  • Fixed Asset Colors.
  • Fixed failing CI tests.
  • Fixed Several logging bugs.
Pull Requests

 

 

v0.1.9 (2020-08-31)

New Features
  • StreamGear API:
    • New API that automates transcoding workflow for generating Ultra-Low Latency, High-Quality, Dynamic & Adaptive Streaming Formats.
    • Implemented multi-platform , standalone, highly extensible and flexible wrapper around FFmpeg for generating chunked-encoded media segments of the media, and easily accessing almost all of its parameters.
    • API automatically transcodes videos/audio files & real-time frames into a sequence of multiple smaller chunks/segments and also creates a Manifest file.
    • Added initial support for MPEG-DASH (Dynamic Adaptive Streaming over HTTP, ISO/IEC 23009-1).
    • Constructed default behavior in StreamGear, for auto-creating a Primary Stream of same resolution and framerate as source.
    • Added TQDM progress bar in non-debugged output for visual representation of internal processes.
    • Implemented several internal methods for preprocessing FFmpeg and internal parameters for producing streams.
    • Several standalone internal checks to ensure robust performance.
    • New terminate() function to terminate StremGear Safely.
    • New StreamGear Dual Modes of Operation:
      • Implemented Single-Source and Real-time Frames like independent Transcoding Modes.
      • Linked -video_source attribute for activating these modes
      • Single-Source Mode, transcodes entire video/audio file (as opposed to frames by frame) into a sequence of multiple smaller segments for streaming
      • Real-time Frames Mode, directly transcodes video-frames (as opposed to a entire file), into a sequence of multiple smaller segments for streaming
      • Added separate functions, stream() for Real-time Frame Mode and transcode_source() for Single-Source Mode for easy transcoding.
      • Included auto-colorspace detection and RGB Mode like features (extracted from WriteGear), into StreamGear.
    • New StreamGear Parameters:
      • Developed several new parameters such as:
        • output: handles assets directory
        • formats: handles adaptive HTTP streaming format.
        • custom_ffmpeg: handles custom FFmpeg location.
        • stream_params: handles internal and FFmpeg parameter seamlessly.
        • logging: turns logging on or off.
      • New stream_params parameter allows us to exploit almost all FFmpeg parameters and flexibly change its internal settings, and seamlessly generating high-quality streams with its attributes:
        • -streams (list of dictionaries) for building additional streams with -resolution, -video_bitrate & -framerate like sub-attributes.
        • -audio for specifying external audio.
        • -video_source for specifying Single-Source Mode source.
        • -input_framerate for handling input framerate in Real-time Frames Mode.
        • -bpp attribute for handling bits-per-pixels used to auto-calculate video-bitrate.
        • -gop to manually specify GOP length.
        • -ffmpeg_download_path to handle custom FFmpeg download path on windows.
        • -clear_prev_assets to remove any previous copies of SteamGear Assets.
    • New StreamGear docs, MPEG-DASH demo, and recommended DASH players list:
      • Added new StreamGear docs, usage examples, parameters, references, new FAQs.
      • Added Several StreamGear usage examples w.r.t Mode of Operation.
      • Implemented Clappr based on Shaka-Player, as Demo Player.
      • Added Adaptive-dimensional behavior for Demo-player, purely in css.
      • Hosted StreamGear generated DASH chunks on GitHub and served with raw.githack.com.
      • Introduced variable quality level-selector plugin for Clapper Player.
      • Provide various required javascripts and implemented additional functionality for player in extra.js.
      • Recommended tested Online, Command-line and GUI Adaptive Stream players.
      • Implemented separate FFmpeg installation doc for StreamGear API.
      • Reduced rebufferingGoal for faster response.
    • New StreamGear CI tests:
      • Added IO and API initialization CI tests for its Modes.
      • Added various mode Streaming check CI tests.
  • NetGear_Async API:
    • Added new send_terminate_signal internal method.
    • Added WindowsSelectorEventLoopPolicy() for windows 3.8+ envs.
    • Moved Client auto-termination to separate method.
    • Implemented graceful termination with signal API on UNIX machines.
    • Added new timeout attribute for controlling Timeout in Connections.
    • Added missing termination optimizer (linger=0) flag.
    • Several ZMQ Optimizer Flags added to boost performance.
  • WriteGear API:
    • Added support for adding duplicate FFmpeg parameters to output_params:
      • Added new -clones attribute in output_params parameter for handing this behavior..
      • Support to pass FFmpeg parameters as list, while maintaining the exact order it was specified.
      • Built support for zmq.REQ/zmq.REP and zmq.PUB/zmq.SUB patterns in this mode.
      • Added new CI tests debugging this behavior.
      • Updated docs accordingly.
    • Added support for Networks URLs in Compression Mode:
      • output_filename parameter supports Networks URLs in compression modes only
      • Added automated handling of non path/file Networks URLs as input.
      • Implemented new is_valid_url helper method to easily validate assigned URLs value.
      • Validates whether the given URL value has scheme/protocol supported by assigned/installed ffmpeg or not.
      • WriteGear will throw ValueError if -output_filename is not supported.
      • Added related CI tests and docs.
    • Added disable_force_termination attribute in WriteGear to disable force-termination.
  • NetGear API:
    • Added option to completely disable Native Frame-Compression:
      • Checks if any Incorrect/Invalid value is assigned on compression_format attribute.
      • Completely disables Native Frame-Compression.
      • Updated docs accordingly.
  • CamGear API:
    • Added new and robust regex for identifying YouTube URLs.
    • Moved youtube_url_validator to Helper.
  • New helper.py methods:
    • Added validate_video function to validate video_source.
    • Added extract_time Extract time from give string value.
    • Added get_video_bitrate to calculate video birate from resolution, framerate, bits-per-pixels values.
    • Added delete_safe to safely delete files of given extension.
    • Added validate_audio to validate audio source.
    • Added new Helper CI tests.
      • Added new check_valid_mpd function to test MPD files validity.
      • Added mpegdash library to CI requirements.
  • Deployed New Docs Upgrades:
    • Added new assets like images, gifs, custom scripts, javascripts fonts etc. for achieving better visual graphics in docs.
    • Added clappr.min.js, dash-shaka-playback.js, clappr-level-selector.min.js third-party javascripts locally.
    • Extended Overview docs Hyperlinks to include all major sub-pages (such as Usage Examples, Reference, FAQs etc.).
    • Replaced GIF with interactive MPEG-DASH Video Example in Stabilizer Docs.
    • Added new pymdownx.keys to replace [Ctrl+C]/[⌘+C] formats.
    • Added new custom.css stylescripts variables for fluid animations in docs.
    • Overridden announce bar and added donation button.
    • Lossless WEBP compressed all PNG assets for faster loading.
    • Enabled lazy-loading for GIFS and Images for performance.
    • Reimplemented Admonitions contexts and added new ones.
    • Added StreamGear and its different modes Docs Assets.
    • Added patch for images & unicodes for PiP flavored markdown in setup.py.
  • Added Request Info and Welcome GitHub Apps to automate PR and issue workflow
    • Added new config.yml for customizations.
    • Added various suitable configurations.
  • Added new -clones attribute to handle FFmpeg parameter clones in StreamGear and WriteGear API.
  • Added new Video-only and Audio-Only sources in bash script.
  • Added new paths in bash script for storing StreamGear & WriteGear assets temporarily.
Updates/Improvements
  • Added patch for NotImplementedError in NetGear_Async API on Windows 3.8+ envs.
  • Check for valid output file extension according to format selected in StreamGear.
  • Completed migration to travis.com.
  • Created new temp_write temp directory for WriteGear Assets in bash script.
  • Deleted old Redundant assets and added new ones.
  • Employed isort library to sort and group imports in Vidgear APIs.
  • Enabled exception for list, tuple, int, float in WriteGear API's output_params dict.
  • Enabled missing support for frame-compression in its primary Receive Mode.
  • Enforced pixel formats for streams.
  • Improved check for valid system path detection in WriteGear API.
  • Overrided pytest-asyncio fixture in NetGear_Async API.
  • Quoted Gear Headline for understanding each gear easily.
  • Re-Positioned Gear's banner images in overview for better readability.
  • Reduced redundant try-except blocks in NetGear Async.
  • Reformatted and Simplified Docs context.
  • Reimplemented return_testvideo_path CI function with variable streams.
  • Reimplemented skip_loop in NetGear_Async to fix asyncio.CancelledError.
  • Reimplemented buggy audio handler in StreamGear.
  • Reimplemented images with <figure> and <figurecaption> like tags.
  • Removed Python < 3.8 condition from all CI tests.
  • Removed or Grouped redundant code for increasing codecov.
  • Removed redundant code and simplified algorithmic complexities in Gears.
  • Replaced ;nbsp with ;thinsp and ;emsp.
  • Replaced IOError with more reliable RuntimeError in StreamGear Pipelines.
  • Replaced del with pop in dicts.
  • Replaced all Netgear CI tests with more reliable try-except-final blocks.
  • Replaced simple lists with pymdownx.tasklist.
  • Replaced subprocess call() with run() for better error handling in execute_ffmpeg_cmd function.
  • Resized over-sized docs images.
  • Simplified delete_safe Helper function.
  • Simplified default audio-bitrate logic in StreamGear
  • Updated CI tests and cleared redundant code from NetGear_Async API.
  • Updated CI with new tests and Bumped Codecov.
  • Updated Issue and PR templates.
  • Updated Licenses for new files and shrink images dimensions.
  • Updated Missing Helpful tips and increased logging.
  • Updated PR guidelines for more clarity.
  • Updated WebGear examples addresses from 0.0.0.0 to localhost.
  • Updated WriteGear and StreamGear CI tests for not supporting temp directory.
  • Updated README.md and changelog.md with new changes.
  • Updated check_output and added force_retrieve_stderr support to **kwargs to extract stderr output even on FFmpeg error.
  • Updated dicts2args to support internal repeated coreX FFmpeg parameters for StreamGear.
  • Updated mkdocs.yml, changelog.md and README.md with latest changes.
  • Updated validate_audio Helper function will now retrieve audio-bitrate for validation.
  • Updated buggy mpegdash dependency with custom dev fork for Windows machines.
  • Updated core parameters for audio handling.
  • Updated logging for debugging selected eventloops in NetGear_Async API.
  • Updated termination linger to zero at Server's end.
Breaking Updates/Changes
  • Changed Webgear API default address to localhost for cross-compatibility between different platforms.
  • In Netgear_Async API, source value can now be NoneType for a custom frame-generator at Server-end only.
  • Temp (such as /tmp in linux) is now not a valid directory for WriteGear & StreamGear API outputs.
  • Moved vidgear docs assets (i.e images, gifs, javascripts and stylescripts) to override directory.
Bug-fixes
  • Added workaround for system path not handle correctly.
  • Fixed Bug: URL Audio format not being handled properly.
  • Fixed Critical Bug in NetGear_Async throwing ValueError with None-type Source.
  • Fixed Critical StreamGear Bug: FFmpeg pipeline terminating prematurely in Single-Source Mode.
  • Fixed Critical external audio handler bug: moved audio-input to input_parameters.
  • Fixed Frozen-threads bug in CI tests.
  • Fixed Mkdocs only accepting Relative paths.
  • Fixed OSError in WriteGear's compression mode.
  • Fixed StreamGear CI bugs for Windows and CI envs.
  • Fixed Typos and Indentation bugs in NetGear API.
  • Fixed ZMQ throwing error on termination if all max-tries exhausted.
  • Fixed NameError bug in NetGear API and CI tests.
  • Fixed TimeoutError bug in NetGear_Async CI tests.
  • Fixed get_valid_ffmpeg_path throwing TypeError with non-string values.
  • Fixed broken links in docs.
  • Fixed critical duplicate logging bug.
  • Fixed default gop value not handle correctly.
  • Fixed handling of incorrect paths detection.
  • Fixed incorrect definitions in NetGear_Async.
  • Fixed left-over attribute bug in WriteGear.
  • Fixed logic and indentation bugs in CI tests.
  • Fixed logic for handling output parameters in WriteGear API.
  • Fixed missing definitions and logic bug in StreamGear.
  • Fixed missing import and incorrect CI definitions.
  • Fixed missing source dimensions from extract_resolutions output in StreamGear API.
  • Fixed missing support for compression parameters in Multi-Clients Mode.
  • Fixed round off error in FPS.
  • Fixed several CI bugs and updated extract_resolutions method.
  • Fixed several bugs from CI Bidirectional Mode tests.
  • Fixed several typos in docs usage examples.
  • Fixed various AttributeError with wrong attribute names and definition in CI Helper functions.
  • Fixed wrong and missing definitions in docs.
  • Fixed wrong logic for extracting OpenCV frames.
  • Fixed wrong type bug in StreamGear API.
  • Fixed wrong type error bug in WriteGear API.
  • Fixed wrong variable assignments bug in WriteGear API.
  • Fixes to CLI tests and missing docs imports.
  • Many minor typos and wrong definitions.
Pull Requests

 

 

v0.1.8 (2020-06-12)

New Features
  • NetGear API:
    • Multiple Clients support:
      • Implemented support for handling any number of Clients simultaneously with a single Server in this mode.
      • Added new multiclient_mode attribute for enabling this mode easily.
      • Built support for zmq.REQ/zmq.REP and zmq.PUB/zmq.SUB patterns in this mode.
      • Implemented ability to receive data from all Client(s) along with frames with zmq.REQ/zmq.REP pattern only.
      • Updated related CI tests
    • Support for robust Lazy Pirate pattern(auto-reconnection) in NetGear API for both server and client ends:
      • Implemented a algorithm where NetGear rather than doing a blocking receive, will now:
        • Poll the socket and receive from it only when it's sure a reply has arrived.
        • Attempt to reconnect, if no reply has arrived within a timeout period.
        • Abandon the connection if there is still no reply after several requests.
      • Implemented its default support for REQ/REP and PAIR messaging patterns internally.
      • Added new max_retries and request_timeout(in seconds) for handling polling.
      • Added DONTWAIT flag for interruption-free data receiving.
      • Both Server and Client can now reconnect even after a premature termination.
    • Performance Updates:
      • Added default Frame Compression support for Bidirectional frame transmission in Bidirectional mode.
      • Added support for Reducer() function in Helper.py to aid reducing frame-size on-the-go for more performance.
      • Added small delay in recv() function at client's end to reduce system load.
      • Reworked and Optimized NetGear termination, and also removed/changed redundant definitions and flags.
  • Docs: Migration to Mkdocs
    • Implemented a beautiful, static documentation site based on MkDocs which will then be hosted on GitHub Pages.
    • Crafted base mkdocs with third-party elegant & simplistic mkdocs-material theme.
    • Implemented new mkdocs.yml for Mkdocs with relevant data.
    • Added new docs folder to handle markdown pages and its assets.
    • Added new Markdown pages(.md) to docs folder, which are carefully crafted documents - [x] based on previous Wiki's docs, and some completely new additions.
    • Added navigation under tabs for easily accessing each document.
    • New Assets:
    • Added Required Plugins and Extensions:
      • Added support for all pymarkdown-extensions.
      • Added support for some important admonition, attr_list, codehilite, def_list, footnotes, meta, and toc like Mkdocs extensions.
      • Enabled search, minify and git-revision-date-localized plugins support.
      • Added various VidGear's social links to yaml.
      • Added support for en (English) language.
    • Auto-Build API Reference with mkdocstrings:
      • Added support for mkdocstrings plugin for auto-building each VidGear's API references.
      • Added python handler for parsing python source-code to mkdocstrings.
    • Auto-Deploy Docs with GitHub Actions:
      • Implemented Automated Docs Deployment on gh-pages through GitHub Actions workflow.
      • Added new workflow yaml with minimal configuration for automated docs deployment.
      • Added all required python dependencies and environment for this workflow.
      • Added master branch on Ubuntu machine to build matrix.
Updates/Improvements
  • Added in-built support for bidirectional frames(NDarray) transfer in Bidirectional mode.
  • Added support for User-Defined compression params in Bidirectional frames transfer.
  • Added workaround for address already in use bug at client's end.
  • Unified Bidirectional and Multi-Clients mode for client's return data transmission.
  • Replaced ValueError with more suitable RuntimeError.
  • Updated logging for better readability.
  • Added CI test for Multi-Clients mode.
  • Reformatted and grouped imports in VidGear.
  • Added Reducer Helper function CI test.
  • Added Reliability tests for both Server and Client end.
  • Disabled reliable reconnection for Multi-Clients mode.
  • Replaced os.devnull with suprocess's inbuilt function.
  • Updated README.md, Issue and PR templates with new information and updates.
  • Moved changelog.md to /docs and updated contribution guidelines.
  • Improved source-code docs for compatibility with mkdocstrings.
  • Added additional dependency mkdocs-exclude, for excluding files from Mkdocs builds.
  • Updated license and compressed images/diagrams.
  • Added new CI tests and Bumped Codecov.
  • Changed YouTube video URL for CI tests to Creative Commons(CC) video.
  • Removed redundant code.
Breaking Updates/Changes
  • VidGear Docs moved to GitHub Pages, Now Available at https://abhitronix.github.io/vidgear.
  • Removed filter attribute from options parameter in NetGear API.
  • Removed force_terminate parameter support from NetGear API.
  • Disabled additional data of datatype numpy.ndarray for Server end in Bidirectional Mode.
Bug-fixes
  • Fixed 'NoneType' object is not subscriptable bug.
  • Fixed bugs related to delayed termination in NetGear API.
  • Reduced default request_timeout value to 4 and also lowered cut-off limit for the same.
  • Removed redundant ZMQ context termination and similar variables.
  • Added missing VidGear installation in workflow.
  • Excluded conflicting assets README.md from Mkdocs builds.
  • Fixed pattern value check bypassed if wrong value is assigned.
  • Fixed incorrect handling of additional data transferred in synchronous mode at both Server and Client end.
  • Replaced Netgear CI test with more reliable try-except-final blocks.
  • Updated termination linger to zero at Server's end.
  • Fixed NameError bug in NetGear API.
  • Fixed missing support for compression parameters in Multi-Clients Mode.
  • Fixed ZMQ throwing error on termination if all max-tries exhausted.
  • Enabled missing support for frame compression in its primary receive mode.
  • Fixed several bugs from CI Bidirectional Mode tests.
  • Removed or Grouped redundant code for increasing codecov.
  • Fixed Mkdocs only accepting Relative paths.
  • Fixed broken links in docs.
  • Fixed round off error in FPS.
  • Many small typos and bugs fixes.
Pull Requests

 

 

v0.1.7 (2020-04-29)

New Features
  • WebGear API:
    • Added a robust Live Video Server API that can transfer live video frames to any web browser on the network in real-time.
    • Implemented a flexible asyncio wrapper around starlette ASGI Application Server.
    • Added seamless access to various starlette's Response classes, Routing tables, Static Files, Template engine(with Jinja2), etc.
    • Added a special internal access to VideoGear API and all its parameters.
    • Implemented a new Auto-Generation Work-flow to generate/download & thereby validate WebGear API data files from its GitHub server automatically.
    • Added on-the-go dictionary parameter in WebGear to tweak performance, Route Tables and other internal properties easily.
    • Added new simple & elegant default Bootstrap Cover Template for WebGear Server.
    • Added __main__.py to directly run WebGear Server through the terminal.
    • Added new gif and related docs for WebGear API.
    • Added and Updated various CI tests for this API.
  • NetGear_Async API:
    • Designed NetGear_Async asynchronous network API built upon ZeroMQ's asyncio API.
    • Implemented support for state-of-the-art asyncio event loop uvloop at its backend.
    • Achieved Unmatchable high-speed and lag-free video streaming over the network with minimal resource constraint.
    • Added exclusive internal wrapper around VideoGear API for this API.
    • Implemented complete server-client handling and options to use variable protocols/patterns for this API.
    • Implemented support for all four ZeroMQ messaging patterns: i.e zmq.PAIR, zmq.REQ/zmq.REP, zmq.PUB/zmq.SUB, and zmq.PUSH/zmq.PULL.
    • Implemented initial support for tcp and ipc protocols.
    • Added new Coverage CI tests for NetGear_Async Network Gear.
    • Added new Benchmark tests for benchmarking NetGear_Async against NetGear.
  • Asynchronous Enhancements:
    • Added asyncio package to for handling asynchronous APIs.
    • Moved WebGear API(webgear.py) to asyncio and created separate asyncio helper.py for it.
    • Various Performance tweaks for Asyncio APIs with concurrency within a single thread.
    • Moved __main__.py to asyncio for easier access to WebGear API through the terminal.
    • Updated setup.py with new dependencies and separated asyncio dependencies.
  • General Enhancements:
    • Added new highly-precise Threaded FPS class for accurate benchmarking with time.perf_counter python module.
    • Added a new Gitter community channel.
    • Added a new Reducer function to reduce the frame size on-the-go.
    • Add Flake8 tests to Travis CI to find undefined names. (PR by @cclauss)
    • Added a new unified logging handler helper function for vidgear.
Updates/Improvements
  • Re-implemented and simplified logic for NetGear Async server-end.
  • Added new dependencies for upcoming asyncio updates to setup.py.
  • Added retry function and replaced wget with curl for Linux test envs.
  • Bumped OpenCV to latest 4.2.0-dev for Linux test envs.
  • Updated YAML files to reflect new changes to different CI envs.
  • Separated each API logger with a common helper method to avoid multiple copies.
  • Limited Importing OpenCV API version check's scope to helper.py only.
  • Implemented case for incorrect color_space value in ScreenGear API.
  • Removed old conflicting logging formatter with a common method and expanded logging.
  • Improved and added shutdown function for safely stopping frame producer threads in WebGear API.
  • Re-implemented and simplified all CI tests with maximum code-coverage in mind.
  • Replaced old mkdir function with new mkdir_safe helper function for creating directories safely.
  • Updated ReadMe.md with updated diagrams, gifs and information.
  • Improve, structured and Simplified the Contribution Guidelines.
  • Bundled CI requirements in a single command.(Suggested by @cclauss)
  • Replaced line endings CRLF with LF endings.
  • Added dos2unix for Travis OSX envs.
  • Bumped Codecov to maximum.
Breaking Updates/Changes
  • Dropped support for Python 3.5 and below legacies. (See issue #99)
  • Dropped and replaced Python 3.5 matrices with new Python 3.8 matrices in all CI environments.
  • Implemented PEP-8 Styled Black formatting throughout the source-code.
  • Limited protocols support to tcp and ipc only, in NetGear API.
Bug-fixes
  • Fixed Major NetGear_Async bug where __address and __port are not set in async mode.(PR by @otter-in-a-suit)
  • Fixed Major PiGear Color-space Conversion logic bug.
  • Workaround for CAP_IMAGES error in YouTube Mode.
  • Replaced incorrect terminate() with join() in PiGear.
  • Removed uvloop for windows as still NOT yet supported.
  • Refactored Asynchronous Package name async to asyncio, since it is used as Keyword in python>=3.7 (raises SyntaxError).
  • Fixed unfinished close of event loops bug in WebGear API.
  • Fixed NameError in helper.py.
  • Added fix for OpenCV installer failure on Linux test envs.
  • Fixed undefined NameError in helper.py context. (@cclauss)
  • Fixed incorrect logic while pulling frames from ScreenGear API.
  • Fixed missing functions in __main__.py.
  • Fixed Typos and definitions in docs.
  • Added missing camera_num parameter to VideoGear.
  • Added OpenSSL's [SSL: CERTIFICATE_VERIFY_FAILED] bug workaround for macOS envs.
  • Removed download_url meta from setup.py.
  • Removed PiGear from CI completely due to hardware emulation limitation.
  • Removed VideoCapture benchmark tests for macOS envs.
  • Removed trivial __main__.py from codecov.
  • Removed several redundant try-catch loops.
  • Renamed youtube_url_validation as youtube_url_validator.
  • Several minor wrong/duplicate variable definitions and various bugs fixed.
  • Fixed, Improved & removed many Redundant CI tests for various APIs.
Pull Requests
New Contributors

 

 

v0.1.6 (2020-01-01)

New Features
  • NetGear API:
    • Added powerful ZMQ Authentication & Data Encryption features for NetGear API:
      • Added exclusive secure_mode param for enabling it.
      • Added support for two most powerful Stonehouse & Ironhouse ZMQ security mechanisms.
      • Added smart auth-certificates/key generation and validation features.
    • Implemented Robust Multi-Servers support for NetGear API:
      • Enables Multiple Servers messaging support with a single client.
      • Added exclusive multiserver_mode param for enabling it.
      • Added support for REQ/REP & PUB/SUB patterns for this mode.
      • Added ability to send additional data of any datatype along with the frame in realtime in this mode.
    • Introducing exclusive Bidirectional Mode for bidirectional data transmission:
      • Added new return_data parameter to recv() function.
      • Added new bidirectional_mode attribute for enabling this mode.
      • Added support for PAIR & REQ/REP patterns for this mode
      • Added support for sending data of any python datatype.
      • Added support for message parameter for non-exclusive primary modes for this mode.
    • Implemented compression support with on-the-fly flexible frame encoding for the Server-end:
      • Added initial support for JPEG, PNG & BMP encoding formats .
      • Added exclusive options attribute compression_format & compression_param to tweak this feature.
      • Client-end will now decode frame automatically based on the encoding as well as support decoding flags.
    • Added force_terminate attribute flag for handling force socket termination at the Server-end if there's latency in the network.
    • Implemented new Publish/Subscribe(zmq.PUB/zmq.SUB) pattern for seamless Live Streaming in NetGear API.
  • PiGear API:
    • Added new threaded internal timing function for PiGear to handle any hardware failures/frozen threads.
    • PiGear will not exit safely with SystemError if Picamera ribbon cable is pulled out to save resources.
    • Added support for new user-defined HWFAILURE_TIMEOUT options attribute to alter timeout.
  • VideoGear API:
    • Added framerate global variable and removed redundant function.
    • Added CROP_N_ZOOM attribute in Videogear API for supporting Crop and Zoom stabilizer feature.
  • WriteGear API:
    • Added new execute_ffmpeg_cmd function to pass a custom command to its FFmpeg pipeline.
  • Stabilizer class:
    • Added new Crop and Zoom feature.
      • Added crop_n_zoom param for enabling this feature.
    • Updated docs.
  • CI & Tests updates:
    • Replaced python 3.5 matrices with latest python 3.8 matrices in Linux environment.
    • Added full support for Codecov in all CI environments.
    • Updated OpenCV to v4.2.0-pre(master branch).
    • Added various Netgear API tests.
    • Added initial Screengear API test.
    • More test RTSP feeds added with better error handling in CamGear network test.
    • Added tests for ZMQ authentication certificate generation.
    • Added badge and Minor doc updates.
  • Added VidGear's official native support for MacOS environments.
Updates/Improvements
  • Replace print logging commands with python's logging module completely.
  • Implemented encapsulation for class functions and variables on all gears.
  • Updated support for screen casting from multiple/all monitors in ScreenGear API.
  • Updated ScreenGear API to use Threaded Queue Mode by default, thereby removed redundant THREADED_QUEUE_MODE param.
  • Updated bash script path to download test dataset in $TMPDIR rather than $HOME directory for downloading testdata.
  • Implemented better error handling of colorspace in various videocapture APIs.
  • Updated bash scripts, Moved FFmpeg static binaries to github.com.
  • Updated bash scripts, Added additional flag to support un-secure apt sources.
  • CamGear API will now throw RuntimeError if source provided is invalid.
  • Updated threaded Queue mode in CamGear API for more robust performance.
  • Added new camera_num to support multiple Picameras.
  • Moved thread exceptions to the main thread and then re-raised.
  • Added alternate github mirror for FFmpeg static binaries auto-installation on windows oses.
  • Added colorlog python module for presentable colored logging.
  • Replaced traceback with sys.exc_info.
  • Overall APIs Code and Docs optimizations.
  • Updated Code Readability and Wiki Docs.
  • Updated ReadMe & Changelog with the latest changes.
  • Updated Travis CI Tests with support for macOS environment.
  • Reformatted & implemented necessary MacOS related changes and dependencies in travis.yml.
Breaking Updates/Changes
  • Python 2.7 legacy support dropped completely.
  • Source-code Relicensed to Apache 2.0 License.
  • Python 3+ are only supported legacies for installing v0.1.6 and above.
  • Python 2.7 and 3.4 legacies support dropped from CI tests.
Bug-fixes
  • Reimplemented Pub/Sub pattern for smoother performance on various networks.
  • Fixed Assertion error in CamGear API during colorspace manipulation.
  • Fixed random freezing in Secure Mode and several related performance updates
  • Fixed multiserver_mode not working properly over some networks.
  • Fixed assigned Port address ignored bug (commit 073bca1).
  • Fixed several wrong definition bugs from NetGear API(commit 8f7153c).
  • Fixed unreliable dataset video URL(rehosted file on github.com).
  • Disabled overwrite_cert for client-end in NetGear API.
  • Disabled Universal Python wheel builds in setup.cfgfile.
  • Removed duplicate code to import MSS(@BoboTiG) from ScreenGear API.
  • Eliminated unused redundant code blocks from library.
  • Fixed Code indentation in setup.py and updated new release information.
  • Fixed code definitions & Typos.
  • Fixed several bugs related to secure_mode & multiserver_mode Modes.
  • Fixed various macOS environment bugs.
Pull Requests
New Contributors

 

 

v0.1.5 (2019-07-24)

New Features
  • Added new ScreenGear API, supports Live ScreenCasting.
  • Added new NetGear API, aids real-time frame transfer through messaging(ZmQ) over network.
  • Added new new Stabilizer Class, for minimum latency Video Stabilization with OpenCV.
  • Added Option to use API's standalone.
  • Added Option to use VideoGear API as internal wrapper around Stabilizer Class.
  • Added new parameter stabilize to API, to enable or disable Video Stabilization.
  • Added support for **option dict attributes to update VidGear's video stabilizer parameters directly.
  • Added brand new logo and functional block diagram (.svg) in readme.md
  • Added new pictures and GIFs for improving readme.md readability
  • Added new contributing.md and changelog.md for reference.
  • Added collections.deque import in Threaded Queue Mode for performance consideration
  • Added new install_opencv.sh bash scripts for Travis cli, to handle OpenCV installation.
  • Added new Project Issue & PR Templates
  • Added new Sponsor Button(FUNDING.yml)
Updates/Improvements
  • Updated New dependencies: mss, pyzmq and rejected redundant ones.
  • Revamped and refreshed look for readme.md and added new badges.
  • Updated Releases Documentation completely.
  • Updated CI tests for new changes
  • Updated Code Documentation.
  • Updated bash scripts and removed redundant information
  • Updated Youtube video URL in tests
  • Completely Reformatted and Updated Wiki Docs with new changes.
Breaking Updates/Changes
  • Implemented experimental Threaded Queue Mode(a.k.a Blocking Mode) for fast, synchronized, error-free multi-threading.
  • Renamed bash script pre-install.sh to prepare_dataset.sh - [x] downloads opensourced test datasets and static FFmpeg binaries for debugging.
  • Changed script folder location to bash/script.
  • Python 3.4 removed from Travis CI tests.
Bug-fixes
  • Temporarily fixed Travis CI bug: Replaced opencv-contrib-python with OpenCV built from scratch as dependency.
  • Fixed CI Timeout Bug: Disable Threaded Queue Mode for CI Tests
  • Fixes** sys.stderr.close() throws ValueError bug: Replaced sys.close() with DEVNULL.close()
  • Fixed Youtube Live Stream bug that return NonType frames in CamGear API.
  • Fixed NoneType frames bug in PiGear class on initialization.
  • Fixed Wrong function definitions
  • Removed /xe2 unicode bug from Stabilizer class.
  • Fixed **output_params KeyError bug in WriteGear API
  • Fixed subprocess not closing properly on exit in WriteGear API.
  • Fixed bugs in ScreenGear: Non-negative monitor values
  • Fixed missing import, typos, wrong variable definitions
  • Removed redundant hack from setup.py
  • Fixed Minor YouTube playback Test CI Bug
  • Fixed new Twitter Intent
  • Fixed bug in bash script that not working properly due to changes at server end.
Pull Requests

 

 

v0.1.4 (2019-05-11)

New Features
  • Added new WriteGear API: for enabling lossless video encoding and compression(built around FFmpeg and OpenCV Video Writer)
  • Added YouTube Mode for direct Video Pipelining from YouTube in CamGear API
  • Added new y_tube to access YouTube Mode in CamGear API.
  • Added flexible Output file Compression control capabilities in compression-mode(WriteGear).
  • Added -output_dimensions special parameter to WriteGear API.
  • Added new helper.py to handle special helper functions.
  • Added feature to auto-download and configure FFmpeg Static binaries(if not found) on Windows platforms.
  • Added -input_framerate special parameter to WriteGear class to change/control output constant framerate in compression mode(WriteGear).
  • Added new Direct Video colorspace Conversion capabilities in CamGear and PiGear API.
  • Added new framerate class variable for CamGear API, to retrieve input framerate.
  • Added new parameter backend - [x] changes the backend of CamGear's API
  • Added automatic required prerequisites installation ability, when installation from source.
  • Added Travis CI Complete Integration for Linux-based Testing for VidGear.
  • Added and configured travis.yml
  • Added Appveyor CI Complete Integration for Windows-based Testing in VidGear.
  • Added and configured new appveyor.yml
  • Added new bash script pre-install.sh to download opensourced test datasets and static FFmpeg binaries for debugging.
  • Added several new Tests(including Benchmarking Tests) for each API for testing with pytest.
  • Added license to code docs.
  • Added Say Thank you! badge to Readme.md.
Updates/Improvements
  • Removed redundant dependencies
  • Updated youtube-dl as a dependency, as required by pafy's backend.
  • Updated common VideoGear API with new parameter.
  • Update robust algorithm to auto-detect FFmpeg executables and test them, if failed, auto fallback to OpenCV's VideoWriter API.
  • Improved system previously installed OpenCV detection in setup.py.
  • Updated setup.py with hack to remove bullets from pypi description.
  • Updated Code Documentation
  • Reformatted & Modernized readme.md with new badges.
  • Reformatted and Updated Wiki Docs.
Breaking Updates/Changes
  • Removed -height and -width parameter from CamGear API.
  • Replaced dependency opencv-python with opencv-contrib-python completely
Bug-fixes
  • Windows Cross-Platform fix: replaced dependency os with platform in setup.py.
  • Fixed Bug: Arises due to spaces in input **options/**output_param dictionary keys.
  • Fixed several wrong/missing variable & function definitions.
  • Fixed code uneven indentation.
  • Fixed several typos in docs.
Pull Requests

 

 

v0.1.3 (2019-04-07)

Bug-fixes
  • Patched Major PiGear Bug: Incorrect import of PiRGBArray function in PiGear Class
  • Several Fixes for backend picamera API handling during frame capture(PiGear)
  • Fixed missing frame variable initialization.
  • Fixed minor typos
Pull Requests

 

 

v0.1.2 (2019-03-27)

New Features
  • Added easy Source manipulation feature in CamGear API, to control features like resolution, brightness, framerate etc.
  • Added new **option parameter to CamGear API, provides the flexibility to manipulate input stream directly.
  • Added new parameters for Camgear API for time delay and logging.
  • Added new Logo to readme.md
  • Added new Wiki Documentation.
Updates/Improvements
  • Reformatted readme.md.
  • Updated Wiki Docs with new changes.
Bug-fixes
  • Improved Error Handling in CamGear & PiGear API.
  • Fixed minor typos in docs.
Pull Requests

 

 

v0.1.1 (2019-03-24)

New Features
  • Release ViGear binaries on the Python Package Index (PyPI)
  • Added new and configured setup.py & setup.cfg
Bug-fixes
  • Fixed PEP bugs: added and configured properly __init__.py in each folder
  • Fixed PEP bugs: improved code Indentation
  • Fixed wrong imports: replaced distutils.core with setuptools
  • Fixed readme.md

 

 

v0.1.0 (2019-03-17)

New Features
  • Initial Release
  • Converted my imutils PR into Python Project.
  • Renamed conventions and reformatted complete source-code from scratch.
  • Added support for both python 2.7 and 3 legacies
  • Added new multi-threaded CamGear, PiGear, and VideoGear APIs
  • Added multi-platform compatibility
  • Added robust & flexible control over the source in PiGear API.
\ No newline at end of file diff --git a/v0.3.3-dev/overrides/hooks/__pycache__/js_hook.cpython-39.pyc b/v0.3.3-dev/overrides/hooks/__pycache__/js_hook.cpython-39.pyc index ea350d72..389b974f 100644 Binary files a/v0.3.3-dev/overrides/hooks/__pycache__/js_hook.cpython-39.pyc and b/v0.3.3-dev/overrides/hooks/__pycache__/js_hook.cpython-39.pyc differ diff --git a/v0.3.3-dev/search/search_index.json b/v0.3.3-dev/search/search_index.json index c4433795..6791c72c 100644 --- a/v0.3.3-dev/search/search_index.json +++ b/v0.3.3-dev/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Overview","text":""},{"location":"#introduction","title":"Introduction","text":"

VidGear is a cross-platform High-Performance Video-Processing Framework for building complex real-time media applications in python

VidGear provides an easy-to-use, highly extensible, Multi-Threaded + Asyncio API Framework on top of many state-of-the-art specialized libraries like OpenCV, FFmpeg, ZeroMQ, picamera2, starlette, yt_dlp, pyscreenshot, dxcam, aiortc and python-mss at its backend, and enable us to flexibly exploit their internal parameters and methods, while silently delivering robust error-handling and real-time performance \u26a1\ufe0f.

\"Write Less and Accomplish More\" \u2014 VidGear's Motto

VidGear focuses on simplicity, and thereby lets programmers and software developers to easily integrate and perform Complex Video Processing Tasks without going through hefty documentation and in just a few lines of code.

"},{"location":"#getting-started","title":"Getting Started","text":"

In case you're run into any problems, consult the Help section.

If you're just getting started with OpenCV-Python programming, then refer this FAQ \u27b6

"},{"location":"#gears","title":"Gears","text":"

VidGear is built with multiple Gears each with some unique functionality.

Each Gear is designed exclusively to handle/control/process different data-specific & device-specific video streams, network streams, and media encoders/decoders.

These Gears can be classified as follows:

"},{"location":"#videocapture-gears","title":"VideoCapture Gears","text":""},{"location":"#videowriter-gears","title":"VideoWriter Gears","text":""},{"location":"#streaming-gears","title":"Streaming Gears","text":""},{"location":"#network-gears","title":"Network Gears","text":"

"},{"location":"#contributions","title":"Contributions","text":"

Contributions are welcome, and greatly appreciated!

Please see our Contribution Guidelines for more details.

"},{"location":"#community-channel","title":"Community Channel","text":"

If you've come up with some new idea, or looking for the fastest way troubleshoot your problems. Please checkout our Gitter community channel \u27b6

"},{"location":"#become-a-stargazer","title":"Become a Stargazer","text":"

You can be a Stargazer by starring us on Github, it helps us a lot and you're making it easier for others to find & trust this library. Thanks!

"},{"location":"#donations","title":"Donations","text":"

VidGear is free and open source and will always remain so.

It is something I am doing with my own free time. But so much more needs to be done, and I need your help to do this. For just the price of a cup of coffee, you can make a difference

"},{"location":"#citation","title":"Citation","text":"

Here is a Bibtex entry you can use to cite this project in a publication:

@software{vidgear,\n  author       = {Abhishek Thakur and\n                  Zoe Papakipos and\n                  Christian Clauss and\n                  Christian Hollinger and\n                  Ian Max Andolina and\n                  Vincent Boivin and\n                  Kyle Ahn and\n                  freol35241 and\n                  Benjamin Lowe and\n                  Micka\u00ebl Schoentgen and\n                  Renaud Bouckenooghe and\n                  Ibtsam Ahmad},\n  title        = {abhiTronix/vidgear: VidGear Stable v0.3.2},\n  month        = sep,\n  year         = 2023,\n  publisher    = {Zenodo},\n  version      = {vidgear-0.3.2},\n  doi          = {10.5281/zenodo.8332548},\n  url          = {https://doi.org/10.5281/zenodo.8332548}\n}\n

"},{"location":"changelog/","title":"Release Notes","text":""},{"location":"changelog/#release-notes","title":"Release Notes","text":""},{"location":"changelog/#v032-2023-09-10","title":"v0.3.2 (2023-09-10)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v031-2023-07-22","title":"v0.3.1 (2023-07-22)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests "},{"location":"changelog/#v030-2023-01-26","title":"v0.3.0 (2023-01-26)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v026-2022-07-05","title":"v0.2.6 (2022-07-05)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v025-2021-02-11","title":"v0.2.5 (2021-02-11)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests "},{"location":"changelog/#v024-2021-12-05","title":"v0.2.4 (2021-12-05)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v023-2021-10-27","title":"v0.2.3 (2021-10-27)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v022-2021-09-02","title":"v0.2.2 (2021-09-02)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v021-2021-04-25","title":"v0.2.1 (2021-04-25)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v020-2021-01-01","title":"v0.2.0 (2021-01-01)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests "},{"location":"changelog/#v019-2020-08-31","title":"v0.1.9 (2020-08-31)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests "},{"location":"changelog/#v018-2020-06-12","title":"v0.1.8 (2020-06-12)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests "},{"location":"changelog/#v017-2020-04-29","title":"v0.1.7 (2020-04-29)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v016-2020-01-01","title":"v0.1.6 (2020-01-01)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v015-2019-07-24","title":"v0.1.5 (2019-07-24)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests "},{"location":"changelog/#v014-2019-05-11","title":"v0.1.4 (2019-05-11)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests "},{"location":"changelog/#v013-2019-04-07","title":"v0.1.3 (2019-04-07)","text":"Bug-fixes Pull Requests "},{"location":"changelog/#v012-2019-03-27","title":"v0.1.2 (2019-03-27)","text":"New Features Updates/Improvements Bug-fixes Pull Requests "},{"location":"changelog/#v011-2019-03-24","title":"v0.1.1 (2019-03-24)","text":"New Features Bug-fixes "},{"location":"changelog/#v010-2019-03-17","title":"v0.1.0 (2019-03-17)","text":"New Features "},{"location":"contribution/","title":"Contribution Overview","text":""},{"location":"contribution/#contribution-overview","title":"Contribution Overview","text":"

Contributions are welcome, We'd love your contribution to VidGear in order to fix bugs or to implement new features!

Contribution Opportunities

If you're looking for something to work on, check for the PR WELCOMED labeled issues on our GitHub Repository.

"},{"location":"contribution/#submission-guidelines","title":"Submission Guidelines","text":""},{"location":"contribution/#submission-contexts","title":"Submission Contexts","text":""},{"location":"contribution/#got-a-question-or-problem","title":"Got a question or problem?","text":"

For quick questions, please refrain from opening an issue, instead read our FAQ & Troubleshooting section or you can reach us on Gitter community channel.

"},{"location":"contribution/#found-a-typo","title":"Found a typo?","text":"

There's no need to contribute for some typos. Just reach us on Gitter \u27b6 community channel, We will correct them in (less than) no time.

"},{"location":"contribution/#found-a-bug","title":"Found a bug?","text":"

If you encountered a bug, you can help us by submitting an issue in our GitHub repository. Even better, you can submit a Pull Request(PR) with a fix, but make sure to read the guidelines \u27b6.

"},{"location":"contribution/#request-for-a-featureimprovement","title":"Request for a feature/improvement?","text":"Subscribe to Github Repository

You can subscribe our GitHub Repository to receive notifications through email for new pull requests, commits and issues that are created in VidGear. Learn more about it here \u27b6

You can request our GitHub Repository for a new feature/improvement based on the type of request:

Please submit an issue with a proposal template for your request to explain how it benefits everyone in the community.

"},{"location":"gears/","title":"Introduction","text":""},{"location":"gears/#introduction","title":"Introduction","text":"Gears: generalized workflow"},{"location":"gears/#gears-what-are-these","title":"Gears , What are these?","text":"

VidGear is built on Standalone APIs - also known as Gears , each with some unique functionality. Each Gears is designed exclusively to handle/control/process different data-specific & device-specific video streams, network streams, and media encoders/decoders.

Gears allows users to work with an inherently optimized, easy-to-use, extensible, and exposed API Framework on top of many state-of-the-art libraries, while silently delivering robust error handling and unmatched real-time performance.

"},{"location":"gears/#gears-classification","title":"Gears Classification","text":"

These Gears can be classified as follows:

"},{"location":"gears/#a-videocapture-gears","title":"A. VideoCapture Gears","text":"

Basic Function: Retrieves numpy.ndarray frames from various sources.

"},{"location":"gears/#b-videowriter-gears","title":"B. VideoWriter Gears","text":"

Basic Function: Writes numpy.ndarray frames to a video file or network stream.

"},{"location":"gears/#c-streaming-gears","title":"C. Streaming Gears","text":"

Basic Function: Transcodes/Broadcasts files and numpy.ndarray frames for streaming.

You can also use WriteGear for streaming with traditional protocols such as RTMP, RTSP/RTP.

"},{"location":"gears/#d-network-gears","title":"D. Network Gears","text":"

Basic Function: Sends/Receives data and numpy.ndarray frames over connected networks.

"},{"location":"help/","title":"Helping VidGear","text":""},{"location":"help/#helping-vidgear","title":"Helping VidGear","text":"

Liked VidGear? Would you like to help VidGear, other users, and the author?

There are many simple ways to help us:

"},{"location":"help/#star-vidgear-on-github","title":"Star VidGear on GitHub","text":"

You can star VidGear on GitHub:

It helps us a lot by making it easier for others to find & trust this library. Thanks!

"},{"location":"help/#help-others-with-issues-on-github","title":"Help others with issues on GitHub","text":"

You can see through any opened or pinned existing issues on our GitHub repository, and try helping others, wherever possible:

"},{"location":"help/#watch-the-github-repository","title":"Watch the GitHub repository","text":"

You can watch \ud83d\udc40 VidGear Activities on GitHub:

When you watch a repository, you will be notified of all conversations for that repository, including when someone creates a new issue, or pushes a new pull request.

You can try helping solving those issues, or give valuable feedback/review on new Pull Requests.

"},{"location":"help/#helping-author","title":"Helping Author","text":"

Donations help keep VidGear's development alive and motivate me (as author).

It is something I am doing with my own free time. But so much more needs to be done, and I need your help to do this. For just the price of a cup of coffee, you can make a difference

Thanks a million!

"},{"location":"help/#connect-with-author","title":"Connect with Author","text":"

You can connect with me, the author \ud83d\udc4b:

"},{"location":"installation/","title":"Installation Overview","text":""},{"location":"installation/#installation-overview","title":"Installation Overview","text":""},{"location":"installation/#supported-systems","title":"Supported Systems","text":"

VidGear is well-tested and supported on the following systems(but not limited to), with python 3.8+ and pip installed:

"},{"location":"installation/#supported-python-legacies","title":"Supported Python legacies","text":"

Depreciation Notice

Python-3.7 legacies support has been dropped from Vidgear.

Python 3.8+ are only supported legacies for installing Vidgear v0.3.1 and above.

"},{"location":"installation/#installation-methods","title":"Installation methods","text":""},{"location":"license/","title":"License","text":""},{"location":"license/#license","title":"License","text":"

This library is released under the Apache 2.0 License.

"},{"location":"license/#copyright-notice","title":"Copyright Notice","text":"
Copyright (c) 2019 Abhishek Thakur(@abhiTronix) <abhi.una12@gmail.com>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n
"},{"location":"switch_from_cv/","title":"Switching from OpenCV","text":""},{"location":"switch_from_cv/#switching-from-opencv-library","title":"Switching from OpenCV Library","text":"

Switching OpenCV with VidGear APIs is fairly painless process, and will just require changing a few lines in your python script.

This document is intended to software developers who want to migrate their python code from OpenCV Library to VidGear APIs.

Prior knowledge of Python or OpenCV won't be covered in this guide. Proficiency with OpenCV-Python (Python API for OpenCV) is a must in order understand this document.

If you're just getting started with OpenCV-Python programming, then refer this FAQ \u27b6

"},{"location":"switch_from_cv/#why-vidgear-is-better-than-opencv","title":"Why VidGear is better than OpenCV?","text":"

Learn more about OpenCV here \u27b6

VidGear employs OpenCV at its backend and enhances its existing capabilities even further by introducing many new state-of-the-art functionalities such as:

Vidgear offers all this at once while maintaining the same standard OpenCV-Python (Python API for OpenCV) coding syntax for all of its APIs, thereby making it even easier to implement complex real-time OpenCV applications in python code without changing things much.

"},{"location":"switch_from_cv/#switching-the-videocapture-apis","title":"Switching the VideoCapture APIs","text":"

Let's compare a bare-minimum python code for extracting frames out of any Webcam/USB-camera (connected at index 0), between OpenCV's VideoCapture Class and VidGear's CamGear VideoCapture API side-by-side:

CamGear API share the same syntax as other VideoCapture APIs, thereby you can easily switch to any of those APIs in a similar manner.

OpenCV VideoCapture ClassVidGear's CamGear API
# import required libraries\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = cv2.VideoCapture(0) \n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n      break\n\n\n    # {do something with the frame here}\n\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.release()\n
# import required libraries\nfrom vidgear.gears import CamGear\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = CamGear(source=0).start() \n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

and both syntax almost looks the same, easy, isn't it?

"},{"location":"switch_from_cv/#differences","title":"Differences","text":"

Let's breakdown a few noteworthy difference in both syntaxes:

Task OpenCV VideoCapture Class VidGear's CamGear API Initiating stream = cv2.VideoCapture(0) stream = CamGear(source=0).start() Reading frames (grabbed, frame) = stream.read() frame = stream.read() Checking empty frame if not grabbed: if frame is None: Terminating stream.release() stream.stop()

Now checkout other VideoCapture Gears \u27b6

"},{"location":"switch_from_cv/#switching-the-videowriter-api","title":"Switching the VideoWriter API","text":"

Let's extend previous bare-minimum python code and save those extracted frames to disk as a valid file, with OpenCV's VideoWriter Class and VidGear's WriteGear (with FFmpeg backend), compared side-to-side:

WriteGear API also provides backend for OpenCV's VideoWriter Class. More information here \u27b6

OpenCV VideoWriter ClassVidGear's WriteGear API
# import required libraries\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = cv2.VideoCapture(0) \n\n# Define the codec and create VideoWriter object with suitable output \n# filename for e.g. `Output.avi`\nfourcc = cv2.VideoWriter_fourcc(*'XVID') \nwriter = cv2.VideoWriter('output.avi', fourcc, 20.0, (640, 480)) \n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n      break\n\n\n    # {do something with the frame here}\n\n\n    # write frame to writer\n    writer.write(frame)\n\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.release()\n\n# safely close writer\nwriter.release() \n
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = CamGear(source=0).start() \n\n# Define WriteGear Object with suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output = 'Output.mp4') \n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if None-type\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

Noticed WriteGear's coding syntax looks similar but less complex?

"},{"location":"switch_from_cv/#differences_1","title":"Differences","text":"

Let's breakdown a few noteworthy difference in both syntaxes:

Task OpenCV VideoWriter Class VidGear's WriteGear API Initiating writer = cv2.VideoWriter('output.avi', cv2.VideoWriter_fourcc(*'XVID'), 20.0, (640, 480)) writer = WriteGear(output='Output.mp4') Writing frames writer.write(frame) writer.write(frame) Terminating writer.release() writer.close()

Now checkout more about WriteGear API here \u27b6

"},{"location":"bonus/TQM/","title":"Threaded Queue Mode","text":""},{"location":"bonus/TQM/#threaded-queue-mode","title":"Threaded Queue Mode","text":""},{"location":"bonus/TQM/#overview","title":"Overview","text":"Threaded-Queue-Mode: generalized timing diagram

Threaded Queue Mode is designed exclusively for VidGear's Videocapture Gears (namely CamGear, VideoGear) and few Network Gears (such as NetGear(Client's end)) for achieving high-performance, asynchronous, error-free video-frames handling.

Threaded-Queue-Mode is enabled by default, but can be disabled, only if extremely necessary.

Threaded-Queue-Mode is NOT required and thereby automatically disabled for Live feed such as Camera Devices/Modules, since .

"},{"location":"bonus/TQM/#what-does-threaded-queue-mode-exactly-do","title":"What does Threaded-Queue-Mode exactly do?","text":"

Threaded-Queue-Mode helps VidGear do the Threaded Video-Processing tasks in highly optimized, well-organized, and most competent way possible:

"},{"location":"bonus/TQM/#a-enables-multi-threading","title":"A. Enables Multi-Threading","text":"

In case you don't already know, OpenCV's' read() is a Blocking I/O function for reading and decoding the next video-frame, and consumes much of the I/O bound memory depending upon our video source properties & system hardware. This essentially means, the corresponding thread that reads data from it, is continuously blocked from retrieving the next frame. As a result, our python program appears slow and sluggish even without any type of computationally expensive image processing operations. This problem is far more severe on low memory SBCs like Raspberry Pis.

In Threaded-Queue-Mode, VidGear creates several Python Threads within one process to offload the frame-decoding task to a different thread. Thereby, VidGear is able to execute different Video I/O-bounded operations at the same time by overlapping there waiting times. Moreover, threads are managed by operating system itself and is capable of distributing them between available CPU cores efficiently. In this way, Threaded-Queue-Mode keeps on processing frames faster in the background without affecting by sluggishness in our main python program thread.

"},{"location":"bonus/TQM/#b-utilizes-fixed-size-queues","title":"B. Utilizes Fixed-Size Queues","text":"

Although Multi-threading is fast, easy, and efficient, it can lead to some serious undesired effects like frame-skipping, Global Interpreter Lock, race conditions, etc. This is because there is no isolation whatsoever in python threads, and in case there is any crash it will cause the whole process to crash. That's not all, the memory of the process is shared by different threads and that may result in random process crashes due to unwanted race conditions.

These problems are avoided in Threaded-Queue-Mode by utilizing Thread-Safe, Memory-Efficient, and Fixed-Size Queues (with approximately same O(1) performance in both directions), that isolates the frame-decoding thread from other parallel threads and provide synchronized access to incoming frames without any obstruction.

"},{"location":"bonus/TQM/#c-accelerates-frame-processing","title":"C. Accelerates Frame Processing","text":"

With queues, VidGear always maintains a fixed-length frames buffer in the memory and blocks the thread temporarily if the queue is full to avoid possible frame drops or otherwise pops out the frames synchronously without any obstructions. This significantly accelerates frame processing rate (and therefore our overall video processing pipeline) comes from dramatically reducing latency \u2014 since we don\u2019t have to wait for the read() method to finish reading and decoding a frame; instead, there is always a pre-decoded frame ready for us to process.

"},{"location":"bonus/TQM/#what-are-the-advantages-of-threaded-queue-mode","title":"What are the advantages of Threaded-Queue-Mode?","text":"

"},{"location":"bonus/TQM/#manually-disabling-threaded-queue-mode","title":"Manually disabling Threaded-Queue-Mode","text":"

To manually disable Threaded-Queue-Mode, VidGear provides THREADED_QUEUE_MODE boolean attribute for options dictionary parameter in respective VideoCapture APIs:

Important Warnings

Disabling Threaded-Queue-Mode may lead to Random Intermittent Bugs that can be quite difficult to discover. More insight can be found here \u27b6

THREADED_QUEUE_MODE (boolean): This attribute can be used to override Threaded-Queue-Mode mode to manually disable it:

options = {'THREADED_QUEUE_MODE': False} # to disable Threaded Queue Mode. \n

and you can pass it to options dictionary parameter of the respective API.

"},{"location":"bonus/colorspace_manipulation/","title":"Colorspace Manipulation","text":""},{"location":"bonus/colorspace_manipulation/#colorspace-manipulation-for-videocapture-gears","title":"Colorspace Manipulation for VideoCapture Gears","text":""},{"location":"bonus/colorspace_manipulation/#source-colorspace-manipulation","title":"Source ColorSpace manipulation","text":"

All VidGear's Videocapture Gears (namely CamGear, ScreenGear, VideoGear) and some Streaming Gears (namely WebGear, WebGear_RTC) and Network Gears (Client's end) - provides exclusive internal support for Source Color Space manipulation.

There are two ways to alter source colorspace:

"},{"location":"bonus/colorspace_manipulation/#using-colorspace-parameter","title":"Using colorspace parameter","text":"

Primarily, the safest way is by colorspace (string) parameter of the respective VideoCapture API, that can be used to easily alter the colorspace of the input source, during initialization. But on the downside, colorspace parameter value CANNOT be changed/altered at runtime.

All possible values for this parameter are discussed below \u27b6

"},{"location":"bonus/colorspace_manipulation/#using-color_space-global-variable","title":"Using color_space global variable","text":"

Alternatively, a more direct approach is by using color_space (integer) global variable the respective VideoCapture API, can be used for directly changing the source colorspace at runtime. It can be used in conjunction with colorspace parameter easily.

Supported Colorspace Conversions

Any conversion from default Source colorspace (i.e. BGR in case of OpenCV), to any other colorspace and vice-versa (use None to revert), is supported.

Important Information

Tip

It is advised to enable logging(logging = True) on the first run for easily identifying any runtime errors.

"},{"location":"bonus/colorspace_manipulation/#supported-colorspace-parameter-values","title":"Supported colorspace parameter values","text":"

All supported string values for colorspace parameter are as follows:

You can check all OpenCV Colorspace Conversion Codes here \u27b6.

Supported Conversion Values Description COLOR_BGR2BGRA BGR to BGRA COLOR_BGR2RGBA BGR to RGBA COLOR_BGR2RGB BGR to RGB backward conversions to RGB/BGR COLOR_BGR2GRAY BGR to GRAY COLOR_BGR2BGR565 BGR to BGR565 COLOR_BGR2BGR555 BGR to BGR555 COLOR_BGR2XYZ BGR to CIE XYZ COLOR_BGR2YCrCb BGR to luma-chroma (aka YCC) COLOR_BGR2HSV BGR to HSV (hue saturation value) COLOR_BGR2Lab BGR to CIE Lab COLOR_BGR2Luv BGR to CIE Luv COLOR_BGR2HLS BGR to HLS (hue lightness saturation) COLOR_BGR2HSV_FULL BGR to HSV_FULL COLOR_BGR2HLS_FULL BGR to HLS_FULL COLOR_BGR2YUV BGR to YUV COLOR_BGR2YUV_I420 BGR to YUV 4:2:0 family COLOR_BGR2YUV_IYUV BGR to IYUV COLOR_BGR2YUV_YV12 BGR to YUV_YV12 None Back to default colorspace (i.e. BGR)

"},{"location":"bonus/colorspace_manipulation/#usage-examples","title":"Usage examples","text":""},{"location":"bonus/colorspace_manipulation/#using-camgear-with-direct-colorspace-manipulation","title":"Using CamGear with Direct Colorspace Manipulation","text":"

The complete usage example can be found here \u27b6

"},{"location":"bonus/colorspace_manipulation/#using-pigear-with-direct-colorspace-manipulation","title":"Using PiGear with Direct Colorspace Manipulation","text":"

The complete usage example can be found here \u27b6

"},{"location":"bonus/colorspace_manipulation/#using-videogear-with-colorspace-manipulation","title":"Using VideoGear with Colorspace Manipulation","text":"

The complete usage example can be found here \u27b6

"},{"location":"bonus/colorspace_manipulation/#using-screengear-with-direct-colorspace-manipulation","title":"Using ScreenGear with Direct Colorspace Manipulation","text":"

The complete usage example can be found here \u27b6

"},{"location":"bonus/reference/camgear/","title":"CamGear API References","text":"

CamGear API usage examples can be found here \u27b6

CamGear API parameters are explained here \u27b6

CamGear supports a diverse range of video streams which can handle/control video stream almost any IP/USB Cameras, multimedia video file format (upto 4k tested), any network stream URL such as http(s), rtp, rtsp, rtmp, mms, etc. It also supports Gstreamer's RAW pipelines.

CamGear API provides a flexible, high-level multi-threaded wrapper around OpenCV's VideoCapture API with direct access to almost all of its available parameters. It relies on Threaded Queue mode for threaded, error-free and synchronized frame handling.

CamGear internally implements yt_dlp backend class for seamlessly pipelining live video-frames and metadata from various streaming services like YouTube, Dailymotion, Twitch, and many more \u27b6

Source code in vidgear/gears/camgear.py
class CamGear:\n    \"\"\"\n    CamGear supports a diverse range of video streams which can handle/control video stream almost any IP/USB Cameras, multimedia video file format (upto 4k tested),\n    any network stream URL such as http(s), rtp, rtsp, rtmp, mms, etc. It also supports Gstreamer's RAW pipelines.\n\n    CamGear API provides a flexible, high-level multi-threaded wrapper around OpenCV's VideoCapture API with direct access to almost all of its available parameters.\n    It relies on Threaded Queue mode for threaded, error-free and synchronized frame handling.\n\n    CamGear internally implements `yt_dlp` backend class for seamlessly pipelining live video-frames and metadata from various streaming services like YouTube, Dailymotion,\n    Twitch, and [many more \u27b6](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md#supported-sites)\n    \"\"\"\n\n    def __init__(\n        self,\n        source=0,\n        stream_mode=False,\n        backend=0,\n        colorspace=None,\n        logging=False,\n        time_delay=0,\n        **options\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the CamGear class.\n\n        Parameters:\n            source (based on input): defines the source for the input stream.\n            stream_mode (bool): controls the exclusive **Stream Mode** for handling streaming URLs.\n            backend (int): selects the backend for OpenCV's VideoCapture class.\n            colorspace (str): selects the colorspace of the input stream.\n            logging (bool): enables/disables logging.\n            time_delay (int): time delay (in sec) before start reading the frames.\n            options (dict): provides ability to alter Source Tweak Parameters.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # initialize global\n        self.ytv_metadata = {}\n\n        # check if Stream-Mode is ON (True)\n        if stream_mode:\n            # TODO: check GStreamer backend support\n            # gst_support = check_gstreamer_support(logging=self.__logging)\n            # handle special Stream Mode parameters\n            stream_resolution = get_supported_resolution(\n                options.pop(\"STREAM_RESOLUTION\", \"best\"), logging=self.__logging\n            )\n            # handle Stream-Mode\n            if not (yt_dlp is None):\n                # extract user-defined params\n                yt_stream_params = options.pop(\"STREAM_PARAMS\", {})\n                if isinstance(yt_stream_params, dict):\n                    yt_stream_params = {\n                        str(k).strip(): v for k, v in yt_stream_params.items()\n                    }\n                else:\n                    yt_stream_params = {}\n                try:\n                    # Validate source for Yt_dlp backend\n                    logger.info(\n                        \"Verifying Streaming URL using yt-dlp backend. Please wait...\"\n                    )\n                    # initialize YT_backend\n                    ytbackend = YT_backend(\n                        source_url=source, logging=self.__logging, **yt_stream_params\n                    )\n                    if ytbackend:\n                        # save video metadata\n                        self.ytv_metadata = ytbackend.meta_data\n                        # handle live-streams\n                        # Throw warning for livestreams\n                        ytbackend.is_livestream and logger.warning(\n                            \"Livestream URL detected. It is strongly recommended to use the GStreamer backend (`backend=cv2.CAP_GSTREAMER`) with these URLs.\"\n                        )\n                        # check whether stream-resolution was specified and available\n                        if not (stream_resolution in ytbackend.streams.keys()):\n                            logger.warning(\n                                \"Specified stream-resolution `{}` is not available. Reverting to `best`!\".format(\n                                    stream_resolution\n                                )\n                            )\n                            # revert to best\n                            stream_resolution = \"best\"\n                        else:\n                            self.__logging and logger.debug(\n                                \"Using `{}` resolution for streaming.\".format(\n                                    stream_resolution\n                                )\n                            )\n                        # extract stream URL as source using stream-resolution\n                        source = ytbackend.streams[stream_resolution]\n                        # log progress\n                        self.__logging and logger.debug(\n                            \"YouTube source ID: `{}`, Title: `{}`, Quality: `{}`\".format(\n                                self.ytv_metadata[\"id\"],\n                                self.ytv_metadata[\"title\"],\n                                stream_resolution,\n                            )\n                        )\n                except Exception as e:\n                    # raise error if something went wrong\n                    raise ValueError(\n                        \"[CamGear:ERROR] :: Stream Mode is enabled but Input URL is invalid!\"\n                    )\n            else:\n                # raise import errors\n                import_dependency_safe(\"yt_dlp\")\n\n        # assigns special parameter to global variable and clear\n        # Threaded Queue Mode\n        self.__threaded_queue_mode = options.pop(\"THREADED_QUEUE_MODE\", True)\n        if not isinstance(self.__threaded_queue_mode, bool):\n            # reset improper values\n            self.__threaded_queue_mode = True\n        # Thread Timeout\n        self.__thread_timeout = options.pop(\"THREAD_TIMEOUT\", None)\n        if self.__thread_timeout and isinstance(self.__thread_timeout, (int, float)):\n            # set values\n            self.__thread_timeout = float(self.__thread_timeout)\n        else:\n            # defaults to 5mins timeout\n            self.__thread_timeout = None\n\n        self.__queue = None\n        # initialize queue for video files only\n        if self.__threaded_queue_mode and isinstance(source, str):\n            # define queue and assign it to global var\n            self.__queue = queue.Queue(maxsize=96)  # max bufferlen 96 to check overflow\n            # log it\n            self.__logging and logger.debug(\n                \"Enabling Threaded Queue Mode for the current video source!\"\n            )\n        else:\n            # otherwise disable it\n            self.__threaded_queue_mode = False\n            # log it\n            self.__logging and logger.warning(\n                \"Threaded Queue Mode is disabled for the current video source!\"\n            )\n\n        self.__thread_timeout and logger.info(\n            \"Setting Video-Thread Timeout to {}s.\".format(self.__thread_timeout)\n        )\n\n        # stream variable initialization\n        self.stream = None\n\n        if backend and isinstance(backend, int):\n            # add backend if specified and initialize the camera stream\n            if check_CV_version() == 3:\n                # Different OpenCV 3.4.x statement\n                self.stream = cv2.VideoCapture(source + backend)\n            else:\n                # Two parameters are available since OpenCV 4+ (master branch)\n                self.stream = cv2.VideoCapture(source, backend)\n            logger.info(\"Setting backend `{}` for this source.\".format(backend))\n        else:\n            # initialize the camera stream\n            self.stream = cv2.VideoCapture(source)\n\n        # initializing colorspace variable\n        self.color_space = None\n\n        # apply attributes to source if specified\n        options = {str(k).strip(): v for k, v in options.items()}\n        for key, value in options.items():\n            property = capPropId(key)\n            not (property is None) and self.stream.set(property, value)\n\n        # handle colorspace value\n        if not (colorspace is None):\n            self.color_space = capPropId(colorspace.strip())\n            self.__logging and not (self.color_space is None) and logger.debug(\n                \"Enabling `{}` colorspace for this video stream!\".format(\n                    colorspace.strip()\n                )\n            )\n\n        # initialize and assign frame-rate variable\n        self.framerate = 0.0\n        _fps = self.stream.get(cv2.CAP_PROP_FPS)\n        if _fps > 1.0:\n            self.framerate = _fps\n\n        # applying time delay to warm-up webcam only if specified\n        time_delay and isinstance(time_delay, (int, float)) and time.sleep(time_delay)\n\n        # frame variable initialization\n        (grabbed, self.frame) = self.stream.read()\n\n        # check if valid stream\n        if grabbed:\n            # render colorspace if defined\n            if not (self.color_space is None):\n                self.frame = cv2.cvtColor(self.frame, self.color_space)\n\n            # initialize and append to queue\n            self.__threaded_queue_mode and self.__queue.put(self.frame)\n        else:\n            raise RuntimeError(\n                \"[CamGear:ERROR] :: Source is invalid, CamGear failed to initialize stream on this source!\"\n            )\n\n        # thread initialization\n        self.__thread = None\n\n        # initialize termination flag event\n        self.__terminate = Event()\n\n        # initialize stream read flag event\n        self.__stream_read = Event()\n\n    def start(self):\n        \"\"\"\n        Launches the internal *Threaded Frames Extractor* daemon.\n\n        **Returns:** A reference to the CamGear class object.\n        \"\"\"\n\n        self.__thread = Thread(target=self.__update, name=\"CamGear\", args=())\n        self.__thread.daemon = True\n        self.__thread.start()\n        return self\n\n    def __update(self):\n        \"\"\"\n        A **Threaded Frames Extractor**, that keep iterating frames from OpenCV's VideoCapture API to a internal monitored queue,\n        until the thread is terminated, or frames runs out.\n        \"\"\"\n\n        # keep iterating infinitely\n        # until the thread is terminated\n        # or frames runs out\n        # if the thread indicator variable is set, stop the thread\n        while not self.__terminate.is_set():\n            # stream not read yet\n            self.__stream_read.clear()\n\n            # otherwise, read the next frame from the stream\n            (grabbed, frame) = self.stream.read()\n\n            # stream read completed\n            self.__stream_read.set()\n\n            # check for valid frame if received\n            if not grabbed:\n                # no frames received, then safely exit\n                if self.__threaded_queue_mode:\n                    if self.__queue.empty():\n                        break\n                    else:\n                        continue\n                else:\n                    break\n\n            # apply colorspace to frames if valid\n            if not (self.color_space is None):\n                # apply colorspace to frames\n                color_frame = None\n                try:\n                    color_frame = cv2.cvtColor(frame, self.color_space)\n                except Exception as e:\n                    # Catch if any error occurred\n                    color_frame = None\n                    self.color_space = None\n                    self.__logging and logger.exception(str(e))\n                    logger.warning(\"Assigned colorspace value is invalid. Discarding!\")\n                self.frame = color_frame if not (color_frame is None) else frame\n            else:\n                self.frame = frame\n\n            # append to queue\n            self.__threaded_queue_mode and self.__queue.put(self.frame)\n\n        # signal queue we're done\n        self.__threaded_queue_mode and self.__queue.put(None)\n        self.__threaded_queue_mode = False\n\n        # indicate immediate termination\n        self.__terminate.set()\n        self.__stream_read.set()\n\n        # release resources\n        self.stream.release()\n\n    def read(self):\n        \"\"\"\n        Extracts frames synchronously from monitored queue, while maintaining a fixed-length frame buffer in the memory,\n        and blocks the thread if the queue is full.\n\n        **Returns:** A n-dimensional numpy array.\n        \"\"\"\n        while self.__threaded_queue_mode and not self.__terminate.is_set():\n            return self.__queue.get(timeout=self.__thread_timeout)\n        # return current frame\n        # only after stream is read\n        return (\n            self.frame\n            if not self.__terminate.is_set()  # check if already terminated\n            and self.__stream_read.wait(timeout=self.__thread_timeout)  # wait for it\n            else None\n        )\n\n    def stop(self):\n        \"\"\"\n        Safely terminates the thread, and release the multi-threaded resources.\n        \"\"\"\n        self.__logging and logger.debug(\"Terminating processes.\")\n        # terminate Threaded queue mode separately\n        self.__threaded_queue_mode = False\n\n        # indicate that the thread\n        # should be terminated immediately\n        self.__stream_read.set()\n        self.__terminate.set()\n\n        # wait until stream resources are released (producer thread might be still grabbing frame)\n        if self.__thread is not None:\n            if not (self.__queue is None):\n                while not self.__queue.empty():\n                    try:\n                        self.__queue.get_nowait()\n                    except queue.Empty:\n                        continue\n                    self.__queue.task_done()\n            self.__thread.join()\n

"},{"location":"bonus/reference/camgear/#vidgear.gears.camgear.CamGear.__init__","title":"__init__(self, source=0, stream_mode=False, backend=0, colorspace=None, logging=False, time_delay=0, **options) special","text":"

This constructor method initializes the object state and attributes of the CamGear class.

Parameters:

Name Type Description Default source based on input

defines the source for the input stream.

0 stream_mode bool

controls the exclusive Stream Mode for handling streaming URLs.

False backend int

selects the backend for OpenCV's VideoCapture class.

0 colorspace str

selects the colorspace of the input stream.

None logging bool

enables/disables logging.

False time_delay int

time delay (in sec) before start reading the frames.

0 options dict

provides ability to alter Source Tweak Parameters.

{} Source code in vidgear/gears/camgear.py
def __init__(\n    self,\n    source=0,\n    stream_mode=False,\n    backend=0,\n    colorspace=None,\n    logging=False,\n    time_delay=0,\n    **options\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the CamGear class.\n\n    Parameters:\n        source (based on input): defines the source for the input stream.\n        stream_mode (bool): controls the exclusive **Stream Mode** for handling streaming URLs.\n        backend (int): selects the backend for OpenCV's VideoCapture class.\n        colorspace (str): selects the colorspace of the input stream.\n        logging (bool): enables/disables logging.\n        time_delay (int): time delay (in sec) before start reading the frames.\n        options (dict): provides ability to alter Source Tweak Parameters.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # initialize global\n    self.ytv_metadata = {}\n\n    # check if Stream-Mode is ON (True)\n    if stream_mode:\n        # TODO: check GStreamer backend support\n        # gst_support = check_gstreamer_support(logging=self.__logging)\n        # handle special Stream Mode parameters\n        stream_resolution = get_supported_resolution(\n            options.pop(\"STREAM_RESOLUTION\", \"best\"), logging=self.__logging\n        )\n        # handle Stream-Mode\n        if not (yt_dlp is None):\n            # extract user-defined params\n            yt_stream_params = options.pop(\"STREAM_PARAMS\", {})\n            if isinstance(yt_stream_params, dict):\n                yt_stream_params = {\n                    str(k).strip(): v for k, v in yt_stream_params.items()\n                }\n            else:\n                yt_stream_params = {}\n            try:\n                # Validate source for Yt_dlp backend\n                logger.info(\n                    \"Verifying Streaming URL using yt-dlp backend. Please wait...\"\n                )\n                # initialize YT_backend\n                ytbackend = YT_backend(\n                    source_url=source, logging=self.__logging, **yt_stream_params\n                )\n                if ytbackend:\n                    # save video metadata\n                    self.ytv_metadata = ytbackend.meta_data\n                    # handle live-streams\n                    # Throw warning for livestreams\n                    ytbackend.is_livestream and logger.warning(\n                        \"Livestream URL detected. It is strongly recommended to use the GStreamer backend (`backend=cv2.CAP_GSTREAMER`) with these URLs.\"\n                    )\n                    # check whether stream-resolution was specified and available\n                    if not (stream_resolution in ytbackend.streams.keys()):\n                        logger.warning(\n                            \"Specified stream-resolution `{}` is not available. Reverting to `best`!\".format(\n                                stream_resolution\n                            )\n                        )\n                        # revert to best\n                        stream_resolution = \"best\"\n                    else:\n                        self.__logging and logger.debug(\n                            \"Using `{}` resolution for streaming.\".format(\n                                stream_resolution\n                            )\n                        )\n                    # extract stream URL as source using stream-resolution\n                    source = ytbackend.streams[stream_resolution]\n                    # log progress\n                    self.__logging and logger.debug(\n                        \"YouTube source ID: `{}`, Title: `{}`, Quality: `{}`\".format(\n                            self.ytv_metadata[\"id\"],\n                            self.ytv_metadata[\"title\"],\n                            stream_resolution,\n                        )\n                    )\n            except Exception as e:\n                # raise error if something went wrong\n                raise ValueError(\n                    \"[CamGear:ERROR] :: Stream Mode is enabled but Input URL is invalid!\"\n                )\n        else:\n            # raise import errors\n            import_dependency_safe(\"yt_dlp\")\n\n    # assigns special parameter to global variable and clear\n    # Threaded Queue Mode\n    self.__threaded_queue_mode = options.pop(\"THREADED_QUEUE_MODE\", True)\n    if not isinstance(self.__threaded_queue_mode, bool):\n        # reset improper values\n        self.__threaded_queue_mode = True\n    # Thread Timeout\n    self.__thread_timeout = options.pop(\"THREAD_TIMEOUT\", None)\n    if self.__thread_timeout and isinstance(self.__thread_timeout, (int, float)):\n        # set values\n        self.__thread_timeout = float(self.__thread_timeout)\n    else:\n        # defaults to 5mins timeout\n        self.__thread_timeout = None\n\n    self.__queue = None\n    # initialize queue for video files only\n    if self.__threaded_queue_mode and isinstance(source, str):\n        # define queue and assign it to global var\n        self.__queue = queue.Queue(maxsize=96)  # max bufferlen 96 to check overflow\n        # log it\n        self.__logging and logger.debug(\n            \"Enabling Threaded Queue Mode for the current video source!\"\n        )\n    else:\n        # otherwise disable it\n        self.__threaded_queue_mode = False\n        # log it\n        self.__logging and logger.warning(\n            \"Threaded Queue Mode is disabled for the current video source!\"\n        )\n\n    self.__thread_timeout and logger.info(\n        \"Setting Video-Thread Timeout to {}s.\".format(self.__thread_timeout)\n    )\n\n    # stream variable initialization\n    self.stream = None\n\n    if backend and isinstance(backend, int):\n        # add backend if specified and initialize the camera stream\n        if check_CV_version() == 3:\n            # Different OpenCV 3.4.x statement\n            self.stream = cv2.VideoCapture(source + backend)\n        else:\n            # Two parameters are available since OpenCV 4+ (master branch)\n            self.stream = cv2.VideoCapture(source, backend)\n        logger.info(\"Setting backend `{}` for this source.\".format(backend))\n    else:\n        # initialize the camera stream\n        self.stream = cv2.VideoCapture(source)\n\n    # initializing colorspace variable\n    self.color_space = None\n\n    # apply attributes to source if specified\n    options = {str(k).strip(): v for k, v in options.items()}\n    for key, value in options.items():\n        property = capPropId(key)\n        not (property is None) and self.stream.set(property, value)\n\n    # handle colorspace value\n    if not (colorspace is None):\n        self.color_space = capPropId(colorspace.strip())\n        self.__logging and not (self.color_space is None) and logger.debug(\n            \"Enabling `{}` colorspace for this video stream!\".format(\n                colorspace.strip()\n            )\n        )\n\n    # initialize and assign frame-rate variable\n    self.framerate = 0.0\n    _fps = self.stream.get(cv2.CAP_PROP_FPS)\n    if _fps > 1.0:\n        self.framerate = _fps\n\n    # applying time delay to warm-up webcam only if specified\n    time_delay and isinstance(time_delay, (int, float)) and time.sleep(time_delay)\n\n    # frame variable initialization\n    (grabbed, self.frame) = self.stream.read()\n\n    # check if valid stream\n    if grabbed:\n        # render colorspace if defined\n        if not (self.color_space is None):\n            self.frame = cv2.cvtColor(self.frame, self.color_space)\n\n        # initialize and append to queue\n        self.__threaded_queue_mode and self.__queue.put(self.frame)\n    else:\n        raise RuntimeError(\n            \"[CamGear:ERROR] :: Source is invalid, CamGear failed to initialize stream on this source!\"\n        )\n\n    # thread initialization\n    self.__thread = None\n\n    # initialize termination flag event\n    self.__terminate = Event()\n\n    # initialize stream read flag event\n    self.__stream_read = Event()\n
"},{"location":"bonus/reference/camgear/#vidgear.gears.camgear.CamGear.read","title":"read(self)","text":"

Extracts frames synchronously from monitored queue, while maintaining a fixed-length frame buffer in the memory, and blocks the thread if the queue is full.

Returns: A n-dimensional numpy array.

Source code in vidgear/gears/camgear.py
def read(self):\n    \"\"\"\n    Extracts frames synchronously from monitored queue, while maintaining a fixed-length frame buffer in the memory,\n    and blocks the thread if the queue is full.\n\n    **Returns:** A n-dimensional numpy array.\n    \"\"\"\n    while self.__threaded_queue_mode and not self.__terminate.is_set():\n        return self.__queue.get(timeout=self.__thread_timeout)\n    # return current frame\n    # only after stream is read\n    return (\n        self.frame\n        if not self.__terminate.is_set()  # check if already terminated\n        and self.__stream_read.wait(timeout=self.__thread_timeout)  # wait for it\n        else None\n    )\n
"},{"location":"bonus/reference/camgear/#vidgear.gears.camgear.CamGear.start","title":"start(self)","text":"

Launches the internal Threaded Frames Extractor daemon.

Returns: A reference to the CamGear class object.

Source code in vidgear/gears/camgear.py
def start(self):\n    \"\"\"\n    Launches the internal *Threaded Frames Extractor* daemon.\n\n    **Returns:** A reference to the CamGear class object.\n    \"\"\"\n\n    self.__thread = Thread(target=self.__update, name=\"CamGear\", args=())\n    self.__thread.daemon = True\n    self.__thread.start()\n    return self\n
"},{"location":"bonus/reference/camgear/#vidgear.gears.camgear.CamGear.stop","title":"stop(self)","text":"

Safely terminates the thread, and release the multi-threaded resources.

Source code in vidgear/gears/camgear.py
def stop(self):\n    \"\"\"\n    Safely terminates the thread, and release the multi-threaded resources.\n    \"\"\"\n    self.__logging and logger.debug(\"Terminating processes.\")\n    # terminate Threaded queue mode separately\n    self.__threaded_queue_mode = False\n\n    # indicate that the thread\n    # should be terminated immediately\n    self.__stream_read.set()\n    self.__terminate.set()\n\n    # wait until stream resources are released (producer thread might be still grabbing frame)\n    if self.__thread is not None:\n        if not (self.__queue is None):\n            while not self.__queue.empty():\n                try:\n                    self.__queue.get_nowait()\n                except queue.Empty:\n                    continue\n                self.__queue.task_done()\n        self.__thread.join()\n
"},{"location":"bonus/reference/helper/","title":"Helper Methods","text":""},{"location":"bonus/reference/helper/#vidgear.gears.helper.logger_handler--logger_handler","title":"logger_handler","text":"

Returns the logger handler

Returns: A logger handler

Source code in vidgear/gears/helper.py
def logger_handler():\n    \"\"\"\n    ## logger_handler\n\n    Returns the logger handler\n\n    **Returns:** A logger handler\n    \"\"\"\n    # logging formatter\n    formatter = ColoredFormatter(\n        \"{green}{asctime}{reset} :: {bold_purple}{name:^13}{reset} :: {log_color}{levelname:^8}{reset} :: {bold_white}{message}\",\n        datefmt=\"%H:%M:%S\",\n        reset=True,\n        log_colors={\n            \"INFO\": \"bold_cyan\",\n            \"DEBUG\": \"bold_yellow\",\n            \"WARNING\": \"bold_red,fg_thin_yellow\",\n            \"ERROR\": \"bold_red\",\n            \"CRITICAL\": \"bold_red,bg_white\",\n        },\n        style=\"{\",\n    )\n    # check if VIDGEAR_LOGFILE defined\n    file_mode = os.environ.get(\"VIDGEAR_LOGFILE\", False)\n    # define handler\n    handler = log.StreamHandler()\n    if file_mode and isinstance(file_mode, str):\n        file_path = os.path.abspath(file_mode)\n        if (os.name == \"nt\" or os.access in os.supports_effective_ids) and os.access(\n            os.path.dirname(file_path), os.W_OK\n        ):\n            file_path = (\n                os.path.join(file_path, \"vidgear.log\")\n                if os.path.isdir(file_path)\n                else file_path\n            )\n            handler = log.FileHandler(file_path, mode=\"a\")\n            formatter = log.Formatter(\n                \"{asctime} :: {name} :: {levelname} :: {message}\",\n                datefmt=\"%H:%M:%S\",\n                style=\"{\",\n            )\n\n    handler.setFormatter(formatter)\n    return handler\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.check_CV_version--check_cv_version","title":"check_CV_version","text":"

Returns: OpenCV's version first bit

Source code in vidgear/gears/helper.py
def check_CV_version():\n    \"\"\"\n    ## check_CV_version\n\n    **Returns:** OpenCV's version first bit\n    \"\"\"\n    if parse_version(cv2.__version__) >= parse_version(\"4\"):\n        return 4\n    else:\n        return 3\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.check_gstreamer_support--check_gstreamer_support","title":"check_gstreamer_support","text":"

Checks whether OpenCV is compiled with Gstreamer(>=1.0.0) support.

Parameters:

Name Type Description Default logging bool

enables logging for its operations

False

Returns: A Boolean value

Source code in vidgear/gears/helper.py
def check_gstreamer_support(logging=False):\n    \"\"\"\n    ## check_gstreamer_support\n\n    Checks whether OpenCV is compiled with Gstreamer(`>=1.0.0`) support.\n\n    Parameters:\n        logging (bool): enables logging for its operations\n\n    **Returns:** A Boolean value\n    \"\"\"\n    raw = cv2.getBuildInformation()\n    gst = [\n        x.strip()\n        for x in raw.split(\"\\n\")\n        if x and re.search(r\"GStreamer[,-:]+\\s*(?:YES|NO)\", x)\n    ]\n    if gst and \"YES\" in gst[0]:\n        version = re.search(r\"(\\d+\\.)?(\\d+\\.)?(\\*|\\d+)\", gst[0])\n        logging and logger.debug(\"Found GStreamer version:{}\".format(version[0]))\n        return version[0] >= \"1.0.0\"\n    else:\n        logger.warning(\"GStreamer not found!\")\n        return False\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.get_supported_resolution--get_supported_resolution","title":"get_supported_resolution","text":"

Parameters:

Name Type Description Default value string

value to be validated

required logging bool

enables logging for its operations

False

Returns: Valid stream resolution

Source code in vidgear/gears/helper.py
def get_supported_resolution(value, logging=False):\n    \"\"\"\n    ## get_supported_resolution\n\n    Parameters:\n        value (string): value to be validated\n        logging (bool): enables logging for its operations\n\n    **Returns:** Valid stream resolution\n    \"\"\"\n    # default to best\n    stream_resolution = \"best\"\n    supported_stream_qualities = [\n        \"144p\",\n        \"240p\",\n        \"360p\",\n        \"480p\",\n        \"720p\",\n        \"1080p\",\n        \"1440p\",\n        \"2160p\",\n        \"4320p\",\n        \"worst\",\n        \"best\",\n    ]\n    if isinstance(value, str):\n        if value.strip().lower() in supported_stream_qualities:\n            stream_resolution = value.strip().lower()\n            logging and logger.debug(\n                \"Selecting `{}` resolution for streams.\".format(stream_resolution)\n            )\n        else:\n            logger.warning(\n                \"Specified stream-resolution `{}` is not supported. Reverting to `best`!\".format(\n                    value\n                )\n            )\n    else:\n        logger.warning(\n            \"Specified stream-resolution `{}` is Invalid. Reverting to `best`!\".format(\n                value\n            )\n        )\n    return stream_resolution\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.dimensions_to_resolutions--dimensions_to_resolutions","title":"dimensions_to_resolutions","text":"

Parameters:

Name Type Description Default value list

list of dimensions (e.g. 640x360)

required

Returns: list of resolutions (e.g. 360p)

Source code in vidgear/gears/helper.py
def dimensions_to_resolutions(value):\n    \"\"\"\n    ## dimensions_to_resolutions\n\n    Parameters:\n        value (list): list of dimensions (e.g. `640x360`)\n\n    **Returns:** list of resolutions (e.g. `360p`)\n    \"\"\"\n    supported_resolutions = {\n        \"256x144\": \"144p\",\n        \"426x240\": \"240p\",\n        \"640x360\": \"360p\",\n        \"854x480\": \"480p\",\n        \"1280x720\": \"720p\",\n        \"1920x1080\": \"1080p\",\n        \"2560x1440\": \"1440p\",\n        \"3840x2160\": \"2160p\",\n        \"7680x4320\": \"4320p\",\n    }\n    return (\n        list(map(supported_resolutions.get, value, value))\n        if isinstance(value, list)\n        else []\n    )\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.mkdir_safe--mkdir_safe","title":"mkdir_safe","text":"

Safely creates directory at given path.

Parameters:

Name Type Description Default dir_path string

path to the directory

required logging bool

enables logging for its operations

False Source code in vidgear/gears/helper.py
def mkdir_safe(dir_path, logging=False):\n    \"\"\"\n    ## mkdir_safe\n\n    Safely creates directory at given path.\n\n    Parameters:\n        dir_path (string): path to the directory\n        logging (bool): enables logging for its operations\n\n    \"\"\"\n    try:\n        os.makedirs(dir_path)\n        logging and logger.debug(\"Created directory at `{}`\".format(dir_path))\n    except (OSError, IOError) as e:\n        if e.errno != errno.EACCES and e.errno != errno.EEXIST:\n            raise\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.delete_ext_safe--delete_ext_safe","title":"delete_ext_safe","text":"

Safely deletes files with given extensions at given path.

Parameters:

Name Type Description Default dir_path string

path to the directory

required extensions list

list of extensions to be deleted

[] logging bool

enables logging for its operations

False Source code in vidgear/gears/helper.py
def delete_ext_safe(dir_path, extensions=[], logging=False):\n    \"\"\"\n    ## delete_ext_safe\n\n    Safely deletes files with given extensions at given path.\n\n    Parameters:\n        dir_path (string): path to the directory\n        extensions (list): list of extensions to be deleted\n        logging (bool): enables logging for its operations\n\n    \"\"\"\n    if not extensions or not os.path.exists(dir_path):\n        logger.warning(\"Invalid input provided for deleting!\")\n        return\n\n    logger.critical(\"Clearing Assets at `{}`!\".format(dir_path))\n\n    for ext in extensions:\n        if len(ext) == 2:\n            files_ext = [\n                os.path.join(dir_path, f)\n                for f in os.listdir(dir_path)\n                if f.startswith(ext[0]) and f.endswith(ext[1])\n            ]\n        else:\n            files_ext = [\n                os.path.join(dir_path, f)\n                for f in os.listdir(dir_path)\n                if f.endswith(ext)\n            ]\n        for file in files_ext:\n            delete_file_safe(file)\n            logging and logger.debug(\"Deleted file: `{}`\".format(file))\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.capPropId--cappropid","title":"capPropId","text":"

Retrieves the OpenCV property's Integer(Actual) value from string.

Parameters:

Name Type Description Default property string

inputs OpenCV property as string.

required logging bool

enables logging for its operations

True

Returns: Resultant integer value.

Source code in vidgear/gears/helper.py
def capPropId(property, logging=True):\n    \"\"\"\n    ## capPropId\n\n    Retrieves the OpenCV property's Integer(Actual) value from string.\n\n    Parameters:\n        property (string): inputs OpenCV property as string.\n        logging (bool): enables logging for its operations\n\n    **Returns:** Resultant integer value.\n    \"\"\"\n    integer_value = 0\n    try:\n        integer_value = getattr(cv2, property)\n    except Exception as e:\n        logging and logger.exception(str(e))\n        logger.critical(\"`{}` is not a valid OpenCV property!\".format(property))\n        return None\n    return integer_value\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.reducer--reducer","title":"reducer","text":"

Reduces frame size by given percentage

Parameters:

Name Type Description Default frame numpy.ndarray

inputs numpy array(frame).

None percentage int/float

inputs size-reduction percentage.

0 interpolation int

Change resize interpolation.

4

Returns: A reduced numpy ndarray array.

Source code in vidgear/gears/helper.py
def reducer(frame=None, percentage=0, interpolation=cv2.INTER_LANCZOS4):\n    \"\"\"\n    ## reducer\n\n    Reduces frame size by given percentage\n\n    Parameters:\n        frame (numpy.ndarray): inputs numpy array(frame).\n        percentage (int/float): inputs size-reduction percentage.\n        interpolation (int): Change resize interpolation.\n\n    **Returns:**  A reduced numpy ndarray array.\n    \"\"\"\n    # check if frame is valid\n    if frame is None:\n        raise ValueError(\"[Helper:ERROR] :: Input frame cannot be NoneType!\")\n\n    # check if valid reduction percentage is given\n    if not (percentage > 0 and percentage < 90):\n        raise ValueError(\n            \"[Helper:ERROR] :: Given frame-size reduction percentage is invalid, Kindly refer docs.\"\n        )\n\n    if not (isinstance(interpolation, int)):\n        raise ValueError(\n            \"[Helper:ERROR] :: Given interpolation is invalid, Kindly refer docs.\"\n        )\n\n    # grab the frame size\n    (height, width) = frame.shape[:2]\n\n    # calculate the ratio of the width from percentage\n    reduction = ((100 - percentage) / 100) * width\n    ratio = reduction / float(width)\n    # construct the dimensions\n    dimensions = (int(reduction), int(height * ratio))\n\n    # return the resized frame\n    return cv2.resize(frame, dimensions, interpolation=interpolation)\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.create_blank_frame--create_blank_frame","title":"create_blank_frame","text":"

Create blank frames of given frame size with text

Parameters:

Name Type Description Default frame numpy.ndarray

inputs numpy array(frame).

None text str

Text to be written on frame.

''

Returns: A reduced numpy ndarray array.

Source code in vidgear/gears/helper.py
def create_blank_frame(frame=None, text=\"\", logging=False):\n    \"\"\"\n    ## create_blank_frame\n\n    Create blank frames of given frame size with text\n\n    Parameters:\n        frame (numpy.ndarray): inputs numpy array(frame).\n        text (str): Text to be written on frame.\n    **Returns:**  A reduced numpy ndarray array.\n    \"\"\"\n    # check if frame is valid\n    if frame is None or not (isinstance(frame, np.ndarray)):\n        raise ValueError(\"[Helper:ERROR] :: Input frame is invalid!\")\n    # grab the frame size\n    (height, width) = frame.shape[:2]\n    # create blank frame\n    blank_frame = np.zeros(frame.shape, frame.dtype)\n    # setup text\n    if text and isinstance(text, str):\n        logging and logger.debug(\"Adding text: {}\".format(text))\n        # setup font\n        font = cv2.FONT_HERSHEY_SCRIPT_COMPLEX\n        # get boundary of this text\n        fontScale = min(height, width) / (25 / 0.25)\n        textsize = cv2.getTextSize(text, font, fontScale, 5)[0]\n        # get coords based on boundary\n        textX = (width - textsize[0]) // 2\n        textY = (height + textsize[1]) // 2\n        # put text\n        cv2.putText(\n            blank_frame, text, (textX, textY), font, fontScale, (125, 125, 125), 6\n        )\n\n    # return frame\n    return blank_frame\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.dict2Args--dict2args","title":"dict2Args","text":"

Converts dictionary attributes to list(args)

Parameters:

Name Type Description Default param_dict dict

Parameters dictionary

required

Returns: Arguments list

Source code in vidgear/gears/helper.py
def dict2Args(param_dict):\n    \"\"\"\n    ## dict2Args\n\n    Converts dictionary attributes to list(args)\n\n    Parameters:\n        param_dict (dict): Parameters dictionary\n\n    **Returns:** Arguments list\n    \"\"\"\n    args = []\n    for key in param_dict.keys():\n        if key in [\"-clones\"] or key.startswith(\"-core\"):\n            if isinstance(param_dict[key], list):\n                args.extend(param_dict[key])\n            else:\n                logger.warning(\n                    \"{} with invalid datatype:`{}`, Skipped!\".format(\n                        \"Core parameter\" if key.startswith(\"-core\") else \"Clone\",\n                        param_dict[key],\n                    )\n                )\n        else:\n            args.append(key)\n            args.append(str(param_dict[key]))\n    return args\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.get_valid_ffmpeg_path--get_valid_ffmpeg_path","title":"get_valid_ffmpeg_path","text":"

Validate the given FFmpeg path/binaries, and returns a valid FFmpeg executable path.

Parameters:

Name Type Description Default custom_ffmpeg string

path to custom FFmpeg executables

'' is_windows boolean

is running on Windows OS?

False ffmpeg_download_path string

FFmpeg static binaries download location (Windows only)

'' logging bool

enables logging for its operations

False

Returns: A valid FFmpeg executable path string.

Source code in vidgear/gears/helper.py
def get_valid_ffmpeg_path(\n    custom_ffmpeg=\"\", is_windows=False, ffmpeg_download_path=\"\", logging=False\n):\n    \"\"\"\n    ## get_valid_ffmpeg_path\n\n    Validate the given FFmpeg path/binaries, and returns a valid FFmpeg executable path.\n\n    Parameters:\n        custom_ffmpeg (string): path to custom FFmpeg executables\n        is_windows (boolean): is running on Windows OS?\n        ffmpeg_download_path (string): FFmpeg static binaries download location _(Windows only)_\n        logging (bool): enables logging for its operations\n\n    **Returns:** A valid FFmpeg executable path string.\n    \"\"\"\n    final_path = \"\"\n    if is_windows:\n        # checks if current os is windows\n        if custom_ffmpeg:\n            # if custom FFmpeg path is given assign to local variable\n            final_path += custom_ffmpeg\n        else:\n            # otherwise auto-download them\n            try:\n                if not (ffmpeg_download_path):\n                    # otherwise save to Temp Directory\n                    import tempfile\n\n                    ffmpeg_download_path = tempfile.gettempdir()\n\n                logging and logger.debug(\n                    \"FFmpeg Windows Download Path: {}\".format(ffmpeg_download_path)\n                )\n\n                # download Binaries\n                os_bit = (\n                    (\"win64\" if platform.machine().endswith(\"64\") else \"win32\")\n                    if is_windows\n                    else \"\"\n                )\n                _path = download_ffmpeg_binaries(\n                    path=ffmpeg_download_path, os_windows=is_windows, os_bit=os_bit\n                )\n                # assign to local variable\n                final_path += _path\n\n            except Exception as e:\n                # log if any error occurred\n                logger.exception(str(e))\n                logger.error(\n                    \"Error in downloading FFmpeg binaries, Check your network and Try again!\"\n                )\n                return False\n\n        if os.path.isfile(final_path):\n            # check if valid FFmpeg file exist\n            pass\n        elif os.path.isfile(os.path.join(final_path, \"ffmpeg.exe\")):\n            # check if FFmpeg directory exists, if does, then check for valid file\n            final_path = os.path.join(final_path, \"ffmpeg.exe\")\n        else:\n            # else return False\n            logging and logger.debug(\n                \"No valid FFmpeg executables found at Custom FFmpeg path!\"\n            )\n            return False\n    else:\n        # otherwise perform test for Unix\n        if custom_ffmpeg:\n            # if custom FFmpeg path is given assign to local variable\n            if os.path.isfile(custom_ffmpeg):\n                # check if valid FFmpeg file exist\n                final_path += custom_ffmpeg\n            elif os.path.isfile(os.path.join(custom_ffmpeg, \"ffmpeg\")):\n                # check if FFmpeg directory exists, if does, then check for valid file\n                final_path = os.path.join(custom_ffmpeg, \"ffmpeg\")\n            else:\n                # else return False\n                logging and logger.debug(\n                    \"No valid FFmpeg executables found at Custom FFmpeg path!\"\n                )\n                return False\n        else:\n            # otherwise assign ffmpeg binaries from system\n            final_path += \"ffmpeg\"\n\n    logging and logger.debug(\"Final FFmpeg Path: {}\".format(final_path))\n\n    # Final Auto-Validation for FFmeg Binaries. returns final path if test is passed\n    return final_path if validate_ffmpeg(final_path, logging=logging) else False\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.download_ffmpeg_binaries--download_ffmpeg_binaries","title":"download_ffmpeg_binaries","text":"

Generates FFmpeg Static Binaries for windows(if not available)

Parameters:

Name Type Description Default path string

path for downloading custom FFmpeg executables

required os_windows boolean

is running on Windows OS?

False os_bit string

32-bit or 64-bit OS?

''

Returns: A valid FFmpeg executable path string.

Source code in vidgear/gears/helper.py
def download_ffmpeg_binaries(path, os_windows=False, os_bit=\"\"):\n    \"\"\"\n    ## download_ffmpeg_binaries\n\n    Generates FFmpeg Static Binaries for windows(if not available)\n\n    Parameters:\n        path (string): path for downloading custom FFmpeg executables\n        os_windows (boolean): is running on Windows OS?\n        os_bit (string): 32-bit or 64-bit OS?\n\n    **Returns:** A valid FFmpeg executable path string.\n    \"\"\"\n    final_path = \"\"\n    if os_windows and os_bit:\n        # initialize with available FFmpeg Static Binaries GitHub Server\n        file_url = \"https://github.com/abhiTronix/FFmpeg-Builds/releases/latest/download/ffmpeg-static-{}-gpl.zip\".format(\n            os_bit\n        )\n\n        file_name = os.path.join(\n            os.path.abspath(path), \"ffmpeg-static-{}-gpl.zip\".format(os_bit)\n        )\n        file_path = os.path.join(\n            os.path.abspath(path),\n            \"ffmpeg-static-{}-gpl/bin/ffmpeg.exe\".format(os_bit),\n        )\n        base_path, _ = os.path.split(file_name)  # extract file base path\n        # check if file already exists\n        if os.path.isfile(file_path):\n            final_path += file_path  # skip download if does\n        else:\n            # import libs\n            import zipfile\n\n            # check if given path has write access\n            assert os.access(path, os.W_OK), (\n                \"[Helper:ERROR] :: Permission Denied, Cannot write binaries to directory = \"\n                + path\n            )\n            # remove leftovers if exists\n            os.path.isfile(file_name) and delete_file_safe(file_name)\n            # download and write file to the given path\n            with open(file_name, \"wb\") as f:\n                logger.debug(\n                    \"No Custom FFmpeg path provided. Auto-Installing FFmpeg static binaries from GitHub Mirror now. Please wait...\"\n                )\n                # create session\n                with requests.Session() as http:\n                    # setup retry strategy\n                    retries = Retry(\n                        total=3,\n                        backoff_factor=1,\n                        status_forcelist=[429, 500, 502, 503, 504],\n                    )\n                    # Mount it for https usage\n                    adapter = TimeoutHTTPAdapter(timeout=2.0, max_retries=retries)\n                    http.mount(\"https://\", adapter)\n                    response = http.get(file_url, stream=True)\n                    response.raise_for_status()\n                    total_length = (\n                        response.headers.get(\"content-length\")\n                        if \"content-length\" in response.headers\n                        else len(response.content)\n                    )\n                    assert not (\n                        total_length is None\n                    ), \"[Helper:ERROR] :: Failed to retrieve files, check your Internet connectivity!\"\n                    bar = tqdm(total=int(total_length), unit=\"B\", unit_scale=True)\n                    for data in response.iter_content(chunk_size=4096):\n                        f.write(data)\n                        len(data) > 0 and bar.update(len(data))\n                    bar.close()\n            logger.debug(\"Extracting executables.\")\n            with zipfile.ZipFile(file_name, \"r\") as zip_ref:\n                zip_fname, _ = os.path.split(zip_ref.infolist()[0].filename)\n                zip_ref.extractall(base_path)\n            # perform cleaning\n            delete_file_safe(file_name)\n            logger.debug(\"FFmpeg binaries for Windows configured successfully!\")\n            final_path += file_path\n    # return final path\n    return final_path\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.validate_ffmpeg--validate_ffmpeg","title":"validate_ffmpeg","text":"

Validate FFmeg Binaries. returns True if tests are passed.

Parameters:

Name Type Description Default path string

absolute path of FFmpeg binaries

required logging bool

enables logging for its operations

False

Returns: A boolean value, confirming whether tests passed, or not?.

Source code in vidgear/gears/helper.py
def validate_ffmpeg(path, logging=False):\n    \"\"\"\n    ## validate_ffmpeg\n\n    Validate FFmeg Binaries. returns `True` if tests are passed.\n\n    Parameters:\n        path (string): absolute path of FFmpeg binaries\n        logging (bool): enables logging for its operations\n\n    **Returns:** A boolean value, confirming whether tests passed, or not?.\n    \"\"\"\n    try:\n        # get the FFmpeg version\n        version = check_output([path, \"-version\"])\n        firstline = version.split(b\"\\n\")[0]\n        version = firstline.split(b\" \")[2].strip()\n        # log if test are passed\n        logging and logger.info(\"FFmpeg validity Test Passed!\")\n        logging and logger.debug(\n            \"Found valid FFmpeg Version: `{}` installed on this system\".format(version)\n        )\n    except Exception as e:\n        # log if test are failed\n        logging and logger.exception(str(e))\n        logger.error(\"FFmpeg validity Test Failed!\")\n        return False\n    return True\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.check_output--check_output","title":"check_output","text":"

Returns stdin output from subprocess module

Source code in vidgear/gears/helper.py
def check_output(*args, **kwargs):\n    \"\"\"\n    ## check_output\n\n    Returns stdin output from subprocess module\n    \"\"\"\n    # import libs\n    import subprocess as sp\n\n    # workaround for python bug: https://bugs.python.org/issue37380\n    if platform.system() == \"Windows\":\n        # see comment https://bugs.python.org/msg370334\n        sp._cleanup = lambda: None\n\n    # handle additional params\n    retrieve_stderr = kwargs.pop(\"force_retrieve_stderr\", False)\n\n    # execute command in subprocess\n    process = sp.Popen(\n        stdout=sp.PIPE,\n        stderr=sp.DEVNULL if not (retrieve_stderr) else sp.PIPE,\n        *args,\n        **kwargs,\n    )\n    output, stderr = process.communicate()\n    retcode = process.poll()\n\n    # handle return code\n    if retcode and not (retrieve_stderr):\n        cmd = kwargs.get(\"args\")\n        if cmd is None:\n            cmd = args[0]\n        error = sp.CalledProcessError(retcode, cmd)\n        error.output = output\n        raise error\n\n    return output if not (retrieve_stderr) else stderr\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.generate_auth_certificates--generate_auth_certificates","title":"generate_auth_certificates","text":"

Auto-Generates, and Auto-validates CURVE ZMQ key-pairs for NetGear API's Secure Mode.

Parameters:

Name Type Description Default path string

path for generating CURVE key-pairs

required overwrite boolean

overwrite existing key-pairs or not?

False logging bool

enables logging for its operations

False

Returns: A valid CURVE key-pairs path as string.

Source code in vidgear/gears/helper.py
def generate_auth_certificates(path, overwrite=False, logging=False):\n    \"\"\"\n    ## generate_auth_certificates\n\n    Auto-Generates, and Auto-validates CURVE ZMQ key-pairs for NetGear API's Secure Mode.\n\n    Parameters:\n        path (string): path for generating CURVE key-pairs\n        overwrite (boolean): overwrite existing key-pairs or not?\n        logging (bool): enables logging for its operations\n\n    **Returns:** A valid CURVE key-pairs path as string.\n    \"\"\"\n    # import necessary lib\n    import zmq.auth\n\n    # check if path corresponds to vidgear only\n    if os.path.basename(path) != \".vidgear\":\n        path = os.path.join(path, \".vidgear\")\n\n    # generate keys dir\n    keys_dir = os.path.join(path, \"keys\")\n    mkdir_safe(keys_dir, logging=logging)\n\n    # generate separate public and private key dirs\n    public_keys_dir = os.path.join(keys_dir, \"public_keys\")\n    secret_keys_dir = os.path.join(keys_dir, \"private_keys\")\n\n    # check if overwriting is allowed\n    if overwrite:\n        # delete previous certificates\n        for dirs in [public_keys_dir, secret_keys_dir]:\n            if os.path.exists(dirs):\n                shutil.rmtree(dirs)\n            mkdir_safe(dirs, logging=logging)\n\n        # generate new keys\n        server_public_file, server_secret_file = zmq.auth.create_certificates(\n            keys_dir, \"server\"\n        )\n        client_public_file, client_secret_file = zmq.auth.create_certificates(\n            keys_dir, \"client\"\n        )\n\n        # move keys to their appropriate directory respectively\n        for key_file in os.listdir(keys_dir):\n            if key_file.endswith(\".key\"):\n                shutil.move(os.path.join(keys_dir, key_file), public_keys_dir)\n            elif key_file.endswith(\".key_secret\"):\n                shutil.move(os.path.join(keys_dir, key_file), secret_keys_dir)\n            else:\n                # clean redundant keys if present\n                redundant_key = os.path.join(keys_dir, key_file)\n                if os.path.isfile(redundant_key):\n                    delete_file_safe(redundant_key)\n    else:\n        # otherwise validate available keys\n        status_public_keys = validate_auth_keys(public_keys_dir, \".key\")\n        status_private_keys = validate_auth_keys(secret_keys_dir, \".key_secret\")\n\n        # check if all valid keys are found\n        if status_private_keys and status_public_keys:\n            return (keys_dir, secret_keys_dir, public_keys_dir)\n\n        # check if valid public keys are found\n        if not (status_public_keys):\n            mkdir_safe(public_keys_dir, logging=logging)\n\n        # check if valid private keys are found\n        if not (status_private_keys):\n            mkdir_safe(secret_keys_dir, logging=logging)\n\n        # generate new keys\n        server_public_file, server_secret_file = zmq.auth.create_certificates(\n            keys_dir, \"server\"\n        )\n        client_public_file, client_secret_file = zmq.auth.create_certificates(\n            keys_dir, \"client\"\n        )\n\n        # move keys to their appropriate directory respectively\n        for key_file in os.listdir(keys_dir):\n            if key_file.endswith(\".key\") and not (status_public_keys):\n                shutil.move(\n                    os.path.join(keys_dir, key_file), os.path.join(public_keys_dir, \".\")\n                )\n            elif key_file.endswith(\".key_secret\") and not (status_private_keys):\n                shutil.move(\n                    os.path.join(keys_dir, key_file), os.path.join(secret_keys_dir, \".\")\n                )\n            else:\n                # clean redundant keys if present\n                redundant_key = os.path.join(keys_dir, key_file)\n                if os.path.isfile(redundant_key):\n                    delete_file_safe(redundant_key)\n\n    # validate newly generated keys\n    status_public_keys = validate_auth_keys(public_keys_dir, \".key\")\n    status_private_keys = validate_auth_keys(secret_keys_dir, \".key_secret\")\n\n    # raise error is validation test fails\n    if not (status_private_keys) or not (status_public_keys):\n        raise RuntimeError(\n            \"[Helper:ERROR] :: Unable to generate valid ZMQ authentication certificates at `{}`!\".format(\n                keys_dir\n            )\n        )\n\n    # finally return valid key paths\n    return (keys_dir, secret_keys_dir, public_keys_dir)\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.validate_audio--validate_audio","title":"validate_audio","text":"

Validates audio by retrieving audio-bitrate from file.

Parameters:

Name Type Description Default path string

absolute path of FFmpeg binaries

required source string/list

source to be validated.

None

Returns: A string value, confirming whether audio is present, or not?.

Source code in vidgear/gears/helper.py
def validate_audio(path, source=None):\n    \"\"\"\n    ## validate_audio\n\n    Validates audio by retrieving audio-bitrate from file.\n\n    Parameters:\n        path (string): absolute path of FFmpeg binaries\n        source (string/list): source to be validated.\n\n    **Returns:** A string value, confirming whether audio is present, or not?.\n    \"\"\"\n    if source is None or not (source):\n        logger.warning(\"Audio input source is empty!\")\n        return \"\"\n\n    # create ffmpeg command\n    cmd = [path, \"-hide_banner\"] + (\n        source if isinstance(source, list) else [\"-i\", source]\n    )\n    # extract metadata\n    metadata = check_output(cmd, force_retrieve_stderr=True)\n    # extract bitrate\n    audio_bitrate_meta = [\n        line.strip()\n        for line in metadata.decode(\"utf-8\").split(\"\\n\")\n        if \"Audio:\" in line\n    ]\n    audio_bitrate = (\n        re.findall(r\"([0-9]+)\\s(kb|mb|gb)\\/s\", audio_bitrate_meta[0])[-1]\n        if audio_bitrate_meta\n        else \"\"\n    )\n    # extract samplerate\n    audio_samplerate_metadata = [\n        line.strip()\n        for line in metadata.decode(\"utf-8\").split(\"\\n\")\n        if all(x in line for x in [\"Audio:\", \"Hz\"])\n    ]\n    audio_samplerate = (\n        re.findall(r\"[0-9]+\\sHz\", audio_samplerate_metadata[0])[0]\n        if audio_samplerate_metadata\n        else \"\"\n    )\n    # format into actual readable bitrate value\n    if audio_bitrate:\n        # return bitrate directly\n        return \"{}{}\".format(int(audio_bitrate[0].strip()), audio_bitrate[1].strip()[0])\n    elif audio_samplerate:\n        # convert samplerate to bitrate first\n        sample_rate_value = int(audio_samplerate.split(\" \")[0])\n        channels_value = 1 if \"mono\" in audio_samplerate_metadata[0] else 2\n        bit_depth_value = re.findall(\n            r\"(u|s|f)([0-9]+)(le|be)\", audio_samplerate_metadata[0]\n        )[0][1]\n        return (\n            (\n                str(\n                    get_audio_bitrate(\n                        sample_rate_value, channels_value, int(bit_depth_value)\n                    )\n                )\n                + \"k\"\n            )\n            if bit_depth_value\n            else \"\"\n        )\n    else:\n        return \"\"\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.extract_time--extract_time","title":"extract_time","text":"

Extract time from give string value.

Parameters:

Name Type Description Default value string

string value.

required

Returns: Time (in seconds) as integer.

Source code in vidgear/gears/helper.py
def extract_time(value):\n    \"\"\"\n    ## extract_time\n\n    Extract time from give string value.\n\n    Parameters:\n        value (string): string value.\n\n    **Returns:** Time _(in seconds)_ as integer.\n    \"\"\"\n    if not (value):\n        logger.warning(\"Value is empty!\")\n        return 0\n    else:\n        stripped_data = value.strip()\n        t_duration = re.findall(r\"\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{2})?\", stripped_data)\n        return (\n            sum(\n                float(x) * 60**i\n                for i, x in enumerate(reversed(t_duration[0].split(\":\")))\n            )\n            if t_duration\n            else 0\n        )\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.validate_video--validate_video","title":"validate_video","text":"

Validates video by retrieving resolution/size and framerate from file.

Parameters:

Name Type Description Default path string

absolute path of FFmpeg binaries

required video_path string

absolute path to Video.

None

Returns: A dictionary of retieved Video resolution (as tuple(width, height)) and framerate (as float).

Source code in vidgear/gears/helper.py
def validate_video(path, video_path=None, logging=False):\n    \"\"\"\n    ## validate_video\n\n    Validates video by retrieving resolution/size and framerate from file.\n\n    Parameters:\n        path (string): absolute path of FFmpeg binaries\n        video_path (string): absolute path to Video.\n\n    **Returns:** A dictionary of retieved Video resolution _(as tuple(width, height))_ and framerate _(as float)_.\n    \"\"\"\n    if video_path is None or not (video_path):\n        logger.warning(\"Video path is empty!\")\n        return None\n\n    # extract metadata\n    metadata = check_output(\n        [path, \"-hide_banner\", \"-i\", video_path], force_retrieve_stderr=True\n    )\n    # clean and search\n    stripped_data = [x.decode(\"utf-8\").strip() for x in metadata.split(b\"\\n\")]\n    logging and logger.debug(stripped_data)\n    result = {}\n    for data in stripped_data:\n        output_a = re.findall(r\"([1-9]\\d+)x([1-9]\\d+)\", data)\n        output_b = re.findall(r\"\\d+(?:\\.\\d+)?\\sfps\", data)\n        if len(result) == 2:\n            break\n        if output_b and not \"framerate\" in result:\n            result[\"framerate\"] = re.findall(r\"[\\d\\.\\d]+\", output_b[0])[0]\n        if output_a and not \"resolution\" in result:\n            result[\"resolution\"] = output_a[-1]\n\n    # return values\n    return result if (len(result) == 2) else None\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.is_valid_url--is_valid_url","title":"is_valid_url","text":"

Checks URL validity by testing its scheme against FFmpeg's supported protocols

Parameters:

Name Type Description Default path string

absolute path of FFmpeg binaries

required url string

URL to be validated

None logging bool

enables logging for its operations

False

Returns: A boolean value, confirming whether tests passed, or not?.

Source code in vidgear/gears/helper.py
def is_valid_url(path, url=None, logging=False):\n    \"\"\"\n    ## is_valid_url\n\n    Checks URL validity by testing its scheme against\n    FFmpeg's supported protocols\n\n    Parameters:\n        path (string): absolute path of FFmpeg binaries\n        url (string): URL to be validated\n        logging (bool): enables logging for its operations\n\n    **Returns:** A boolean value, confirming whether tests passed, or not?.\n    \"\"\"\n    if url is None or not (url):\n        logger.warning(\"URL is empty!\")\n        return False\n    # extract URL scheme\n    extracted_scheme_url = url.split(\"://\", 1)[0]\n    # extract all FFmpeg supported protocols\n    protocols = check_output([path, \"-hide_banner\", \"-protocols\"])\n    splitted = [x.decode(\"utf-8\").strip() for x in protocols.split(b\"\\n\")]\n    supported_protocols = splitted[splitted.index(\"Output:\") + 1 : len(splitted) - 1]\n    # RTSP is a demuxer somehow\n    # support both RTSP and RTSPS(over SSL)\n    supported_protocols += (\n        [\"rtsp\", \"rtsps\"] if \"rtsp\" in get_supported_demuxers(path) else []\n    )\n    # Test and return result whether scheme is supported\n    if extracted_scheme_url and extracted_scheme_url in supported_protocols:\n        logging and logger.debug(\n            \"URL scheme `{}` is supported by FFmpeg.\".format(extracted_scheme_url)\n        )\n        return True\n    else:\n        logger.warning(\n            \"URL scheme `{}` isn't supported by FFmpeg!\".format(extracted_scheme_url)\n        )\n        return False\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.import_dependency_safe--import_dependency_safe","title":"import_dependency_safe","text":"

Imports specified dependency safely. By default(error = raise), if a dependency is missing, an ImportError with a meaningful message will be raised. Otherwise if error = log a warning will be logged and on error = silent everything will be quit. But If a dependency is present, but older than specified, an error is raised if specified.

Parameters:

Name Type Description Default name string

name of dependency to be imported.

required error string

raise or Log or silence ImportError. Possible values are \"raise\", \"log\" and silent. Default is \"raise\".

'raise' pkg_name string

(Optional) package name of dependency(if different pip name). Otherwise name will be used.

None min_version string

(Optional) required minimum version of the dependency to be imported.

None custom_message string

(Optional) custom Import error message to be raised or logged.

None

Returns: The imported module, when found and the version is correct(if specified). Otherwise None.

Source code in vidgear/gears/helper.py
def import_dependency_safe(\n    name,\n    error=\"raise\",\n    pkg_name=None,\n    min_version=None,\n    custom_message=None,\n):\n    \"\"\"\n    ## import_dependency_safe\n\n    Imports specified dependency safely. By default(`error = raise`), if a dependency is missing,\n    an ImportError with a meaningful message will be raised. Otherwise if `error = log` a warning\n    will be logged and on `error = silent` everything will be quit. But If a dependency is present,\n    but older than specified, an error is raised if specified.\n\n    Parameters:\n        name (string): name of dependency to be imported.\n        error (string): raise or Log or silence ImportError. Possible values are `\"raise\"`, `\"log\"` and `silent`. Default is `\"raise\"`.\n        pkg_name (string): (Optional) package name of dependency(if different `pip` name). Otherwise `name` will be used.\n        min_version (string): (Optional) required minimum version of the dependency to be imported.\n        custom_message (string): (Optional) custom Import error message to be raised or logged.\n\n    **Returns:** The imported module, when found and the version is correct(if specified). Otherwise `None`.\n    \"\"\"\n    # check specified parameters\n    sub_class = \"\"\n    if not name or not isinstance(name, str):\n        return None\n    else:\n        # extract name in case of relative import\n        name = name.strip()\n        if name.startswith(\"from\"):\n            name = name.split(\" \")\n            name, sub_class = (name[1].strip(), name[-1].strip())\n\n    assert error in [\n        \"raise\",\n        \"log\",\n        \"silent\",\n    ], \"[Vidgear:ERROR] :: Invalid value at `error` parameter.\"\n\n    # specify package name of dependency(if defined). Otherwise use name\n    install_name = pkg_name if not (pkg_name is None) else name\n\n    # create message\n    msg = (\n        custom_message\n        if not (custom_message is None)\n        else \"Failed to find required dependency '{}'. Install it with  `pip install {}` command.\".format(\n            name, install_name\n        )\n    )\n    # try importing dependency\n    try:\n        module = importlib.import_module(name)\n        module = getattr(module, sub_class) if sub_class else module\n    except Exception as e:\n        if error == \"raise\":\n            if isinstance(e, ModuleNotFoundError):\n                # raise message\n                raise ModuleNotFoundError(msg) from None\n            else:\n                # raise error+message\n                raise ImportError(msg) from e\n        elif error == \"log\":\n            logger.error(msg, exc_info=sys.exc_info())\n            return None\n        else:\n            return None\n\n    # check if minimum required version\n    if not (min_version) is None:\n        # Handle submodules\n        parent_module = name.split(\".\")[0]\n        if parent_module != name:\n            # grab parent module\n            module_to_get = sys.modules[parent_module]\n        else:\n            module_to_get = module\n        # extract version\n        version = get_module_version(module_to_get)\n        # verify\n        if parse_version(version) < parse_version(min_version):\n            # create message\n            msg = \"\"\"Unsupported version '{}' found. Vidgear requires '{}' dependency installed with version '{}' or greater. \n            Update it with  `pip install -U {}` command.\"\"\".format(\n                parent_module, min_version, version, install_name\n            )\n            # handle errors.\n            if error == \"silent\":\n                return None\n            else:\n                # raise\n                raise ImportError(msg)\n\n    return module\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.get_video_bitrate--get_video_bitrate","title":"get_video_bitrate","text":"

Calculate optimum Bitrate from resolution, framerate, bits-per-pixels values

Parameters:

Name Type Description Default width int

video-width

required height int

video-height

required fps float

video-framerate

required bpp float

bit-per-pixels value

required

Returns: Video bitrate (in Kbps) as integer.

Source code in vidgear/gears/helper.py
def get_video_bitrate(width, height, fps, bpp):\n    \"\"\"\n    ## get_video_bitrate\n\n    Calculate optimum Bitrate from resolution, framerate, bits-per-pixels values\n\n    Parameters:\n        width (int): video-width\n        height (int): video-height\n        fps (float): video-framerate\n        bpp (float): bit-per-pixels value\n\n    **Returns:** Video bitrate _(in Kbps)_ as integer.\n    \"\"\"\n    return round((width * height * bpp * fps) / 1000)\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.check_WriteAccess--check_writeaccess","title":"check_WriteAccess","text":"

Checks whether given path directory has Write-Access.

Parameters:

Name Type Description Default path string

absolute path of directory

required is_windows boolean

is running on Windows OS?

False logging bool

enables logging for its operations

False

Returns: A boolean value, confirming whether Write-Access available, or not?.

Source code in vidgear/gears/helper.py
def check_WriteAccess(path, is_windows=False, logging=False):\n    \"\"\"\n    ## check_WriteAccess\n\n    Checks whether given path directory has Write-Access.\n\n    Parameters:\n        path (string): absolute path of directory\n        is_windows (boolean): is running on Windows OS?\n        logging (bool): enables logging for its operations\n\n    **Returns:** A boolean value, confirming whether Write-Access available, or not?.\n    \"\"\"\n    # check if path exists\n    dirpath = Path(path)\n    try:\n        if not (dirpath.exists() and dirpath.is_dir()):\n            logger.warning(\n                \"Specified directory `{}` doesn't exists or valid.\".format(path)\n            )\n            return False\n        else:\n            path = dirpath.resolve()\n    except:\n        return False\n    # check filepath on *nix systems\n    if not is_windows:\n        uid = os.geteuid()\n        gid = os.getegid()\n        s = os.stat(path)\n        mode = s[stat.ST_MODE]\n        return (\n            ((s[stat.ST_UID] == uid) and (mode & stat.S_IWUSR))\n            or ((s[stat.ST_GID] == gid) and (mode & stat.S_IWGRP))\n            or (mode & stat.S_IWOTH)\n        )\n    # otherwise, check filepath on windows\n    else:\n        write_accessible = False\n        temp_fname = os.path.join(path, \"temp.tmp\")\n        try:\n            fd = os.open(temp_fname, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)\n            os.close(fd)\n            write_accessible = True\n        except Exception as e:\n            if isinstance(e, PermissionError):\n                logger.error(\n                    \"You don't have adequate access rights to use `{}` directory!\".format(\n                        path\n                    )\n                )\n            logging and logger.exception(str(e))\n        finally:\n            delete_file_safe(temp_fname)\n        return write_accessible\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.check_open_port--check_open_port","title":"check_open_port","text":"

Checks whether specified port open at given IP address.

Parameters:

Name Type Description Default address string

given IP address.

required port int

check if port is open at given address.

22

Returns: A boolean value, confirming whether given port is open at given IP address.

Source code in vidgear/gears/helper.py
def check_open_port(address, port=22):\n    \"\"\"\n    ## check_open_port\n\n    Checks whether specified port open at given IP address.\n\n    Parameters:\n        address (string): given IP address.\n        port (int): check if port is open at given address.\n\n    **Returns:** A boolean value, confirming whether given port is open at given IP address.\n    \"\"\"\n    if not address:\n        return False\n    with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:\n        if sock.connect_ex((address, port)) == 0:\n            return True\n        else:\n            return False\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.delete_file_safe--delete_ext_safe","title":"delete_ext_safe","text":"

Safely deletes files at given path.

Parameters:

Name Type Description Default file_path string

path to the file

required Source code in vidgear/gears/helper.py
def delete_file_safe(file_path):\n    \"\"\"\n    ## delete_ext_safe\n\n    Safely deletes files at given path.\n\n    Parameters:\n        file_path (string): path to the file\n    \"\"\"\n    try:\n        dfile = Path(file_path)\n        dfile.unlink(missing_ok=True)\n    except Exception as e:\n        logger.exception(str(e))\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.get_supported_demuxers--get_supported_demuxers","title":"get_supported_demuxers","text":"

Find and returns FFmpeg's supported demuxers

Parameters:

Name Type Description Default path string

absolute path of FFmpeg binaries

required

Returns: List of supported demuxers.

Source code in vidgear/gears/helper.py
def get_supported_demuxers(path):\n    \"\"\"\n    ## get_supported_demuxers\n\n    Find and returns FFmpeg's supported demuxers\n\n    Parameters:\n        path (string): absolute path of FFmpeg binaries\n\n    **Returns:** List of supported demuxers.\n    \"\"\"\n    demuxers = check_output([path, \"-hide_banner\", \"-demuxers\"])\n    splitted = [x.decode(\"utf-8\").strip() for x in demuxers.split(b\"\\n\")]\n    split_index = [idx for idx, s in enumerate(splitted) if \"--\" in s][0]\n    supported_demuxers = splitted[split_index + 1 : len(splitted) - 1]\n    # compile regex\n    finder = re.compile(r\"\\s\\s[a-z0-9_,-]+\\s+\")\n    # find all outputs\n    outputs = finder.findall(\"\\n\".join(supported_demuxers))\n    # return output findings\n    return [o.strip() for o in outputs]\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.get_supported_vencoders--get_supported_vencoders","title":"get_supported_vencoders","text":"

Find and returns FFmpeg's supported video encoders

Parameters:

Name Type Description Default path string

absolute path of FFmpeg binaries

required

Returns: List of supported encoders.

Source code in vidgear/gears/helper.py
def get_supported_vencoders(path):\n    \"\"\"\n    ## get_supported_vencoders\n\n    Find and returns FFmpeg's supported video encoders\n\n    Parameters:\n        path (string): absolute path of FFmpeg binaries\n\n    **Returns:** List of supported encoders.\n    \"\"\"\n    encoders = check_output([path, \"-hide_banner\", \"-encoders\"])\n    splitted = encoders.split(b\"\\n\")\n    # extract video encoders\n    supported_vencoders = [\n        x.decode(\"utf-8\").strip()\n        for x in splitted[2 : len(splitted) - 1]\n        if x.decode(\"utf-8\").strip().startswith(\"V\")\n    ]\n    # compile regex\n    finder = re.compile(r\"[A-Z]*[\\.]+[A-Z]*\\s[a-z0-9_-]*\")\n    # find all outputs\n    outputs = finder.findall(\"\\n\".join(supported_vencoders))\n    # return output findings\n    return [[s for s in o.split(\" \")][-1] for o in outputs]\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.validate_auth_keys--validate_auth_keys","title":"validate_auth_keys","text":"

Validates, and also maintains generated ZMQ CURVE Key-pairs.

Parameters:

Name Type Description Default path string

path of generated CURVE key-pairs

required extension string

type of key-pair to be validated

required

Returns: A boolean value, confirming whether tests passed, or not?.

Source code in vidgear/gears/helper.py
def validate_auth_keys(path, extension):\n    \"\"\"\n    ## validate_auth_keys\n\n    Validates, and also maintains generated ZMQ CURVE Key-pairs.\n\n    Parameters:\n        path (string): path of generated CURVE key-pairs\n        extension (string): type of key-pair to be validated\n\n    **Returns:** A boolean value, confirming whether tests passed, or not?.\n    \"\"\"\n    # check for valid path\n    if not (os.path.exists(path)):\n        return False\n\n    # check if directory empty\n    if not (os.listdir(path)):\n        return False\n\n    keys_buffer = []  # stores auth-keys\n\n    # loop over auth-keys\n    for key_file in os.listdir(path):\n        key = os.path.splitext(key_file)\n        # check if valid key is generated\n        if key and (key[0] in [\"server\", \"client\"]) and (key[1] == extension):\n            keys_buffer.append(key_file)  # store it\n\n    # remove invalid keys if found\n    len(keys_buffer) == 1 and delete_file_safe(os.path.join(path, keys_buffer[0]))\n\n    # return results\n    return True if (len(keys_buffer) == 2) else False\n
"},{"location":"bonus/reference/helper_async/","title":"Helper Methods","text":""},{"location":"bonus/reference/helper_async/#vidgear.gears.asyncio.helper.reducer--reducer","title":"reducer","text":"

Asynchronous method that reduces frame size by given percentage.

Parameters:

Name Type Description Default frame numpy.ndarray

inputs numpy array(frame).

None percentage int/float

inputs size-reduction percentage.

0 interpolation int

Change resize interpolation.

4

Returns: A reduced numpy ndarray array.

Source code in vidgear/gears/asyncio/helper.py
async def reducer(frame=None, percentage=0, interpolation=cv2.INTER_LANCZOS4):\n    \"\"\"\n    ## reducer\n\n    Asynchronous method that reduces frame size by given percentage.\n\n    Parameters:\n        frame (numpy.ndarray): inputs numpy array(frame).\n        percentage (int/float): inputs size-reduction percentage.\n        interpolation (int): Change resize interpolation.\n\n    **Returns:**  A reduced numpy ndarray array.\n    \"\"\"\n    # check if frame is valid\n    if frame is None:\n        raise ValueError(\"[Helper:ERROR] :: Input frame cannot be NoneType!\")\n\n    # check if valid reduction percentage is given\n    if not (percentage > 0 and percentage < 90):\n        raise ValueError(\n            \"[Helper:ERROR] :: Given frame-size reduction percentage is invalid, Kindly refer docs.\"\n        )\n\n    if not (isinstance(interpolation, int)):\n        raise ValueError(\n            \"[Helper:ERROR] :: Given interpolation is invalid, Kindly refer docs.\"\n        )\n\n    # grab the frame size\n    (height, width) = frame.shape[:2]\n\n    # calculate the ratio of the width from percentage\n    reduction = ((100 - percentage) / 100) * width\n    ratio = reduction / float(width)\n    # construct the dimensions\n    dimensions = (int(reduction), int(height * ratio))\n\n    # return the resized frame\n    return cv2.resize(frame, dimensions, interpolation=interpolation)\n
"},{"location":"bonus/reference/helper_async/#vidgear.gears.asyncio.helper.create_blank_frame--create_blank_frame","title":"create_blank_frame","text":"

Create blank frames of given frame size with text

Parameters:

Name Type Description Default frame numpy.ndarray

inputs numpy array(frame).

None text str

Text to be written on frame.

''

Returns: A reduced numpy ndarray array.

Source code in vidgear/gears/asyncio/helper.py
def create_blank_frame(frame=None, text=\"\", logging=False):\n    \"\"\"\n    ## create_blank_frame\n\n    Create blank frames of given frame size with text\n\n    Parameters:\n        frame (numpy.ndarray): inputs numpy array(frame).\n        text (str): Text to be written on frame.\n    **Returns:**  A reduced numpy ndarray array.\n    \"\"\"\n    # check if frame is valid\n    if frame is None or not (isinstance(frame, np.ndarray)):\n        raise ValueError(\"[Helper:ERROR] :: Input frame is invalid!\")\n    # grab the frame size\n    (height, width) = frame.shape[:2]\n    # create blank frame\n    blank_frame = np.zeros(frame.shape, frame.dtype)\n    # setup text\n    if text and isinstance(text, str):\n        if logging:\n            logger.debug(\"Adding text: {}\".format(text))\n        # setup font\n        font = cv2.FONT_HERSHEY_SCRIPT_COMPLEX\n        # get boundary of this text\n        fontScale = min(height, width) / (25 / 0.25)\n        textsize = cv2.getTextSize(text, font, fontScale, 5)[0]\n        # get coords based on boundary\n        textX = (width - textsize[0]) // 2\n        textY = (height + textsize[1]) // 2\n        # put text\n        cv2.putText(\n            blank_frame, text, (textX, textY), font, fontScale, (125, 125, 125), 6\n        )\n\n    # return frame\n    return blank_frame\n
"},{"location":"bonus/reference/helper_async/#vidgear.gears.asyncio.helper.generate_webdata--generate_webdata","title":"generate_webdata","text":"

Auto-Generates, and Auto-validates default data for WebGear and WebGear_RTC APIs.

Parameters:

Name Type Description Default path string

path for generating data

required c_name string

class name that is generating files

'webgear' overwrite_default boolean

overwrite existing data or not?

False logging bool

enables logging for its operations

False

Returns: A valid data path as string.

Source code in vidgear/gears/asyncio/helper.py
def generate_webdata(path, c_name=\"webgear\", overwrite_default=False, logging=False):\n    \"\"\"\n    ## generate_webdata\n\n    Auto-Generates, and Auto-validates default data for WebGear and WebGear_RTC APIs.\n\n    Parameters:\n        path (string): path for generating data\n        c_name (string): class name that is generating files\n        overwrite_default (boolean): overwrite existing data or not?\n        logging (bool): enables logging for its operations\n\n    **Returns:** A valid data path as string.\n    \"\"\"\n    # check if path corresponds to vidgear only\n    if os.path.basename(path) != \".vidgear\":\n        path = os.path.join(path, \".vidgear\")\n\n    # generate parent directory\n    path = os.path.join(path, c_name)\n    mkdir_safe(path, logging=logging)\n\n    # self-generate dirs\n    template_dir = os.path.join(path, \"templates\")  # generates HTML templates dir\n    static_dir = os.path.join(path, \"static\")  # generates static dir\n    # generate js & css static and favicon img subdirs\n    js_static_dir = os.path.join(static_dir, \"js\")\n    css_static_dir = os.path.join(static_dir, \"css\")\n    favicon_dir = os.path.join(static_dir, \"img\")\n\n    mkdir_safe(static_dir, logging=logging)\n    mkdir_safe(template_dir, logging=logging)\n    mkdir_safe(js_static_dir, logging=logging)\n    mkdir_safe(css_static_dir, logging=logging)\n    mkdir_safe(favicon_dir, logging=logging)\n\n    # check if overwriting is enabled\n    if overwrite_default or not validate_webdata(\n        template_dir, [\"index.html\", \"404.html\", \"500.html\"]\n    ):\n        logger.critical(\n            \"Overwriting existing {} data-files with default data-files from the server!\".format(\n                c_name.capitalize()\n            )\n            if overwrite_default\n            else \"Failed to detect critical {} data-files: index.html, 404.html & 500.html!\".format(\n                c_name.capitalize()\n            )\n        )\n        # download default files\n        logging and logger.info(\n            \"Downloading default data-files from the Gitlab Server: {}\".format(\n                \"https://gitlab.com/abhiTronix/vidgear-vitals\"\n            )\n        )\n        download_webdata(\n            template_dir,\n            c_name=c_name,\n            files=[\"index.html\", \"404.html\", \"500.html\", \"base.html\"],\n            logging=logging,\n        )\n        download_webdata(\n            css_static_dir, c_name=c_name, files=[\"custom.css\"], logging=logging\n        )\n        download_webdata(\n            js_static_dir,\n            c_name=c_name,\n            files=[\"custom.js\"],\n            logging=logging,\n        )\n        download_webdata(\n            favicon_dir, c_name=c_name, files=[\"favicon-32x32.png\"], logging=logging\n        )\n    else:\n        # validate important data-files\n        if logging:\n            logger.debug(\"Found valid WebGear data-files successfully.\")\n\n    return path\n
"},{"location":"bonus/reference/helper_async/#vidgear.gears.asyncio.helper.download_webdata--download_webdata","title":"download_webdata","text":"

Downloads given list of files for WebGear and WebGear_RTC APIs(if not available) from GitHub/Gitlab Servers, and also Validates them.

Parameters:

Name Type Description Default path string

path for downloading data

required c_name string

class name that is generating files

'webgear' files list

list of files to be downloaded

[] logging bool

enables logging for its operations

False

Returns: A valid path as string.

Source code in vidgear/gears/asyncio/helper.py
def download_webdata(path, c_name=\"webgear\", files=[], logging=False):\n    \"\"\"\n    ## download_webdata\n\n    Downloads given list of files for WebGear and WebGear_RTC APIs(if not available) from GitHub/Gitlab Servers,\n    and also Validates them.\n\n    Parameters:\n        path (string): path for downloading data\n        c_name (string): class name that is generating files\n        files (list): list of files to be downloaded\n        logging (bool): enables logging for its operations\n\n    **Returns:** A valid path as string.\n    \"\"\"\n    basename = os.path.basename(path)\n    if logging:\n        logger.debug(\"Downloading {} data-files at `{}`\".format(basename, path))\n\n    # list all registered urls\n    reg_urls = [\n        \"https://gitlab.com/abhiTronix/vidgear-vitals/-/raw/main\",\n        \"https://raw.githubusercontent.com/abhiTronix/vidgear-vitals/main\",\n    ]\n\n    # create session\n    with requests.Session() as http:\n        for url in reg_urls:\n            try:\n                for file in files:\n                    # get filename\n                    file_name = os.path.join(path, file)\n                    # get URL\n                    file_url = \"{}/{}{}/{}/{}\".format(\n                        url,\n                        c_name,\n                        \"/static\" if basename != \"templates\" else \"\",\n                        basename,\n                        file,\n                    )\n                    # download and write file to the given path\n                    logging and logger.debug(\n                        \"Downloading {} data-file: {}.\".format(basename, file)\n                    )\n\n                    with open(file_name, \"wb\") as f:\n                        # setup retry strategy\n                        retries = Retry(\n                            total=3,\n                            backoff_factor=1,\n                            status_forcelist=[429, 500, 502, 503, 504],\n                        )\n                        # Mount it for https usage\n                        adapter = TimeoutHTTPAdapter(timeout=2.0, max_retries=retries)\n                        http.mount(\"https://\", adapter)\n                        response = http.get(file_url, stream=True)\n                        response.raise_for_status()\n                        total_length = (\n                            response.headers.get(\"content-length\")\n                            if \"content-length\" in response.headers\n                            else len(response.content)\n                        )\n                        assert not (\n                            total_length is None\n                        ), \"[Helper:ERROR] :: Failed to retrieve files, check your Internet connectivity!\"\n                        bar = tqdm(total=int(total_length), unit=\"B\", unit_scale=True)\n                        for data in response.iter_content(chunk_size=256):\n                            f.write(data)\n                            if len(data) > 0:\n                                bar.update(len(data))\n                        bar.close()\n            except AssertionError as e:\n                # raise if connection error\n                raise e\n            except Exception as e:\n                # log error\n                logger.exception(str(e))\n                # log event if necessary\n                url != reg_urls[1] and logger.error(\n                    \"Download failed for Gitlab Server! Retrying from GitHub Server: {}\".format(\n                        url, \"https://github.com/abhiTronix/vidgear-vitals\"\n                    )\n                )\n            else:\n                # break otherwise\n                break\n\n    if logging:\n        logger.debug(\"Verifying downloaded data:\")\n    if validate_webdata(path, files=files, logging=logging):\n        if logging:\n            logger.info(\"Successful!\")\n        return path\n    else:\n        raise RuntimeError(\n            \"[Helper:ERROR] :: Failed to download required {} data-files at: {}, Check your Internet connectivity!\".format(\n                basename, path\n            )\n        )\n
"},{"location":"bonus/reference/helper_async/#vidgear.gears.asyncio.helper.validate_webdata--validate_auth_keys","title":"validate_auth_keys","text":"

Validates, and also maintains downloaded list of files.

Parameters:

Name Type Description Default path string

path of downloaded files

required files list

list of files to be validated

[] logging bool

enables logging for its operations

False

Returns: A boolean value, confirming whether tests passed, or not?.

Source code in vidgear/gears/asyncio/helper.py
def validate_webdata(path, files=[], logging=False):\n    \"\"\"\n    ## validate_auth_keys\n\n    Validates, and also maintains downloaded list of files.\n\n    Parameters:\n        path (string): path of downloaded files\n        files (list): list of files to be validated\n        logging (bool): enables logging for its operations\n\n    **Returns:** A  boolean value, confirming whether tests passed, or not?.\n    \"\"\"\n    # check if valid path or directory empty\n    if not (os.path.exists(path)) or not (os.listdir(path)):\n        return False\n\n    files_buffer = []\n    # loop over files\n    for file in os.listdir(path):\n        if file in files:\n            files_buffer.append(file)  # store them\n\n    # return results\n    if len(files_buffer) < len(files):\n        if logging:\n            logger.warning(\n                \"`{}` file(s) missing from data-files!\".format(\n                    \" ,\".join(list(set(files_buffer) ^ set(files)))\n                )\n            )\n        return False\n    else:\n        return True\n
"},{"location":"bonus/reference/netgear/","title":"NetGear API References","text":"

NetGear API usage examples can be found here \u27b6

NetGear API parameters are explained here \u27b6

NetGear is exclusively designed to transfer video frames synchronously and asynchronously between interconnecting systems over the network in real-time.

NetGear implements a high-level wrapper around PyZmQ python library that contains python bindings for ZeroMQ - a high-performance asynchronous distributed messaging library that provides a message queue, but unlike message-oriented middleware, its system can run without a dedicated message broker.

NetGear also supports real-time Frame Compression capabilities for optimizing performance while sending the frames directly over the network, by encoding the frame before sending it and decoding it on the client's end automatically in real-time.

Info

NetGear API now internally implements robust Lazy Pirate pattern (auto-reconnection) for its synchronous messaging patterns (i.e. zmq.PAIR & zmq.REQ/zmq.REP) at both Server and Client ends, where its API instead of doing a blocking receive, will:

NetGear as of now seamlessly supports three ZeroMQ messaging patterns:

whereas the supported protocol are: tcp and ipc.

Modes of Operation Source code in vidgear/gears/netgear.py
class NetGear:\n    \"\"\"\n    NetGear is exclusively designed to transfer video frames synchronously and asynchronously between interconnecting systems over the network in real-time.\n\n    NetGear implements a high-level wrapper around PyZmQ python library that contains python bindings for ZeroMQ - a high-performance asynchronous distributed messaging library\n    that provides a message queue, but unlike message-oriented middleware, its system can run without a dedicated message broker.\n\n    NetGear also supports real-time Frame Compression capabilities for optimizing performance while sending the frames directly over the network, by encoding the frame before sending\n    it and decoding it on the client's end automatically in real-time.\n\n    !!! info\n        NetGear API now internally implements robust *Lazy Pirate pattern* (auto-reconnection) for its synchronous messaging patterns _(i.e. `zmq.PAIR` & `zmq.REQ/zmq.REP`)_\n        at both Server and Client ends, where its API instead of doing a blocking receive, will:\n\n        * Poll the socket and receive from it only when it's sure a reply has arrived.\n        * Attempt to reconnect, if no reply has arrived within a timeout period.\n        * Abandon the connection if there is still no reply after several requests.\n\n    NetGear as of now seamlessly supports three ZeroMQ messaging patterns:\n\n    - `zmq.PAIR` _(ZMQ Pair Pattern)_\n    - `zmq.REQ/zmq.REP` _(ZMQ Request/Reply Pattern)_\n    - `zmq.PUB/zmq.SUB` _(ZMQ Publish/Subscribe Pattern)_\n\n    _whereas the supported protocol are: `tcp` and `ipc`_.\n\n    ??? tip \"Modes of Operation\"\n\n        * **Primary Modes**\n\n            NetGear API primarily has two modes of operations:\n\n            * **Send Mode:** _which employs `send()` function to send video frames over the network in real-time._\n\n            * **Receive Mode:** _which employs `recv()` function to receive frames, sent over the network with *Send Mode* in real-time. The mode sends back confirmation when the\n            frame is received successfully in few patterns._\n\n        * **Exclusive Modes**\n\n            In addition to these primary modes, NetGear API offers applications-specific Exclusive Modes:\n\n            * **Multi-Servers Mode:** _In this exclusive mode, NetGear API robustly **handles multiple servers at once**, thereby providing seamless access to frames and unidirectional\n            data transfer from multiple Servers/Publishers across the network in real-time._\n\n            * **Multi-Clients Mode:** _In this exclusive mode, NetGear API robustly **handles multiple clients at once**, thereby providing seamless access to frames and unidirectional\n            data transfer to multiple Client/Consumers across the network in real-time._\n\n            * **Bidirectional Mode:** _This exclusive mode **provides seamless support for bidirectional data transmission between between Server and Client along with video frames**._\n\n            * **Secure Mode:** _In this exclusive mode, NetGear API **provides easy access to powerful, smart & secure ZeroMQ's Security Layers** that enables strong encryption on\n            data, and unbreakable authentication between the Server and Client with the help of custom certificates/keys that brings cheap, standardized privacy and authentication\n            for distributed systems over the network._\n    \"\"\"\n\n    def __init__(\n        self,\n        address=None,\n        port=None,\n        protocol=None,\n        pattern=0,\n        receive_mode=False,\n        logging=False,\n        **options\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the NetGear class.\n\n        Parameters:\n            address (str): sets the valid network address of the Server/Client.\n            port (str): sets the valid Network Port of the Server/Client.\n            protocol (str): sets the valid messaging protocol between Server/Client.\n            pattern (int): sets the supported messaging pattern(flow of communication) between Server/Client\n            receive_mode (bool): select the Netgear's Mode of operation.\n            logging (bool): enables/disables logging.\n            options (dict): provides the flexibility to alter various NetGear internal properties.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # raise error(s) for critical Class imports\n        import_dependency_safe(\n            \"zmq\" if zmq is None else \"\", min_version=\"4.0\", pkg_name=\"pyzmq\"\n        )\n        import_dependency_safe(\n            \"simplejpeg\" if simplejpeg is None else \"\", error=\"log\", min_version=\"1.6.1\"\n        )\n\n        # define valid messaging patterns => `0`: zmq.PAIR, `1`:(zmq.REQ,zmq.REP), and `1`:(zmq.SUB,zmq.PUB)\n        valid_messaging_patterns = {\n            0: (zmq.PAIR, zmq.PAIR),\n            1: (zmq.REQ, zmq.REP),\n            2: (zmq.PUB, zmq.SUB),\n        }\n\n        # Handle messaging pattern\n        msg_pattern = None\n        # check whether user-defined messaging pattern is valid\n        if isinstance(pattern, int) and pattern in valid_messaging_patterns.keys():\n            # assign value\n            msg_pattern = valid_messaging_patterns[pattern]\n        else:\n            # otherwise default to 0:`zmq.PAIR`\n            pattern = 0\n            msg_pattern = valid_messaging_patterns[pattern]\n            self.__logging and logger.warning(\n                \"Wrong pattern value, Defaulting to `zmq.PAIR`! Kindly refer Docs for more Information.\"\n            )\n        # assign pattern to global parameter for further use\n        self.__pattern = pattern\n\n        # Handle messaging protocol\n        if protocol is None or not (protocol in [\"tcp\", \"ipc\"]):\n            # else default to `tcp` protocol\n            protocol = \"tcp\"\n            # log it\n            self.__logging and logger.warning(\n                \"Protocol is not supported or not provided. Defaulting to `tcp` protocol!\"\n            )\n\n        # Handle connection params\n\n        self.__msg_flag = 0  # handles connection flags\n        self.__msg_copy = False  # handles whether to copy data\n        self.__msg_track = False  # handles whether to track packets\n\n        # Handle NetGear's internal exclusive modes and params\n\n        # define Secure Mode\n        self.__z_auth = None\n\n        # define SSH Tunneling Mode\n        self.__ssh_tunnel_mode = None  # handles ssh_tunneling mode state\n        self.__ssh_tunnel_pwd = None\n        self.__ssh_tunnel_keyfile = None\n        self.__paramiko_present = False if paramiko is None else True\n\n        # define Multi-Server mode\n        self.__multiserver_mode = False  # handles multi-server mode state\n\n        # define Multi-Client mode\n        self.__multiclient_mode = False  # handles multi-client mode state\n\n        # define Bidirectional mode\n        self.__bi_mode = False  # handles Bidirectional mode state\n\n        # define Secure mode\n        valid_security_mech = {0: \"Grasslands\", 1: \"StoneHouse\", 2: \"IronHouse\"}\n        self.__secure_mode = 0  # handles ZMQ security layer status\n        auth_cert_dir = \"\"  # handles valid ZMQ certificates dir\n        self.__auth_publickeys_dir = \"\"  # handles valid ZMQ public certificates dir\n        self.__auth_secretkeys_dir = \"\"  # handles valid ZMQ private certificates dir\n        overwrite_cert = False  # checks if certificates overwriting allowed\n        custom_cert_location = \"\"  # handles custom ZMQ certificates path\n\n        # define frame-compression handler\n        self.__jpeg_compression = (\n            True if not (simplejpeg is None) else False\n        )  # enabled by default for all connections if simplejpeg is installed\n        self.__jpeg_compression_quality = 90  # 90% quality\n        self.__jpeg_compression_fastdct = True  # fastest DCT on by default\n        self.__jpeg_compression_fastupsample = False  # fastupsample off by default\n        self.__jpeg_compression_colorspace = \"BGR\"  # use BGR colorspace by default\n\n        # defines frame compression on return data\n        self.__ex_compression_params = None\n\n        # define receiver return data handler\n        self.__return_data = None\n\n        # generate 8-digit random system id\n        self.__id = \"\".join(\n            secrets.choice(string.ascii_uppercase + string.digits) for i in range(8)\n        )\n\n        # define termination flag\n        self.__terminate = False\n\n        # additional settings for reliability\n        if pattern < 2:\n            # define zmq poller for reliable transmission\n            self.__poll = zmq.Poller()\n            # define max retries\n            self.__max_retries = 3\n            # request timeout\n            self.__request_timeout = 4000  # 4 secs\n        else:\n            # subscriber timeout\n            self.__subscriber_timeout = None\n\n        # Handle user-defined options dictionary values\n        # reformat dictionary\n        options = {str(k).strip(): v for k, v in options.items()}\n\n        # loop over dictionary key & values and assign to global variables if valid\n        for key, value in options.items():\n            # handle multi-server mode\n            if key == \"multiserver_mode\" and isinstance(value, bool):\n                # check if valid pattern assigned\n                if pattern > 0:\n                    # activate Multi-server mode\n                    self.__multiserver_mode = value\n                else:\n                    # otherwise disable it and raise error\n                    self.__multiserver_mode = False\n                    logger.critical(\"Multi-Server Mode is disabled!\")\n                    raise ValueError(\n                        \"[NetGear:ERROR] :: `{}` pattern is not valid when Multi-Server Mode is enabled. Kindly refer Docs for more Information.\".format(\n                            pattern\n                        )\n                    )\n\n            # handle multi-client mode\n            elif key == \"multiclient_mode\" and isinstance(value, bool):\n                # check if valid pattern assigned\n                if pattern > 0:\n                    # activate Multi-client mode\n                    self.__multiclient_mode = value\n                else:\n                    # otherwise disable it and raise error\n                    self.__multiclient_mode = False\n                    logger.critical(\"Multi-Client Mode is disabled!\")\n                    raise ValueError(\n                        \"[NetGear:ERROR] :: `{}` pattern is not valid when Multi-Client Mode is enabled. Kindly refer Docs for more Information.\".format(\n                            pattern\n                        )\n                    )\n\n            # handle bidirectional mode\n            elif key == \"bidirectional_mode\" and isinstance(value, bool):\n                # check if pattern is valid\n                if pattern < 2:\n                    # activate Bidirectional mode if specified\n                    self.__bi_mode = value\n                else:\n                    # otherwise disable it and raise error\n                    self.__bi_mode = False\n                    logger.warning(\"Bidirectional data transmission is disabled!\")\n                    raise ValueError(\n                        \"[NetGear:ERROR] :: `{}` pattern is not valid when Bidirectional Mode is enabled. Kindly refer Docs for more Information!\".format(\n                            pattern\n                        )\n                    )\n\n            # handle secure mode\n            elif (\n                key == \"secure_mode\"\n                and isinstance(value, int)\n                and (value in valid_security_mech)\n            ):\n                self.__secure_mode = value\n\n            elif key == \"custom_cert_location\" and isinstance(value, str):\n                # verify custom auth certificates path for secure mode\n                custom_cert_location = os.path.abspath(value)\n                assert os.path.isdir(\n                    custom_cert_location\n                ), \"[NetGear:ERROR] :: `custom_cert_location` value must be the path to a valid directory!\"\n                assert check_WriteAccess(\n                    custom_cert_location,\n                    is_windows=True if os.name == \"nt\" else False,\n                    logging=self.__logging,\n                ), \"[NetGear:ERROR] :: Permission Denied!, cannot write ZMQ authentication certificates to '{}' directory!\".format(\n                    value\n                )\n            elif key == \"overwrite_cert\" and isinstance(value, bool):\n                # enable/disable auth certificate overwriting in secure mode\n                overwrite_cert = value\n\n            # handle ssh-tunneling mode\n            elif key == \"ssh_tunnel_mode\" and isinstance(value, str):\n                # enable SSH Tunneling Mode\n                self.__ssh_tunnel_mode = value.strip()\n            elif key == \"ssh_tunnel_pwd\" and isinstance(value, str):\n                # add valid SSH Tunneling password\n                self.__ssh_tunnel_pwd = value\n            elif key == \"ssh_tunnel_keyfile\" and isinstance(value, str):\n                # add valid SSH Tunneling key-file\n                self.__ssh_tunnel_keyfile = value if os.path.isfile(value) else None\n                if self.__ssh_tunnel_keyfile is None:\n                    logger.warning(\n                        \"Discarded invalid or non-existential SSH Tunnel Key-file at {}!\".format(\n                            value\n                        )\n                    )\n\n            # handle jpeg compression\n            elif (\n                key == \"jpeg_compression\"\n                and not (simplejpeg is None)\n                and isinstance(value, (bool, str))\n            ):\n                if isinstance(value, str) and value.strip().upper() in [\n                    \"RGB\",\n                    \"BGR\",\n                    \"RGBX\",\n                    \"BGRX\",\n                    \"XBGR\",\n                    \"XRGB\",\n                    \"GRAY\",\n                    \"RGBA\",\n                    \"BGRA\",\n                    \"ABGR\",\n                    \"ARGB\",\n                    \"CMYK\",\n                ]:\n                    # set encoding colorspace\n                    self.__jpeg_compression_colorspace = value.strip().upper()\n                    # enable frame-compression encoding value\n                    self.__jpeg_compression = True\n                else:\n                    # enable frame-compression encoding value\n                    self.__jpeg_compression = value\n            elif key == \"jpeg_compression_quality\" and isinstance(value, (int, float)):\n                # set valid jpeg quality\n                if value >= 10 and value <= 100:\n                    self.__jpeg_compression_quality = int(value)\n                else:\n                    logger.warning(\"Skipped invalid `jpeg_compression_quality` value!\")\n            elif key == \"jpeg_compression_fastdct\" and isinstance(value, bool):\n                # enable jpeg fastdct\n                self.__jpeg_compression_fastdct = value\n            elif key == \"jpeg_compression_fastupsample\" and isinstance(value, bool):\n                # enable jpeg  fastupsample\n                self.__jpeg_compression_fastupsample = value\n\n            # assign maximum retries in synchronous patterns\n            elif key == \"max_retries\" and isinstance(value, int) and pattern < 2:\n                if value >= 0:\n                    self.__max_retries = value\n                else:\n                    logger.warning(\"Invalid `max_retries` value skipped!\")\n\n            # assign request timeout in synchronous patterns\n            elif key == \"request_timeout\" and isinstance(value, int) and pattern < 2:\n                if value >= 4:\n                    self.__request_timeout = value * 1000  # covert to milliseconds\n                else:\n                    logger.warning(\"Invalid `request_timeout` value skipped!\")\n\n            # assign subscriber timeout\n            elif (\n                key == \"subscriber_timeout\" and isinstance(value, int) and pattern == 2\n            ):\n                if value > 0:\n                    self.__subscriber_timeout = value * 1000  # covert to milliseconds\n                else:\n                    logger.warning(\"Invalid `request_timeout` value skipped!\")\n\n            # handle ZMQ flags\n            elif key == \"flag\" and isinstance(value, int):\n                self.__msg_flag = value\n                self.__msg_flag and logger.warning(\n                    \"The flag optional value is set to `1` (NOBLOCK) for this run. This might cause NetGear to not terminate gracefully.\"\n                )\n            elif key == \"copy\" and isinstance(value, bool):\n                self.__msg_copy = value\n            elif key == \"track\" and isinstance(value, bool):\n                self.__msg_track = value\n                self.__msg_copy and self.__msg_track and logger.info(\n                    \"The `track` optional value will be ignored for this run because `copy=True` is also defined.\"\n                )\n            else:\n                pass\n\n        # Handle ssh tunneling if enabled\n        if not (self.__ssh_tunnel_mode is None):\n            # SSH Tunnel Mode only available for server mode\n            if receive_mode:\n                logger.error(\"SSH Tunneling cannot be enabled for Client-end!\")\n            else:\n                # check if SSH tunneling possible\n                ssh_address = self.__ssh_tunnel_mode\n                ssh_address, ssh_port = (\n                    ssh_address.split(\":\")\n                    if \":\" in ssh_address\n                    else [ssh_address, \"22\"]\n                )  # default to port 22\n                if \"47\" in ssh_port:\n                    self.__ssh_tunnel_mode = self.__ssh_tunnel_mode.replace(\n                        \":47\", \"\"\n                    )  # port-47 is reserved for testing\n                else:\n                    # extract ip for validation\n                    ssh_user, ssh_ip = (\n                        ssh_address.split(\"@\")\n                        if \"@\" in ssh_address\n                        else [\"\", ssh_address]\n                    )\n                    # validate ip specified port\n                    assert check_open_port(\n                        ssh_ip, port=int(ssh_port)\n                    ), \"[NetGear:ERROR] :: Host `{}` is not available for SSH Tunneling at port-{}!\".format(\n                        ssh_address, ssh_port\n                    )\n\n        # Handle multiple exclusive modes if enabled\n        if self.__multiclient_mode and self.__multiserver_mode:\n            raise ValueError(\n                \"[NetGear:ERROR] :: Multi-Client and Multi-Server Mode cannot be enabled simultaneously!\"\n            )\n        elif self.__multiserver_mode or self.__multiclient_mode:\n            # check if Bidirectional Mode also enabled\n            if self.__bi_mode:\n                # log it\n                self.__logging and logger.debug(\n                    \"Bidirectional Data Transmission is also enabled for this connection!\"\n                )\n            # check if SSH Tunneling Mode also enabled\n            if self.__ssh_tunnel_mode:\n                # raise error\n                raise ValueError(\n                    \"[NetGear:ERROR] :: SSH Tunneling and {} Mode cannot be enabled simultaneously. Kindly refer docs!\".format(\n                        \"Multi-Server\" if self.__multiserver_mode else \"Multi-Client\"\n                    )\n                )\n        elif self.__bi_mode:\n            # log Bidirectional mode activation\n            self.__logging and logger.debug(\n                \"Bidirectional Data Transmission is enabled for this connection!\"\n            )\n        elif self.__ssh_tunnel_mode:\n            # log Bidirectional mode activation\n            self.__logging and logger.debug(\n                \"SSH Tunneling is enabled for host:`{}` with `{}` back-end.\".format(\n                    self.__ssh_tunnel_mode,\n                    \"paramiko\" if self.__paramiko_present else \"pexpect\",\n                )\n            )\n\n        # On Windows, NetGear requires the ``WindowsSelectorEventLoop`` but Python 3.8 and above,\n        # defaults to an ``ProactorEventLoop`` loop that is not compatible with it. Thereby,\n        # we had to set it manually.\n        platform.system() == \"Windows\" and asyncio.set_event_loop_policy(\n            asyncio.WindowsSelectorEventLoopPolicy()\n        )\n\n        # define ZMQ messaging context instance\n        self.__msg_context = zmq.Context.instance()\n\n        # initialize and assign receive mode to global variable\n        self.__receive_mode = receive_mode\n\n        # Handle Secure mode\n        if self.__secure_mode > 0:\n            # activate and log if overwriting is enabled\n            if receive_mode:\n                overwrite_cert = False\n                overwrite_cert and logger.warning(\n                    \"Overwriting ZMQ Authentication certificates is disabled for Client's end!\"\n                )\n            else:\n                overwrite_cert and self.__logging and logger.info(\n                    \"Overwriting ZMQ Authentication certificates over previous ones!\"\n                )\n\n            # Validate certificate generation paths\n            # Start threaded authenticator for this context\n            try:\n                # check if custom certificates path is specified\n                if custom_cert_location:\n                    (\n                        auth_cert_dir,\n                        self.__auth_secretkeys_dir,\n                        self.__auth_publickeys_dir,\n                    ) = generate_auth_certificates(\n                        custom_cert_location, overwrite=overwrite_cert, logging=logging\n                    )\n                else:\n                    # otherwise auto-generate suitable path\n                    (\n                        auth_cert_dir,\n                        self.__auth_secretkeys_dir,\n                        self.__auth_publickeys_dir,\n                    ) = generate_auth_certificates(\n                        os.path.join(expanduser(\"~\"), \".vidgear\"),\n                        overwrite=overwrite_cert,\n                        logging=logging,\n                    )\n                # log it\n                self.__logging and logger.debug(\n                    \"`{}` is the default location for storing ZMQ authentication certificates/keys.\".format(\n                        auth_cert_dir\n                    )\n                )\n\n                # start an authenticator for this context\n                self.__z_auth = ThreadAuthenticator(self.__msg_context)\n                self.__z_auth.start()\n                self.__z_auth.allow(str(address))  # allow current address\n\n                # check if `IronHouse` is activated\n                if self.__secure_mode == 2:\n                    # tell authenticator to use the certificate from given valid dir\n                    self.__z_auth.configure_curve(\n                        domain=\"*\", location=self.__auth_publickeys_dir\n                    )\n                else:\n                    # otherwise tell the authenticator how to handle the CURVE requests, if `StoneHouse` is activated\n                    self.__z_auth.configure_curve(\n                        domain=\"*\", location=auth.CURVE_ALLOW_ANY\n                    )\n            except zmq.ZMQError as e:\n                if \"Address in use\" in str(e):\n                    logger.info(\"ZMQ Authenticator already running.\")\n                else:\n                    # catch if any error occurred and disable Secure mode\n                    logger.exception(str(e))\n                    self.__secure_mode = 0\n                    logger.error(\n                        \"ZMQ Security Mechanism is disabled for this connection due to errors!\"\n                    )\n\n        # check whether `receive_mode` is enabled\n        if self.__receive_mode:\n            # define connection address\n            address = \"*\" if address is None else address\n\n            # check if multiserver_mode is enabled\n            if self.__multiserver_mode:\n                # check if unique server port address list/tuple is assigned or not in multiserver_mode\n                if port is None or not isinstance(port, (tuple, list)):\n                    # raise error if not\n                    raise ValueError(\n                        \"[NetGear:ERROR] :: Incorrect port value! Kindly provide a list/tuple of Server ports while Multi-Server mode is enabled. For more information refer VidGear docs.\"\n                    )\n                else:\n                    # otherwise log it\n                    logger.debug(\n                        \"Enabling Multi-Server Mode at PORTS: {}!\".format(port)\n                    )\n                # create port address buffer for keeping track of connected client's port(s)\n                self.__port_buffer = []\n            # check if multiclient_mode is enabled\n            elif self.__multiclient_mode:\n                # check if unique server port address is assigned or not in multiclient_mode\n                if port is None:\n                    # raise error if not\n                    raise ValueError(\n                        \"[NetGear:ERROR] :: Kindly provide a unique & valid port value at Client-end. For more information refer VidGear docs.\"\n                    )\n                else:\n                    # otherwise log it\n                    logger.debug(\n                        \"Enabling Multi-Client Mode at PORT: {} on this device!\".format(\n                            port\n                        )\n                    )\n                # assign value to global variable\n                self.__port = port\n            else:\n                # otherwise assign local port address if None\n                port = \"5555\" if port is None else port\n\n            try:\n                # define thread-safe messaging socket\n                self.__msg_socket = self.__msg_context.socket(msg_pattern[1])\n\n                # define pub-sub flag\n                self.__pattern == 2 and self.__msg_socket.set_hwm(1)\n\n                # enable specified secure mode for the socket\n                if self.__secure_mode > 0:\n                    # load server key\n                    server_secret_file = os.path.join(\n                        self.__auth_secretkeys_dir, \"server.key_secret\"\n                    )\n                    server_public, server_secret = auth.load_certificate(\n                        server_secret_file\n                    )\n                    # load  all CURVE keys\n                    self.__msg_socket.curve_secretkey = server_secret\n                    self.__msg_socket.curve_publickey = server_public\n                    # enable CURVE connection for this socket\n                    self.__msg_socket.curve_server = True\n\n                # define exclusive socket options for `patterns=2`\n                if self.__pattern == 2:\n                    self.__msg_socket.setsockopt_string(zmq.SUBSCRIBE, \"\")\n                    self.__subscriber_timeout and self.__msg_socket.setsockopt(\n                        zmq.RCVTIMEO, self.__subscriber_timeout\n                    )\n                    self.__subscriber_timeout and self.__msg_socket.setsockopt(\n                        zmq.LINGER, 0\n                    )\n\n                # if multiserver_mode is enabled, then assign port addresses to zmq socket\n                if self.__multiserver_mode:\n                    # bind socket to given server protocol, address and ports\n                    for pt in port:\n                        self.__msg_socket.bind(\n                            protocol + \"://\" + str(address) + \":\" + str(pt)\n                        )\n                else:\n                    # bind socket to given protocol, address and port normally\n                    self.__msg_socket.bind(\n                        protocol + \"://\" + str(address) + \":\" + str(port)\n                    )\n\n                # additional settings\n                if pattern < 2:\n                    if self.__multiserver_mode:\n                        self.__connection_address = []\n                        for pt in port:\n                            self.__connection_address.append(\n                                protocol + \"://\" + str(address) + \":\" + str(pt)\n                            )\n                    else:\n                        self.__connection_address = (\n                            protocol + \"://\" + str(address) + \":\" + str(port)\n                        )\n                    self.__msg_pattern = msg_pattern[1]\n                    self.__poll.register(self.__msg_socket, zmq.POLLIN)\n                    self.__logging and logger.debug(\n                        \"Reliable transmission is enabled for this pattern with max-retries: {} and timeout: {} secs.\".format(\n                            self.__max_retries, self.__request_timeout / 1000\n                        )\n                    )\n                else:\n                    self.__logging and self.__subscriber_timeout and logger.debug(\n                        \"Timeout: {} secs is enabled for this system.\".format(\n                            self.__subscriber_timeout / 1000\n                        )\n                    )\n\n            except Exception as e:\n                # otherwise log and raise error\n                logger.exception(str(e))\n                # Handle Secure Mode\n                self.__secure_mode and logger.critical(\n                    \"Failed to activate Secure Mode: `{}` for this connection!\".format(\n                        valid_security_mech[self.__secure_mode]\n                    )\n                )\n                # raise errors for exclusive modes\n                if self.__multiserver_mode or self.__multiclient_mode:\n                    raise RuntimeError(\n                        \"[NetGear:ERROR] :: Receive Mode failed to activate {} Mode at address: {} with pattern: {}! Kindly recheck all parameters.\".format(\n                            (\n                                \"Multi-Server\"\n                                if self.__multiserver_mode\n                                else \"Multi-Client\"\n                            ),\n                            (protocol + \"://\" + str(address) + \":\" + str(port)),\n                            pattern,\n                        )\n                    )\n                else:\n                    self.__bi_mode and logger.critical(\n                        \"Failed to activate Bidirectional Mode for this connection!\"\n                    )\n                    raise RuntimeError(\n                        \"[NetGear:ERROR] :: Receive Mode failed to bind address: {} and pattern: {}! Kindly recheck all parameters.\".format(\n                            (protocol + \"://\" + str(address) + \":\" + str(port)), pattern\n                        )\n                    )\n\n            # Handle threaded queue mode\n            self.__logging and logger.debug(\n                \"Threaded Queue Mode is enabled by default for this connection.\"\n            )\n\n            # define deque and assign it to global var\n            self.__queue = deque(maxlen=96)  # max len 96 to check overflow\n\n            # initialize and start threaded recv_handler\n            self.__thread = Thread(target=self.__recv_handler, name=\"NetGear\", args=())\n            self.__thread.daemon = True\n            self.__thread.start()\n\n            if self.__logging:\n                # finally log progress\n                logger.debug(\n                    \"Successfully Binded to address: {} with pattern: {}.\".format(\n                        (protocol + \"://\" + str(address) + \":\" + str(port)), pattern\n                    )\n                )\n                self.__jpeg_compression and logger.debug(\n                    \"JPEG Frame-Compression is activated for this connection with Colorspace:`{}`, Quality:`{}`%, Fastdct:`{}`, and Fastupsample:`{}`.\".format(\n                        self.__jpeg_compression_colorspace,\n                        self.__jpeg_compression_quality,\n                        (\"enabled\" if self.__jpeg_compression_fastdct else \"disabled\"),\n                        (\n                            \"enabled\"\n                            if self.__jpeg_compression_fastupsample\n                            else \"disabled\"\n                        ),\n                    )\n                )\n                self.__secure_mode and logger.debug(\n                    \"Successfully enabled ZMQ Security Mechanism: `{}` for this connection.\".format(\n                        valid_security_mech[self.__secure_mode]\n                    )\n                )\n                logger.debug(\"Multi-threaded Receive Mode is successfully enabled.\")\n                logger.debug(\"Unique System ID is {}.\".format(self.__id))\n                logger.debug(\"Receive Mode is now activated.\")\n\n        else:\n            # otherwise default to `Send Mode`\n            # define connection address\n            address = \"localhost\" if address is None else address\n\n            # check if multiserver_mode is enabled\n            if self.__multiserver_mode:\n                # check if unique server port address is assigned or not in multiserver_mode\n                if port is None:\n                    # raise error if not\n                    raise ValueError(\n                        \"[NetGear:ERROR] :: Kindly provide a unique & valid port value at Server-end. For more information refer VidGear docs.\"\n                    )\n                else:\n                    # otherwise log it\n                    logger.debug(\n                        \"Enabling Multi-Server Mode at PORT: {} on this device!\".format(\n                            port\n                        )\n                    )\n                # assign value to global variable\n                self.__port = port\n            # check if multiclient_mode is enabled\n            elif self.__multiclient_mode:\n                # check if unique client port address list/tuple is assigned or not in multiclient_mode\n                if port is None or not isinstance(port, (tuple, list)):\n                    # raise error if not\n                    raise ValueError(\n                        \"[NetGear:ERROR] :: Incorrect port value! Kindly provide a list/tuple of Client ports while Multi-Client mode is enabled. For more information refer VidGear docs.\"\n                    )\n                else:\n                    # otherwise log it\n                    logger.debug(\n                        \"Enabling Multi-Client Mode at PORTS: {}!\".format(port)\n                    )\n                # create port address buffer for keeping track of connected client ports\n                self.__port_buffer = []\n            else:\n                # otherwise assign local port address if None\n                port = \"5555\" if port is None else port\n\n            try:\n                # define thread-safe messaging socket\n                self.__msg_socket = self.__msg_context.socket(msg_pattern[0])\n\n                # if req/rep pattern, define additional flags\n                if self.__pattern == 1:\n                    self.__msg_socket.REQ_RELAXED = True\n                    self.__msg_socket.REQ_CORRELATE = True\n\n                # if pub/sub pattern, define additional optimizer\n                if self.__pattern == 2:\n                    self.__msg_socket.set_hwm(1)\n\n                # enable specified secure mode for the socket\n                if self.__secure_mode > 0:\n                    # load client key\n                    client_secret_file = os.path.join(\n                        self.__auth_secretkeys_dir, \"client.key_secret\"\n                    )\n                    client_public, client_secret = auth.load_certificate(\n                        client_secret_file\n                    )\n                    # load  all CURVE keys\n                    self.__msg_socket.curve_secretkey = client_secret\n                    self.__msg_socket.curve_publickey = client_public\n                    # load server key\n                    server_public_file = os.path.join(\n                        self.__auth_publickeys_dir, \"server.key\"\n                    )\n                    server_public, _ = auth.load_certificate(server_public_file)\n                    # inject public key to make a CURVE connection.\n                    self.__msg_socket.curve_serverkey = server_public\n\n                # check if multi-client_mode is enabled\n                if self.__multiclient_mode:\n                    # bind socket to given server protocol, address and ports\n                    for pt in port:\n                        self.__msg_socket.connect(\n                            protocol + \"://\" + str(address) + \":\" + str(pt)\n                        )\n                else:\n                    # handle SSH tunneling if enabled\n                    if self.__ssh_tunnel_mode:\n                        # establish tunnel connection\n                        ssh.tunnel_connection(\n                            self.__msg_socket,\n                            protocol + \"://\" + str(address) + \":\" + str(port),\n                            self.__ssh_tunnel_mode,\n                            keyfile=self.__ssh_tunnel_keyfile,\n                            password=self.__ssh_tunnel_pwd,\n                            paramiko=self.__paramiko_present,\n                        )\n                    else:\n                        # connect socket to given protocol, address and port\n                        self.__msg_socket.connect(\n                            protocol + \"://\" + str(address) + \":\" + str(port)\n                        )\n\n                # additional settings\n                if pattern < 2:\n                    if self.__multiclient_mode:\n                        self.__connection_address = []\n                        for pt in port:\n                            self.__connection_address.append(\n                                protocol + \"://\" + str(address) + \":\" + str(pt)\n                            )\n                    else:\n                        self.__connection_address = (\n                            protocol + \"://\" + str(address) + \":\" + str(port)\n                        )\n                    self.__msg_pattern = msg_pattern[0]\n                    self.__poll.register(self.__msg_socket, zmq.POLLIN)\n\n                    self.__logging and logger.debug(\n                        \"Reliable transmission is enabled for this pattern with max-retries: {} and timeout: {} secs.\".format(\n                            self.__max_retries, self.__request_timeout / 1000\n                        )\n                    )\n\n            except Exception as e:\n                # otherwise log and raise error\n                logger.exception(str(e))\n                # Handle Secure Mode\n                self.__secure_mode and logger.critical(\n                    \"Failed to activate Secure Mode: `{}` for this connection!\".format(\n                        valid_security_mech[self.__secure_mode]\n                    )\n                )\n                # raise errors for exclusive modes\n                if self.__multiserver_mode or self.__multiclient_mode:\n                    raise RuntimeError(\n                        \"[NetGear:ERROR] :: Send Mode failed to activate {} Mode at address: {} with pattern: {}! Kindly recheck all parameters.\".format(\n                            (\n                                \"Multi-Server\"\n                                if self.__multiserver_mode\n                                else \"Multi-Client\"\n                            ),\n                            (protocol + \"://\" + str(address) + \":\" + str(port)),\n                            pattern,\n                        )\n                    )\n                else:\n                    self.__bi_mode and logger.critical(\n                        \"Failed to activate Bidirectional Mode for this connection!\"\n                    )\n                    self.__ssh_tunnel_mode and logger.critical(\n                        \"Failed to initiate SSH Tunneling Mode for this server with `{}` back-end!\".format(\n                            \"paramiko\" if self.__paramiko_present else \"pexpect\"\n                        )\n                    )\n                    raise RuntimeError(\n                        \"[NetGear:ERROR] :: Send Mode failed to connect address: {} and pattern: {}! Kindly recheck all parameters.\".format(\n                            (protocol + \"://\" + str(address) + \":\" + str(port)), pattern\n                        )\n                    )\n\n            if self.__logging:\n                # finally log progress\n                logger.debug(\n                    \"Successfully connected to address: {} with pattern: {}.\".format(\n                        (protocol + \"://\" + str(address) + \":\" + str(port)), pattern\n                    )\n                )\n                self.__jpeg_compression and logger.debug(\n                    \"JPEG Frame-Compression is activated for this connection with Colorspace:`{}`, Quality:`{}`%, Fastdct:`{}`, and Fastupsample:`{}`.\".format(\n                        self.__jpeg_compression_colorspace,\n                        self.__jpeg_compression_quality,\n                        (\"enabled\" if self.__jpeg_compression_fastdct else \"disabled\"),\n                        (\n                            \"enabled\"\n                            if self.__jpeg_compression_fastupsample\n                            else \"disabled\"\n                        ),\n                    )\n                )\n                self.__secure_mode and logger.debug(\n                    \"Enabled ZMQ Security Mechanism: `{}` for this connection.\".format(\n                        valid_security_mech[self.__secure_mode]\n                    )\n                )\n                logger.debug(\"Unique System ID is {}.\".format(self.__id))\n                logger.debug(\n                    \"Send Mode is successfully activated and ready to send data.\"\n                )\n\n    def __recv_handler(self):\n        \"\"\"\n        A threaded receiver handler, that keep iterating data from ZMQ socket to a internally monitored deque,\n        until the thread is terminated, or socket disconnects.\n        \"\"\"\n        # initialize variables\n        frame = None\n        msg_json = None\n\n        # keep looping infinitely until the thread is terminated\n        while not self.__terminate:\n            # check queue buffer for overflow\n            if len(self.__queue) >= 96:\n                # stop iterating if overflowing occurs\n                time.sleep(0.000001)\n                continue\n\n            if self.__pattern < 2:\n                socks = dict(self.__poll.poll(self.__request_timeout * 3))\n                if socks.get(self.__msg_socket) == zmq.POLLIN:\n                    msg_json = self.__msg_socket.recv_json(\n                        flags=self.__msg_flag | zmq.DONTWAIT\n                    )\n                else:\n                    logger.critical(\"No response from Server(s), Reconnecting again...\")\n                    self.__msg_socket.close(linger=0)\n                    self.__poll.unregister(self.__msg_socket)\n                    self.__max_retries -= 1\n\n                    if not (self.__max_retries):\n                        if self.__multiserver_mode:\n                            logger.error(\"All Servers seems to be offline, Abandoning!\")\n                        else:\n                            logger.error(\"Server seems to be offline, Abandoning!\")\n                        self.__terminate = True\n                        continue\n\n                    # Create new connection\n                    try:\n                        self.__msg_socket = self.__msg_context.socket(\n                            self.__msg_pattern\n                        )\n                        if isinstance(self.__connection_address, list):\n                            for _connection in self.__connection_address:\n                                self.__msg_socket.bind(_connection)\n                        else:\n                            self.__msg_socket.bind(self.__connection_address)\n                    except Exception as e:\n                        logger.exception(str(e))\n                        self.__terminate = True\n                        raise RuntimeError(\"API failed to restart the Client-end!\")\n                    self.__poll.register(self.__msg_socket, zmq.POLLIN)\n\n                    continue\n            else:\n                try:\n                    msg_json = self.__msg_socket.recv_json(flags=self.__msg_flag)\n                except zmq.ZMQError as e:\n                    if e.errno == zmq.EAGAIN:\n                        logger.critical(\"Connection Timeout. Exiting!\")\n                        self.__terminate = True\n                        self.__queue.append(None)\n                        break\n\n            # check if terminate_flag` received\n            if msg_json and msg_json[\"terminate_flag\"]:\n                # if multiserver_mode is enabled\n                if self.__multiserver_mode:\n                    # check and remove from which ports signal is received\n                    if msg_json[\"port\"] in self.__port_buffer:\n                        # if pattern is 1, then send back server the info about termination\n                        if self.__pattern == 1:\n                            self.__msg_socket.send_string(\n                                \"Termination signal successfully received at client!\"\n                            )\n                        self.__port_buffer.remove(msg_json[\"port\"])\n                        self.__logging and logger.warning(\n                            \"Termination signal received from Server at port: {}!\".format(\n                                msg_json[\"port\"]\n                            )\n                        )\n                    # if termination signal received from all servers then exit client.\n                    if not self.__port_buffer:\n                        logger.critical(\n                            \"Termination signal received from all Servers!!!\"\n                        )\n                        self.__terminate = True  # termination\n                else:\n                    # if pattern is 1, then send back server the info about termination\n                    if self.__pattern == 1:\n                        self.__msg_socket.send_string(\n                            \"Termination signal successfully received at Client's end!\"\n                        )\n                    # termination\n                    self.__terminate = True\n                    # notify client\n                    self.__logging and logger.critical(\n                        \"Termination signal received from server!\"\n                    )\n                continue\n\n            try:\n                msg_data = self.__msg_socket.recv(\n                    flags=self.__msg_flag | zmq.DONTWAIT,\n                    copy=self.__msg_copy,\n                    track=self.__msg_track,\n                )\n            except zmq.ZMQError as e:\n                logger.critical(\"Socket Session Expired. Exiting!\")\n                self.__terminate = True\n                self.__queue.append(None)\n                break\n\n            # handle data transfer in synchronous modes.\n            if self.__pattern < 2:\n                if self.__bi_mode or self.__multiclient_mode:\n                    # check if we are returning `ndarray` frames\n                    if not (self.__return_data is None) and isinstance(\n                        self.__return_data, np.ndarray\n                    ):\n                        # handle return data for compression\n                        return_data = np.copy(self.__return_data)\n\n                        # check whether exit_flag is False\n                        if not (return_data.flags[\"C_CONTIGUOUS\"]):\n                            # check whether the incoming frame is contiguous\n                            return_data = np.ascontiguousarray(\n                                return_data, dtype=return_data.dtype\n                            )\n\n                        # handle jpeg-compression encoding\n                        if self.__jpeg_compression:\n                            if self.__jpeg_compression_colorspace == \"GRAY\":\n                                if return_data.ndim == 2:\n                                    # patch for https://gitlab.com/jfolz/simplejpeg/-/issues/11\n                                    return_data = return_data[:, :, np.newaxis]\n                                return_data = simplejpeg.encode_jpeg(\n                                    return_data,\n                                    quality=self.__jpeg_compression_quality,\n                                    colorspace=self.__jpeg_compression_colorspace,\n                                    fastdct=self.__jpeg_compression_fastdct,\n                                )\n                            else:\n                                return_data = simplejpeg.encode_jpeg(\n                                    return_data,\n                                    quality=self.__jpeg_compression_quality,\n                                    colorspace=self.__jpeg_compression_colorspace,\n                                    colorsubsampling=\"422\",\n                                    fastdct=self.__jpeg_compression_fastdct,\n                                )\n\n                        return_dict = (\n                            dict(port=self.__port)\n                            if self.__multiclient_mode\n                            else dict()\n                        )\n\n                        return_dict.update(\n                            dict(\n                                return_type=(type(self.__return_data).__name__),\n                                compression=(\n                                    {\n                                        \"dct\": self.__jpeg_compression_fastdct,\n                                        \"ups\": self.__jpeg_compression_fastupsample,\n                                        \"colorspace\": self.__jpeg_compression_colorspace,\n                                    }\n                                    if self.__jpeg_compression\n                                    else False\n                                ),\n                                array_dtype=(\n                                    str(self.__return_data.dtype)\n                                    if not (self.__jpeg_compression)\n                                    else \"\"\n                                ),\n                                array_shape=(\n                                    self.__return_data.shape\n                                    if not (self.__jpeg_compression)\n                                    else \"\"\n                                ),\n                                data=None,\n                            )\n                        )\n\n                        # send the json dict\n                        self.__msg_socket.send_json(\n                            return_dict, self.__msg_flag | zmq.SNDMORE\n                        )\n                        # send the array with correct flags\n                        self.__msg_socket.send(\n                            return_data,\n                            flags=self.__msg_flag,\n                            copy=self.__msg_copy,\n                            track=self.__msg_track,\n                        )\n                    else:\n                        return_dict = (\n                            dict(port=self.__port)\n                            if self.__multiclient_mode\n                            else dict()\n                        )\n                        return_dict.update(\n                            dict(\n                                return_type=(type(self.__return_data).__name__),\n                                data=self.__return_data,\n                            )\n                        )\n                        self.__msg_socket.send_json(return_dict, self.__msg_flag)\n                else:\n                    # send confirmation message to server\n                    self.__msg_socket.send_string(\n                        \"Data received on device: {} !\".format(self.__id)\n                    )\n            else:\n                # else raise warning\n                if self.__return_data:\n                    logger.warning(\"`return_data` is disabled for this pattern!\")\n\n            # check if encoding was enabled\n            if msg_json[\"compression\"]:\n                # decode JPEG frame\n                frame = simplejpeg.decode_jpeg(\n                    msg_data,\n                    colorspace=msg_json[\"compression\"][\"colorspace\"],\n                    fastdct=self.__jpeg_compression_fastdct\n                    or msg_json[\"compression\"][\"dct\"],\n                    fastupsample=self.__jpeg_compression_fastupsample\n                    or msg_json[\"compression\"][\"ups\"],\n                )\n                # check if valid frame returned\n                if frame is None:\n                    self.__terminate = True\n                    # otherwise raise error and exit\n                    raise RuntimeError(\n                        \"[NetGear:ERROR] :: Received compressed JPEG frame decoding failed\"\n                    )\n                if msg_json[\"compression\"][\"colorspace\"] == \"GRAY\" and frame.ndim == 3:\n                    # patch for https://gitlab.com/jfolz/simplejpeg/-/issues/11\n                    frame = np.squeeze(frame, axis=2)\n            else:\n                # recover and reshape frame from buffer\n                frame_buffer = np.frombuffer(msg_data, dtype=msg_json[\"dtype\"])\n                frame = frame_buffer.reshape(msg_json[\"shape\"])\n\n            # check if multiserver_mode\n            if self.__multiserver_mode:\n                # save the unique port addresses\n                if not msg_json[\"port\"] in self.__port_buffer:\n                    self.__port_buffer.append(msg_json[\"port\"])\n                # extract if any message from server and display it\n                if msg_json[\"message\"]:\n                    self.__queue.append((msg_json[\"port\"], msg_json[\"message\"], frame))\n                else:\n                    # append recovered unique port and frame to queue\n                    self.__queue.append((msg_json[\"port\"], frame))\n            # extract if any message from server if Bidirectional Mode is enabled\n            elif self.__bi_mode:\n                if msg_json[\"message\"]:\n                    # append grouped frame and data to queue\n                    self.__queue.append((msg_json[\"message\"], frame))\n                else:\n                    self.__queue.append((None, frame))\n            else:\n                # otherwise append recovered frame to queue\n                self.__queue.append(frame)\n\n    def recv(self, return_data=None):\n        \"\"\"\n        A Receiver end method, that extracts received frames synchronously from monitored deque, while maintaining a\n        fixed-length frame buffer in the memory, and blocks the thread if the deque is full.\n\n        Parameters:\n            return_data (any): inputs return data _(of any datatype)_, for sending back to Server.\n\n        **Returns:** A n-dimensional numpy array.\n        \"\"\"\n        # check whether `receive mode` is activated\n        if not (self.__receive_mode):\n            # raise value error and exit\n            self.__terminate = True\n            raise ValueError(\n                \"[NetGear:ERROR] :: `recv()` function cannot be used while receive_mode is disabled. Kindly refer vidgear docs!\"\n            )\n\n        # handle Bidirectional return data\n        if (self.__bi_mode or self.__multiclient_mode) and not (return_data is None):\n            self.__return_data = return_data\n\n        # check whether or not termination flag is enabled\n        while not self.__terminate:\n            try:\n                # check if queue is empty\n                if len(self.__queue) > 0:\n                    return self.__queue.popleft()\n                else:\n                    time.sleep(0.00001)\n                    continue\n            except KeyboardInterrupt:\n                self.__terminate = True\n                break\n        # otherwise return NoneType\n        return None\n\n    def send(self, frame, message=None):\n        \"\"\"\n        A Server end method, that sends the data and frames over the network to Client(s).\n\n        Parameters:\n            frame (numpy.ndarray): inputs numpy array(frame).\n            message (any): input for sending additional data _(of any datatype except `numpy.ndarray`)_ to Client(s).\n\n        **Returns:** Data _(of any datatype)_ in selected exclusive modes, otherwise None-type.\n\n        \"\"\"\n        # check whether `receive_mode` is disabled\n        if self.__receive_mode:\n            # raise value error and exit\n            self.__terminate = True\n            raise ValueError(\n                \"[NetGear:ERROR] :: `send()` function cannot be used while receive_mode is enabled. Kindly refer vidgear docs!\"\n            )\n\n        if not (message is None) and isinstance(message, np.ndarray):\n            logger.warning(\n                \"Skipped unsupported `message` of datatype: {}!\".format(\n                    type(message).__name__\n                )\n            )\n            message = None\n\n        # define exit_flag and assign value\n        exit_flag = True if (frame is None or self.__terminate) else False\n\n        # check whether exit_flag is False\n        if not (exit_flag) and not (frame.flags[\"C_CONTIGUOUS\"]):\n            # check whether the incoming frame is contiguous\n            frame = np.ascontiguousarray(frame, dtype=frame.dtype)\n\n        # handle JPEG compression encoding\n        if self.__jpeg_compression:\n            if self.__jpeg_compression_colorspace == \"GRAY\":\n                if frame.ndim == 2:\n                    # patch for https://gitlab.com/jfolz/simplejpeg/-/issues/11\n                    frame = np.expand_dims(frame, axis=2)\n                frame = simplejpeg.encode_jpeg(\n                    frame,\n                    quality=self.__jpeg_compression_quality,\n                    colorspace=self.__jpeg_compression_colorspace,\n                    fastdct=self.__jpeg_compression_fastdct,\n                )\n            else:\n                frame = simplejpeg.encode_jpeg(\n                    frame,\n                    quality=self.__jpeg_compression_quality,\n                    colorspace=self.__jpeg_compression_colorspace,\n                    colorsubsampling=\"422\",\n                    fastdct=self.__jpeg_compression_fastdct,\n                )\n\n        # check if multiserver_mode is activated and assign values with unique port\n        msg_dict = dict(port=self.__port) if self.__multiserver_mode else dict()\n\n        # prepare the exclusive json dict\n        msg_dict.update(\n            dict(\n                terminate_flag=exit_flag,\n                compression=(\n                    {\n                        \"dct\": self.__jpeg_compression_fastdct,\n                        \"ups\": self.__jpeg_compression_fastupsample,\n                        \"colorspace\": self.__jpeg_compression_colorspace,\n                    }\n                    if self.__jpeg_compression\n                    else False\n                ),\n                message=message,\n                pattern=str(self.__pattern),\n                dtype=str(frame.dtype) if not (self.__jpeg_compression) else \"\",\n                shape=frame.shape if not (self.__jpeg_compression) else \"\",\n            )\n        )\n\n        # send the json dict\n        self.__msg_socket.send_json(msg_dict, self.__msg_flag | zmq.SNDMORE)\n        # send the frame array with correct flags\n        self.__msg_socket.send(\n            frame, flags=self.__msg_flag, copy=self.__msg_copy, track=self.__msg_track\n        )\n\n        # check if synchronous patterns, then wait for confirmation\n        if self.__pattern < 2:\n            # check if Bidirectional data transmission is enabled\n            if self.__bi_mode or self.__multiclient_mode:\n                # handles return data\n                recvd_data = None\n\n                socks = dict(self.__poll.poll(self.__request_timeout))\n                if socks.get(self.__msg_socket) == zmq.POLLIN:\n                    # handle return data\n                    recv_json = self.__msg_socket.recv_json(flags=self.__msg_flag)\n                else:\n                    logger.critical(\"No response from Client, Reconnecting again...\")\n                    # Socket is confused. Close and remove it.\n                    self.__msg_socket.setsockopt(zmq.LINGER, 0)\n                    self.__msg_socket.close()\n                    self.__poll.unregister(self.__msg_socket)\n                    self.__max_retries -= 1\n\n                    if not (self.__max_retries):\n                        if self.__multiclient_mode:\n                            logger.error(\n                                \"All Clients failed to respond on multiple attempts.\"\n                            )\n                        else:\n                            logger.error(\n                                \"Client failed to respond on multiple attempts.\"\n                            )\n                        self.__terminate = True\n                        raise RuntimeError(\n                            \"[NetGear:ERROR] :: Client(s) seems to be offline, Abandoning.\"\n                        )\n\n                    # Create new connection\n                    self.__msg_socket = self.__msg_context.socket(self.__msg_pattern)\n                    if isinstance(self.__connection_address, list):\n                        for _connection in self.__connection_address:\n                            self.__msg_socket.connect(_connection)\n                    else:\n                        # handle SSH tunneling if enabled\n                        if self.__ssh_tunnel_mode:\n                            # establish tunnel connection\n                            ssh.tunnel_connection(\n                                self.__msg_socket,\n                                self.__connection_address,\n                                self.__ssh_tunnel_mode,\n                                keyfile=self.__ssh_tunnel_keyfile,\n                                password=self.__ssh_tunnel_pwd,\n                                paramiko=self.__paramiko_present,\n                            )\n                        else:\n                            # connect normally\n                            self.__msg_socket.connect(self.__connection_address)\n                    self.__poll.register(self.__msg_socket, zmq.POLLIN)\n                    # return None for mean-time\n                    return None\n\n                # save the unique port addresses\n                if (\n                    self.__multiclient_mode\n                    and not recv_json[\"port\"] in self.__port_buffer\n                ):\n                    self.__port_buffer.append(recv_json[\"port\"])\n\n                if recv_json[\"return_type\"] == \"ndarray\":\n                    recv_array = self.__msg_socket.recv(\n                        flags=self.__msg_flag,\n                        copy=self.__msg_copy,\n                        track=self.__msg_track,\n                    )\n                    # check if encoding was enabled\n                    if recv_json[\"compression\"]:\n                        # decode JPEG frame\n                        recvd_data = simplejpeg.decode_jpeg(\n                            recv_array,\n                            colorspace=recv_json[\"compression\"][\"colorspace\"],\n                            fastdct=self.__jpeg_compression_fastdct\n                            or recv_json[\"compression\"][\"dct\"],\n                            fastupsample=self.__jpeg_compression_fastupsample\n                            or recv_json[\"compression\"][\"ups\"],\n                        )\n                        # check if valid frame returned\n                        if recvd_data is None:\n                            self.__terminate = True\n                            # otherwise raise error and exit\n                            raise RuntimeError(\n                                \"[NetGear:ERROR] :: Received compressed frame `{}` decoding failed with flag: {}.\".format(\n                                    recv_json[\"compression\"],\n                                    self.__ex_compression_params,\n                                )\n                            )\n\n                        if (\n                            recv_json[\"compression\"][\"colorspace\"] == \"GRAY\"\n                            and recvd_data.ndim == 3\n                        ):\n                            # patch for https://gitlab.com/jfolz/simplejpeg/-/issues/11\n                            recvd_data = np.squeeze(recvd_data, axis=2)\n                    else:\n                        recvd_data = np.frombuffer(\n                            recv_array, dtype=recv_json[\"array_dtype\"]\n                        ).reshape(recv_json[\"array_shape\"])\n                else:\n                    recvd_data = recv_json[\"data\"]\n\n                return (\n                    (recv_json[\"port\"], recvd_data)\n                    if self.__multiclient_mode\n                    else recvd_data\n                )\n            else:\n                # otherwise log normally\n                socks = dict(self.__poll.poll(self.__request_timeout))\n                if socks.get(self.__msg_socket) == zmq.POLLIN:\n                    recv_confirmation = self.__msg_socket.recv()\n                else:\n                    logger.critical(\"No response from Client, Reconnecting again...\")\n                    # Socket is confused. Close and remove it.\n                    self.__msg_socket.setsockopt(zmq.LINGER, 0)\n                    self.__msg_socket.close()\n                    self.__poll.unregister(self.__msg_socket)\n                    self.__max_retries -= 1\n\n                    if not (self.__max_retries):\n                        logger.error(\"Client failed to respond on repeated attempts.\")\n                        self.__terminate = True\n                        raise RuntimeError(\n                            \"[NetGear:ERROR] :: Client seems to be offline, Abandoning!\"\n                        )\n\n                    # Create new connection\n                    self.__msg_socket = self.__msg_context.socket(self.__msg_pattern)\n                    # handle SSH tunneling if enabled\n                    if self.__ssh_tunnel_mode:\n                        # establish tunnel connection\n                        ssh.tunnel_connection(\n                            self.__msg_socket,\n                            self.__connection_address,\n                            self.__ssh_tunnel_mode,\n                            keyfile=self.__ssh_tunnel_keyfile,\n                            password=self.__ssh_tunnel_pwd,\n                            paramiko=self.__paramiko_present,\n                        )\n                    else:\n                        # connect normally\n                        self.__msg_socket.connect(self.__connection_address)\n                    self.__poll.register(self.__msg_socket, zmq.POLLIN)\n                    return None\n\n                # log confirmation\n                self.__logging and logger.debug(recv_confirmation)\n\n    def close(self, kill=False):\n        \"\"\"\n        Safely terminates the threads, and NetGear resources.\n\n        Parameters:\n            kill (bool): Kills ZMQ context instead of graceful exiting in receive mode.\n        \"\"\"\n        # log it\n        self.__logging and logger.debug(\n            \"Terminating various {} Processes.\".format(\n                \"Receive Mode\" if self.__receive_mode else \"Send Mode\"\n            )\n        )\n        #  whether `receive_mode` is enabled or not\n        if self.__receive_mode:\n            # check whether queue mode is empty\n            if not (self.__queue is None) and self.__queue:\n                self.__queue.clear()\n            # call immediate termination\n            self.__terminate = True\n            # properly close the socket\n            self.__logging and logger.debug(\"Terminating. Please wait...\")\n            # Handle Secure Mode Thread\n            if self.__z_auth:\n                self.__logging and logger.debug(\"Terminating Authenticator Thread.\")\n                self.__z_auth.stop()\n                while self.__z_auth.is_alive():\n                    pass\n            # wait until stream resources are released\n            # (producer thread might be still grabbing frame)\n            if self.__thread is not None:\n                self.__logging and logger.debug(\"Terminating Main Thread.\")\n                # properly handle thread exit\n                if self.__thread.is_alive() and kill:\n                    # force close if still alive\n                    logger.warning(\"Thread still running...Killing it forcefully!\")\n                    self.__msg_context.destroy()\n                    self.__thread.join()\n                else:\n                    self.__msg_socket.close(linger=0)\n                    self.__thread.join()\n                self.__thread = None\n            self.__logging and logger.debug(\"Terminated Successfully!\")\n        else:\n            # indicate that process should be terminated\n            self.__terminate = True\n            # log if kill enabled\n            kill and logger.warning(\n                \"`kill` parmeter is only available in the receive mode.\"\n            )\n            # Handle Secure Mode Thread\n            if self.__z_auth:\n                self.__logging and logger.debug(\"Terminating Authenticator Thread.\")\n                self.__z_auth.stop()\n                while self.__z_auth.is_alive():\n                    pass\n            # check if all attempts of reconnecting failed, then skip to closure\n            if (self.__pattern < 2 and not self.__max_retries) or (\n                self.__multiclient_mode and not self.__port_buffer\n            ):\n                try:\n                    # properly close the socket\n                    self.__msg_socket.setsockopt(zmq.LINGER, 0)\n                    self.__msg_socket.close()\n                except ZMQError:\n                    pass\n                finally:\n                    # exit\n                    return\n\n            if self.__multiserver_mode:\n                # check if multiserver_mode\n                # send termination flag to client with its unique port\n                term_dict = dict(terminate_flag=True, port=self.__port)\n            else:\n                # otherwise send termination flag to client\n                term_dict = dict(terminate_flag=True)\n\n            try:\n                if self.__multiclient_mode:\n                    for _ in self.__port_buffer:\n                        self.__msg_socket.send_json(term_dict)\n                else:\n                    self.__msg_socket.send_json(term_dict)\n\n                # check for confirmation if available within 1/5 timeout\n                if self.__pattern < 2:\n                    self.__logging and logger.debug(\"Terminating. Please wait...\")\n                    if self.__msg_socket.poll(self.__request_timeout // 5, zmq.POLLIN):\n                        self.__msg_socket.recv()\n            except Exception as e:\n                if not isinstance(e, ZMQError):\n                    logger.exception(str(e))\n            finally:\n                # properly close the socket\n                self.__msg_socket.setsockopt(zmq.LINGER, 0)\n                self.__msg_socket.close()\n                self.__logging and logger.debug(\"Terminated Successfully!\")\n

"},{"location":"bonus/reference/netgear/#vidgear.gears.netgear.NetGear.__init__","title":"__init__(self, address=None, port=None, protocol=None, pattern=0, receive_mode=False, logging=False, **options) special","text":"

This constructor method initializes the object state and attributes of the NetGear class.

Parameters:

Name Type Description Default address str

sets the valid network address of the Server/Client.

None port str

sets the valid Network Port of the Server/Client.

None protocol str

sets the valid messaging protocol between Server/Client.

None pattern int

sets the supported messaging pattern(flow of communication) between Server/Client

0 receive_mode bool

select the Netgear's Mode of operation.

False logging bool

enables/disables logging.

False options dict

provides the flexibility to alter various NetGear internal properties.

{} Source code in vidgear/gears/netgear.py
def __init__(\n    self,\n    address=None,\n    port=None,\n    protocol=None,\n    pattern=0,\n    receive_mode=False,\n    logging=False,\n    **options\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the NetGear class.\n\n    Parameters:\n        address (str): sets the valid network address of the Server/Client.\n        port (str): sets the valid Network Port of the Server/Client.\n        protocol (str): sets the valid messaging protocol between Server/Client.\n        pattern (int): sets the supported messaging pattern(flow of communication) between Server/Client\n        receive_mode (bool): select the Netgear's Mode of operation.\n        logging (bool): enables/disables logging.\n        options (dict): provides the flexibility to alter various NetGear internal properties.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # raise error(s) for critical Class imports\n    import_dependency_safe(\n        \"zmq\" if zmq is None else \"\", min_version=\"4.0\", pkg_name=\"pyzmq\"\n    )\n    import_dependency_safe(\n        \"simplejpeg\" if simplejpeg is None else \"\", error=\"log\", min_version=\"1.6.1\"\n    )\n\n    # define valid messaging patterns => `0`: zmq.PAIR, `1`:(zmq.REQ,zmq.REP), and `1`:(zmq.SUB,zmq.PUB)\n    valid_messaging_patterns = {\n        0: (zmq.PAIR, zmq.PAIR),\n        1: (zmq.REQ, zmq.REP),\n        2: (zmq.PUB, zmq.SUB),\n    }\n\n    # Handle messaging pattern\n    msg_pattern = None\n    # check whether user-defined messaging pattern is valid\n    if isinstance(pattern, int) and pattern in valid_messaging_patterns.keys():\n        # assign value\n        msg_pattern = valid_messaging_patterns[pattern]\n    else:\n        # otherwise default to 0:`zmq.PAIR`\n        pattern = 0\n        msg_pattern = valid_messaging_patterns[pattern]\n        self.__logging and logger.warning(\n            \"Wrong pattern value, Defaulting to `zmq.PAIR`! Kindly refer Docs for more Information.\"\n        )\n    # assign pattern to global parameter for further use\n    self.__pattern = pattern\n\n    # Handle messaging protocol\n    if protocol is None or not (protocol in [\"tcp\", \"ipc\"]):\n        # else default to `tcp` protocol\n        protocol = \"tcp\"\n        # log it\n        self.__logging and logger.warning(\n            \"Protocol is not supported or not provided. Defaulting to `tcp` protocol!\"\n        )\n\n    # Handle connection params\n\n    self.__msg_flag = 0  # handles connection flags\n    self.__msg_copy = False  # handles whether to copy data\n    self.__msg_track = False  # handles whether to track packets\n\n    # Handle NetGear's internal exclusive modes and params\n\n    # define Secure Mode\n    self.__z_auth = None\n\n    # define SSH Tunneling Mode\n    self.__ssh_tunnel_mode = None  # handles ssh_tunneling mode state\n    self.__ssh_tunnel_pwd = None\n    self.__ssh_tunnel_keyfile = None\n    self.__paramiko_present = False if paramiko is None else True\n\n    # define Multi-Server mode\n    self.__multiserver_mode = False  # handles multi-server mode state\n\n    # define Multi-Client mode\n    self.__multiclient_mode = False  # handles multi-client mode state\n\n    # define Bidirectional mode\n    self.__bi_mode = False  # handles Bidirectional mode state\n\n    # define Secure mode\n    valid_security_mech = {0: \"Grasslands\", 1: \"StoneHouse\", 2: \"IronHouse\"}\n    self.__secure_mode = 0  # handles ZMQ security layer status\n    auth_cert_dir = \"\"  # handles valid ZMQ certificates dir\n    self.__auth_publickeys_dir = \"\"  # handles valid ZMQ public certificates dir\n    self.__auth_secretkeys_dir = \"\"  # handles valid ZMQ private certificates dir\n    overwrite_cert = False  # checks if certificates overwriting allowed\n    custom_cert_location = \"\"  # handles custom ZMQ certificates path\n\n    # define frame-compression handler\n    self.__jpeg_compression = (\n        True if not (simplejpeg is None) else False\n    )  # enabled by default for all connections if simplejpeg is installed\n    self.__jpeg_compression_quality = 90  # 90% quality\n    self.__jpeg_compression_fastdct = True  # fastest DCT on by default\n    self.__jpeg_compression_fastupsample = False  # fastupsample off by default\n    self.__jpeg_compression_colorspace = \"BGR\"  # use BGR colorspace by default\n\n    # defines frame compression on return data\n    self.__ex_compression_params = None\n\n    # define receiver return data handler\n    self.__return_data = None\n\n    # generate 8-digit random system id\n    self.__id = \"\".join(\n        secrets.choice(string.ascii_uppercase + string.digits) for i in range(8)\n    )\n\n    # define termination flag\n    self.__terminate = False\n\n    # additional settings for reliability\n    if pattern < 2:\n        # define zmq poller for reliable transmission\n        self.__poll = zmq.Poller()\n        # define max retries\n        self.__max_retries = 3\n        # request timeout\n        self.__request_timeout = 4000  # 4 secs\n    else:\n        # subscriber timeout\n        self.__subscriber_timeout = None\n\n    # Handle user-defined options dictionary values\n    # reformat dictionary\n    options = {str(k).strip(): v for k, v in options.items()}\n\n    # loop over dictionary key & values and assign to global variables if valid\n    for key, value in options.items():\n        # handle multi-server mode\n        if key == \"multiserver_mode\" and isinstance(value, bool):\n            # check if valid pattern assigned\n            if pattern > 0:\n                # activate Multi-server mode\n                self.__multiserver_mode = value\n            else:\n                # otherwise disable it and raise error\n                self.__multiserver_mode = False\n                logger.critical(\"Multi-Server Mode is disabled!\")\n                raise ValueError(\n                    \"[NetGear:ERROR] :: `{}` pattern is not valid when Multi-Server Mode is enabled. Kindly refer Docs for more Information.\".format(\n                        pattern\n                    )\n                )\n\n        # handle multi-client mode\n        elif key == \"multiclient_mode\" and isinstance(value, bool):\n            # check if valid pattern assigned\n            if pattern > 0:\n                # activate Multi-client mode\n                self.__multiclient_mode = value\n            else:\n                # otherwise disable it and raise error\n                self.__multiclient_mode = False\n                logger.critical(\"Multi-Client Mode is disabled!\")\n                raise ValueError(\n                    \"[NetGear:ERROR] :: `{}` pattern is not valid when Multi-Client Mode is enabled. Kindly refer Docs for more Information.\".format(\n                        pattern\n                    )\n                )\n\n        # handle bidirectional mode\n        elif key == \"bidirectional_mode\" and isinstance(value, bool):\n            # check if pattern is valid\n            if pattern < 2:\n                # activate Bidirectional mode if specified\n                self.__bi_mode = value\n            else:\n                # otherwise disable it and raise error\n                self.__bi_mode = False\n                logger.warning(\"Bidirectional data transmission is disabled!\")\n                raise ValueError(\n                    \"[NetGear:ERROR] :: `{}` pattern is not valid when Bidirectional Mode is enabled. Kindly refer Docs for more Information!\".format(\n                        pattern\n                    )\n                )\n\n        # handle secure mode\n        elif (\n            key == \"secure_mode\"\n            and isinstance(value, int)\n            and (value in valid_security_mech)\n        ):\n            self.__secure_mode = value\n\n        elif key == \"custom_cert_location\" and isinstance(value, str):\n            # verify custom auth certificates path for secure mode\n            custom_cert_location = os.path.abspath(value)\n            assert os.path.isdir(\n                custom_cert_location\n            ), \"[NetGear:ERROR] :: `custom_cert_location` value must be the path to a valid directory!\"\n            assert check_WriteAccess(\n                custom_cert_location,\n                is_windows=True if os.name == \"nt\" else False,\n                logging=self.__logging,\n            ), \"[NetGear:ERROR] :: Permission Denied!, cannot write ZMQ authentication certificates to '{}' directory!\".format(\n                value\n            )\n        elif key == \"overwrite_cert\" and isinstance(value, bool):\n            # enable/disable auth certificate overwriting in secure mode\n            overwrite_cert = value\n\n        # handle ssh-tunneling mode\n        elif key == \"ssh_tunnel_mode\" and isinstance(value, str):\n            # enable SSH Tunneling Mode\n            self.__ssh_tunnel_mode = value.strip()\n        elif key == \"ssh_tunnel_pwd\" and isinstance(value, str):\n            # add valid SSH Tunneling password\n            self.__ssh_tunnel_pwd = value\n        elif key == \"ssh_tunnel_keyfile\" and isinstance(value, str):\n            # add valid SSH Tunneling key-file\n            self.__ssh_tunnel_keyfile = value if os.path.isfile(value) else None\n            if self.__ssh_tunnel_keyfile is None:\n                logger.warning(\n                    \"Discarded invalid or non-existential SSH Tunnel Key-file at {}!\".format(\n                        value\n                    )\n                )\n\n        # handle jpeg compression\n        elif (\n            key == \"jpeg_compression\"\n            and not (simplejpeg is None)\n            and isinstance(value, (bool, str))\n        ):\n            if isinstance(value, str) and value.strip().upper() in [\n                \"RGB\",\n                \"BGR\",\n                \"RGBX\",\n                \"BGRX\",\n                \"XBGR\",\n                \"XRGB\",\n                \"GRAY\",\n                \"RGBA\",\n                \"BGRA\",\n                \"ABGR\",\n                \"ARGB\",\n                \"CMYK\",\n            ]:\n                # set encoding colorspace\n                self.__jpeg_compression_colorspace = value.strip().upper()\n                # enable frame-compression encoding value\n                self.__jpeg_compression = True\n            else:\n                # enable frame-compression encoding value\n                self.__jpeg_compression = value\n        elif key == \"jpeg_compression_quality\" and isinstance(value, (int, float)):\n            # set valid jpeg quality\n            if value >= 10 and value <= 100:\n                self.__jpeg_compression_quality = int(value)\n            else:\n                logger.warning(\"Skipped invalid `jpeg_compression_quality` value!\")\n        elif key == \"jpeg_compression_fastdct\" and isinstance(value, bool):\n            # enable jpeg fastdct\n            self.__jpeg_compression_fastdct = value\n        elif key == \"jpeg_compression_fastupsample\" and isinstance(value, bool):\n            # enable jpeg  fastupsample\n            self.__jpeg_compression_fastupsample = value\n\n        # assign maximum retries in synchronous patterns\n        elif key == \"max_retries\" and isinstance(value, int) and pattern < 2:\n            if value >= 0:\n                self.__max_retries = value\n            else:\n                logger.warning(\"Invalid `max_retries` value skipped!\")\n\n        # assign request timeout in synchronous patterns\n        elif key == \"request_timeout\" and isinstance(value, int) and pattern < 2:\n            if value >= 4:\n                self.__request_timeout = value * 1000  # covert to milliseconds\n            else:\n                logger.warning(\"Invalid `request_timeout` value skipped!\")\n\n        # assign subscriber timeout\n        elif (\n            key == \"subscriber_timeout\" and isinstance(value, int) and pattern == 2\n        ):\n            if value > 0:\n                self.__subscriber_timeout = value * 1000  # covert to milliseconds\n            else:\n                logger.warning(\"Invalid `request_timeout` value skipped!\")\n\n        # handle ZMQ flags\n        elif key == \"flag\" and isinstance(value, int):\n            self.__msg_flag = value\n            self.__msg_flag and logger.warning(\n                \"The flag optional value is set to `1` (NOBLOCK) for this run. This might cause NetGear to not terminate gracefully.\"\n            )\n        elif key == \"copy\" and isinstance(value, bool):\n            self.__msg_copy = value\n        elif key == \"track\" and isinstance(value, bool):\n            self.__msg_track = value\n            self.__msg_copy and self.__msg_track and logger.info(\n                \"The `track` optional value will be ignored for this run because `copy=True` is also defined.\"\n            )\n        else:\n            pass\n\n    # Handle ssh tunneling if enabled\n    if not (self.__ssh_tunnel_mode is None):\n        # SSH Tunnel Mode only available for server mode\n        if receive_mode:\n            logger.error(\"SSH Tunneling cannot be enabled for Client-end!\")\n        else:\n            # check if SSH tunneling possible\n            ssh_address = self.__ssh_tunnel_mode\n            ssh_address, ssh_port = (\n                ssh_address.split(\":\")\n                if \":\" in ssh_address\n                else [ssh_address, \"22\"]\n            )  # default to port 22\n            if \"47\" in ssh_port:\n                self.__ssh_tunnel_mode = self.__ssh_tunnel_mode.replace(\n                    \":47\", \"\"\n                )  # port-47 is reserved for testing\n            else:\n                # extract ip for validation\n                ssh_user, ssh_ip = (\n                    ssh_address.split(\"@\")\n                    if \"@\" in ssh_address\n                    else [\"\", ssh_address]\n                )\n                # validate ip specified port\n                assert check_open_port(\n                    ssh_ip, port=int(ssh_port)\n                ), \"[NetGear:ERROR] :: Host `{}` is not available for SSH Tunneling at port-{}!\".format(\n                    ssh_address, ssh_port\n                )\n\n    # Handle multiple exclusive modes if enabled\n    if self.__multiclient_mode and self.__multiserver_mode:\n        raise ValueError(\n            \"[NetGear:ERROR] :: Multi-Client and Multi-Server Mode cannot be enabled simultaneously!\"\n        )\n    elif self.__multiserver_mode or self.__multiclient_mode:\n        # check if Bidirectional Mode also enabled\n        if self.__bi_mode:\n            # log it\n            self.__logging and logger.debug(\n                \"Bidirectional Data Transmission is also enabled for this connection!\"\n            )\n        # check if SSH Tunneling Mode also enabled\n        if self.__ssh_tunnel_mode:\n            # raise error\n            raise ValueError(\n                \"[NetGear:ERROR] :: SSH Tunneling and {} Mode cannot be enabled simultaneously. Kindly refer docs!\".format(\n                    \"Multi-Server\" if self.__multiserver_mode else \"Multi-Client\"\n                )\n            )\n    elif self.__bi_mode:\n        # log Bidirectional mode activation\n        self.__logging and logger.debug(\n            \"Bidirectional Data Transmission is enabled for this connection!\"\n        )\n    elif self.__ssh_tunnel_mode:\n        # log Bidirectional mode activation\n        self.__logging and logger.debug(\n            \"SSH Tunneling is enabled for host:`{}` with `{}` back-end.\".format(\n                self.__ssh_tunnel_mode,\n                \"paramiko\" if self.__paramiko_present else \"pexpect\",\n            )\n        )\n\n    # On Windows, NetGear requires the ``WindowsSelectorEventLoop`` but Python 3.8 and above,\n    # defaults to an ``ProactorEventLoop`` loop that is not compatible with it. Thereby,\n    # we had to set it manually.\n    platform.system() == \"Windows\" and asyncio.set_event_loop_policy(\n        asyncio.WindowsSelectorEventLoopPolicy()\n    )\n\n    # define ZMQ messaging context instance\n    self.__msg_context = zmq.Context.instance()\n\n    # initialize and assign receive mode to global variable\n    self.__receive_mode = receive_mode\n\n    # Handle Secure mode\n    if self.__secure_mode > 0:\n        # activate and log if overwriting is enabled\n        if receive_mode:\n            overwrite_cert = False\n            overwrite_cert and logger.warning(\n                \"Overwriting ZMQ Authentication certificates is disabled for Client's end!\"\n            )\n        else:\n            overwrite_cert and self.__logging and logger.info(\n                \"Overwriting ZMQ Authentication certificates over previous ones!\"\n            )\n\n        # Validate certificate generation paths\n        # Start threaded authenticator for this context\n        try:\n            # check if custom certificates path is specified\n            if custom_cert_location:\n                (\n                    auth_cert_dir,\n                    self.__auth_secretkeys_dir,\n                    self.__auth_publickeys_dir,\n                ) = generate_auth_certificates(\n                    custom_cert_location, overwrite=overwrite_cert, logging=logging\n                )\n            else:\n                # otherwise auto-generate suitable path\n                (\n                    auth_cert_dir,\n                    self.__auth_secretkeys_dir,\n                    self.__auth_publickeys_dir,\n                ) = generate_auth_certificates(\n                    os.path.join(expanduser(\"~\"), \".vidgear\"),\n                    overwrite=overwrite_cert,\n                    logging=logging,\n                )\n            # log it\n            self.__logging and logger.debug(\n                \"`{}` is the default location for storing ZMQ authentication certificates/keys.\".format(\n                    auth_cert_dir\n                )\n            )\n\n            # start an authenticator for this context\n            self.__z_auth = ThreadAuthenticator(self.__msg_context)\n            self.__z_auth.start()\n            self.__z_auth.allow(str(address))  # allow current address\n\n            # check if `IronHouse` is activated\n            if self.__secure_mode == 2:\n                # tell authenticator to use the certificate from given valid dir\n                self.__z_auth.configure_curve(\n                    domain=\"*\", location=self.__auth_publickeys_dir\n                )\n            else:\n                # otherwise tell the authenticator how to handle the CURVE requests, if `StoneHouse` is activated\n                self.__z_auth.configure_curve(\n                    domain=\"*\", location=auth.CURVE_ALLOW_ANY\n                )\n        except zmq.ZMQError as e:\n            if \"Address in use\" in str(e):\n                logger.info(\"ZMQ Authenticator already running.\")\n            else:\n                # catch if any error occurred and disable Secure mode\n                logger.exception(str(e))\n                self.__secure_mode = 0\n                logger.error(\n                    \"ZMQ Security Mechanism is disabled for this connection due to errors!\"\n                )\n\n    # check whether `receive_mode` is enabled\n    if self.__receive_mode:\n        # define connection address\n        address = \"*\" if address is None else address\n\n        # check if multiserver_mode is enabled\n        if self.__multiserver_mode:\n            # check if unique server port address list/tuple is assigned or not in multiserver_mode\n            if port is None or not isinstance(port, (tuple, list)):\n                # raise error if not\n                raise ValueError(\n                    \"[NetGear:ERROR] :: Incorrect port value! Kindly provide a list/tuple of Server ports while Multi-Server mode is enabled. For more information refer VidGear docs.\"\n                )\n            else:\n                # otherwise log it\n                logger.debug(\n                    \"Enabling Multi-Server Mode at PORTS: {}!\".format(port)\n                )\n            # create port address buffer for keeping track of connected client's port(s)\n            self.__port_buffer = []\n        # check if multiclient_mode is enabled\n        elif self.__multiclient_mode:\n            # check if unique server port address is assigned or not in multiclient_mode\n            if port is None:\n                # raise error if not\n                raise ValueError(\n                    \"[NetGear:ERROR] :: Kindly provide a unique & valid port value at Client-end. For more information refer VidGear docs.\"\n                )\n            else:\n                # otherwise log it\n                logger.debug(\n                    \"Enabling Multi-Client Mode at PORT: {} on this device!\".format(\n                        port\n                    )\n                )\n            # assign value to global variable\n            self.__port = port\n        else:\n            # otherwise assign local port address if None\n            port = \"5555\" if port is None else port\n\n        try:\n            # define thread-safe messaging socket\n            self.__msg_socket = self.__msg_context.socket(msg_pattern[1])\n\n            # define pub-sub flag\n            self.__pattern == 2 and self.__msg_socket.set_hwm(1)\n\n            # enable specified secure mode for the socket\n            if self.__secure_mode > 0:\n                # load server key\n                server_secret_file = os.path.join(\n                    self.__auth_secretkeys_dir, \"server.key_secret\"\n                )\n                server_public, server_secret = auth.load_certificate(\n                    server_secret_file\n                )\n                # load  all CURVE keys\n                self.__msg_socket.curve_secretkey = server_secret\n                self.__msg_socket.curve_publickey = server_public\n                # enable CURVE connection for this socket\n                self.__msg_socket.curve_server = True\n\n            # define exclusive socket options for `patterns=2`\n            if self.__pattern == 2:\n                self.__msg_socket.setsockopt_string(zmq.SUBSCRIBE, \"\")\n                self.__subscriber_timeout and self.__msg_socket.setsockopt(\n                    zmq.RCVTIMEO, self.__subscriber_timeout\n                )\n                self.__subscriber_timeout and self.__msg_socket.setsockopt(\n                    zmq.LINGER, 0\n                )\n\n            # if multiserver_mode is enabled, then assign port addresses to zmq socket\n            if self.__multiserver_mode:\n                # bind socket to given server protocol, address and ports\n                for pt in port:\n                    self.__msg_socket.bind(\n                        protocol + \"://\" + str(address) + \":\" + str(pt)\n                    )\n            else:\n                # bind socket to given protocol, address and port normally\n                self.__msg_socket.bind(\n                    protocol + \"://\" + str(address) + \":\" + str(port)\n                )\n\n            # additional settings\n            if pattern < 2:\n                if self.__multiserver_mode:\n                    self.__connection_address = []\n                    for pt in port:\n                        self.__connection_address.append(\n                            protocol + \"://\" + str(address) + \":\" + str(pt)\n                        )\n                else:\n                    self.__connection_address = (\n                        protocol + \"://\" + str(address) + \":\" + str(port)\n                    )\n                self.__msg_pattern = msg_pattern[1]\n                self.__poll.register(self.__msg_socket, zmq.POLLIN)\n                self.__logging and logger.debug(\n                    \"Reliable transmission is enabled for this pattern with max-retries: {} and timeout: {} secs.\".format(\n                        self.__max_retries, self.__request_timeout / 1000\n                    )\n                )\n            else:\n                self.__logging and self.__subscriber_timeout and logger.debug(\n                    \"Timeout: {} secs is enabled for this system.\".format(\n                        self.__subscriber_timeout / 1000\n                    )\n                )\n\n        except Exception as e:\n            # otherwise log and raise error\n            logger.exception(str(e))\n            # Handle Secure Mode\n            self.__secure_mode and logger.critical(\n                \"Failed to activate Secure Mode: `{}` for this connection!\".format(\n                    valid_security_mech[self.__secure_mode]\n                )\n            )\n            # raise errors for exclusive modes\n            if self.__multiserver_mode or self.__multiclient_mode:\n                raise RuntimeError(\n                    \"[NetGear:ERROR] :: Receive Mode failed to activate {} Mode at address: {} with pattern: {}! Kindly recheck all parameters.\".format(\n                        (\n                            \"Multi-Server\"\n                            if self.__multiserver_mode\n                            else \"Multi-Client\"\n                        ),\n                        (protocol + \"://\" + str(address) + \":\" + str(port)),\n                        pattern,\n                    )\n                )\n            else:\n                self.__bi_mode and logger.critical(\n                    \"Failed to activate Bidirectional Mode for this connection!\"\n                )\n                raise RuntimeError(\n                    \"[NetGear:ERROR] :: Receive Mode failed to bind address: {} and pattern: {}! Kindly recheck all parameters.\".format(\n                        (protocol + \"://\" + str(address) + \":\" + str(port)), pattern\n                    )\n                )\n\n        # Handle threaded queue mode\n        self.__logging and logger.debug(\n            \"Threaded Queue Mode is enabled by default for this connection.\"\n        )\n\n        # define deque and assign it to global var\n        self.__queue = deque(maxlen=96)  # max len 96 to check overflow\n\n        # initialize and start threaded recv_handler\n        self.__thread = Thread(target=self.__recv_handler, name=\"NetGear\", args=())\n        self.__thread.daemon = True\n        self.__thread.start()\n\n        if self.__logging:\n            # finally log progress\n            logger.debug(\n                \"Successfully Binded to address: {} with pattern: {}.\".format(\n                    (protocol + \"://\" + str(address) + \":\" + str(port)), pattern\n                )\n            )\n            self.__jpeg_compression and logger.debug(\n                \"JPEG Frame-Compression is activated for this connection with Colorspace:`{}`, Quality:`{}`%, Fastdct:`{}`, and Fastupsample:`{}`.\".format(\n                    self.__jpeg_compression_colorspace,\n                    self.__jpeg_compression_quality,\n                    (\"enabled\" if self.__jpeg_compression_fastdct else \"disabled\"),\n                    (\n                        \"enabled\"\n                        if self.__jpeg_compression_fastupsample\n                        else \"disabled\"\n                    ),\n                )\n            )\n            self.__secure_mode and logger.debug(\n                \"Successfully enabled ZMQ Security Mechanism: `{}` for this connection.\".format(\n                    valid_security_mech[self.__secure_mode]\n                )\n            )\n            logger.debug(\"Multi-threaded Receive Mode is successfully enabled.\")\n            logger.debug(\"Unique System ID is {}.\".format(self.__id))\n            logger.debug(\"Receive Mode is now activated.\")\n\n    else:\n        # otherwise default to `Send Mode`\n        # define connection address\n        address = \"localhost\" if address is None else address\n\n        # check if multiserver_mode is enabled\n        if self.__multiserver_mode:\n            # check if unique server port address is assigned or not in multiserver_mode\n            if port is None:\n                # raise error if not\n                raise ValueError(\n                    \"[NetGear:ERROR] :: Kindly provide a unique & valid port value at Server-end. For more information refer VidGear docs.\"\n                )\n            else:\n                # otherwise log it\n                logger.debug(\n                    \"Enabling Multi-Server Mode at PORT: {} on this device!\".format(\n                        port\n                    )\n                )\n            # assign value to global variable\n            self.__port = port\n        # check if multiclient_mode is enabled\n        elif self.__multiclient_mode:\n            # check if unique client port address list/tuple is assigned or not in multiclient_mode\n            if port is None or not isinstance(port, (tuple, list)):\n                # raise error if not\n                raise ValueError(\n                    \"[NetGear:ERROR] :: Incorrect port value! Kindly provide a list/tuple of Client ports while Multi-Client mode is enabled. For more information refer VidGear docs.\"\n                )\n            else:\n                # otherwise log it\n                logger.debug(\n                    \"Enabling Multi-Client Mode at PORTS: {}!\".format(port)\n                )\n            # create port address buffer for keeping track of connected client ports\n            self.__port_buffer = []\n        else:\n            # otherwise assign local port address if None\n            port = \"5555\" if port is None else port\n\n        try:\n            # define thread-safe messaging socket\n            self.__msg_socket = self.__msg_context.socket(msg_pattern[0])\n\n            # if req/rep pattern, define additional flags\n            if self.__pattern == 1:\n                self.__msg_socket.REQ_RELAXED = True\n                self.__msg_socket.REQ_CORRELATE = True\n\n            # if pub/sub pattern, define additional optimizer\n            if self.__pattern == 2:\n                self.__msg_socket.set_hwm(1)\n\n            # enable specified secure mode for the socket\n            if self.__secure_mode > 0:\n                # load client key\n                client_secret_file = os.path.join(\n                    self.__auth_secretkeys_dir, \"client.key_secret\"\n                )\n                client_public, client_secret = auth.load_certificate(\n                    client_secret_file\n                )\n                # load  all CURVE keys\n                self.__msg_socket.curve_secretkey = client_secret\n                self.__msg_socket.curve_publickey = client_public\n                # load server key\n                server_public_file = os.path.join(\n                    self.__auth_publickeys_dir, \"server.key\"\n                )\n                server_public, _ = auth.load_certificate(server_public_file)\n                # inject public key to make a CURVE connection.\n                self.__msg_socket.curve_serverkey = server_public\n\n            # check if multi-client_mode is enabled\n            if self.__multiclient_mode:\n                # bind socket to given server protocol, address and ports\n                for pt in port:\n                    self.__msg_socket.connect(\n                        protocol + \"://\" + str(address) + \":\" + str(pt)\n                    )\n            else:\n                # handle SSH tunneling if enabled\n                if self.__ssh_tunnel_mode:\n                    # establish tunnel connection\n                    ssh.tunnel_connection(\n                        self.__msg_socket,\n                        protocol + \"://\" + str(address) + \":\" + str(port),\n                        self.__ssh_tunnel_mode,\n                        keyfile=self.__ssh_tunnel_keyfile,\n                        password=self.__ssh_tunnel_pwd,\n                        paramiko=self.__paramiko_present,\n                    )\n                else:\n                    # connect socket to given protocol, address and port\n                    self.__msg_socket.connect(\n                        protocol + \"://\" + str(address) + \":\" + str(port)\n                    )\n\n            # additional settings\n            if pattern < 2:\n                if self.__multiclient_mode:\n                    self.__connection_address = []\n                    for pt in port:\n                        self.__connection_address.append(\n                            protocol + \"://\" + str(address) + \":\" + str(pt)\n                        )\n                else:\n                    self.__connection_address = (\n                        protocol + \"://\" + str(address) + \":\" + str(port)\n                    )\n                self.__msg_pattern = msg_pattern[0]\n                self.__poll.register(self.__msg_socket, zmq.POLLIN)\n\n                self.__logging and logger.debug(\n                    \"Reliable transmission is enabled for this pattern with max-retries: {} and timeout: {} secs.\".format(\n                        self.__max_retries, self.__request_timeout / 1000\n                    )\n                )\n\n        except Exception as e:\n            # otherwise log and raise error\n            logger.exception(str(e))\n            # Handle Secure Mode\n            self.__secure_mode and logger.critical(\n                \"Failed to activate Secure Mode: `{}` for this connection!\".format(\n                    valid_security_mech[self.__secure_mode]\n                )\n            )\n            # raise errors for exclusive modes\n            if self.__multiserver_mode or self.__multiclient_mode:\n                raise RuntimeError(\n                    \"[NetGear:ERROR] :: Send Mode failed to activate {} Mode at address: {} with pattern: {}! Kindly recheck all parameters.\".format(\n                        (\n                            \"Multi-Server\"\n                            if self.__multiserver_mode\n                            else \"Multi-Client\"\n                        ),\n                        (protocol + \"://\" + str(address) + \":\" + str(port)),\n                        pattern,\n                    )\n                )\n            else:\n                self.__bi_mode and logger.critical(\n                    \"Failed to activate Bidirectional Mode for this connection!\"\n                )\n                self.__ssh_tunnel_mode and logger.critical(\n                    \"Failed to initiate SSH Tunneling Mode for this server with `{}` back-end!\".format(\n                        \"paramiko\" if self.__paramiko_present else \"pexpect\"\n                    )\n                )\n                raise RuntimeError(\n                    \"[NetGear:ERROR] :: Send Mode failed to connect address: {} and pattern: {}! Kindly recheck all parameters.\".format(\n                        (protocol + \"://\" + str(address) + \":\" + str(port)), pattern\n                    )\n                )\n\n        if self.__logging:\n            # finally log progress\n            logger.debug(\n                \"Successfully connected to address: {} with pattern: {}.\".format(\n                    (protocol + \"://\" + str(address) + \":\" + str(port)), pattern\n                )\n            )\n            self.__jpeg_compression and logger.debug(\n                \"JPEG Frame-Compression is activated for this connection with Colorspace:`{}`, Quality:`{}`%, Fastdct:`{}`, and Fastupsample:`{}`.\".format(\n                    self.__jpeg_compression_colorspace,\n                    self.__jpeg_compression_quality,\n                    (\"enabled\" if self.__jpeg_compression_fastdct else \"disabled\"),\n                    (\n                        \"enabled\"\n                        if self.__jpeg_compression_fastupsample\n                        else \"disabled\"\n                    ),\n                )\n            )\n            self.__secure_mode and logger.debug(\n                \"Enabled ZMQ Security Mechanism: `{}` for this connection.\".format(\n                    valid_security_mech[self.__secure_mode]\n                )\n            )\n            logger.debug(\"Unique System ID is {}.\".format(self.__id))\n            logger.debug(\n                \"Send Mode is successfully activated and ready to send data.\"\n            )\n
"},{"location":"bonus/reference/netgear/#vidgear.gears.netgear.NetGear.close","title":"close(self, kill=False)","text":"

Safely terminates the threads, and NetGear resources.

Parameters:

Name Type Description Default kill bool

Kills ZMQ context instead of graceful exiting in receive mode.

False Source code in vidgear/gears/netgear.py
def close(self, kill=False):\n    \"\"\"\n    Safely terminates the threads, and NetGear resources.\n\n    Parameters:\n        kill (bool): Kills ZMQ context instead of graceful exiting in receive mode.\n    \"\"\"\n    # log it\n    self.__logging and logger.debug(\n        \"Terminating various {} Processes.\".format(\n            \"Receive Mode\" if self.__receive_mode else \"Send Mode\"\n        )\n    )\n    #  whether `receive_mode` is enabled or not\n    if self.__receive_mode:\n        # check whether queue mode is empty\n        if not (self.__queue is None) and self.__queue:\n            self.__queue.clear()\n        # call immediate termination\n        self.__terminate = True\n        # properly close the socket\n        self.__logging and logger.debug(\"Terminating. Please wait...\")\n        # Handle Secure Mode Thread\n        if self.__z_auth:\n            self.__logging and logger.debug(\"Terminating Authenticator Thread.\")\n            self.__z_auth.stop()\n            while self.__z_auth.is_alive():\n                pass\n        # wait until stream resources are released\n        # (producer thread might be still grabbing frame)\n        if self.__thread is not None:\n            self.__logging and logger.debug(\"Terminating Main Thread.\")\n            # properly handle thread exit\n            if self.__thread.is_alive() and kill:\n                # force close if still alive\n                logger.warning(\"Thread still running...Killing it forcefully!\")\n                self.__msg_context.destroy()\n                self.__thread.join()\n            else:\n                self.__msg_socket.close(linger=0)\n                self.__thread.join()\n            self.__thread = None\n        self.__logging and logger.debug(\"Terminated Successfully!\")\n    else:\n        # indicate that process should be terminated\n        self.__terminate = True\n        # log if kill enabled\n        kill and logger.warning(\n            \"`kill` parmeter is only available in the receive mode.\"\n        )\n        # Handle Secure Mode Thread\n        if self.__z_auth:\n            self.__logging and logger.debug(\"Terminating Authenticator Thread.\")\n            self.__z_auth.stop()\n            while self.__z_auth.is_alive():\n                pass\n        # check if all attempts of reconnecting failed, then skip to closure\n        if (self.__pattern < 2 and not self.__max_retries) or (\n            self.__multiclient_mode and not self.__port_buffer\n        ):\n            try:\n                # properly close the socket\n                self.__msg_socket.setsockopt(zmq.LINGER, 0)\n                self.__msg_socket.close()\n            except ZMQError:\n                pass\n            finally:\n                # exit\n                return\n\n        if self.__multiserver_mode:\n            # check if multiserver_mode\n            # send termination flag to client with its unique port\n            term_dict = dict(terminate_flag=True, port=self.__port)\n        else:\n            # otherwise send termination flag to client\n            term_dict = dict(terminate_flag=True)\n\n        try:\n            if self.__multiclient_mode:\n                for _ in self.__port_buffer:\n                    self.__msg_socket.send_json(term_dict)\n            else:\n                self.__msg_socket.send_json(term_dict)\n\n            # check for confirmation if available within 1/5 timeout\n            if self.__pattern < 2:\n                self.__logging and logger.debug(\"Terminating. Please wait...\")\n                if self.__msg_socket.poll(self.__request_timeout // 5, zmq.POLLIN):\n                    self.__msg_socket.recv()\n        except Exception as e:\n            if not isinstance(e, ZMQError):\n                logger.exception(str(e))\n        finally:\n            # properly close the socket\n            self.__msg_socket.setsockopt(zmq.LINGER, 0)\n            self.__msg_socket.close()\n            self.__logging and logger.debug(\"Terminated Successfully!\")\n
"},{"location":"bonus/reference/netgear/#vidgear.gears.netgear.NetGear.recv","title":"recv(self, return_data=None)","text":"

A Receiver end method, that extracts received frames synchronously from monitored deque, while maintaining a fixed-length frame buffer in the memory, and blocks the thread if the deque is full.

Parameters:

Name Type Description Default return_data any

inputs return data (of any datatype), for sending back to Server.

None

Returns: A n-dimensional numpy array.

Source code in vidgear/gears/netgear.py
def recv(self, return_data=None):\n    \"\"\"\n    A Receiver end method, that extracts received frames synchronously from monitored deque, while maintaining a\n    fixed-length frame buffer in the memory, and blocks the thread if the deque is full.\n\n    Parameters:\n        return_data (any): inputs return data _(of any datatype)_, for sending back to Server.\n\n    **Returns:** A n-dimensional numpy array.\n    \"\"\"\n    # check whether `receive mode` is activated\n    if not (self.__receive_mode):\n        # raise value error and exit\n        self.__terminate = True\n        raise ValueError(\n            \"[NetGear:ERROR] :: `recv()` function cannot be used while receive_mode is disabled. Kindly refer vidgear docs!\"\n        )\n\n    # handle Bidirectional return data\n    if (self.__bi_mode or self.__multiclient_mode) and not (return_data is None):\n        self.__return_data = return_data\n\n    # check whether or not termination flag is enabled\n    while not self.__terminate:\n        try:\n            # check if queue is empty\n            if len(self.__queue) > 0:\n                return self.__queue.popleft()\n            else:\n                time.sleep(0.00001)\n                continue\n        except KeyboardInterrupt:\n            self.__terminate = True\n            break\n    # otherwise return NoneType\n    return None\n
"},{"location":"bonus/reference/netgear/#vidgear.gears.netgear.NetGear.send","title":"send(self, frame, message=None)","text":"

A Server end method, that sends the data and frames over the network to Client(s).

Parameters:

Name Type Description Default frame numpy.ndarray

inputs numpy array(frame).

required message any

input for sending additional data (of any datatype except numpy.ndarray) to Client(s).

None

Returns: Data (of any datatype) in selected exclusive modes, otherwise None-type.

Source code in vidgear/gears/netgear.py
def send(self, frame, message=None):\n    \"\"\"\n    A Server end method, that sends the data and frames over the network to Client(s).\n\n    Parameters:\n        frame (numpy.ndarray): inputs numpy array(frame).\n        message (any): input for sending additional data _(of any datatype except `numpy.ndarray`)_ to Client(s).\n\n    **Returns:** Data _(of any datatype)_ in selected exclusive modes, otherwise None-type.\n\n    \"\"\"\n    # check whether `receive_mode` is disabled\n    if self.__receive_mode:\n        # raise value error and exit\n        self.__terminate = True\n        raise ValueError(\n            \"[NetGear:ERROR] :: `send()` function cannot be used while receive_mode is enabled. Kindly refer vidgear docs!\"\n        )\n\n    if not (message is None) and isinstance(message, np.ndarray):\n        logger.warning(\n            \"Skipped unsupported `message` of datatype: {}!\".format(\n                type(message).__name__\n            )\n        )\n        message = None\n\n    # define exit_flag and assign value\n    exit_flag = True if (frame is None or self.__terminate) else False\n\n    # check whether exit_flag is False\n    if not (exit_flag) and not (frame.flags[\"C_CONTIGUOUS\"]):\n        # check whether the incoming frame is contiguous\n        frame = np.ascontiguousarray(frame, dtype=frame.dtype)\n\n    # handle JPEG compression encoding\n    if self.__jpeg_compression:\n        if self.__jpeg_compression_colorspace == \"GRAY\":\n            if frame.ndim == 2:\n                # patch for https://gitlab.com/jfolz/simplejpeg/-/issues/11\n                frame = np.expand_dims(frame, axis=2)\n            frame = simplejpeg.encode_jpeg(\n                frame,\n                quality=self.__jpeg_compression_quality,\n                colorspace=self.__jpeg_compression_colorspace,\n                fastdct=self.__jpeg_compression_fastdct,\n            )\n        else:\n            frame = simplejpeg.encode_jpeg(\n                frame,\n                quality=self.__jpeg_compression_quality,\n                colorspace=self.__jpeg_compression_colorspace,\n                colorsubsampling=\"422\",\n                fastdct=self.__jpeg_compression_fastdct,\n            )\n\n    # check if multiserver_mode is activated and assign values with unique port\n    msg_dict = dict(port=self.__port) if self.__multiserver_mode else dict()\n\n    # prepare the exclusive json dict\n    msg_dict.update(\n        dict(\n            terminate_flag=exit_flag,\n            compression=(\n                {\n                    \"dct\": self.__jpeg_compression_fastdct,\n                    \"ups\": self.__jpeg_compression_fastupsample,\n                    \"colorspace\": self.__jpeg_compression_colorspace,\n                }\n                if self.__jpeg_compression\n                else False\n            ),\n            message=message,\n            pattern=str(self.__pattern),\n            dtype=str(frame.dtype) if not (self.__jpeg_compression) else \"\",\n            shape=frame.shape if not (self.__jpeg_compression) else \"\",\n        )\n    )\n\n    # send the json dict\n    self.__msg_socket.send_json(msg_dict, self.__msg_flag | zmq.SNDMORE)\n    # send the frame array with correct flags\n    self.__msg_socket.send(\n        frame, flags=self.__msg_flag, copy=self.__msg_copy, track=self.__msg_track\n    )\n\n    # check if synchronous patterns, then wait for confirmation\n    if self.__pattern < 2:\n        # check if Bidirectional data transmission is enabled\n        if self.__bi_mode or self.__multiclient_mode:\n            # handles return data\n            recvd_data = None\n\n            socks = dict(self.__poll.poll(self.__request_timeout))\n            if socks.get(self.__msg_socket) == zmq.POLLIN:\n                # handle return data\n                recv_json = self.__msg_socket.recv_json(flags=self.__msg_flag)\n            else:\n                logger.critical(\"No response from Client, Reconnecting again...\")\n                # Socket is confused. Close and remove it.\n                self.__msg_socket.setsockopt(zmq.LINGER, 0)\n                self.__msg_socket.close()\n                self.__poll.unregister(self.__msg_socket)\n                self.__max_retries -= 1\n\n                if not (self.__max_retries):\n                    if self.__multiclient_mode:\n                        logger.error(\n                            \"All Clients failed to respond on multiple attempts.\"\n                        )\n                    else:\n                        logger.error(\n                            \"Client failed to respond on multiple attempts.\"\n                        )\n                    self.__terminate = True\n                    raise RuntimeError(\n                        \"[NetGear:ERROR] :: Client(s) seems to be offline, Abandoning.\"\n                    )\n\n                # Create new connection\n                self.__msg_socket = self.__msg_context.socket(self.__msg_pattern)\n                if isinstance(self.__connection_address, list):\n                    for _connection in self.__connection_address:\n                        self.__msg_socket.connect(_connection)\n                else:\n                    # handle SSH tunneling if enabled\n                    if self.__ssh_tunnel_mode:\n                        # establish tunnel connection\n                        ssh.tunnel_connection(\n                            self.__msg_socket,\n                            self.__connection_address,\n                            self.__ssh_tunnel_mode,\n                            keyfile=self.__ssh_tunnel_keyfile,\n                            password=self.__ssh_tunnel_pwd,\n                            paramiko=self.__paramiko_present,\n                        )\n                    else:\n                        # connect normally\n                        self.__msg_socket.connect(self.__connection_address)\n                self.__poll.register(self.__msg_socket, zmq.POLLIN)\n                # return None for mean-time\n                return None\n\n            # save the unique port addresses\n            if (\n                self.__multiclient_mode\n                and not recv_json[\"port\"] in self.__port_buffer\n            ):\n                self.__port_buffer.append(recv_json[\"port\"])\n\n            if recv_json[\"return_type\"] == \"ndarray\":\n                recv_array = self.__msg_socket.recv(\n                    flags=self.__msg_flag,\n                    copy=self.__msg_copy,\n                    track=self.__msg_track,\n                )\n                # check if encoding was enabled\n                if recv_json[\"compression\"]:\n                    # decode JPEG frame\n                    recvd_data = simplejpeg.decode_jpeg(\n                        recv_array,\n                        colorspace=recv_json[\"compression\"][\"colorspace\"],\n                        fastdct=self.__jpeg_compression_fastdct\n                        or recv_json[\"compression\"][\"dct\"],\n                        fastupsample=self.__jpeg_compression_fastupsample\n                        or recv_json[\"compression\"][\"ups\"],\n                    )\n                    # check if valid frame returned\n                    if recvd_data is None:\n                        self.__terminate = True\n                        # otherwise raise error and exit\n                        raise RuntimeError(\n                            \"[NetGear:ERROR] :: Received compressed frame `{}` decoding failed with flag: {}.\".format(\n                                recv_json[\"compression\"],\n                                self.__ex_compression_params,\n                            )\n                        )\n\n                    if (\n                        recv_json[\"compression\"][\"colorspace\"] == \"GRAY\"\n                        and recvd_data.ndim == 3\n                    ):\n                        # patch for https://gitlab.com/jfolz/simplejpeg/-/issues/11\n                        recvd_data = np.squeeze(recvd_data, axis=2)\n                else:\n                    recvd_data = np.frombuffer(\n                        recv_array, dtype=recv_json[\"array_dtype\"]\n                    ).reshape(recv_json[\"array_shape\"])\n            else:\n                recvd_data = recv_json[\"data\"]\n\n            return (\n                (recv_json[\"port\"], recvd_data)\n                if self.__multiclient_mode\n                else recvd_data\n            )\n        else:\n            # otherwise log normally\n            socks = dict(self.__poll.poll(self.__request_timeout))\n            if socks.get(self.__msg_socket) == zmq.POLLIN:\n                recv_confirmation = self.__msg_socket.recv()\n            else:\n                logger.critical(\"No response from Client, Reconnecting again...\")\n                # Socket is confused. Close and remove it.\n                self.__msg_socket.setsockopt(zmq.LINGER, 0)\n                self.__msg_socket.close()\n                self.__poll.unregister(self.__msg_socket)\n                self.__max_retries -= 1\n\n                if not (self.__max_retries):\n                    logger.error(\"Client failed to respond on repeated attempts.\")\n                    self.__terminate = True\n                    raise RuntimeError(\n                        \"[NetGear:ERROR] :: Client seems to be offline, Abandoning!\"\n                    )\n\n                # Create new connection\n                self.__msg_socket = self.__msg_context.socket(self.__msg_pattern)\n                # handle SSH tunneling if enabled\n                if self.__ssh_tunnel_mode:\n                    # establish tunnel connection\n                    ssh.tunnel_connection(\n                        self.__msg_socket,\n                        self.__connection_address,\n                        self.__ssh_tunnel_mode,\n                        keyfile=self.__ssh_tunnel_keyfile,\n                        password=self.__ssh_tunnel_pwd,\n                        paramiko=self.__paramiko_present,\n                    )\n                else:\n                    # connect normally\n                    self.__msg_socket.connect(self.__connection_address)\n                self.__poll.register(self.__msg_socket, zmq.POLLIN)\n                return None\n\n            # log confirmation\n            self.__logging and logger.debug(recv_confirmation)\n
"},{"location":"bonus/reference/netgear_async/","title":"NetGear_Async API References","text":"

NetGear_Async API usage examples can be found here \u27b6

NetGear_Async API parameters are explained here \u27b6

NetGear_Async can generate the same performance as NetGear API at about one-third the memory consumption, and also provide complete server-client handling with various options to use variable protocols/patterns similar to NetGear, but lacks in term of flexibility as it supports only a few NetGear's Exclusive Modes.

NetGear_Async is built on zmq.asyncio, and powered by a high-performance asyncio event loop called uvloop to achieve unwatchable high-speed and lag-free video streaming over the network with minimal resource constraints. NetGear_Async can transfer thousands of frames in just a few seconds without causing any significant load on your system.

NetGear_Async provides complete server-client handling and options to use variable protocols/patterns similar to NetGear API. Furthermore, NetGear_Async allows us to define our custom Server as source to transform frames easily before sending them across the network.

NetGear_Async now supports additional bidirectional data transmission between receiver(client) and sender(server) while transferring frames. Users can easily build complex applications such as like Real-Time Video Chat in just few lines of code.

In addition to all this, NetGear_Async API also provides internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs, thereby granting it exclusive power for transferring frames incoming from any source to the network.

NetGear_Async as of now supports four ZeroMQ messaging patterns:

Whereas supported protocol are: tcp and ipc.

Source code in vidgear/gears/asyncio/netgear_async.py
class NetGear_Async:\n    \"\"\"\n    NetGear_Async can generate the same performance as NetGear API at about one-third the memory consumption, and also provide complete server-client handling with various\n    options to use variable protocols/patterns similar to NetGear, but lacks in term of flexibility as it supports only a few NetGear's Exclusive Modes.\n\n    NetGear_Async is built on `zmq.asyncio`, and powered by a high-performance asyncio event loop called uvloop to achieve unwatchable high-speed and lag-free video streaming\n    over the network with minimal resource constraints. NetGear_Async can transfer thousands of frames in just a few seconds without causing any significant load on your\n    system.\n\n    NetGear_Async provides complete server-client handling and options to use variable protocols/patterns similar to NetGear API. Furthermore, NetGear_Async allows us to define\n     our custom Server as source to transform frames easily before sending them across the network.\n\n    NetGear_Async now supports additional **bidirectional data transmission** between receiver(client) and sender(server) while transferring frames.\n    Users can easily build complex applications such as like _Real-Time Video Chat_ in just few lines of code.\n\n    In addition to all this, NetGear_Async API also provides internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs, thereby\n    granting it exclusive power for transferring frames incoming from any source to the network.\n\n    NetGear_Async as of now supports four ZeroMQ messaging patterns:\n\n    - `zmq.PAIR` _(ZMQ Pair Pattern)_\n    - `zmq.REQ/zmq.REP` _(ZMQ Request/Reply Pattern)_\n    - `zmq.PUB/zmq.SUB` _(ZMQ Publish/Subscribe Pattern)_\n    - `zmq.PUSH/zmq.PULL` _(ZMQ Push/Pull Pattern)_\n\n    Whereas supported protocol are: `tcp` and `ipc`.\n    \"\"\"\n\n    def __init__(\n        self,\n        # NetGear_Async parameters\n        address=None,\n        port=None,\n        protocol=\"tcp\",\n        pattern=0,\n        receive_mode=False,\n        timeout=0.0,\n        # Videogear parameters\n        enablePiCamera=False,\n        stabilize=False,\n        source=None,\n        camera_num=0,\n        stream_mode=False,\n        backend=0,\n        colorspace=None,\n        resolution=(640, 480),\n        framerate=25,\n        time_delay=0,\n        # common parameters\n        logging=False,\n        **options\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the NetGear_Async class.\n\n        Parameters:\n            address (str): sets the valid network address of the Server/Client.\n            port (str): sets the valid Network Port of the Server/Client.\n            protocol (str): sets the valid messaging protocol between Server/Client.\n            pattern (int): sets the supported messaging pattern(flow of communication) between Server/Client\n            receive_mode (bool): select the NetGear_Async's Mode of operation.\n            timeout (int/float): controls the maximum waiting time(in sec) after which Client throws `TimeoutError`.\n            enablePiCamera (bool): provide access to PiGear(if True) or CamGear(if False) APIs respectively.\n            stabilize (bool): enable access to Stabilizer Class for stabilizing frames.\n            camera_num (int): selects the camera module index which will be used as Rpi source.\n            resolution (tuple): sets the resolution (i.e. `(width,height)`) of the Rpi source.\n            framerate (int/float): sets the framerate of the Rpi source.\n            source (based on input): defines the source for the input stream.\n            stream_mode (bool): controls the exclusive YouTube Mode.\n            backend (int): selects the backend for OpenCV's VideoCapture class.\n            colorspace (str): selects the colorspace of the input stream.\n            logging (bool): enables/disables logging.\n            time_delay (int): time delay (in sec) before start reading the frames.\n            options (dict): provides ability to alter Tweak Parameters of NetGear_Async, CamGear, PiGear & Stabilizer.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # raise error(s) for critical Class imports\n        import_dependency_safe(\n            \"zmq\" if zmq is None else \"\", min_version=\"4.0\", pkg_name=\"pyzmq\"\n        )\n        import_dependency_safe(\"msgpack\" if msgpack is None else \"\")\n        import_dependency_safe(\"msgpack_numpy\" if m is None else \"\")\n\n        # define valid messaging patterns => `0`: PAIR, `1`:(REQ, REP), `2`:(SUB, PUB), `3`:(PUSH, PULL)\n        valid_messaging_patterns = {\n            0: (zmq.PAIR, zmq.PAIR),\n            1: (zmq.REQ, zmq.REP),\n            2: (zmq.PUB, zmq.SUB),\n            3: (zmq.PUSH, zmq.PULL),\n        }\n\n        # check whether user-defined messaging pattern is valid\n        if isinstance(pattern, int) and pattern in valid_messaging_patterns:\n            # assign value\n            self.__msg_pattern = pattern\n            self.__pattern = valid_messaging_patterns[pattern]\n        else:\n            # otherwise default to 0:`zmq.PAIR`\n            self.__msg_pattern = 0\n            self.__pattern = valid_messaging_patterns[self.__msg_pattern]\n            self.__logging and logger.warning(\n                \"Invalid pattern {pattern}. Defaulting to `zmq.PAIR`!\".format(\n                    pattern=pattern\n                )\n            )\n\n        # check  whether user-defined messaging protocol is valid\n        if isinstance(protocol, str) and protocol in [\"tcp\", \"ipc\"]:\n            # assign value\n            self.__protocol = protocol\n        else:\n            # else default to `tcp` protocol\n            self.__protocol = \"tcp\"\n            self.__logging and logger.warning(\"Invalid protocol. Defaulting to `tcp`!\")\n\n        # initialize Termination flag\n        self.__terminate = False\n        # initialize and assign `Receive Mode`\n        self.__receive_mode = receive_mode\n        # initialize stream handler\n        self.__stream = None\n        # initialize Messaging Socket\n        self.__msg_socket = None\n        # initialize NetGear_Async's configuration dictionary\n        self.config = {}\n        # asyncio queue handler\n        self.__queue = None\n        # define Bidirectional mode\n        self.__bi_mode = False  # handles Bidirectional mode state\n\n        # assign timeout for Receiver end\n        if timeout and isinstance(timeout, (int, float)):\n            self.__timeout = float(timeout)\n        else:\n            self.__timeout = 15.0\n\n        # generate 8-digit random system id\n        self.__id = \"\".join(\n            secrets.choice(string.ascii_uppercase + string.digits) for i in range(8)\n        )\n\n        # Handle user-defined options dictionary values\n        # reformat dictionary\n        options = {str(k).strip(): v for k, v in options.items()}\n        # handle bidirectional mode\n        if \"bidirectional_mode\" in options:\n            value = options[\"bidirectional_mode\"]\n            # also check if pattern and source is valid\n            if isinstance(value, bool) and pattern < 2 and source is None:\n                # activate Bidirectional mode if specified\n                self.__bi_mode = value\n            else:\n                # otherwise disable it\n                self.__bi_mode = False\n                logger.warning(\"Bidirectional data transmission is disabled!\")\n            # handle errors and logging\n            if pattern >= 2:\n                # raise error\n                raise ValueError(\n                    \"[NetGear_Async:ERROR] :: `{}` pattern is not valid when Bidirectional Mode is enabled. Kindly refer Docs for more Information!\".format(\n                        pattern\n                    )\n                )\n            elif not (source is None):\n                raise ValueError(\n                    \"[NetGear_Async:ERROR] :: Custom source must be used when Bidirectional Mode is enabled. Kindly refer Docs for more Information!\".format(\n                        pattern\n                    )\n                )\n            elif isinstance(value, bool) and self.__logging:\n                # log Bidirectional mode activation\n                logger.debug(\n                    \"Bidirectional Data Transmission is {} for this connection!\".format(\n                        \"enabled\" if value else \"disabled\"\n                    )\n                )\n            else:\n                logger.error(\"`bidirectional_mode` value is invalid!\")\n            # clean\n            del options[\"bidirectional_mode\"]\n\n        # Setup and assign event loop policy\n        if platform.system() == \"Windows\":\n            # On Windows, VidGear requires the ``WindowsSelectorEventLoop``, but Python 3.8 and above,\n            # defaults to an ``ProactorEventLoop`` loop that is not compatible with it. Thereby,\n            # we had to set it manually.\n            asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())\n        else:\n            if not (uvloop is None):\n                # Latest uvloop eventloop is only available for UNIX machines.\n                asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())\n            else:\n                # log if not present\n                import_dependency_safe(\"uvloop\", error=\"log\")\n\n        # Retrieve event loop and assign it\n        try:\n            self.loop = asyncio.get_running_loop()\n        except RuntimeError:\n            # otherwise create one\n            logger.critical(\"No running event loop found. Creating a new one.\")\n            self.loop = asyncio.new_event_loop()\n\n        # log eventloop for debugging\n        self.__logging and logger.info(\n            \"Using ``{}`` event loop for this process.\".format(\n                self.loop.__class__.__name__\n            )\n        )\n\n        # define messaging asynchronous Context\n        self.__msg_context = zmq.asyncio.Context()\n\n        # check whether `Receive Mode` is enabled\n        if receive_mode:\n            # assign local IP address if None\n            if address is None:\n                self.__address = \"*\"  # define address\n            else:\n                self.__address = address\n            # assign default port address if None\n            if port is None:\n                self.__port = \"5555\"\n            else:\n                self.__port = port\n        else:\n            # Handle video source\n            if source is None:\n                self.config = {\"generator\": None}\n                self.__logging and logger.warning(\"Given source is of NoneType!\")\n            else:\n                # define stream with necessary params\n                self.__stream = VideoGear(\n                    enablePiCamera=enablePiCamera,\n                    stabilize=stabilize,\n                    source=source,\n                    camera_num=camera_num,\n                    stream_mode=stream_mode,\n                    backend=backend,\n                    colorspace=colorspace,\n                    resolution=resolution,\n                    framerate=framerate,\n                    logging=logging,\n                    time_delay=time_delay,\n                    **options\n                )\n                # define default frame generator in configuration\n                self.config = {\"generator\": self.__frame_generator()}\n            # assign local ip address if None\n            if address is None:\n                self.__address = \"localhost\"\n            else:\n                self.__address = address\n            # assign default port address if None\n            if port is None:\n                self.__port = \"5555\"\n            else:\n                self.__port = port\n            # add server task handler\n            self.task = None\n\n        # create asyncio queue if bidirectional mode activated\n        self.__queue = asyncio.Queue() if self.__bi_mode else None\n\n    def launch(self):\n        \"\"\"\n        Launches an asynchronous generators and loop executors for respective task.\n        \"\"\"\n        # check if receive mode enabled\n        if self.__receive_mode:\n            self.__logging and logger.debug(\n                \"Launching NetGear_Async asynchronous generator!\"\n            )\n            # run loop executor for Receiver asynchronous generator\n            self.loop.run_in_executor(None, self.recv_generator)\n        else:\n            # Otherwise launch Server handler\n            self.__logging and logger.debug(\n                \"Creating NetGear_Async asynchronous server handler!\"\n            )\n            # create task for Server Handler\n            self.task = self.loop.create_task(self.__server_handler())\n        # return instance\n        return self\n\n    async def __server_handler(self):\n        \"\"\"\n        Handles various Server-end processes/tasks.\n        \"\"\"\n        # validate assigned frame generator in NetGear_Async configuration\n        if isinstance(self.config, dict) and \"generator\" in self.config:\n            # check if its  assigned value is a asynchronous generator\n            if self.config[\"generator\"] is None or not inspect.isasyncgen(\n                self.config[\"generator\"]\n            ):\n                # otherwise raise error\n                raise ValueError(\n                    \"[NetGear_Async:ERROR] :: Invalid configuration. Assigned generator must be a asynchronous generator function/method only!\"\n                )\n        else:\n            # raise error if validation fails\n            raise RuntimeError(\n                \"[NetGear_Async:ERROR] :: Assigned NetGear_Async configuration is invalid!\"\n            )\n\n        # define our messaging socket\n        self.__msg_socket = self.__msg_context.socket(self.__pattern[0])\n\n        # if req/rep pattern, define additional flags\n        if self.__msg_pattern == 1:\n            self.__msg_socket.REQ_RELAXED = True\n            self.__msg_socket.REQ_CORRELATE = True\n\n        # if pub/sub pattern, define additional optimizer\n        if self.__msg_pattern == 2:\n            self.__msg_socket.set_hwm(1)\n\n        # try connecting socket to assigned protocol, address and port\n        try:\n            self.__msg_socket.connect(\n                self.__protocol + \"://\" + str(self.__address) + \":\" + str(self.__port)\n            )\n            # finally log if successful\n            self.__logging and logger.debug(\n                \"Successfully connected to address: {} with pattern: {}.\".format(\n                    (\n                        self.__protocol\n                        + \"://\"\n                        + str(self.__address)\n                        + \":\"\n                        + str(self.__port)\n                    ),\n                    self.__msg_pattern,\n                )\n            )\n            logger.critical(\n                \"Send Mode is successfully activated and ready to send data!\"\n            )\n        except Exception as e:\n            # log ad raise error if failed\n            logger.exception(str(e))\n            if self.__bi_mode:\n                logger.error(\n                    \"Failed to activate Bidirectional Mode for this connection!\"\n                )\n            raise ValueError(\n                \"[NetGear_Async:ERROR] :: Failed to connect address: {} and pattern: {}!\".format(\n                    (\n                        self.__protocol\n                        + \"://\"\n                        + str(self.__address)\n                        + \":\"\n                        + str(self.__port)\n                    ),\n                    self.__msg_pattern,\n                )\n            )\n\n        # loop over our Asynchronous frame generator\n        async for dataframe in self.config[\"generator\"]:\n            # extract data if bidirectional mode\n            if self.__bi_mode and len(dataframe) == 2:\n                (data, frame) = dataframe\n                if not (data is None) and isinstance(data, np.ndarray):\n                    logger.warning(\n                        \"Skipped unsupported `data` of datatype: {}!\".format(\n                            type(data).__name__\n                        )\n                    )\n                    data = None\n                assert isinstance(\n                    frame, np.ndarray\n                ), \"[NetGear_Async:ERROR] :: Invalid data received from server end!\"\n            elif self.__bi_mode:\n                # raise error for invalid data\n                raise ValueError(\n                    \"[NetGear_Async:ERROR] :: Send Mode only accepts tuple(data, frame) as input in Bidirectional Mode. \\\n                    Kindly refer vidgear docs!\"\n                )\n            else:\n                # otherwise just make a copy of frame\n                frame = np.copy(dataframe)\n                data = None\n\n            # check if retrieved frame is `CONTIGUOUS`\n            if not (frame.flags[\"C_CONTIGUOUS\"]):\n                # otherwise make it\n                frame = np.ascontiguousarray(frame, dtype=frame.dtype)\n\n            # create data dict\n            data_dict = dict(\n                terminate=False,\n                bi_mode=self.__bi_mode,\n                data=data if not (data is None) else \"\",\n            )\n            # encode it\n            data_enc = msgpack.packb(data_dict)\n            # send the encoded data with correct flags\n            await self.__msg_socket.send(data_enc, flags=zmq.SNDMORE)\n\n            # encode frame\n            frame_enc = msgpack.packb(frame, default=m.encode)\n            # send the encoded frame\n            await self.__msg_socket.send_multipart([frame_enc])\n\n            # check if bidirectional patterns used\n            if self.__msg_pattern < 2:\n                # handle bidirectional data transfer if enabled\n                if self.__bi_mode:\n                    # get receiver encoded message withing timeout limit\n                    recvdmsg_encoded = await asyncio.wait_for(\n                        self.__msg_socket.recv(), timeout=self.__timeout\n                    )\n                    # retrieve receiver data from encoded message\n                    recvd_data = msgpack.unpackb(recvdmsg_encoded, use_list=False)\n                    # check message type\n                    if recvd_data[\"return_type\"] == \"ndarray\":  # numpy.ndarray\n                        # get encoded frame from receiver\n                        recvdframe_encoded = await asyncio.wait_for(\n                            self.__msg_socket.recv_multipart(), timeout=self.__timeout\n                        )\n                        # retrieve frame and put in queue\n                        await self.__queue.put(\n                            msgpack.unpackb(\n                                recvdframe_encoded[0],\n                                use_list=False,\n                                object_hook=m.decode,\n                            )\n                        )\n                    else:\n                        # otherwise put data directly in queue\n                        await self.__queue.put(\n                            recvd_data[\"return_data\"]\n                            if recvd_data[\"return_data\"]\n                            else None\n                        )\n                else:\n                    # otherwise log received confirmation\n                    recv_confirmation = await asyncio.wait_for(\n                        self.__msg_socket.recv(), timeout=self.__timeout\n                    )\n                    self.__logging and logger.debug(recv_confirmation)\n\n    async def recv_generator(self):\n        \"\"\"\n        A default Asynchronous Frame Generator for NetGear_Async's Receiver-end.\n        \"\"\"\n        # check whether `receive mode` is activated\n        if not (self.__receive_mode):\n            # raise Value error and exit\n            self.__terminate = True\n            raise ValueError(\n                \"[NetGear_Async:ERROR] :: `recv_generator()` function cannot be accessed while `receive_mode` is disabled. Kindly refer vidgear docs!\"\n            )\n\n        # initialize and define messaging socket\n        self.__msg_socket = self.__msg_context.socket(self.__pattern[1])\n\n        # define exclusive socket options for patterns\n        if self.__msg_pattern == 2:\n            self.__msg_socket.set_hwm(1)\n            self.__msg_socket.setsockopt(zmq.SUBSCRIBE, b\"\")\n\n        try:\n            # bind socket to the assigned protocol, address and port\n            self.__msg_socket.bind(\n                self.__protocol + \"://\" + str(self.__address) + \":\" + str(self.__port)\n            )\n            # finally log progress\n            self.__logging and logger.debug(\n                \"Successfully binded to address: {} with pattern: {}.\".format(\n                    (\n                        self.__protocol\n                        + \"://\"\n                        + str(self.__address)\n                        + \":\"\n                        + str(self.__port)\n                    ),\n                    self.__msg_pattern,\n                )\n            )\n            logger.critical(\"Receive Mode is activated successfully!\")\n        except Exception as e:\n            logger.exception(str(e))\n            raise RuntimeError(\n                \"[NetGear_Async:ERROR] :: Failed to bind address: {} and pattern: {}{}!\".format(\n                    (\n                        self.__protocol\n                        + \"://\"\n                        + str(self.__address)\n                        + \":\"\n                        + str(self.__port)\n                    ),\n                    self.__msg_pattern,\n                    \" and Bidirectional Mode enabled\" if self.__bi_mode else \"\",\n                )\n            )\n\n        # loop until terminated\n        while not self.__terminate:\n            # get encoded data message from server withing timeout limit\n            datamsg_encoded = await asyncio.wait_for(\n                self.__msg_socket.recv(), timeout=self.__timeout\n            )\n            # retrieve data from message\n            data = msgpack.unpackb(datamsg_encoded, use_list=False)\n            # terminate if exit` flag received from server\n            if data[\"terminate\"]:\n                # send confirmation message to server if bidirectional patterns\n                if self.__msg_pattern < 2:\n                    # create termination confirmation message\n                    return_dict = dict(\n                        terminated=\"Client-`{}` successfully terminated!\".format(\n                            self.__id\n                        ),\n                    )\n                    # encode message\n                    retdata_enc = msgpack.packb(return_dict)\n                    # send message back to server\n                    await self.__msg_socket.send(retdata_enc)\n                self.__logging and logger.info(\n                    \"Termination signal received from server!\"\n                )\n                # break loop and terminate\n                self.__terminate = True\n                break\n            # get encoded frame message from server withing timeout limit\n            framemsg_encoded = await asyncio.wait_for(\n                self.__msg_socket.recv_multipart(), timeout=self.__timeout\n            )\n            # retrieve frame from message\n            frame = msgpack.unpackb(\n                framemsg_encoded[0], use_list=False, object_hook=m.decode\n            )\n\n            # check if bidirectional patterns\n            if self.__msg_pattern < 2:\n                # handle bidirectional data transfer if enabled\n                if self.__bi_mode and data[\"bi_mode\"]:\n                    # handle empty queue\n                    if not self.__queue.empty():\n                        return_data = await self.__queue.get()\n                        self.__queue.task_done()\n                    else:\n                        return_data = None\n                    # check if we are returning `ndarray` frames\n                    if not (return_data is None) and isinstance(\n                        return_data, np.ndarray\n                    ):\n                        # check whether the incoming frame is contiguous\n                        if not (return_data.flags[\"C_CONTIGUOUS\"]):\n                            return_data = np.ascontiguousarray(\n                                return_data, dtype=return_data.dtype\n                            )\n\n                        # create return type dict without data\n                        rettype_dict = dict(\n                            return_type=(type(return_data).__name__),\n                            return_data=None,\n                        )\n                        # encode it\n                        rettype_enc = msgpack.packb(rettype_dict)\n                        # send it to server with correct flags\n                        await self.__msg_socket.send(rettype_enc, flags=zmq.SNDMORE)\n\n                        # encode return ndarray data\n                        retframe_enc = msgpack.packb(return_data, default=m.encode)\n                        # send it over network to server\n                        await self.__msg_socket.send_multipart([retframe_enc])\n                    else:\n                        # otherwise create type and data dict\n                        return_dict = dict(\n                            return_type=(type(return_data).__name__),\n                            return_data=(\n                                return_data if not (return_data is None) else \"\"\n                            ),\n                        )\n                        # encode it\n                        retdata_enc = msgpack.packb(return_dict)\n                        # send it over network to server\n                        await self.__msg_socket.send(retdata_enc)\n                elif self.__bi_mode or data[\"bi_mode\"]:\n                    # raise error if bidirectional mode is disabled at server or client but not both\n                    raise RuntimeError(\n                        \"[NetGear_Async:ERROR] :: Invalid configuration! Bidirectional Mode is not activate on {} end.\".format(\n                            \"client\" if self.__bi_mode else \"server\"\n                        )\n                    )\n                else:\n                    # otherwise just send confirmation message to server\n                    await self.__msg_socket.send(\n                        bytes(\n                            \"Data received on client: {} !\".format(self.__id), \"utf-8\"\n                        )\n                    )\n            # yield received tuple(data-frame) if bidirectional mode or else just frame\n            if self.__bi_mode:\n                yield (data[\"data\"], frame) if data[\"data\"] else (None, frame)\n            else:\n                yield frame\n            # sleep for sometime\n            await asyncio.sleep(0)\n\n    async def __frame_generator(self):\n        \"\"\"\n        Returns a default frame-generator for NetGear_Async's Server Handler.\n        \"\"\"\n        # start stream\n        self.__stream.start()\n        # loop over stream until its terminated\n        while not self.__terminate:\n            # read frames\n            frame = self.__stream.read()\n            # break if NoneType\n            if frame is None:\n                break\n            # yield frame\n            yield frame\n            # sleep for sometime\n            await asyncio.sleep(0)\n\n    async def transceive_data(self, data=None):\n        \"\"\"\n        Bidirectional Mode exclusive method to Transmit data _(in Receive mode)_ and Receive data _(in Send mode)_.\n\n        Parameters:\n            data (any): inputs data _(of any datatype)_ for sending back to Server.\n        \"\"\"\n        recvd_data = None\n        if not self.__terminate:\n            if self.__bi_mode:\n                if self.__receive_mode:\n                    await self.__queue.put(data)\n                else:\n                    if not self.__queue.empty():\n                        recvd_data = await self.__queue.get()\n                        self.__queue.task_done()\n            else:\n                logger.error(\n                    \"`transceive_data()` function cannot be used when Bidirectional Mode is disabled.\"\n                )\n        return recvd_data\n\n    async def __terminate_connection(self, disable_confirmation=False):\n        \"\"\"\n        Internal asyncio method to safely terminate ZMQ connection and queues\n\n        Parameters:\n            disable_confirmation (boolean): Force disable termination confirmation from client in bidirectional patterns.\n        \"\"\"\n        # log termination\n        self.__logging and logger.debug(\n            \"Terminating various {} Processes. Please wait.\".format(\n                \"Receive Mode\" if self.__receive_mode else \"Send Mode\"\n            )\n        )\n\n        # check whether `receive_mode` is enabled or not\n        if self.__receive_mode:\n            # indicate that process should be terminated\n            self.__terminate = True\n        else:\n            # indicate that process should be terminated\n            self.__terminate = True\n            # terminate stream\n            if not (self.__stream is None):\n                self.__stream.stop()\n            # signal `exit` flag for termination!\n            data_dict = dict(terminate=True)\n            data_enc = msgpack.packb(data_dict)\n            await self.__msg_socket.send(data_enc)\n            # check if bidirectional patterns\n            if self.__msg_pattern < 2 and not disable_confirmation:\n                # then receive and log confirmation\n                recv_confirmation = await self.__msg_socket.recv()\n                recvd_conf = msgpack.unpackb(recv_confirmation, use_list=False)\n                self.__logging and \"terminated\" in recvd_conf and logger.debug(\n                    recvd_conf[\"terminated\"]\n                )\n        # close socket\n        self.__msg_socket.setsockopt(zmq.LINGER, 0)\n        self.__msg_socket.close()\n        # handle asyncio queues in bidirectional mode\n        if self.__bi_mode:\n            # empty queue if not\n            while not self.__queue.empty():\n                try:\n                    self.__queue.get_nowait()\n                except asyncio.QueueEmpty:\n                    continue\n                self.__queue.task_done()\n            # join queues\n            await self.__queue.join()\n\n        logger.critical(\n            \"{} successfully terminated!\".format(\n                \"Receive Mode\" if self.__receive_mode else \"Send Mode\"\n            )\n        )\n\n    def close(self, skip_loop=False):\n        \"\"\"\n        Terminates all NetGear_Async Asynchronous processes gracefully.\n\n        Parameters:\n            skip_loop (Boolean): (optional)used only if don't want to close eventloop(required in pytest).\n        \"\"\"\n        # close event loop if specified\n        if not (skip_loop):\n            # close connection gracefully\n            self.loop.run_until_complete(self.__terminate_connection())\n            self.loop.close()\n        else:\n            # otherwise create a task\n            asyncio.ensure_future(\n                self.__terminate_connection(disable_confirmation=True)\n            )\n

"},{"location":"bonus/reference/netgear_async/#vidgear.gears.asyncio.netgear_async.NetGear_Async.__init__","title":"__init__(self, address=None, port=None, protocol='tcp', pattern=0, receive_mode=False, timeout=0.0, enablePiCamera=False, stabilize=False, source=None, camera_num=0, stream_mode=False, backend=0, colorspace=None, resolution=(640, 480), framerate=25, time_delay=0, logging=False, **options) special","text":"

This constructor method initializes the object state and attributes of the NetGear_Async class.

Parameters:

Name Type Description Default address str

sets the valid network address of the Server/Client.

None port str

sets the valid Network Port of the Server/Client.

None protocol str

sets the valid messaging protocol between Server/Client.

'tcp' pattern int

sets the supported messaging pattern(flow of communication) between Server/Client

0 receive_mode bool

select the NetGear_Async's Mode of operation.

False timeout int/float

controls the maximum waiting time(in sec) after which Client throws TimeoutError.

0.0 enablePiCamera bool

provide access to PiGear(if True) or CamGear(if False) APIs respectively.

False stabilize bool

enable access to Stabilizer Class for stabilizing frames.

False camera_num int

selects the camera module index which will be used as Rpi source.

0 resolution tuple

sets the resolution (i.e. (width,height)) of the Rpi source.

(640, 480) framerate int/float

sets the framerate of the Rpi source.

25 source based on input

defines the source for the input stream.

None stream_mode bool

controls the exclusive YouTube Mode.

False backend int

selects the backend for OpenCV's VideoCapture class.

0 colorspace str

selects the colorspace of the input stream.

None logging bool

enables/disables logging.

False time_delay int

time delay (in sec) before start reading the frames.

0 options dict

provides ability to alter Tweak Parameters of NetGear_Async, CamGear, PiGear & Stabilizer.

{} Source code in vidgear/gears/asyncio/netgear_async.py
def __init__(\n    self,\n    # NetGear_Async parameters\n    address=None,\n    port=None,\n    protocol=\"tcp\",\n    pattern=0,\n    receive_mode=False,\n    timeout=0.0,\n    # Videogear parameters\n    enablePiCamera=False,\n    stabilize=False,\n    source=None,\n    camera_num=0,\n    stream_mode=False,\n    backend=0,\n    colorspace=None,\n    resolution=(640, 480),\n    framerate=25,\n    time_delay=0,\n    # common parameters\n    logging=False,\n    **options\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the NetGear_Async class.\n\n    Parameters:\n        address (str): sets the valid network address of the Server/Client.\n        port (str): sets the valid Network Port of the Server/Client.\n        protocol (str): sets the valid messaging protocol between Server/Client.\n        pattern (int): sets the supported messaging pattern(flow of communication) between Server/Client\n        receive_mode (bool): select the NetGear_Async's Mode of operation.\n        timeout (int/float): controls the maximum waiting time(in sec) after which Client throws `TimeoutError`.\n        enablePiCamera (bool): provide access to PiGear(if True) or CamGear(if False) APIs respectively.\n        stabilize (bool): enable access to Stabilizer Class for stabilizing frames.\n        camera_num (int): selects the camera module index which will be used as Rpi source.\n        resolution (tuple): sets the resolution (i.e. `(width,height)`) of the Rpi source.\n        framerate (int/float): sets the framerate of the Rpi source.\n        source (based on input): defines the source for the input stream.\n        stream_mode (bool): controls the exclusive YouTube Mode.\n        backend (int): selects the backend for OpenCV's VideoCapture class.\n        colorspace (str): selects the colorspace of the input stream.\n        logging (bool): enables/disables logging.\n        time_delay (int): time delay (in sec) before start reading the frames.\n        options (dict): provides ability to alter Tweak Parameters of NetGear_Async, CamGear, PiGear & Stabilizer.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # raise error(s) for critical Class imports\n    import_dependency_safe(\n        \"zmq\" if zmq is None else \"\", min_version=\"4.0\", pkg_name=\"pyzmq\"\n    )\n    import_dependency_safe(\"msgpack\" if msgpack is None else \"\")\n    import_dependency_safe(\"msgpack_numpy\" if m is None else \"\")\n\n    # define valid messaging patterns => `0`: PAIR, `1`:(REQ, REP), `2`:(SUB, PUB), `3`:(PUSH, PULL)\n    valid_messaging_patterns = {\n        0: (zmq.PAIR, zmq.PAIR),\n        1: (zmq.REQ, zmq.REP),\n        2: (zmq.PUB, zmq.SUB),\n        3: (zmq.PUSH, zmq.PULL),\n    }\n\n    # check whether user-defined messaging pattern is valid\n    if isinstance(pattern, int) and pattern in valid_messaging_patterns:\n        # assign value\n        self.__msg_pattern = pattern\n        self.__pattern = valid_messaging_patterns[pattern]\n    else:\n        # otherwise default to 0:`zmq.PAIR`\n        self.__msg_pattern = 0\n        self.__pattern = valid_messaging_patterns[self.__msg_pattern]\n        self.__logging and logger.warning(\n            \"Invalid pattern {pattern}. Defaulting to `zmq.PAIR`!\".format(\n                pattern=pattern\n            )\n        )\n\n    # check  whether user-defined messaging protocol is valid\n    if isinstance(protocol, str) and protocol in [\"tcp\", \"ipc\"]:\n        # assign value\n        self.__protocol = protocol\n    else:\n        # else default to `tcp` protocol\n        self.__protocol = \"tcp\"\n        self.__logging and logger.warning(\"Invalid protocol. Defaulting to `tcp`!\")\n\n    # initialize Termination flag\n    self.__terminate = False\n    # initialize and assign `Receive Mode`\n    self.__receive_mode = receive_mode\n    # initialize stream handler\n    self.__stream = None\n    # initialize Messaging Socket\n    self.__msg_socket = None\n    # initialize NetGear_Async's configuration dictionary\n    self.config = {}\n    # asyncio queue handler\n    self.__queue = None\n    # define Bidirectional mode\n    self.__bi_mode = False  # handles Bidirectional mode state\n\n    # assign timeout for Receiver end\n    if timeout and isinstance(timeout, (int, float)):\n        self.__timeout = float(timeout)\n    else:\n        self.__timeout = 15.0\n\n    # generate 8-digit random system id\n    self.__id = \"\".join(\n        secrets.choice(string.ascii_uppercase + string.digits) for i in range(8)\n    )\n\n    # Handle user-defined options dictionary values\n    # reformat dictionary\n    options = {str(k).strip(): v for k, v in options.items()}\n    # handle bidirectional mode\n    if \"bidirectional_mode\" in options:\n        value = options[\"bidirectional_mode\"]\n        # also check if pattern and source is valid\n        if isinstance(value, bool) and pattern < 2 and source is None:\n            # activate Bidirectional mode if specified\n            self.__bi_mode = value\n        else:\n            # otherwise disable it\n            self.__bi_mode = False\n            logger.warning(\"Bidirectional data transmission is disabled!\")\n        # handle errors and logging\n        if pattern >= 2:\n            # raise error\n            raise ValueError(\n                \"[NetGear_Async:ERROR] :: `{}` pattern is not valid when Bidirectional Mode is enabled. Kindly refer Docs for more Information!\".format(\n                    pattern\n                )\n            )\n        elif not (source is None):\n            raise ValueError(\n                \"[NetGear_Async:ERROR] :: Custom source must be used when Bidirectional Mode is enabled. Kindly refer Docs for more Information!\".format(\n                    pattern\n                )\n            )\n        elif isinstance(value, bool) and self.__logging:\n            # log Bidirectional mode activation\n            logger.debug(\n                \"Bidirectional Data Transmission is {} for this connection!\".format(\n                    \"enabled\" if value else \"disabled\"\n                )\n            )\n        else:\n            logger.error(\"`bidirectional_mode` value is invalid!\")\n        # clean\n        del options[\"bidirectional_mode\"]\n\n    # Setup and assign event loop policy\n    if platform.system() == \"Windows\":\n        # On Windows, VidGear requires the ``WindowsSelectorEventLoop``, but Python 3.8 and above,\n        # defaults to an ``ProactorEventLoop`` loop that is not compatible with it. Thereby,\n        # we had to set it manually.\n        asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())\n    else:\n        if not (uvloop is None):\n            # Latest uvloop eventloop is only available for UNIX machines.\n            asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())\n        else:\n            # log if not present\n            import_dependency_safe(\"uvloop\", error=\"log\")\n\n    # Retrieve event loop and assign it\n    try:\n        self.loop = asyncio.get_running_loop()\n    except RuntimeError:\n        # otherwise create one\n        logger.critical(\"No running event loop found. Creating a new one.\")\n        self.loop = asyncio.new_event_loop()\n\n    # log eventloop for debugging\n    self.__logging and logger.info(\n        \"Using ``{}`` event loop for this process.\".format(\n            self.loop.__class__.__name__\n        )\n    )\n\n    # define messaging asynchronous Context\n    self.__msg_context = zmq.asyncio.Context()\n\n    # check whether `Receive Mode` is enabled\n    if receive_mode:\n        # assign local IP address if None\n        if address is None:\n            self.__address = \"*\"  # define address\n        else:\n            self.__address = address\n        # assign default port address if None\n        if port is None:\n            self.__port = \"5555\"\n        else:\n            self.__port = port\n    else:\n        # Handle video source\n        if source is None:\n            self.config = {\"generator\": None}\n            self.__logging and logger.warning(\"Given source is of NoneType!\")\n        else:\n            # define stream with necessary params\n            self.__stream = VideoGear(\n                enablePiCamera=enablePiCamera,\n                stabilize=stabilize,\n                source=source,\n                camera_num=camera_num,\n                stream_mode=stream_mode,\n                backend=backend,\n                colorspace=colorspace,\n                resolution=resolution,\n                framerate=framerate,\n                logging=logging,\n                time_delay=time_delay,\n                **options\n            )\n            # define default frame generator in configuration\n            self.config = {\"generator\": self.__frame_generator()}\n        # assign local ip address if None\n        if address is None:\n            self.__address = \"localhost\"\n        else:\n            self.__address = address\n        # assign default port address if None\n        if port is None:\n            self.__port = \"5555\"\n        else:\n            self.__port = port\n        # add server task handler\n        self.task = None\n\n    # create asyncio queue if bidirectional mode activated\n    self.__queue = asyncio.Queue() if self.__bi_mode else None\n
"},{"location":"bonus/reference/netgear_async/#vidgear.gears.asyncio.netgear_async.NetGear_Async.close","title":"close(self, skip_loop=False)","text":"

Terminates all NetGear_Async Asynchronous processes gracefully.

Parameters:

Name Type Description Default skip_loop Boolean

(optional)used only if don't want to close eventloop(required in pytest).

False Source code in vidgear/gears/asyncio/netgear_async.py
def close(self, skip_loop=False):\n    \"\"\"\n    Terminates all NetGear_Async Asynchronous processes gracefully.\n\n    Parameters:\n        skip_loop (Boolean): (optional)used only if don't want to close eventloop(required in pytest).\n    \"\"\"\n    # close event loop if specified\n    if not (skip_loop):\n        # close connection gracefully\n        self.loop.run_until_complete(self.__terminate_connection())\n        self.loop.close()\n    else:\n        # otherwise create a task\n        asyncio.ensure_future(\n            self.__terminate_connection(disable_confirmation=True)\n        )\n
"},{"location":"bonus/reference/netgear_async/#vidgear.gears.asyncio.netgear_async.NetGear_Async.launch","title":"launch(self)","text":"

Launches an asynchronous generators and loop executors for respective task.

Source code in vidgear/gears/asyncio/netgear_async.py
def launch(self):\n    \"\"\"\n    Launches an asynchronous generators and loop executors for respective task.\n    \"\"\"\n    # check if receive mode enabled\n    if self.__receive_mode:\n        self.__logging and logger.debug(\n            \"Launching NetGear_Async asynchronous generator!\"\n        )\n        # run loop executor for Receiver asynchronous generator\n        self.loop.run_in_executor(None, self.recv_generator)\n    else:\n        # Otherwise launch Server handler\n        self.__logging and logger.debug(\n            \"Creating NetGear_Async asynchronous server handler!\"\n        )\n        # create task for Server Handler\n        self.task = self.loop.create_task(self.__server_handler())\n    # return instance\n    return self\n
"},{"location":"bonus/reference/netgear_async/#vidgear.gears.asyncio.netgear_async.NetGear_Async.recv_generator","title":"recv_generator(self)","text":"

A default Asynchronous Frame Generator for NetGear_Async's Receiver-end.

Source code in vidgear/gears/asyncio/netgear_async.py
async def recv_generator(self):\n    \"\"\"\n    A default Asynchronous Frame Generator for NetGear_Async's Receiver-end.\n    \"\"\"\n    # check whether `receive mode` is activated\n    if not (self.__receive_mode):\n        # raise Value error and exit\n        self.__terminate = True\n        raise ValueError(\n            \"[NetGear_Async:ERROR] :: `recv_generator()` function cannot be accessed while `receive_mode` is disabled. Kindly refer vidgear docs!\"\n        )\n\n    # initialize and define messaging socket\n    self.__msg_socket = self.__msg_context.socket(self.__pattern[1])\n\n    # define exclusive socket options for patterns\n    if self.__msg_pattern == 2:\n        self.__msg_socket.set_hwm(1)\n        self.__msg_socket.setsockopt(zmq.SUBSCRIBE, b\"\")\n\n    try:\n        # bind socket to the assigned protocol, address and port\n        self.__msg_socket.bind(\n            self.__protocol + \"://\" + str(self.__address) + \":\" + str(self.__port)\n        )\n        # finally log progress\n        self.__logging and logger.debug(\n            \"Successfully binded to address: {} with pattern: {}.\".format(\n                (\n                    self.__protocol\n                    + \"://\"\n                    + str(self.__address)\n                    + \":\"\n                    + str(self.__port)\n                ),\n                self.__msg_pattern,\n            )\n        )\n        logger.critical(\"Receive Mode is activated successfully!\")\n    except Exception as e:\n        logger.exception(str(e))\n        raise RuntimeError(\n            \"[NetGear_Async:ERROR] :: Failed to bind address: {} and pattern: {}{}!\".format(\n                (\n                    self.__protocol\n                    + \"://\"\n                    + str(self.__address)\n                    + \":\"\n                    + str(self.__port)\n                ),\n                self.__msg_pattern,\n                \" and Bidirectional Mode enabled\" if self.__bi_mode else \"\",\n            )\n        )\n\n    # loop until terminated\n    while not self.__terminate:\n        # get encoded data message from server withing timeout limit\n        datamsg_encoded = await asyncio.wait_for(\n            self.__msg_socket.recv(), timeout=self.__timeout\n        )\n        # retrieve data from message\n        data = msgpack.unpackb(datamsg_encoded, use_list=False)\n        # terminate if exit` flag received from server\n        if data[\"terminate\"]:\n            # send confirmation message to server if bidirectional patterns\n            if self.__msg_pattern < 2:\n                # create termination confirmation message\n                return_dict = dict(\n                    terminated=\"Client-`{}` successfully terminated!\".format(\n                        self.__id\n                    ),\n                )\n                # encode message\n                retdata_enc = msgpack.packb(return_dict)\n                # send message back to server\n                await self.__msg_socket.send(retdata_enc)\n            self.__logging and logger.info(\n                \"Termination signal received from server!\"\n            )\n            # break loop and terminate\n            self.__terminate = True\n            break\n        # get encoded frame message from server withing timeout limit\n        framemsg_encoded = await asyncio.wait_for(\n            self.__msg_socket.recv_multipart(), timeout=self.__timeout\n        )\n        # retrieve frame from message\n        frame = msgpack.unpackb(\n            framemsg_encoded[0], use_list=False, object_hook=m.decode\n        )\n\n        # check if bidirectional patterns\n        if self.__msg_pattern < 2:\n            # handle bidirectional data transfer if enabled\n            if self.__bi_mode and data[\"bi_mode\"]:\n                # handle empty queue\n                if not self.__queue.empty():\n                    return_data = await self.__queue.get()\n                    self.__queue.task_done()\n                else:\n                    return_data = None\n                # check if we are returning `ndarray` frames\n                if not (return_data is None) and isinstance(\n                    return_data, np.ndarray\n                ):\n                    # check whether the incoming frame is contiguous\n                    if not (return_data.flags[\"C_CONTIGUOUS\"]):\n                        return_data = np.ascontiguousarray(\n                            return_data, dtype=return_data.dtype\n                        )\n\n                    # create return type dict without data\n                    rettype_dict = dict(\n                        return_type=(type(return_data).__name__),\n                        return_data=None,\n                    )\n                    # encode it\n                    rettype_enc = msgpack.packb(rettype_dict)\n                    # send it to server with correct flags\n                    await self.__msg_socket.send(rettype_enc, flags=zmq.SNDMORE)\n\n                    # encode return ndarray data\n                    retframe_enc = msgpack.packb(return_data, default=m.encode)\n                    # send it over network to server\n                    await self.__msg_socket.send_multipart([retframe_enc])\n                else:\n                    # otherwise create type and data dict\n                    return_dict = dict(\n                        return_type=(type(return_data).__name__),\n                        return_data=(\n                            return_data if not (return_data is None) else \"\"\n                        ),\n                    )\n                    # encode it\n                    retdata_enc = msgpack.packb(return_dict)\n                    # send it over network to server\n                    await self.__msg_socket.send(retdata_enc)\n            elif self.__bi_mode or data[\"bi_mode\"]:\n                # raise error if bidirectional mode is disabled at server or client but not both\n                raise RuntimeError(\n                    \"[NetGear_Async:ERROR] :: Invalid configuration! Bidirectional Mode is not activate on {} end.\".format(\n                        \"client\" if self.__bi_mode else \"server\"\n                    )\n                )\n            else:\n                # otherwise just send confirmation message to server\n                await self.__msg_socket.send(\n                    bytes(\n                        \"Data received on client: {} !\".format(self.__id), \"utf-8\"\n                    )\n                )\n        # yield received tuple(data-frame) if bidirectional mode or else just frame\n        if self.__bi_mode:\n            yield (data[\"data\"], frame) if data[\"data\"] else (None, frame)\n        else:\n            yield frame\n        # sleep for sometime\n        await asyncio.sleep(0)\n
"},{"location":"bonus/reference/netgear_async/#vidgear.gears.asyncio.netgear_async.NetGear_Async.transceive_data","title":"transceive_data(self, data=None) async","text":"

Bidirectional Mode exclusive method to Transmit data (in Receive mode) and Receive data (in Send mode).

Parameters:

Name Type Description Default data any

inputs data (of any datatype) for sending back to Server.

None Source code in vidgear/gears/asyncio/netgear_async.py
async def transceive_data(self, data=None):\n    \"\"\"\n    Bidirectional Mode exclusive method to Transmit data _(in Receive mode)_ and Receive data _(in Send mode)_.\n\n    Parameters:\n        data (any): inputs data _(of any datatype)_ for sending back to Server.\n    \"\"\"\n    recvd_data = None\n    if not self.__terminate:\n        if self.__bi_mode:\n            if self.__receive_mode:\n                await self.__queue.put(data)\n            else:\n                if not self.__queue.empty():\n                    recvd_data = await self.__queue.get()\n                    self.__queue.task_done()\n        else:\n            logger.error(\n                \"`transceive_data()` function cannot be used when Bidirectional Mode is disabled.\"\n            )\n    return recvd_data\n
"},{"location":"bonus/reference/pigear/","title":"PiGear API References","text":"

PiGear API usage examples can be found here \u27b6

PiGear API parameters are explained here \u27b6

PiGear implements a seamless and robust wrapper around the picamera2 python library, simplifying integration with minimal code changes and ensuring a smooth transition for developers already familiar with the Picamera2 API. PiGear leverages the libcamera API under the hood with multi-threading, providing high-performance , enhanced control and functionality for Raspberry Pi camera modules.

PiGear handles common configuration parameters and non-standard settings for various camera types, simplifying the integration process. PiGear currently supports picamera2 API parameters such as sensor, controls, transform, and stride, with internal type and sanity checks for robust performance.

While primarily focused on Raspberry Pi camera modules, PiGear also provides basic functionality for USB webcams only with Picamera2 API, along with the ability to accurately differentiate between USB and Raspberry Pi cameras using metadata.

Backward compatibility with picamera library

PiGear seamlessly switches to the legacy picamera library if the picamera2 library is unavailable, ensuring seamless backward compatibility. For this, PiGear also provides a flexible multi-threaded framework around complete picamera API, allowing developers to effortlessly exploit a wide range of parameters, such as brightness, saturation, sensor_mode, iso, exposure, and more.

Furthermore, PiGear supports the use of multiple camera modules, including those found on Raspberry Pi Compute Module IO boards and USB cameras (only with Picamera2 API).

Threaded Internal Timer

PiGear ensures proper resource release during the termination of the API, preventing potential issues or resource leaks. PiGear API internally implements a Threaded Internal Timer that silently keeps active track of any frozen-threads or hardware-failures and exits safely if any do occur. This means that if you're running the PiGear API in your script and someone accidentally pulls the Camera-Module cable out, instead of going into a possible kernel panic, the API will exit safely to save resources.

Make sure to enable Raspberry Pi hardware-specific settings prior using this API, otherwise nothing will work.

Source code in vidgear/gears/pigear.py
class PiGear:\n    \"\"\"\n    PiGear implements a seamless and robust wrapper around the [picamera2](https://github.com/raspberrypi/picamera2) python library, simplifying integration with minimal code changes and ensuring a\n    smooth transition for developers already familiar with the Picamera2 API. PiGear leverages the `libcamera` API under the hood with multi-threading, providing high-performance :fire:, enhanced\n    control and functionality for Raspberry Pi camera modules.\n\n    PiGear handles common configuration parameters and non-standard settings for various camera types, simplifying the integration process. PiGear currently supports picamera2 API parameters such as\n    `sensor`, `controls`, `transform`, and `stride`, with internal type and sanity checks for robust performance.\n\n    While primarily focused on Raspberry Pi camera modules, PiGear also provides basic functionality for USB webcams only with Picamera2 API, along with the ability to accurately differentiate between\n    USB and Raspberry Pi cameras using metadata.\n\n    ???+ info \"Backward compatibility with `picamera` library\"\n        PiGear seamlessly switches to the legacy [picamera](https://picamera.readthedocs.io/en/release-1.13/index.html) library if the `picamera2` library is unavailable, ensuring seamless backward\n        compatibility. For this, PiGear also provides a flexible multi-threaded framework around complete `picamera` API, allowing developers to effortlessly exploit a wide range of parameters, such\n        as `brightness`, `saturation`, `sensor_mode`, `iso`, `exposure`, and more.\n\n    Furthermore, PiGear supports the use of multiple camera modules, including those found on Raspberry Pi Compute Module IO boards and USB cameras _(only with Picamera2 API)_.\n\n    ??? new \"Threaded Internal Timer :material-camera-timer:\"\n        PiGear ensures proper resource release during the termination of the API, preventing potential issues or resource leaks. PiGear API internally implements a\n        ==Threaded Internal Timer== that silently keeps active track of any frozen-threads or hardware-failures and exits safely if any do occur. This means that if\n        you're running the PiGear API in your script and someone accidentally pulls the Camera-Module cable out, instead of going into a possible kernel panic,\n        the API will exit safely to save resources.\n\n    !!! failure \"Make sure to [enable Raspberry Pi hardware-specific settings](https://picamera.readthedocs.io/en/release-1.13/quickstart.html) prior using this API, otherwise nothing will work.\"\n    \"\"\"\n\n    def __init__(\n        self,\n        camera_num=0,\n        resolution=(640, 480),\n        framerate=30,\n        colorspace=None,\n        logging=False,\n        time_delay=0,\n        **options\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the PiGear class.\n\n        Parameters:\n            camera_num (int): selects the camera module index which will be used as source.\n            resolution (tuple): sets the resolution (i.e. `(width,height)`) of the source..\n            framerate (int/float): sets the framerate of the source.\n            colorspace (str): selects the colorspace of the input stream.\n            logging (bool): enables/disables logging.\n            time_delay (int): time delay (in sec) before start reading the frames.\n            options (dict): provides ability to alter Source Tweak Parameters.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # raise error(s) for critical Class imports\n        global picamera, picamera2\n        if picamera2:\n            # log if picamera2\n            self.__logging and logger.info(\"picamera2 API is currently being accessed.\")\n        elif picamera:\n            # switch to picamera otherwise\n            logger.critical(\n                \"picamera2 library not installed on this system. Defaulting to legacy picamera API.\"\n            )\n        else:\n            # raise error if none\n            import_dependency_safe(\"picamera\")\n\n        assert (\n            isinstance(framerate, (int, float)) and framerate > 0.0\n        ), \"[PiGear:ERROR] :: Input framerate value `{}` is a Invalid! Kindly read docs.\".format(\n            framerate\n        )\n        assert (\n            isinstance(resolution, (tuple, list)) and len(resolution) == 2\n        ), \"[PiGear:ERROR] :: Input resolution value `{}` is a Invalid! Kindly read docs.\".format(\n            resolution\n        )\n        if not (isinstance(camera_num, int) and camera_num >= 0):\n            camera_num = 0\n            logger.warning(\n                \"Input camera_num value `{}` is invalid, Defaulting to index 0!\"\n            )\n\n        # reformat dict\n        options = {str(k).strip(): v for k, v in options.items()}\n\n        # check if legacy picamera backend is enforced\n        enforce_legacy_picamera = options.pop(\"enforce_legacy_picamera\", False)\n        if isinstance(enforce_legacy_picamera, bool) and enforce_legacy_picamera:\n            # check if picamera library is available.\n            if picamera:\n                logger.critical(\n                    \"Enforcing legacy picamera API for this run. picamera2 API access will be disabled!\"\n                )\n                # disable picamera2\n                picamera2 = None\n            else:\n                # raise error otherwise\n                logger.error(\n                    \"`picamera` is unavailable or unsupported on this system, `enforce_legacy_picamera` will be discarded!\"\n                )\n                import_dependency_safe(\"picamera\")\n\n        if picamera2:\n            # handle logging\n            not (self.__logging) and not os.getenv(\n                \"LIBCAMERA_LOG_LEVELS\", False\n            ) and logger.info(\n                \"Kindly set `LIBCAMERA_LOG_LEVELS=2` environment variable to disable common libcamera API messages.\"\n            )\n            # collect metadata\n            cameras_metadata = Picamera2.global_camera_info()\n            # initialize the picamera stream at given index\n            self.__camera = Picamera2(camera_num=camera_num)\n            # extract metadata for current camera\n            camera_metadata = [x for x in cameras_metadata if x[\"Num\"] == camera_num][0]\n            # check connected camera is USB or I2C\n            self.__camera_is_usb = True if \"usb\" in camera_metadata[\"Id\"] else False\n            # handle framerate control\n            if not self.__camera_is_usb:\n                self.__camera.set_controls({\"FrameRate\": framerate})\n            else:\n                logger.warning(\n                    \"USB camera detected. Setting input framerate is NOT supported with Picamera2 API!\"\n                )\n            # log\n            self.__logging and logger.debug(\n                \"Activating Picamera2 API for `{}` camera at index: {} with resolution: {} & framerate: {}\".format(\n                    camera_metadata[\"Model\"],\n                    camera_num,\n                    resolution if not self.__camera_is_usb else \"default\",\n                    framerate,\n                )\n            )\n        else:\n            # initialize the picamera stream at given index\n            self.__camera = PiCamera(camera_num=camera_num)\n            self.__camera.resolution = tuple(resolution)\n            self.__camera.framerate = framerate\n            self.__logging and logger.debug(\n                \"Activating Picamera API at index: {} with resolution: {} & framerate: {}\".format(\n                    camera_num, resolution, framerate\n                )\n            )\n\n        # initialize framerate (Read-only) variable\n        self.framerate = framerate\n\n        # initializing colorspace variable\n        self.color_space = None\n\n        # define timeout variable default value(handles hardware failures)\n        self.__failure_timeout = options.pop(\"HWFAILURE_TIMEOUT\", 2.0)\n        if isinstance(self.__failure_timeout, (int, float)):\n            if not (10.0 > self.__failure_timeout > 1.0):\n                raise ValueError(\n                    \"[PiGear:ERROR] :: `HWFAILURE_TIMEOUT` value can only be between 1.0 ~ 10.0\"\n                )\n            self.__logging and logger.debug(\n                \"Setting HW Failure Timeout: {} seconds\".format(self.__failure_timeout)\n            )\n        else:\n            # reset improper values\n            self.__failure_timeout = 2.0\n\n        try:\n            if picamera2:\n                # define common supported picamera2 config parameters\n                valid_config_options = [\n                    \"auto_align_output_size\",  # internal\n                    \"enable_verbose_logs\",  # internal\n                    \"format\",\n                    \"sensor\",\n                ]\n\n                # define non-USB supported picamera2 config parameters\n                non_usb_options = [\n                    \"controls\",  # not-supported on USB\n                    \"transform\",  # not-working on USB\n                    \"buffer_count\",  # not-supported on USB\n                    \"queue\",  # not-supported on USB\n                ]  # Less are supported (will be changed in future)\n\n                # filter parameter supported with non-USB cameras only\n                if self.__camera_is_usb:\n                    unsupported_config_keys = set(list(options.keys())).intersection(\n                        set(non_usb_options)\n                    )\n                    unsupported_config_keys and logger.warning(\n                        \"Setting parameters: `{}` for USB camera is NOT supported with Picamera2 API!\".format(\n                            \"`, `\".join(unsupported_config_keys)\n                        )\n                    )\n                else:\n                    valid_config_options += non_usb_options\n\n                # log all invalid keys\n                invalid_config_keys = set(list(options.keys())) - set(\n                    valid_config_options\n                )\n                invalid_config_keys and logger.warning(\n                    \"Discarding invalid options NOT supported by Picamera2 API for current Camera Sensor: `{}`\".format(\n                        \"`, `\".join(invalid_config_keys)\n                    )\n                )\n                # delete all unsupported options\n                options = {\n                    x: y for x, y in options.items() if x in valid_config_options\n                }\n\n                # setting size, already defined\n                options.update({\"size\": tuple(resolution)})\n\n                # set 24-bit, BGR format by default\n                if not \"format\" in options:\n                    # auto defaults for USB cameras\n                    not self.__camera_is_usb and options.update({\"format\": \"RGB888\"})\n                elif self.__camera_is_usb:\n                    # check the supported formats, if USB camera\n                    avail_formats = [\n                        mode[\"format\"] for mode in self.__camera.sensor_modes\n                    ]\n                    # handle unsupported formats\n                    if not options[\"format\"] in avail_formats:\n                        logger.warning(\n                            \"Discarding `format={}`. `{}` are the only available formats for USB camera in use!\".format(\n                                options[\"format\"], \"`, `\".join(avail_formats)\n                            )\n                        )\n                        del options[\"format\"]\n                    else:\n                        # `colorspace` parameter must define with  `format` optional parameter\n                        # unless format is MPEG (tested)\n                        (\n                            not (colorspace is None) or options[\"format\"] == \"MPEG\"\n                        ) and logger.warning(\n                            \"Custom Output frames `format={}` detected. It is advised to define `colorspace` parameter or handle this format manually in your code!\".format(\n                                options[\"format\"]\n                            )\n                        )\n                else:\n                    # `colorspace` parameter must define with  `format` optional parameter\n                    # unless format is either BGR or BGRA\n                    (\n                        not (colorspace is None)\n                        or options[\"format\"]\n                        in [\n                            \"RGB888\",\n                            \"XRGB8888\",\n                        ]\n                    ) and logger.warning(\n                        \"Custom Output frames `format={}` detected. It is advised to define `colorspace` parameter or handle this format manually in your code!\".format(\n                            options[\"format\"]\n                        )\n                    )\n\n                # enable verbose logging mode (handled by Picamera2 API)\n                verbose = options.pop(\"enable_verbose_logs\", False)\n                if self.__logging and isinstance(verbose, bool) and verbose:\n                    self.__camera.set_logging(Picamera2.DEBUG)\n                else:\n                    # setup logging\n                    self.__camera.set_logging(Picamera2.WARNING)\n\n                # handle transformations, if specified\n                transform = options.pop(\"transform\", Transform())\n                if not isinstance(transform, Transform):\n                    logger.warning(\"`transform` value is of invalid type, Discarding!\")\n                    transform = Transform()\n\n                # handle sensor configurations, if specified\n                sensor = options.pop(\"sensor\", {})\n                if isinstance(sensor, dict):\n                    # extract all valid sensor keys\n                    valid_sensor = [\"output_size\", \"bit_depth\"]\n                    # log all invalid keys\n                    invalid_sensor_keys = set(list(sensor)) - set(valid_sensor)\n                    invalid_sensor_keys and logger.warning(\n                        \"Discarding sensor properties NOT supported by current Camera Sensor: `{}`. Only supported are: (`{}`)\".format(\n                            \"`, `\".join(invalid_sensor_keys),\n                            \"`, `\".join(valid_sensor),\n                        )\n                    )\n                    # delete all unsupported control keys\n                    sensor = {x: y for x, y in sensor.items() if x in valid_sensor}\n                    # remove size if output size is defined\n                    if \"output_size\" in sensor:\n                        del options[\"size\"]\n                        logger.critical(\n                            \"Overriding output frame size with `output_size={}!\".format(\n                                sensor[\"output_size\"]\n                            )\n                        )\n                else:\n                    logger.warning(\"`sensor` value is of invalid type, Discarding!\")\n                    sensor = {}\n\n                # handle controls, if specified\n                controls = options.pop(\"controls\", {})\n                if isinstance(controls, dict):\n                    # extract all valid control keys\n                    valid_controls = self.__camera.camera_controls\n                    # remove any fps controls, assigned already\n                    valid_controls.pop(\"FrameDuration\", None)\n                    valid_controls.pop(\"FrameDurationLimits\", None)\n                    # log all invalid keys\n                    invalid_control_keys = set(list(controls.keys())) - set(\n                        list(valid_controls.keys())\n                    )\n                    invalid_control_keys and logger.warning(\n                        \"Discarding control properties NOT supported by current Camera Sensor: `{}`. Only supported are: (`{}`)\".format(\n                            \"`, `\".join(invalid_control_keys),\n                            \"`, `\".join(list(valid_controls.keys())),\n                        )\n                    )\n                    # delete all unsupported control keys\n                    controls = {\n                        x: y for x, y in controls.items() if x in valid_controls.keys()\n                    }\n                else:\n                    logger.warning(\"`controls` value is of invalid type, Discarding!\")\n                    controls = {}\n\n                # handle buffer_count, if specified\n                buffer_count = options.pop(\"buffer_count\", 4)\n                if (\n                    not isinstance(buffer_count, int) or buffer_count < 1\n                ):  # must be greater than 1\n                    logger.warning(\n                        \"`buffer_count` value is of invalid type, Discarding!\"\n                    )\n                    # `create_preview_configuration` requests 4 sets of buffers\n                    buffer_count = 4\n\n                # handle queue, if specified\n                queue = options.pop(\"queue\", True)\n                if not isinstance(queue, bool):\n                    logger.warning(\"`queue` value is of invalid type, Discarding!\")\n                    queue = True\n\n                # check if auto-align camera configuration is specified\n                auto_align_output_size = options.pop(\"auto_align_output_size\", False)\n\n                # create default configuration for camera\n                config = self.__camera.create_preview_configuration(\n                    main=options,\n                    transform=transform,\n                    sensor=sensor,\n                    controls=controls,\n                    buffer_count=buffer_count,\n                    queue=queue,\n                )\n\n                # auto-align camera configuration, if specified\n                if isinstance(auto_align_output_size, bool) and auto_align_output_size:\n                    self.__logging and logger.debug(\n                        \"Re-aligning Output frames to optimal size supported by current Camera Sensor.\"\n                    )\n                    self.__camera.align_configuration(config)\n\n                # configure camera\n                self.__camera.configure(config)\n                self.__logging and logger.debug(\n                    \"Setting Picamera2 API Parameters: `{}`, controls: `{}`, sensor: `{}`, buffer_count: `{}`, and queue: `{}`.\".format(\n                        self.__camera.camera_configuration()[\"main\"],\n                        controls,\n                        sensor,\n                        buffer_count,\n                        queue,\n                    )\n                )\n            else:\n                # apply attributes to source if specified\n                for key, value in options.items():\n                    self.__logging and logger.debug(\n                        \"Setting {} API Parameter for Picamera: `{}`\".format(key, value)\n                    )\n                    setattr(self.__camera, key, value)\n        except Exception as e:\n            # Catch if any error occurred\n            logger.exception(str(e))\n\n        # separately handle colorspace value to int conversion\n        if not (colorspace is None):\n            self.color_space = capPropId(colorspace.strip())\n            if self.__logging and not (self.color_space is None):\n                logger.debug(\n                    \"Enabling `{}` colorspace for this video stream!\".format(\n                        colorspace.strip()\n                    )\n                )\n\n        # enable rgb capture array thread and capture stream\n        if not picamera2:\n            self.__rawCapture = PiRGBArray(self.__camera, size=resolution)\n            self.stream = self.__camera.capture_continuous(\n                self.__rawCapture, format=\"bgr\", use_video_port=True\n            )\n\n        # initialize frame variable\n        # with captured frame\n        try:\n            if picamera2:\n                # start camera thread\n                self.__camera.start()\n                # capture frame array\n                self.frame = self.__camera.capture_array(\"main\")\n                # assign camera as stream for setting\n                # parameters after starting the camera\n                self.stream = self.__camera\n            else:\n                # capture frame array from stream\n                stream = next(self.stream)\n                self.frame = stream.array\n                self.__rawCapture.seek(0)\n                self.__rawCapture.truncate()\n            # render colorspace if defined\n            if not (self.frame is None) and not (self.color_space is None):\n                self.frame = cv2.cvtColor(self.frame, self.color_space)\n        except Exception as e:\n            logger.exception(str(e))\n            raise RuntimeError(\"[PiGear:ERROR] :: Camera Module failed to initialize!\")\n\n        # applying time delay to warm-up picamera only if specified\n        if time_delay and isinstance(time_delay, (int, float)):\n            time.sleep(time_delay)\n\n        # thread initialization\n        self.__thread = None\n\n        # timer thread initialization(Keeps check on frozen thread)\n        self.__timer = None\n        self.__t_elapsed = 0.0  # records time taken by thread\n\n        # catching thread exceptions\n        self.__exceptions = None\n\n        # initialize termination flag\n        self.__terminate = False\n\n    def start(self):\n        \"\"\"\n        Launches the internal *Threaded Frames Extractor* daemon\n\n        **Returns:** A reference to the PiGear class object.\n        \"\"\"\n        # Start frame producer thread\n        self.__thread = Thread(target=self.__update, name=\"PiGear\", args=())\n        self.__thread.daemon = True\n        self.__thread.start()\n\n        # Start internal timer thread\n        self.__timer = Thread(target=self.__timeit, name=\"PiTimer\", args=())\n        self.__timer.daemon = True\n        self.__timer.start()\n\n        return self\n\n    def __timeit(self):\n        \"\"\"\n        Threaded Internal Timer that keep checks on thread execution timing\n        \"\"\"\n        # assign current time\n        self.__t_elapsed = time.time()\n\n        # loop until terminated\n        while not (self.__terminate):\n            # check for frozen thread\n            if time.time() - self.__t_elapsed > self.__failure_timeout:\n                # log failure\n                self.__logging and logger.critical(\"Camera Module Disconnected!\")\n                # prepare for clean exit\n                self.__exceptions = True\n                self.__terminate = True  # self-terminate\n\n    def __update(self):\n        \"\"\"\n        A **Threaded Frames Extractor**, that keep iterating frames from PiCamera API to a internal monitored deque,\n        until the thread is terminated, or frames runs out.\n        \"\"\"\n        # keep looping infinitely until the thread is terminated\n        while not (self.__terminate):\n            if not picamera2:\n                try:\n                    # Try to iterate next frame from generator\n                    stream = next(self.stream)\n                except Exception:\n                    # catch and save any exceptions\n                    self.__exceptions = sys.exc_info()\n                    break  # exit\n\n            # __update timer\n            self.__t_elapsed = time.time()\n\n            # grab the frame from the stream\n            if picamera2:\n                frame = self.__camera.capture_array(\"main\")\n            else:\n                frame = stream.array\n                # clear the stream in preparation\n                # for the next frame\n                self.__rawCapture.seek(0)\n                self.__rawCapture.truncate()\n\n            # apply colorspace if specified\n            if not (self.color_space is None):\n                # apply colorspace to frames\n                color_frame = None\n                try:\n                    color_frame = cv2.cvtColor(frame, self.color_space)\n                except Exception as e:\n                    # Catch if any error occurred\n                    color_frame = None\n                    self.color_space = None\n                    self.__logging and logger.exception(str(e))\n                    logger.warning(\"Assigned colorspace value is invalid. Discarding!\")\n                self.frame = color_frame if not (color_frame is None) else frame\n            else:\n                self.frame = frame\n\n        # terminate processes\n        if not (self.__terminate):\n            self.__terminate = True\n\n        # release resources\n        if picamera2:\n            self.__camera.stop()\n        else:\n            self.__rawCapture.close()\n            self.__camera.close()\n\n    def read(self):\n        \"\"\"\n        Extracts frames synchronously from monitored deque, while maintaining a fixed-length frame buffer in the memory,\n        and blocks the thread if the deque is full.\n\n        **Returns:** A n-dimensional numpy array.\n        \"\"\"\n        # check if there are any thread exceptions\n        if not (self.__exceptions is None):\n            if isinstance(self.__exceptions, bool):\n                # clear frame\n                self.frame = None\n                # notify user about hardware failure\n                raise SystemError(\n                    \"[PiGear:ERROR] :: Hardware failure occurred, Kindly reconnect Camera Module and restart your Pi!\"\n                )\n            else:\n                # clear frame\n                self.frame = None\n                # re-raise error for debugging\n                error_msg = (\n                    \"[PiGear:ERROR] :: Camera Module API failure occurred: {}\".format(\n                        self.__exceptions[1]\n                    )\n                )\n                raise RuntimeError(error_msg).with_traceback(self.__exceptions[2])\n        # return the frame\n        return self.frame\n\n    def stop(self):\n        \"\"\"\n        Safely terminates the thread, and release the multi-threaded resources.\n        \"\"\"\n        # log termination\n        self.__logging and logger.debug(\"Terminating PiGear Processes.\")\n\n        # make sure that the threads should be terminated\n        self.__terminate = True\n\n        # stop timer thread\n        if not (self.__timer is None):\n            self.__timer.join()\n            self.__timer = None\n\n        # handle camera thread\n        if not (self.__thread is None):\n            # check if hardware failure occurred\n            if not (self.__exceptions is None) and isinstance(self.__exceptions, bool):\n                if picamera2:\n                    # release picamera2 resources\n                    self.__camera.stop()\n                else:\n                    # force release picamera resources\n                    self.__rawCapture.close()\n                    self.__camera.close()\n            # properly handle thread exit\n            # wait if still process is still\n            # processing some information\n            self.__thread.join()\n            # remove any threads\n            self.__thread = None\n

"},{"location":"bonus/reference/pigear/#vidgear.gears.pigear.PiGear.__init__","title":"__init__(self, camera_num=0, resolution=(640, 480), framerate=30, colorspace=None, logging=False, time_delay=0, **options) special","text":"

This constructor method initializes the object state and attributes of the PiGear class.

Parameters:

Name Type Description Default camera_num int

selects the camera module index which will be used as source.

0 resolution tuple

sets the resolution (i.e. (width,height)) of the source..

(640, 480) framerate int/float

sets the framerate of the source.

30 colorspace str

selects the colorspace of the input stream.

None logging bool

enables/disables logging.

False time_delay int

time delay (in sec) before start reading the frames.

0 options dict

provides ability to alter Source Tweak Parameters.

{} Source code in vidgear/gears/pigear.py
def __init__(\n    self,\n    camera_num=0,\n    resolution=(640, 480),\n    framerate=30,\n    colorspace=None,\n    logging=False,\n    time_delay=0,\n    **options\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the PiGear class.\n\n    Parameters:\n        camera_num (int): selects the camera module index which will be used as source.\n        resolution (tuple): sets the resolution (i.e. `(width,height)`) of the source..\n        framerate (int/float): sets the framerate of the source.\n        colorspace (str): selects the colorspace of the input stream.\n        logging (bool): enables/disables logging.\n        time_delay (int): time delay (in sec) before start reading the frames.\n        options (dict): provides ability to alter Source Tweak Parameters.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # raise error(s) for critical Class imports\n    global picamera, picamera2\n    if picamera2:\n        # log if picamera2\n        self.__logging and logger.info(\"picamera2 API is currently being accessed.\")\n    elif picamera:\n        # switch to picamera otherwise\n        logger.critical(\n            \"picamera2 library not installed on this system. Defaulting to legacy picamera API.\"\n        )\n    else:\n        # raise error if none\n        import_dependency_safe(\"picamera\")\n\n    assert (\n        isinstance(framerate, (int, float)) and framerate > 0.0\n    ), \"[PiGear:ERROR] :: Input framerate value `{}` is a Invalid! Kindly read docs.\".format(\n        framerate\n    )\n    assert (\n        isinstance(resolution, (tuple, list)) and len(resolution) == 2\n    ), \"[PiGear:ERROR] :: Input resolution value `{}` is a Invalid! Kindly read docs.\".format(\n        resolution\n    )\n    if not (isinstance(camera_num, int) and camera_num >= 0):\n        camera_num = 0\n        logger.warning(\n            \"Input camera_num value `{}` is invalid, Defaulting to index 0!\"\n        )\n\n    # reformat dict\n    options = {str(k).strip(): v for k, v in options.items()}\n\n    # check if legacy picamera backend is enforced\n    enforce_legacy_picamera = options.pop(\"enforce_legacy_picamera\", False)\n    if isinstance(enforce_legacy_picamera, bool) and enforce_legacy_picamera:\n        # check if picamera library is available.\n        if picamera:\n            logger.critical(\n                \"Enforcing legacy picamera API for this run. picamera2 API access will be disabled!\"\n            )\n            # disable picamera2\n            picamera2 = None\n        else:\n            # raise error otherwise\n            logger.error(\n                \"`picamera` is unavailable or unsupported on this system, `enforce_legacy_picamera` will be discarded!\"\n            )\n            import_dependency_safe(\"picamera\")\n\n    if picamera2:\n        # handle logging\n        not (self.__logging) and not os.getenv(\n            \"LIBCAMERA_LOG_LEVELS\", False\n        ) and logger.info(\n            \"Kindly set `LIBCAMERA_LOG_LEVELS=2` environment variable to disable common libcamera API messages.\"\n        )\n        # collect metadata\n        cameras_metadata = Picamera2.global_camera_info()\n        # initialize the picamera stream at given index\n        self.__camera = Picamera2(camera_num=camera_num)\n        # extract metadata for current camera\n        camera_metadata = [x for x in cameras_metadata if x[\"Num\"] == camera_num][0]\n        # check connected camera is USB or I2C\n        self.__camera_is_usb = True if \"usb\" in camera_metadata[\"Id\"] else False\n        # handle framerate control\n        if not self.__camera_is_usb:\n            self.__camera.set_controls({\"FrameRate\": framerate})\n        else:\n            logger.warning(\n                \"USB camera detected. Setting input framerate is NOT supported with Picamera2 API!\"\n            )\n        # log\n        self.__logging and logger.debug(\n            \"Activating Picamera2 API for `{}` camera at index: {} with resolution: {} & framerate: {}\".format(\n                camera_metadata[\"Model\"],\n                camera_num,\n                resolution if not self.__camera_is_usb else \"default\",\n                framerate,\n            )\n        )\n    else:\n        # initialize the picamera stream at given index\n        self.__camera = PiCamera(camera_num=camera_num)\n        self.__camera.resolution = tuple(resolution)\n        self.__camera.framerate = framerate\n        self.__logging and logger.debug(\n            \"Activating Picamera API at index: {} with resolution: {} & framerate: {}\".format(\n                camera_num, resolution, framerate\n            )\n        )\n\n    # initialize framerate (Read-only) variable\n    self.framerate = framerate\n\n    # initializing colorspace variable\n    self.color_space = None\n\n    # define timeout variable default value(handles hardware failures)\n    self.__failure_timeout = options.pop(\"HWFAILURE_TIMEOUT\", 2.0)\n    if isinstance(self.__failure_timeout, (int, float)):\n        if not (10.0 > self.__failure_timeout > 1.0):\n            raise ValueError(\n                \"[PiGear:ERROR] :: `HWFAILURE_TIMEOUT` value can only be between 1.0 ~ 10.0\"\n            )\n        self.__logging and logger.debug(\n            \"Setting HW Failure Timeout: {} seconds\".format(self.__failure_timeout)\n        )\n    else:\n        # reset improper values\n        self.__failure_timeout = 2.0\n\n    try:\n        if picamera2:\n            # define common supported picamera2 config parameters\n            valid_config_options = [\n                \"auto_align_output_size\",  # internal\n                \"enable_verbose_logs\",  # internal\n                \"format\",\n                \"sensor\",\n            ]\n\n            # define non-USB supported picamera2 config parameters\n            non_usb_options = [\n                \"controls\",  # not-supported on USB\n                \"transform\",  # not-working on USB\n                \"buffer_count\",  # not-supported on USB\n                \"queue\",  # not-supported on USB\n            ]  # Less are supported (will be changed in future)\n\n            # filter parameter supported with non-USB cameras only\n            if self.__camera_is_usb:\n                unsupported_config_keys = set(list(options.keys())).intersection(\n                    set(non_usb_options)\n                )\n                unsupported_config_keys and logger.warning(\n                    \"Setting parameters: `{}` for USB camera is NOT supported with Picamera2 API!\".format(\n                        \"`, `\".join(unsupported_config_keys)\n                    )\n                )\n            else:\n                valid_config_options += non_usb_options\n\n            # log all invalid keys\n            invalid_config_keys = set(list(options.keys())) - set(\n                valid_config_options\n            )\n            invalid_config_keys and logger.warning(\n                \"Discarding invalid options NOT supported by Picamera2 API for current Camera Sensor: `{}`\".format(\n                    \"`, `\".join(invalid_config_keys)\n                )\n            )\n            # delete all unsupported options\n            options = {\n                x: y for x, y in options.items() if x in valid_config_options\n            }\n\n            # setting size, already defined\n            options.update({\"size\": tuple(resolution)})\n\n            # set 24-bit, BGR format by default\n            if not \"format\" in options:\n                # auto defaults for USB cameras\n                not self.__camera_is_usb and options.update({\"format\": \"RGB888\"})\n            elif self.__camera_is_usb:\n                # check the supported formats, if USB camera\n                avail_formats = [\n                    mode[\"format\"] for mode in self.__camera.sensor_modes\n                ]\n                # handle unsupported formats\n                if not options[\"format\"] in avail_formats:\n                    logger.warning(\n                        \"Discarding `format={}`. `{}` are the only available formats for USB camera in use!\".format(\n                            options[\"format\"], \"`, `\".join(avail_formats)\n                        )\n                    )\n                    del options[\"format\"]\n                else:\n                    # `colorspace` parameter must define with  `format` optional parameter\n                    # unless format is MPEG (tested)\n                    (\n                        not (colorspace is None) or options[\"format\"] == \"MPEG\"\n                    ) and logger.warning(\n                        \"Custom Output frames `format={}` detected. It is advised to define `colorspace` parameter or handle this format manually in your code!\".format(\n                            options[\"format\"]\n                        )\n                    )\n            else:\n                # `colorspace` parameter must define with  `format` optional parameter\n                # unless format is either BGR or BGRA\n                (\n                    not (colorspace is None)\n                    or options[\"format\"]\n                    in [\n                        \"RGB888\",\n                        \"XRGB8888\",\n                    ]\n                ) and logger.warning(\n                    \"Custom Output frames `format={}` detected. It is advised to define `colorspace` parameter or handle this format manually in your code!\".format(\n                        options[\"format\"]\n                    )\n                )\n\n            # enable verbose logging mode (handled by Picamera2 API)\n            verbose = options.pop(\"enable_verbose_logs\", False)\n            if self.__logging and isinstance(verbose, bool) and verbose:\n                self.__camera.set_logging(Picamera2.DEBUG)\n            else:\n                # setup logging\n                self.__camera.set_logging(Picamera2.WARNING)\n\n            # handle transformations, if specified\n            transform = options.pop(\"transform\", Transform())\n            if not isinstance(transform, Transform):\n                logger.warning(\"`transform` value is of invalid type, Discarding!\")\n                transform = Transform()\n\n            # handle sensor configurations, if specified\n            sensor = options.pop(\"sensor\", {})\n            if isinstance(sensor, dict):\n                # extract all valid sensor keys\n                valid_sensor = [\"output_size\", \"bit_depth\"]\n                # log all invalid keys\n                invalid_sensor_keys = set(list(sensor)) - set(valid_sensor)\n                invalid_sensor_keys and logger.warning(\n                    \"Discarding sensor properties NOT supported by current Camera Sensor: `{}`. Only supported are: (`{}`)\".format(\n                        \"`, `\".join(invalid_sensor_keys),\n                        \"`, `\".join(valid_sensor),\n                    )\n                )\n                # delete all unsupported control keys\n                sensor = {x: y for x, y in sensor.items() if x in valid_sensor}\n                # remove size if output size is defined\n                if \"output_size\" in sensor:\n                    del options[\"size\"]\n                    logger.critical(\n                        \"Overriding output frame size with `output_size={}!\".format(\n                            sensor[\"output_size\"]\n                        )\n                    )\n            else:\n                logger.warning(\"`sensor` value is of invalid type, Discarding!\")\n                sensor = {}\n\n            # handle controls, if specified\n            controls = options.pop(\"controls\", {})\n            if isinstance(controls, dict):\n                # extract all valid control keys\n                valid_controls = self.__camera.camera_controls\n                # remove any fps controls, assigned already\n                valid_controls.pop(\"FrameDuration\", None)\n                valid_controls.pop(\"FrameDurationLimits\", None)\n                # log all invalid keys\n                invalid_control_keys = set(list(controls.keys())) - set(\n                    list(valid_controls.keys())\n                )\n                invalid_control_keys and logger.warning(\n                    \"Discarding control properties NOT supported by current Camera Sensor: `{}`. Only supported are: (`{}`)\".format(\n                        \"`, `\".join(invalid_control_keys),\n                        \"`, `\".join(list(valid_controls.keys())),\n                    )\n                )\n                # delete all unsupported control keys\n                controls = {\n                    x: y for x, y in controls.items() if x in valid_controls.keys()\n                }\n            else:\n                logger.warning(\"`controls` value is of invalid type, Discarding!\")\n                controls = {}\n\n            # handle buffer_count, if specified\n            buffer_count = options.pop(\"buffer_count\", 4)\n            if (\n                not isinstance(buffer_count, int) or buffer_count < 1\n            ):  # must be greater than 1\n                logger.warning(\n                    \"`buffer_count` value is of invalid type, Discarding!\"\n                )\n                # `create_preview_configuration` requests 4 sets of buffers\n                buffer_count = 4\n\n            # handle queue, if specified\n            queue = options.pop(\"queue\", True)\n            if not isinstance(queue, bool):\n                logger.warning(\"`queue` value is of invalid type, Discarding!\")\n                queue = True\n\n            # check if auto-align camera configuration is specified\n            auto_align_output_size = options.pop(\"auto_align_output_size\", False)\n\n            # create default configuration for camera\n            config = self.__camera.create_preview_configuration(\n                main=options,\n                transform=transform,\n                sensor=sensor,\n                controls=controls,\n                buffer_count=buffer_count,\n                queue=queue,\n            )\n\n            # auto-align camera configuration, if specified\n            if isinstance(auto_align_output_size, bool) and auto_align_output_size:\n                self.__logging and logger.debug(\n                    \"Re-aligning Output frames to optimal size supported by current Camera Sensor.\"\n                )\n                self.__camera.align_configuration(config)\n\n            # configure camera\n            self.__camera.configure(config)\n            self.__logging and logger.debug(\n                \"Setting Picamera2 API Parameters: `{}`, controls: `{}`, sensor: `{}`, buffer_count: `{}`, and queue: `{}`.\".format(\n                    self.__camera.camera_configuration()[\"main\"],\n                    controls,\n                    sensor,\n                    buffer_count,\n                    queue,\n                )\n            )\n        else:\n            # apply attributes to source if specified\n            for key, value in options.items():\n                self.__logging and logger.debug(\n                    \"Setting {} API Parameter for Picamera: `{}`\".format(key, value)\n                )\n                setattr(self.__camera, key, value)\n    except Exception as e:\n        # Catch if any error occurred\n        logger.exception(str(e))\n\n    # separately handle colorspace value to int conversion\n    if not (colorspace is None):\n        self.color_space = capPropId(colorspace.strip())\n        if self.__logging and not (self.color_space is None):\n            logger.debug(\n                \"Enabling `{}` colorspace for this video stream!\".format(\n                    colorspace.strip()\n                )\n            )\n\n    # enable rgb capture array thread and capture stream\n    if not picamera2:\n        self.__rawCapture = PiRGBArray(self.__camera, size=resolution)\n        self.stream = self.__camera.capture_continuous(\n            self.__rawCapture, format=\"bgr\", use_video_port=True\n        )\n\n    # initialize frame variable\n    # with captured frame\n    try:\n        if picamera2:\n            # start camera thread\n            self.__camera.start()\n            # capture frame array\n            self.frame = self.__camera.capture_array(\"main\")\n            # assign camera as stream for setting\n            # parameters after starting the camera\n            self.stream = self.__camera\n        else:\n            # capture frame array from stream\n            stream = next(self.stream)\n            self.frame = stream.array\n            self.__rawCapture.seek(0)\n            self.__rawCapture.truncate()\n        # render colorspace if defined\n        if not (self.frame is None) and not (self.color_space is None):\n            self.frame = cv2.cvtColor(self.frame, self.color_space)\n    except Exception as e:\n        logger.exception(str(e))\n        raise RuntimeError(\"[PiGear:ERROR] :: Camera Module failed to initialize!\")\n\n    # applying time delay to warm-up picamera only if specified\n    if time_delay and isinstance(time_delay, (int, float)):\n        time.sleep(time_delay)\n\n    # thread initialization\n    self.__thread = None\n\n    # timer thread initialization(Keeps check on frozen thread)\n    self.__timer = None\n    self.__t_elapsed = 0.0  # records time taken by thread\n\n    # catching thread exceptions\n    self.__exceptions = None\n\n    # initialize termination flag\n    self.__terminate = False\n
"},{"location":"bonus/reference/pigear/#vidgear.gears.pigear.PiGear.read","title":"read(self)","text":"

Extracts frames synchronously from monitored deque, while maintaining a fixed-length frame buffer in the memory, and blocks the thread if the deque is full.

Returns: A n-dimensional numpy array.

Source code in vidgear/gears/pigear.py
def read(self):\n    \"\"\"\n    Extracts frames synchronously from monitored deque, while maintaining a fixed-length frame buffer in the memory,\n    and blocks the thread if the deque is full.\n\n    **Returns:** A n-dimensional numpy array.\n    \"\"\"\n    # check if there are any thread exceptions\n    if not (self.__exceptions is None):\n        if isinstance(self.__exceptions, bool):\n            # clear frame\n            self.frame = None\n            # notify user about hardware failure\n            raise SystemError(\n                \"[PiGear:ERROR] :: Hardware failure occurred, Kindly reconnect Camera Module and restart your Pi!\"\n            )\n        else:\n            # clear frame\n            self.frame = None\n            # re-raise error for debugging\n            error_msg = (\n                \"[PiGear:ERROR] :: Camera Module API failure occurred: {}\".format(\n                    self.__exceptions[1]\n                )\n            )\n            raise RuntimeError(error_msg).with_traceback(self.__exceptions[2])\n    # return the frame\n    return self.frame\n
"},{"location":"bonus/reference/pigear/#vidgear.gears.pigear.PiGear.start","title":"start(self)","text":"

Launches the internal Threaded Frames Extractor daemon

Returns: A reference to the PiGear class object.

Source code in vidgear/gears/pigear.py
def start(self):\n    \"\"\"\n    Launches the internal *Threaded Frames Extractor* daemon\n\n    **Returns:** A reference to the PiGear class object.\n    \"\"\"\n    # Start frame producer thread\n    self.__thread = Thread(target=self.__update, name=\"PiGear\", args=())\n    self.__thread.daemon = True\n    self.__thread.start()\n\n    # Start internal timer thread\n    self.__timer = Thread(target=self.__timeit, name=\"PiTimer\", args=())\n    self.__timer.daemon = True\n    self.__timer.start()\n\n    return self\n
"},{"location":"bonus/reference/pigear/#vidgear.gears.pigear.PiGear.stop","title":"stop(self)","text":"

Safely terminates the thread, and release the multi-threaded resources.

Source code in vidgear/gears/pigear.py
def stop(self):\n    \"\"\"\n    Safely terminates the thread, and release the multi-threaded resources.\n    \"\"\"\n    # log termination\n    self.__logging and logger.debug(\"Terminating PiGear Processes.\")\n\n    # make sure that the threads should be terminated\n    self.__terminate = True\n\n    # stop timer thread\n    if not (self.__timer is None):\n        self.__timer.join()\n        self.__timer = None\n\n    # handle camera thread\n    if not (self.__thread is None):\n        # check if hardware failure occurred\n        if not (self.__exceptions is None) and isinstance(self.__exceptions, bool):\n            if picamera2:\n                # release picamera2 resources\n                self.__camera.stop()\n            else:\n                # force release picamera resources\n                self.__rawCapture.close()\n                self.__camera.close()\n        # properly handle thread exit\n        # wait if still process is still\n        # processing some information\n        self.__thread.join()\n        # remove any threads\n        self.__thread = None\n
"},{"location":"bonus/reference/screengear/","title":"ScreenGear API References","text":"

ScreenGear API usage examples can be found here \u27b6

ScreenGear API parameters are explained here \u27b6

ScreenGear is designed exclusively for targeting rapid Screencasting Capabilities, which means it can grab frames from your monitor in real-time, either by defining an area on the computer screen or full-screen, at the expense of inconsiderable latency. ScreenGear also seamlessly support frame capturing from multiple monitors as well as supports multiple backends.

ScreenGear API implements a multi-threaded wrapper around dxcam, pyscreenshot, python-mss python library, and also flexibly supports its internal parameter.

Source code in vidgear/gears/screengear.py
class ScreenGear:\n    \"\"\"\n    ScreenGear is designed exclusively for targeting rapid Screencasting Capabilities, which means it can\n    grab frames from your monitor in real-time, either by defining an area on the computer screen or full-screen,\n    at the expense of inconsiderable latency. ScreenGear also seamlessly support frame capturing from multiple\n    monitors as well as supports multiple backends.\n\n    ScreenGear API implements a multi-threaded wrapper around dxcam, pyscreenshot, python-mss python library,\n    and also flexibly supports its internal parameter.\n    \"\"\"\n\n    def __init__(\n        self, monitor=None, backend=None, colorspace=None, logging=False, **options\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the ScreenGear class.\n\n        Parameters:\n            monitor (int): enables `mss` backend and sets the index of the monitor screen.\n            backend (str): select suitable backend for extracting frames.\n            colorspace (str): selects the colorspace of the input stream.\n            logging (bool): enables/disables logging.\n            options (dict): provides the flexibility to easily alter backend library parameters. Such as, manually set the dimensions of capture screen area etc.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # create instances for the user-defined monitor\n        self.__monitor_instance = None\n        self.__backend = None\n\n        # validate monitor instance\n        assert (\n            monitor is None or monitor and isinstance(monitor, (int, tuple))\n        ), \"[ScreenGear:ERROR] :: Invalid `monitor` value detected!\"\n\n        # initialize backend\n        if backend and monitor is None:\n            self.__backend = backend.lower().strip()\n        else:\n            # enforce `dxcam` for Windows machines if undefined (or monitor is defined)\n            self.__backend = (\n                \"dxcam\" if platform.system() == \"Windows\" and dxcam else None\n            )\n\n        # initiate screen dimension handler\n        screen_dims = {}\n        # reformat proper mss dict and assign to screen dimension handler\n        screen_dims = {\n            k.strip(): v\n            for k, v in options.items()\n            if k.strip() in [\"top\", \"left\", \"width\", \"height\"]\n        }\n        # check whether user-defined dimensions are provided\n        if screen_dims and len(screen_dims) == 4:\n            key_order = (\n                (\"top\", \"left\", \"width\", \"height\")\n                if self.__backend != \"dxcam\"\n                else (\"left\", \"top\", \"width\", \"height\")\n            )\n            screen_dims = OrderedDict((k, screen_dims[k]) for k in key_order)\n            self.__logging and logger.debug(\n                \"Setting Capture-Area dimensions: {}\".format(json.dumps(screen_dims))\n            )\n        else:\n            screen_dims.clear()\n\n        # handle backends\n        if self.__backend == \"dxcam\":\n            # get target fps in case of DXcam\n            self.__target_fps = options.pop(\"dxcam_target_fps\", 0)\n            if self.__target_fps and isinstance(self.__target_fps, (int, float)):\n                # set values\n                self.__target_fps = int(self.__target_fps)\n                self.__logging and logger.debug(\n                    \"Setting Target FPS: {}\".format(self.__target_fps)\n                )\n            else:\n                # defaults to 0fps\n                self.__target_fps = 0\n            # check if platform is windows\n            assert (\n                platform.system() == \"Windows\"\n            ), \"`dxcam` backend is only available for Windows Machines.\"\n            # verify monitor values if tuple\n            assert (\n                monitor is None\n                or isinstance(monitor, int)\n                or (\n                    isinstance(monitor, tuple)\n                    and len(monitor) == 2\n                    and all(isinstance(x, int) for x in monitor)\n                )\n            ), \"For dxcam` backend, monitor` tuple value must be format `int` or `(int, int)` only.\"\n            # raise error(s) for critical Class imports\n            import_dependency_safe(\"dxcam\" if dxcam is None else \"\")\n            if monitor is None:\n                self.__capture_object = dxcam.create(\n                    region=tuple(screen_dims.values()) if screen_dims else None\n                )\n            else:\n                self.__capture_object = (\n                    dxcam.create(\n                        device_idx=monitor[0],\n                        output_idx=monitor[1],\n                        region=tuple(screen_dims.values()) if screen_dims else None,\n                    )\n                    if isinstance(monitor, tuple)\n                    else dxcam.create(\n                        device_idx=monitor,\n                        region=tuple(screen_dims.values()) if screen_dims else None,\n                    )\n                )\n        else:\n            if monitor is None:\n                # raise error(s) for critical Class imports\n                import_dependency_safe(\"pyscreenshot\" if pysct is None else \"\")\n                # reset backend if not provided\n                self.__backend = \"pil\" if self.__backend is None else self.__backend\n                # check if valid backend\n                assert (\n                    self.__backend in pysct.backends()\n                ), \"Unsupported backend {} provided!\".format(backend)\n                # create capture object\n                self.__capture_object = pysct\n            else:\n                # monitor value must be integer\n                assert monitor and isinstance(\n                    monitor, int\n                ), \"[ScreenGear:ERROR] :: Invalid `monitor` value must be integer with mss backend.\"\n                # raise error(s) for critical Class imports\n                import_dependency_safe(\n                    \"from mss import mss\" if mss is None else \"\", pkg_name=\"mss\"\n                )\n                # create capture object\n                self.__capture_object = mss()\n                self.__backend and logger.warning(\n                    \"Backends are disabled for Monitor Indexing(monitor>=0)!\"\n                )\n                self.__monitor_instance = self.__capture_object.monitors[monitor]\n\n        # log backend\n        self.__backend and self.__logging and logger.debug(\n            \"Setting Backend: {}\".format(self.__backend.upper())\n        )\n\n        # assigns special parameter to global variable and clear\n        # separately handle colorspace value to int conversion\n        if colorspace:\n            self.color_space = capPropId(colorspace.strip())\n            self.__logging and not (self.color_space is None) and logger.debug(\n                \"Enabling `{}` colorspace for this video stream!\".format(\n                    colorspace.strip()\n                )\n            )\n        else:\n            self.color_space = None\n\n        # initialize mss capture instance\n        self.__mss_capture_instance = None\n        try:\n            if self.__backend == \"dxcam\":\n                # extract global frame from instance\n                self.frame = self.__capture_object.grab()\n            else:\n                if self.__monitor_instance is None:\n                    if screen_dims:\n                        self.__mss_capture_instance = tuple(screen_dims.values())\n                    # extract global frame from instance\n                    self.frame = np.asanyarray(\n                        self.__capture_object.grab(\n                            bbox=self.__mss_capture_instance,\n                            childprocess=False,\n                            backend=self.__backend,\n                        )\n                    )\n                else:\n                    if screen_dims:\n                        self.__mss_capture_instance = {\n                            \"top\": self.__monitor_instance[\"top\"] + screen_dims[\"top\"],\n                            \"left\": self.__monitor_instance[\"left\"]\n                            + screen_dims[\"left\"],\n                            \"width\": screen_dims[\"width\"],\n                            \"height\": screen_dims[\"height\"],\n                            \"mon\": monitor,\n                        }\n                    else:\n                        self.__mss_capture_instance = (\n                            self.__monitor_instance  # otherwise create instance from monitor\n                        )\n                    # extract global frame from instance\n                    self.frame = np.asanyarray(\n                        self.__capture_object.grab(self.__mss_capture_instance)\n                    )\n            # convert to bgr frame if applicable\n            self.frame = (\n                self.frame[:, :, ::-1]\n                if self.__backend == \"dxcam\" or not (pysct is None)\n                else self.frame\n            )\n            # render colorspace if defined\n            if not (self.frame is None) and not (self.color_space is None):\n                self.frame = cv2.cvtColor(self.frame, self.color_space)\n        except Exception as e:\n            if isinstance(e, ScreenShotError):\n                # otherwise catch and log errors\n                self.__logging and logger.exception(\n                    self.__capture_object.get_error_details()\n                )\n                raise ValueError(\n                    \"[ScreenGear:ERROR] :: ScreenShotError caught, Wrong dimensions passed to python-mss, Kindly Refer Docs!\"\n                )\n            else:\n                raise SystemError(\n                    \"[ScreenGear:ERROR] :: Unable to grab any instance on this system, Are you running headless?\"\n                )\n        # thread initialization\n        self.__thread = None\n        # initialize termination flag\n        self.__terminate = Event()\n\n    def start(self):\n        \"\"\"\n        Launches the internal *Threaded Frames Extractor* daemon\n\n        **Returns:** A reference to the ScreenGear class object.\n        \"\"\"\n        self.__thread = Thread(target=self.__update, name=\"ScreenGear\", args=())\n        self.__thread.daemon = True\n        self.__thread.start()\n        if self.__backend == \"dxcam\":\n            self.__capture_object.start(\n                target_fps=self.__target_fps,\n                video_mode=True,\n            )\n            self.__logging and self.__target_fps and logger.debug(\n                \"Targeting FPS: {}\".format(self.__target_fps)\n            )\n        return self\n\n    def __update(self):\n        \"\"\"\n        A **Threaded Frames Extractor**, that keep iterating frames from `mss` API to a internal monitored deque,\n        until the thread is terminated, or frames runs out.\n        \"\"\"\n        # initialize frame variable\n        frame = None\n        # keep looping infinitely until the thread is terminated\n        while not self.__terminate.is_set():\n            try:\n                if self.__backend == \"dxcam\":\n                    # extract global frame from instance\n                    frame = self.__capture_object.get_latest_frame()\n                else:\n                    if self.__monitor_instance:\n                        frame = np.asanyarray(\n                            self.__capture_object.grab(self.__mss_capture_instance)\n                        )\n                    else:\n                        frame = np.asanyarray(\n                            self.__capture_object.grab(\n                                bbox=self.__mss_capture_instance,\n                                childprocess=False,\n                                backend=self.__backend,\n                            )\n                        )\n                # check if valid frame\n                assert not (\n                    frame is None or np.shape(frame) == ()\n                ), \"[ScreenGear:ERROR] :: Failed to retrieve valid frame!\"\n                # convert to bgr frame if applicable\n                frame = (\n                    frame[:, :, ::-1]\n                    if self.__backend == \"dxcam\" or not (pysct is None)\n                    else frame\n                )\n            except Exception as e:\n                if isinstance(e, ScreenShotError):\n                    raise RuntimeError(self.__capture_object.get_error_details())\n                else:\n                    logger.exception(str(e))\n                self.__terminate.set()\n                continue\n\n            if not (self.color_space is None):\n                # apply colorspace to frames\n                color_frame = None\n                try:\n                    color_frame = cv2.cvtColor(frame, self.color_space)\n                except Exception as e:\n                    # Catch if any error occurred\n                    color_frame = None\n                    self.color_space = None\n                    self.__logging and logger.exception(str(e))\n                    logger.warning(\"Assigned colorspace value is invalid. Discarding!\")\n                self.frame = color_frame if not (color_frame is None) else frame\n            else:\n                self.frame = frame\n\n        # indicate immediate termination\n        self.__terminate.set()\n\n        # finally release mss resources\n        if self.__monitor_instance:\n            self.__capture_object.close()\n        if self.__backend == \"dxcam\":\n            self.__capture_object.stop()\n            del self.__capture_object\n\n    def read(self):\n        \"\"\"\n        Extracts frames synchronously from monitored deque, while maintaining a fixed-length frame buffer in the memory,\n        and blocks the thread if the deque is full.\n\n        **Returns:** A n-dimensional numpy array.\n        \"\"\"\n        # return the frame\n        return self.frame\n\n    def stop(self):\n        \"\"\"\n        Safely terminates the thread, and release the resources.\n        \"\"\"\n        self.__logging and logger.debug(\"Terminating ScreenGear Processes.\")\n\n        # indicate that the thread should be terminate\n        self.__terminate.set()\n\n        # wait until stream resources are released (producer thread might be still grabbing frame)\n        not (self.__thread is None) and self.__thread.join()\n

"},{"location":"bonus/reference/screengear/#vidgear.gears.screengear.ScreenGear.__init__","title":"__init__(self, monitor=None, backend=None, colorspace=None, logging=False, **options) special","text":"

This constructor method initializes the object state and attributes of the ScreenGear class.

Parameters:

Name Type Description Default monitor int

enables mss backend and sets the index of the monitor screen.

None backend str

select suitable backend for extracting frames.

None colorspace str

selects the colorspace of the input stream.

None logging bool

enables/disables logging.

False options dict

provides the flexibility to easily alter backend library parameters. Such as, manually set the dimensions of capture screen area etc.

{} Source code in vidgear/gears/screengear.py
def __init__(\n    self, monitor=None, backend=None, colorspace=None, logging=False, **options\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the ScreenGear class.\n\n    Parameters:\n        monitor (int): enables `mss` backend and sets the index of the monitor screen.\n        backend (str): select suitable backend for extracting frames.\n        colorspace (str): selects the colorspace of the input stream.\n        logging (bool): enables/disables logging.\n        options (dict): provides the flexibility to easily alter backend library parameters. Such as, manually set the dimensions of capture screen area etc.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # create instances for the user-defined monitor\n    self.__monitor_instance = None\n    self.__backend = None\n\n    # validate monitor instance\n    assert (\n        monitor is None or monitor and isinstance(monitor, (int, tuple))\n    ), \"[ScreenGear:ERROR] :: Invalid `monitor` value detected!\"\n\n    # initialize backend\n    if backend and monitor is None:\n        self.__backend = backend.lower().strip()\n    else:\n        # enforce `dxcam` for Windows machines if undefined (or monitor is defined)\n        self.__backend = (\n            \"dxcam\" if platform.system() == \"Windows\" and dxcam else None\n        )\n\n    # initiate screen dimension handler\n    screen_dims = {}\n    # reformat proper mss dict and assign to screen dimension handler\n    screen_dims = {\n        k.strip(): v\n        for k, v in options.items()\n        if k.strip() in [\"top\", \"left\", \"width\", \"height\"]\n    }\n    # check whether user-defined dimensions are provided\n    if screen_dims and len(screen_dims) == 4:\n        key_order = (\n            (\"top\", \"left\", \"width\", \"height\")\n            if self.__backend != \"dxcam\"\n            else (\"left\", \"top\", \"width\", \"height\")\n        )\n        screen_dims = OrderedDict((k, screen_dims[k]) for k in key_order)\n        self.__logging and logger.debug(\n            \"Setting Capture-Area dimensions: {}\".format(json.dumps(screen_dims))\n        )\n    else:\n        screen_dims.clear()\n\n    # handle backends\n    if self.__backend == \"dxcam\":\n        # get target fps in case of DXcam\n        self.__target_fps = options.pop(\"dxcam_target_fps\", 0)\n        if self.__target_fps and isinstance(self.__target_fps, (int, float)):\n            # set values\n            self.__target_fps = int(self.__target_fps)\n            self.__logging and logger.debug(\n                \"Setting Target FPS: {}\".format(self.__target_fps)\n            )\n        else:\n            # defaults to 0fps\n            self.__target_fps = 0\n        # check if platform is windows\n        assert (\n            platform.system() == \"Windows\"\n        ), \"`dxcam` backend is only available for Windows Machines.\"\n        # verify monitor values if tuple\n        assert (\n            monitor is None\n            or isinstance(monitor, int)\n            or (\n                isinstance(monitor, tuple)\n                and len(monitor) == 2\n                and all(isinstance(x, int) for x in monitor)\n            )\n        ), \"For dxcam` backend, monitor` tuple value must be format `int` or `(int, int)` only.\"\n        # raise error(s) for critical Class imports\n        import_dependency_safe(\"dxcam\" if dxcam is None else \"\")\n        if monitor is None:\n            self.__capture_object = dxcam.create(\n                region=tuple(screen_dims.values()) if screen_dims else None\n            )\n        else:\n            self.__capture_object = (\n                dxcam.create(\n                    device_idx=monitor[0],\n                    output_idx=monitor[1],\n                    region=tuple(screen_dims.values()) if screen_dims else None,\n                )\n                if isinstance(monitor, tuple)\n                else dxcam.create(\n                    device_idx=monitor,\n                    region=tuple(screen_dims.values()) if screen_dims else None,\n                )\n            )\n    else:\n        if monitor is None:\n            # raise error(s) for critical Class imports\n            import_dependency_safe(\"pyscreenshot\" if pysct is None else \"\")\n            # reset backend if not provided\n            self.__backend = \"pil\" if self.__backend is None else self.__backend\n            # check if valid backend\n            assert (\n                self.__backend in pysct.backends()\n            ), \"Unsupported backend {} provided!\".format(backend)\n            # create capture object\n            self.__capture_object = pysct\n        else:\n            # monitor value must be integer\n            assert monitor and isinstance(\n                monitor, int\n            ), \"[ScreenGear:ERROR] :: Invalid `monitor` value must be integer with mss backend.\"\n            # raise error(s) for critical Class imports\n            import_dependency_safe(\n                \"from mss import mss\" if mss is None else \"\", pkg_name=\"mss\"\n            )\n            # create capture object\n            self.__capture_object = mss()\n            self.__backend and logger.warning(\n                \"Backends are disabled for Monitor Indexing(monitor>=0)!\"\n            )\n            self.__monitor_instance = self.__capture_object.monitors[monitor]\n\n    # log backend\n    self.__backend and self.__logging and logger.debug(\n        \"Setting Backend: {}\".format(self.__backend.upper())\n    )\n\n    # assigns special parameter to global variable and clear\n    # separately handle colorspace value to int conversion\n    if colorspace:\n        self.color_space = capPropId(colorspace.strip())\n        self.__logging and not (self.color_space is None) and logger.debug(\n            \"Enabling `{}` colorspace for this video stream!\".format(\n                colorspace.strip()\n            )\n        )\n    else:\n        self.color_space = None\n\n    # initialize mss capture instance\n    self.__mss_capture_instance = None\n    try:\n        if self.__backend == \"dxcam\":\n            # extract global frame from instance\n            self.frame = self.__capture_object.grab()\n        else:\n            if self.__monitor_instance is None:\n                if screen_dims:\n                    self.__mss_capture_instance = tuple(screen_dims.values())\n                # extract global frame from instance\n                self.frame = np.asanyarray(\n                    self.__capture_object.grab(\n                        bbox=self.__mss_capture_instance,\n                        childprocess=False,\n                        backend=self.__backend,\n                    )\n                )\n            else:\n                if screen_dims:\n                    self.__mss_capture_instance = {\n                        \"top\": self.__monitor_instance[\"top\"] + screen_dims[\"top\"],\n                        \"left\": self.__monitor_instance[\"left\"]\n                        + screen_dims[\"left\"],\n                        \"width\": screen_dims[\"width\"],\n                        \"height\": screen_dims[\"height\"],\n                        \"mon\": monitor,\n                    }\n                else:\n                    self.__mss_capture_instance = (\n                        self.__monitor_instance  # otherwise create instance from monitor\n                    )\n                # extract global frame from instance\n                self.frame = np.asanyarray(\n                    self.__capture_object.grab(self.__mss_capture_instance)\n                )\n        # convert to bgr frame if applicable\n        self.frame = (\n            self.frame[:, :, ::-1]\n            if self.__backend == \"dxcam\" or not (pysct is None)\n            else self.frame\n        )\n        # render colorspace if defined\n        if not (self.frame is None) and not (self.color_space is None):\n            self.frame = cv2.cvtColor(self.frame, self.color_space)\n    except Exception as e:\n        if isinstance(e, ScreenShotError):\n            # otherwise catch and log errors\n            self.__logging and logger.exception(\n                self.__capture_object.get_error_details()\n            )\n            raise ValueError(\n                \"[ScreenGear:ERROR] :: ScreenShotError caught, Wrong dimensions passed to python-mss, Kindly Refer Docs!\"\n            )\n        else:\n            raise SystemError(\n                \"[ScreenGear:ERROR] :: Unable to grab any instance on this system, Are you running headless?\"\n            )\n    # thread initialization\n    self.__thread = None\n    # initialize termination flag\n    self.__terminate = Event()\n
"},{"location":"bonus/reference/screengear/#vidgear.gears.screengear.ScreenGear.read","title":"read(self)","text":"

Extracts frames synchronously from monitored deque, while maintaining a fixed-length frame buffer in the memory, and blocks the thread if the deque is full.

Returns: A n-dimensional numpy array.

Source code in vidgear/gears/screengear.py
def read(self):\n    \"\"\"\n    Extracts frames synchronously from monitored deque, while maintaining a fixed-length frame buffer in the memory,\n    and blocks the thread if the deque is full.\n\n    **Returns:** A n-dimensional numpy array.\n    \"\"\"\n    # return the frame\n    return self.frame\n
"},{"location":"bonus/reference/screengear/#vidgear.gears.screengear.ScreenGear.start","title":"start(self)","text":"

Launches the internal Threaded Frames Extractor daemon

Returns: A reference to the ScreenGear class object.

Source code in vidgear/gears/screengear.py
def start(self):\n    \"\"\"\n    Launches the internal *Threaded Frames Extractor* daemon\n\n    **Returns:** A reference to the ScreenGear class object.\n    \"\"\"\n    self.__thread = Thread(target=self.__update, name=\"ScreenGear\", args=())\n    self.__thread.daemon = True\n    self.__thread.start()\n    if self.__backend == \"dxcam\":\n        self.__capture_object.start(\n            target_fps=self.__target_fps,\n            video_mode=True,\n        )\n        self.__logging and self.__target_fps and logger.debug(\n            \"Targeting FPS: {}\".format(self.__target_fps)\n        )\n    return self\n
"},{"location":"bonus/reference/screengear/#vidgear.gears.screengear.ScreenGear.stop","title":"stop(self)","text":"

Safely terminates the thread, and release the resources.

Source code in vidgear/gears/screengear.py
def stop(self):\n    \"\"\"\n    Safely terminates the thread, and release the resources.\n    \"\"\"\n    self.__logging and logger.debug(\"Terminating ScreenGear Processes.\")\n\n    # indicate that the thread should be terminate\n    self.__terminate.set()\n\n    # wait until stream resources are released (producer thread might be still grabbing frame)\n    not (self.__thread is None) and self.__thread.join()\n
"},{"location":"bonus/reference/stabilizer/","title":"API References","text":"

Stabilizer API usage examples can be found here \u27b6

Stabilizer API parameters are explained here \u27b6

This is an auxiliary class that enables Video Stabilization for vidgear with minimalistic latency, and at the expense of little to no additional computational requirements.

The basic idea behind it is to tracks and save the salient feature array for the given number of frames and then uses these anchor point to cancel out all perturbations relative to it for the incoming frames in the queue. This class relies heavily on Threaded Queue mode for error-free & ultra-fast frame handling.

Source code in vidgear/gears/stabilizer.py
class Stabilizer:\n    \"\"\"\n    This is an auxiliary class that enables Video Stabilization for vidgear with minimalistic latency, and at the expense\n    of little to no additional computational requirements.\n\n    The basic idea behind it is to tracks and save the salient feature array for the given number of frames and then uses\n    these anchor point to cancel out all perturbations relative to it for the incoming frames in the queue. This class relies\n    heavily on **Threaded Queue mode** for error-free & ultra-fast frame handling.\n    \"\"\"\n\n    def __init__(\n        self,\n        smoothing_radius=25,\n        border_type=\"black\",\n        border_size=0,\n        crop_n_zoom=False,\n        logging=False,\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the Stabilizer class.\n\n        Parameters:\n            smoothing_radius (int): alter averaging window size.\n            border_type (str): changes the extended border type.\n            border_size (int): enables and set the value for extended border size to reduce the black borders.\n            crop_n_zoom (bool): enables cropping and zooming of frames(to original size) to reduce the black borders.\n            logging (bool): enables/disables logging.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # initialize deques for handling input frames and its indexes\n        self.__frame_queue = deque(maxlen=smoothing_radius)\n        self.__frame_queue_indexes = deque(maxlen=smoothing_radius)\n\n        # define and create Adaptive histogram equalization (AHE) object for optimizations\n        self.__clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))\n\n        # initialize global vars\n        self.__smoothing_radius = smoothing_radius  # averaging window, handles the quality of stabilization at expense of latency and sudden panning\n        self.__smoothed_path = None  # handles the smoothed path with box filter\n        self.__path = None  # handles path i.e cumulative sum of previous_2_current transformations along a axis\n        self.__transforms = []  # handles previous_2_current transformations [dx,dy,da]\n        self.__frame_transforms_smoothed = None  # handles smoothed array of previous_2_current transformations w.r.t to frames\n        self.__previous_gray = None  # handles previous gray frame\n        self.__previous_keypoints = (\n            None  # handles previous detect_GFTTed keypoints w.r.t previous gray frame\n        )\n        self.__frame_height, self.frame_width = (\n            0,\n            0,\n        )  # handles width and height of input frames\n        self.__crop_n_zoom = 0  # handles cropping and zooms frames to reduce the black borders from stabilization being too noticeable.\n\n        # if check if crop_n_zoom defined\n        if crop_n_zoom and border_size:\n            self.__crop_n_zoom = border_size  # crops and zoom frame to original size\n            self.__border_size = 0  # zero out border size\n            self.__frame_size = None  # handles frame size for zooming\n            self.__logging and logger.debug(\n                \"Setting Cropping margin {} pixels\".format(border_size)\n            )\n        else:\n            # Add output borders to frame\n            self.__border_size = border_size\n            self.__logging and border_size and logger.debug(\n                \"Setting Border size {} pixels\".format(border_size)\n            )\n\n        # define valid border modes\n        border_modes = {\n            \"black\": cv2.BORDER_CONSTANT,\n            \"reflect\": cv2.BORDER_REFLECT,\n            \"reflect_101\": cv2.BORDER_REFLECT_101,\n            \"replicate\": cv2.BORDER_REPLICATE,\n            \"wrap\": cv2.BORDER_WRAP,\n        }\n        # choose valid border_mode from border_type\n        if border_type in [\"black\", \"reflect\", \"reflect_101\", \"replicate\", \"wrap\"]:\n            if not crop_n_zoom:\n                # initialize global border mode variable\n                self.__border_mode = border_modes[border_type]\n                self.__logging and border_type != \"black\" and logger.info(\n                    \"Setting Border type: {}\".format(border_type)\n                )\n            else:\n                # log and reset to default\n                self.__logging and border_type != \"black\" and logger.debug(\n                    \"Setting border type is disabled if cropping is enabled!\"\n                )\n                self.__border_mode = border_modes[\"black\"]\n        else:\n            # otherwise log if not\n            self.__logging and logger.debug(\"Invalid input border type!\")\n            self.__border_mode = border_modes[\"black\"]  # reset to default mode\n\n        # define OpenCV version\n        self.__cv2_version = check_CV_version()\n\n        # retrieve best interpolation\n        self.__interpolation = retrieve_best_interpolation(\n            [\"INTER_LINEAR_EXACT\", \"INTER_LINEAR\", \"INTER_AREA\"]\n        )\n\n        # define normalized box filter\n        self.__box_filter = np.ones(smoothing_radius) / smoothing_radius\n\n    def stabilize(self, frame):\n        \"\"\"\n        This method takes an unstabilized video frame, and returns a stabilized one.\n\n        Parameters:\n            frame (numpy.ndarray): inputs unstabilized video frames.\n        \"\"\"\n        # check if frame is None\n        if frame is None:\n            # return if it does\n            return\n\n        # save frame size for zooming\n        if self.__crop_n_zoom and self.__frame_size == None:\n            self.__frame_size = frame.shape[:2]\n\n        # initiate transformations capturing\n        if not self.__frame_queue:\n            # for first frame\n            previous_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)  # convert to gray\n            previous_gray = self.__clahe.apply(previous_gray)  # optimize gray frame\n            self.__previous_keypoints = cv2.goodFeaturesToTrack(\n                previous_gray,\n                maxCorners=200,\n                qualityLevel=0.05,\n                minDistance=30.0,\n                blockSize=3,\n                mask=None,\n                useHarrisDetector=False,\n                k=0.04,\n            )  # track features using GFTT\n            self.__frame_height, self.frame_width = frame.shape[\n                :2\n            ]  # save input frame height and width\n            self.__frame_queue.append(frame)  # save frame to deque\n            self.__frame_queue_indexes.append(0)  # save frame index to deque\n            self.__previous_gray = previous_gray[\n                :\n            ]  # save gray frame clone for further processing\n\n        elif self.__frame_queue_indexes[-1] < self.__smoothing_radius - 1:\n            # for rest of frames\n            self.__frame_queue.append(frame)  # save frame to deque\n            self.__frame_queue_indexes.append(\n                self.__frame_queue_indexes[-1] + 1\n            )  # save frame index\n            self.__generate_transformations()  # generate transformations\n        else:\n            # start applying transformations\n            self.__frame_queue.append(frame)  # save frame to deque\n            self.__frame_queue_indexes.append(\n                self.__frame_queue_indexes[-1] + 1\n            )  # save frame index\n            self.__generate_transformations()  # generate transformations\n            # calculate smooth path once transformation capturing is completed\n            for i in range(3):\n                # apply normalized box filter to the path\n                self.__smoothed_path[:, i] = self.__box_filter_convolve(\n                    (self.__path[:, i]), window_size=self.__smoothing_radius\n                )\n            # calculate deviation of path from smoothed path\n            deviation = self.__smoothed_path - self.__path\n            # save smoothed transformation\n            self.__frame_transforms_smoothed = self.frame_transform + deviation\n            # return transformation applied stabilized frame\n            return self.__apply_transformations()\n\n    def __generate_transformations(self):\n        \"\"\"\n        An internal method that generate previous-to-current transformations [dx,dy,da].\n        \"\"\"\n        frame_gray = cv2.cvtColor(\n            self.__frame_queue[-1], cv2.COLOR_BGR2GRAY\n        )  # retrieve current frame and convert to gray\n        frame_gray = self.__clahe.apply(frame_gray)  # optimize it\n\n        transformation = None\n        try:\n            # calculate optical flow using Lucas-Kanade differential method\n            curr_kps, status, error = cv2.calcOpticalFlowPyrLK(\n                self.__previous_gray, frame_gray, self.__previous_keypoints, None\n            )\n\n            # select only valid key-points\n            valid_curr_kps = curr_kps[status == 1]  # current\n            valid_previous_keypoints = self.__previous_keypoints[\n                status == 1\n            ]  # previous\n\n            # calculate optimal affine transformation between previous_2_current key-points\n            if self.__cv2_version == 3:\n                # backward compatibility with OpenCV3\n                transformation = cv2.estimateRigidTransform(\n                    valid_previous_keypoints, valid_curr_kps, False\n                )\n            else:\n                transformation = cv2.estimateAffinePartial2D(\n                    valid_previous_keypoints, valid_curr_kps\n                )[0]\n        except cv2.error as e:\n            # catch any OpenCV assertion errors and warn user\n            logger.warning(\"Video-Frame is too dark to generate any transformations!\")\n            transformation = None\n\n        # check if transformation is not None\n        if not (transformation is None):\n            # previous_2_current translation in x direction\n            dx = transformation[0, 2]\n            # previous_2_current translation in y direction\n            dy = transformation[1, 2]\n            # previous_2_current rotation in angle\n            da = np.arctan2(transformation[1, 0], transformation[0, 0])\n        else:\n            # otherwise zero it\n            dx = dy = da = 0\n\n        # save this transformation\n        self.__transforms.append([dx, dy, da])\n\n        # calculate path from cumulative transformations sum\n        self.frame_transform = np.array(self.__transforms, dtype=\"float32\")\n        self.__path = np.cumsum(self.frame_transform, axis=0)\n        # create smoothed path from a copy of path\n        self.__smoothed_path = np.copy(self.__path)\n\n        # re-calculate and save GFTT key-points for current gray frame\n        self.__previous_keypoints = cv2.goodFeaturesToTrack(\n            frame_gray,\n            maxCorners=200,\n            qualityLevel=0.05,\n            minDistance=30.0,\n            blockSize=3,\n            mask=None,\n            useHarrisDetector=False,\n            k=0.04,\n        )\n        # save this gray frame for further processing\n        self.__previous_gray = frame_gray[:]\n\n    def __box_filter_convolve(self, path, window_size):\n        \"\"\"\n        An internal method that applies *normalized linear box filter* to path w.r.t averaging window\n\n        Parameters:\n\n        * path (numpy.ndarray): a cumulative sum of transformations\n        * window_size (int): averaging window size\n        \"\"\"\n        # pad path to size of averaging window\n        path_padded = np.pad(path, (window_size, window_size), \"median\")\n        # apply linear box filter to path\n        path_smoothed = np.convolve(path_padded, self.__box_filter, mode=\"same\")\n        # crop the smoothed path to original path\n        path_smoothed = path_smoothed[window_size:-window_size]\n        # assert if cropping is completed\n        assert path.shape == path_smoothed.shape\n        # return smoothed path\n        return path_smoothed\n\n    def __apply_transformations(self):\n        \"\"\"\n        An internal method that applies affine transformation to the given frame\n        from previously calculated transformations\n        \"\"\"\n        # extract frame and its index from deque\n        queue_frame = self.__frame_queue.popleft()\n        queue_frame_index = self.__frame_queue_indexes.popleft()\n\n        # create border around extracted frame w.r.t border_size\n        bordered_frame = cv2.copyMakeBorder(\n            queue_frame,\n            top=self.__border_size,\n            bottom=self.__border_size,\n            left=self.__border_size,\n            right=self.__border_size,\n            borderType=self.__border_mode,\n            value=[0, 0, 0],\n        )\n        alpha_bordered_frame = cv2.cvtColor(\n            bordered_frame, cv2.COLOR_BGR2BGRA\n        )  # create alpha channel\n        # extract alpha channel\n        alpha_bordered_frame[:, :, 3] = 0\n        alpha_bordered_frame[\n            self.__border_size : self.__border_size + self.__frame_height,\n            self.__border_size : self.__border_size + self.frame_width,\n            3,\n        ] = 255\n\n        # extracting Transformations w.r.t frame index\n        dx = self.__frame_transforms_smoothed[queue_frame_index, 0]  # x-axis\n        dy = self.__frame_transforms_smoothed[queue_frame_index, 1]  # y-axis\n        da = self.__frame_transforms_smoothed[queue_frame_index, 2]  # angle\n\n        # building 2x3 transformation matrix from extracted transformations\n        queue_frame_transform = np.zeros((2, 3), np.float32)\n        queue_frame_transform[0, 0] = np.cos(da)\n        queue_frame_transform[0, 1] = -np.sin(da)\n        queue_frame_transform[1, 0] = np.sin(da)\n        queue_frame_transform[1, 1] = np.cos(da)\n        queue_frame_transform[0, 2] = dx\n        queue_frame_transform[1, 2] = dy\n\n        # Applying an affine transformation to the frame\n        frame_wrapped = cv2.warpAffine(\n            alpha_bordered_frame,\n            queue_frame_transform,\n            alpha_bordered_frame.shape[:2][::-1],\n            borderMode=self.__border_mode,\n        )\n\n        # drop alpha channel\n        frame_stabilized = frame_wrapped[:, :, :3]\n\n        # crop and zoom\n        if self.__crop_n_zoom:\n            # crop stabilized frame\n            frame_cropped = frame_stabilized[\n                self.__crop_n_zoom : -self.__crop_n_zoom,\n                self.__crop_n_zoom : -self.__crop_n_zoom,\n            ]\n            # zoom stabilized frame\n            frame_stabilized = cv2.resize(\n                frame_cropped,\n                self.__frame_size[::-1],\n                interpolation=self.__interpolation,\n            )\n\n        # finally return stabilized frame\n        return frame_stabilized\n\n    def clean(self):\n        \"\"\"\n        Cleans Stabilizer resources\n        \"\"\"\n        # check if deque present\n        if self.__frame_queue:\n            # clear frame deque\n            self.__frame_queue.clear()\n            # clear frame indexes deque\n            self.__frame_queue_indexes.clear()\n

"},{"location":"bonus/reference/stabilizer/#vidgear.gears.stabilizer.Stabilizer.__init__","title":"__init__(self, smoothing_radius=25, border_type='black', border_size=0, crop_n_zoom=False, logging=False) special","text":"

This constructor method initializes the object state and attributes of the Stabilizer class.

Parameters:

Name Type Description Default smoothing_radius int

alter averaging window size.

25 border_type str

changes the extended border type.

'black' border_size int

enables and set the value for extended border size to reduce the black borders.

0 crop_n_zoom bool

enables cropping and zooming of frames(to original size) to reduce the black borders.

False logging bool

enables/disables logging.

False Source code in vidgear/gears/stabilizer.py
def __init__(\n    self,\n    smoothing_radius=25,\n    border_type=\"black\",\n    border_size=0,\n    crop_n_zoom=False,\n    logging=False,\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the Stabilizer class.\n\n    Parameters:\n        smoothing_radius (int): alter averaging window size.\n        border_type (str): changes the extended border type.\n        border_size (int): enables and set the value for extended border size to reduce the black borders.\n        crop_n_zoom (bool): enables cropping and zooming of frames(to original size) to reduce the black borders.\n        logging (bool): enables/disables logging.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # initialize deques for handling input frames and its indexes\n    self.__frame_queue = deque(maxlen=smoothing_radius)\n    self.__frame_queue_indexes = deque(maxlen=smoothing_radius)\n\n    # define and create Adaptive histogram equalization (AHE) object for optimizations\n    self.__clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))\n\n    # initialize global vars\n    self.__smoothing_radius = smoothing_radius  # averaging window, handles the quality of stabilization at expense of latency and sudden panning\n    self.__smoothed_path = None  # handles the smoothed path with box filter\n    self.__path = None  # handles path i.e cumulative sum of previous_2_current transformations along a axis\n    self.__transforms = []  # handles previous_2_current transformations [dx,dy,da]\n    self.__frame_transforms_smoothed = None  # handles smoothed array of previous_2_current transformations w.r.t to frames\n    self.__previous_gray = None  # handles previous gray frame\n    self.__previous_keypoints = (\n        None  # handles previous detect_GFTTed keypoints w.r.t previous gray frame\n    )\n    self.__frame_height, self.frame_width = (\n        0,\n        0,\n    )  # handles width and height of input frames\n    self.__crop_n_zoom = 0  # handles cropping and zooms frames to reduce the black borders from stabilization being too noticeable.\n\n    # if check if crop_n_zoom defined\n    if crop_n_zoom and border_size:\n        self.__crop_n_zoom = border_size  # crops and zoom frame to original size\n        self.__border_size = 0  # zero out border size\n        self.__frame_size = None  # handles frame size for zooming\n        self.__logging and logger.debug(\n            \"Setting Cropping margin {} pixels\".format(border_size)\n        )\n    else:\n        # Add output borders to frame\n        self.__border_size = border_size\n        self.__logging and border_size and logger.debug(\n            \"Setting Border size {} pixels\".format(border_size)\n        )\n\n    # define valid border modes\n    border_modes = {\n        \"black\": cv2.BORDER_CONSTANT,\n        \"reflect\": cv2.BORDER_REFLECT,\n        \"reflect_101\": cv2.BORDER_REFLECT_101,\n        \"replicate\": cv2.BORDER_REPLICATE,\n        \"wrap\": cv2.BORDER_WRAP,\n    }\n    # choose valid border_mode from border_type\n    if border_type in [\"black\", \"reflect\", \"reflect_101\", \"replicate\", \"wrap\"]:\n        if not crop_n_zoom:\n            # initialize global border mode variable\n            self.__border_mode = border_modes[border_type]\n            self.__logging and border_type != \"black\" and logger.info(\n                \"Setting Border type: {}\".format(border_type)\n            )\n        else:\n            # log and reset to default\n            self.__logging and border_type != \"black\" and logger.debug(\n                \"Setting border type is disabled if cropping is enabled!\"\n            )\n            self.__border_mode = border_modes[\"black\"]\n    else:\n        # otherwise log if not\n        self.__logging and logger.debug(\"Invalid input border type!\")\n        self.__border_mode = border_modes[\"black\"]  # reset to default mode\n\n    # define OpenCV version\n    self.__cv2_version = check_CV_version()\n\n    # retrieve best interpolation\n    self.__interpolation = retrieve_best_interpolation(\n        [\"INTER_LINEAR_EXACT\", \"INTER_LINEAR\", \"INTER_AREA\"]\n    )\n\n    # define normalized box filter\n    self.__box_filter = np.ones(smoothing_radius) / smoothing_radius\n
"},{"location":"bonus/reference/stabilizer/#vidgear.gears.stabilizer.Stabilizer.clean","title":"clean(self)","text":"

Cleans Stabilizer resources

Source code in vidgear/gears/stabilizer.py
def clean(self):\n    \"\"\"\n    Cleans Stabilizer resources\n    \"\"\"\n    # check if deque present\n    if self.__frame_queue:\n        # clear frame deque\n        self.__frame_queue.clear()\n        # clear frame indexes deque\n        self.__frame_queue_indexes.clear()\n
"},{"location":"bonus/reference/stabilizer/#vidgear.gears.stabilizer.Stabilizer.stabilize","title":"stabilize(self, frame)","text":"

This method takes an unstabilized video frame, and returns a stabilized one.

Parameters:

Name Type Description Default frame numpy.ndarray

inputs unstabilized video frames.

required Source code in vidgear/gears/stabilizer.py
def stabilize(self, frame):\n    \"\"\"\n    This method takes an unstabilized video frame, and returns a stabilized one.\n\n    Parameters:\n        frame (numpy.ndarray): inputs unstabilized video frames.\n    \"\"\"\n    # check if frame is None\n    if frame is None:\n        # return if it does\n        return\n\n    # save frame size for zooming\n    if self.__crop_n_zoom and self.__frame_size == None:\n        self.__frame_size = frame.shape[:2]\n\n    # initiate transformations capturing\n    if not self.__frame_queue:\n        # for first frame\n        previous_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)  # convert to gray\n        previous_gray = self.__clahe.apply(previous_gray)  # optimize gray frame\n        self.__previous_keypoints = cv2.goodFeaturesToTrack(\n            previous_gray,\n            maxCorners=200,\n            qualityLevel=0.05,\n            minDistance=30.0,\n            blockSize=3,\n            mask=None,\n            useHarrisDetector=False,\n            k=0.04,\n        )  # track features using GFTT\n        self.__frame_height, self.frame_width = frame.shape[\n            :2\n        ]  # save input frame height and width\n        self.__frame_queue.append(frame)  # save frame to deque\n        self.__frame_queue_indexes.append(0)  # save frame index to deque\n        self.__previous_gray = previous_gray[\n            :\n        ]  # save gray frame clone for further processing\n\n    elif self.__frame_queue_indexes[-1] < self.__smoothing_radius - 1:\n        # for rest of frames\n        self.__frame_queue.append(frame)  # save frame to deque\n        self.__frame_queue_indexes.append(\n            self.__frame_queue_indexes[-1] + 1\n        )  # save frame index\n        self.__generate_transformations()  # generate transformations\n    else:\n        # start applying transformations\n        self.__frame_queue.append(frame)  # save frame to deque\n        self.__frame_queue_indexes.append(\n            self.__frame_queue_indexes[-1] + 1\n        )  # save frame index\n        self.__generate_transformations()  # generate transformations\n        # calculate smooth path once transformation capturing is completed\n        for i in range(3):\n            # apply normalized box filter to the path\n            self.__smoothed_path[:, i] = self.__box_filter_convolve(\n                (self.__path[:, i]), window_size=self.__smoothing_radius\n            )\n        # calculate deviation of path from smoothed path\n        deviation = self.__smoothed_path - self.__path\n        # save smoothed transformation\n        self.__frame_transforms_smoothed = self.frame_transform + deviation\n        # return transformation applied stabilized frame\n        return self.__apply_transformations()\n
"},{"location":"bonus/reference/streamgear/","title":"StreamGear API References","text":"

StreamGear API usage examples for: Single-Source Mode \u27b6 and Real-time Frames Mode \u27b6

StreamGear API parameters are explained here \u27b6

StreamGear automates transcoding workflow for generating Ultra-Low Latency, High-Quality, Dynamic & Adaptive Streaming Formats (such as MPEG-DASH and HLS) in just few lines of python code. StreamGear provides a standalone, highly extensible, and flexible wrapper around FFmpeg multimedia framework for generating chunked-encoded media segments of the content.

SteamGear easily transcodes source videos/audio files & real-time video-frames and breaks them into a sequence of multiple smaller chunks/segments of suitable length. These segments make it possible to stream videos at different quality levels (different bitrate or spatial resolutions) and can be switched in the middle of a video from one quality level to another - if bandwidth permits - on a per-segment basis. A user can serve these segments on a web server that makes it easier to download them through HTTP standard-compliant GET requests.

SteamGear also creates a Manifest/Playlist file (such as MPD in-case of DASH and M3U8 in-case of HLS) besides segments that describe these segment information (timing, URL, media characteristics like video resolution and bit rates) and is provided to the client before the streaming session.

SteamGear currently supports MPEG-DASH (Dynamic Adaptive Streaming over HTTP, ISO/IEC 23009-1) and Apple HLS (HTTP live streaming).

Source code in vidgear/gears/streamgear.py
class StreamGear:\n    \"\"\"\n    StreamGear automates transcoding workflow for generating Ultra-Low Latency, High-Quality, Dynamic & Adaptive Streaming Formats (such as MPEG-DASH and HLS) in just few lines of python code.\n    StreamGear provides a standalone, highly extensible, and flexible wrapper around FFmpeg multimedia framework for generating chunked-encoded media segments of the content.\n\n    SteamGear easily transcodes source videos/audio files & real-time video-frames and breaks them into a sequence of multiple smaller chunks/segments of suitable length. These segments make it\n    possible to stream videos at different quality levels _(different bitrate or spatial resolutions)_ and can be switched in the middle of a video from one quality level to another - if bandwidth\n    permits - on a per-segment basis. A user can serve these segments on a web server that makes it easier to download them through HTTP standard-compliant GET requests.\n\n    SteamGear also creates a Manifest/Playlist file (such as MPD in-case of DASH and M3U8 in-case of HLS) besides segments that describe these segment information\n    (timing, URL, media characteristics like video resolution and bit rates) and is provided to the client before the streaming session.\n\n    SteamGear currently supports MPEG-DASH (Dynamic Adaptive Streaming over HTTP, ISO/IEC 23009-1) and Apple HLS (HTTP live streaming).\n    \"\"\"\n\n    def __init__(\n        self, output=\"\", format=\"dash\", custom_ffmpeg=\"\", logging=False, **stream_params\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the StreamGear class.\n\n        Parameters:\n            output (str): sets the valid filename/path for generating the StreamGear assets.\n            format (str): select the adaptive HTTP streaming format(DASH and HLS).\n            custom_ffmpeg (str): assigns the location of custom path/directory for custom FFmpeg executables.\n            logging (bool): enables/disables logging.\n            stream_params (dict): provides the flexibility to control supported internal parameters and FFmpeg properties.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # checks if machine in-use is running windows os or not\n        self.__os_windows = True if os.name == \"nt\" else False\n\n        # initialize various class variables\n        # handles user-defined parameters\n        self.__params = {}\n        # handle input video/frame resolution and channels\n        self.__inputheight = None\n        self.__inputwidth = None\n        self.__inputchannels = None\n        self.__sourceframerate = None\n        # handle process to be frames written\n        self.__process = None\n        # handle valid FFmpeg assets location\n        self.__ffmpeg = \"\"\n        # handle one time process for valid process initialization\n        self.__initiate_stream = True\n\n        # cleans and reformat user-defined parameters\n        self.__params = {\n            str(k).strip(): (v.strip() if isinstance(v, str) else v)\n            for k, v in stream_params.items()\n        }\n\n        # handle where to save the downloaded FFmpeg Static assets on Windows(if specified)\n        __ffmpeg_download_path = self.__params.pop(\"-ffmpeg_download_path\", \"\")\n        if not isinstance(__ffmpeg_download_path, (str)):\n            # reset improper values\n            __ffmpeg_download_path = \"\"\n\n        # validate the FFmpeg assets and return location (also downloads static assets on windows)\n        self.__ffmpeg = get_valid_ffmpeg_path(\n            str(custom_ffmpeg),\n            self.__os_windows,\n            ffmpeg_download_path=__ffmpeg_download_path,\n            logging=self.__logging,\n        )\n\n        # check if valid FFmpeg path returned\n        if self.__ffmpeg:\n            self.__logging and logger.debug(\n                \"Found valid FFmpeg executables: `{}`.\".format(self.__ffmpeg)\n            )\n        else:\n            # else raise error\n            raise RuntimeError(\n                \"[StreamGear:ERROR] :: Failed to find FFmpeg assets on this system. Kindly compile/install FFmpeg or provide a valid custom FFmpeg binary path!\"\n            )\n\n        # handle streaming format\n        supported_formats = [\"dash\", \"hls\"]  # TODO will be extended in future\n        if format and isinstance(format, str):\n            _format = format.strip().lower()\n            if _format in supported_formats:\n                self.__format = _format\n                logger.info(\n                    \"StreamGear will generate asset files for {} streaming format.\".format(\n                        self.__format.upper()\n                    )\n                )\n            elif difflib.get_close_matches(_format, supported_formats):\n                raise ValueError(\n                    \"[StreamGear:ERROR] :: Incorrect `format` parameter value! Did you mean `{}`?\".format(\n                        difflib.get_close_matches(_format, supported_formats)[0]\n                    )\n                )\n            else:\n                raise ValueError(\n                    \"[StreamGear:ERROR] :: The `format` parameter value `{}` not valid/supported!\".format(\n                        format\n                    )\n                )\n        else:\n            raise ValueError(\n                \"[StreamGear:ERROR] :: The `format` parameter value is Missing or Invalid!\"\n            )\n\n        # handle Audio-Input\n        audio = self.__params.pop(\"-audio\", False)\n        if audio and isinstance(audio, str):\n            if os.path.isfile(audio):\n                self.__audio = os.path.abspath(audio)\n            elif is_valid_url(self.__ffmpeg, url=audio, logging=self.__logging):\n                self.__audio = audio\n            else:\n                self.__audio = False\n        elif audio and isinstance(audio, list):\n            self.__audio = audio\n        else:\n            self.__audio = False\n        # log external audio source\n        self.__audio and self.__logging and logger.debug(\n            \"External audio source `{}` detected.\".format(self.__audio)\n        )\n\n        # handle Video-Source input\n        source = self.__params.pop(\"-video_source\", False)\n        # Check if input is valid string\n        if source and isinstance(source, str) and len(source) > 1:\n            # Differentiate input\n            if os.path.isfile(source):\n                self.__video_source = os.path.abspath(source)\n            elif is_valid_url(self.__ffmpeg, url=source, logging=self.__logging):\n                self.__video_source = source\n            else:\n                # discard the value otherwise\n                self.__video_source = False\n\n            # Validate input\n            if self.__video_source:\n                validation_results = validate_video(\n                    self.__ffmpeg, video_path=self.__video_source\n                )\n                assert not (\n                    validation_results is None\n                ), \"[StreamGear:ERROR] :: Given `{}` video_source is Invalid, Check Again!\".format(\n                    self.__video_source\n                )\n                self.__aspect_source = validation_results[\"resolution\"]\n                self.__fps_source = validation_results[\"framerate\"]\n                # log it\n                self.__logging and logger.debug(\n                    \"Given video_source is valid and has {}x{} resolution, and a framerate of {} fps.\".format(\n                        self.__aspect_source[0],\n                        self.__aspect_source[1],\n                        self.__fps_source,\n                    )\n                )\n            else:\n                # log warning\n                logger.warning(\"Discarded invalid `-video_source` value provided.\")\n        else:\n            if source:\n                # log warning if source provided\n                logger.warning(\"Invalid `-video_source` value provided.\")\n            else:\n                # log normally\n                logger.info(\"No `-video_source` value provided.\")\n            # discard the value otherwise\n            self.__video_source = False\n\n        # handle user-defined framerate\n        self.__inputframerate = self.__params.pop(\"-input_framerate\", 0.0)\n        if isinstance(self.__inputframerate, (float, int)):\n            # must be float\n            self.__inputframerate = float(self.__inputframerate)\n        else:\n            # reset improper values\n            self.__inputframerate = 0.0\n\n        # handle old assets\n        clear_assets = self.__params.pop(\"-clear_prev_assets\", False)\n        if isinstance(clear_assets, bool):\n            self.__clear_assets = clear_assets\n            # log if clearing assets is enabled\n            clear_assets and logger.info(\n                \"The `-clear_prev_assets` parameter is enabled successfully. All previous StreamGear API assets for `{}` format will be removed for this run.\".format(\n                    self.__format.upper()\n                )\n            )\n        else:\n            # reset improper values\n            self.__clear_assets = False\n\n        # handle whether to livestream?\n        livestreaming = self.__params.pop(\"-livestream\", False)\n        if isinstance(livestreaming, bool) and livestreaming:\n            # NOTE:  `livestream` is only available with real-time mode.\n            self.__livestreaming = livestreaming if not (self.__video_source) else False\n            if self.__video_source:\n                logger.error(\n                    \"Live-Streaming is only available with Real-time Mode. Refer docs for more information.\"\n                )\n            else:\n                # log if live streaming is enabled\n                livestreaming and logger.info(\n                    \"Live-Streaming is successfully enabled for this run.\"\n                )\n        else:\n            # reset improper values\n            self.__livestreaming = False\n\n        # handle the special-case of forced-termination\n        enable_force_termination = self.__params.pop(\"-enable_force_termination\", False)\n        # check if value is valid\n        if isinstance(enable_force_termination, bool):\n            self.__forced_termination = enable_force_termination\n            # log if forced termination is enabled\n            self.__forced_termination and logger.warning(\n                \"Forced termination is enabled for this run. This may result in corrupted output in certain scenarios!\"\n            )\n        else:\n            # handle improper values\n            self.__forced_termination = False\n\n        # handle streaming format\n        supported_formats = [\"dash\", \"hls\"]  # TODO will be extended in future\n        if format and isinstance(format, str):\n            _format = format.strip().lower()\n            if _format in supported_formats:\n                self.__format = _format\n                logger.info(\n                    \"StreamGear will generate asset files for {} streaming format.\".format(\n                        self.__format.upper()\n                    )\n                )\n            elif difflib.get_close_matches(_format, supported_formats):\n                raise ValueError(\n                    \"[StreamGear:ERROR] :: Incorrect `format` parameter value! Did you mean `{}`?\".format(\n                        difflib.get_close_matches(_format, supported_formats)[0]\n                    )\n                )\n            else:\n                raise ValueError(\n                    \"[StreamGear:ERROR] :: The `format` parameter value `{}` not valid/supported!\".format(\n                        format\n                    )\n                )\n        else:\n            raise ValueError(\n                \"[StreamGear:ERROR] :: The `format` parameter value is Missing or Invalid!\"\n            )\n\n        # handles output asset filenames\n        if output:\n            # validate this class has the access rights to specified directory or not\n            abs_path = os.path.abspath(output)\n            # check if given output is a valid system path\n            if check_WriteAccess(\n                os.path.dirname(abs_path),\n                is_windows=self.__os_windows,\n                logging=self.__logging,\n            ):\n                # get all assets extensions\n                valid_extension = \"mpd\" if self.__format == \"dash\" else \"m3u8\"\n                assets_exts = [\n                    (\"chunk-stream\", \".m4s\"),  # filename prefix, extension\n                    (\"chunk-stream\", \".ts\"),  # filename prefix, extension\n                    \".{}\".format(valid_extension),\n                ]\n                # add source file extension too\n                self.__video_source and assets_exts.append(\n                    (\n                        \"chunk-stream\",\n                        os.path.splitext(self.__video_source)[1],\n                    )  # filename prefix, extension\n                )\n                # handle output\n                # check if path is a directory\n                if os.path.isdir(abs_path):\n                    # clear previous assets if specified\n                    self.__clear_assets and delete_ext_safe(\n                        abs_path, assets_exts, logging=self.__logging\n                    )\n                    # auto-assign valid name and adds it to path\n                    abs_path = os.path.join(\n                        abs_path,\n                        \"{}-{}.{}\".format(\n                            self.__format,\n                            time.strftime(\"%Y%m%d-%H%M%S\"),\n                            valid_extension,\n                        ),\n                    )\n                # or check if path is a file\n                elif os.path.isfile(abs_path) and self.__clear_assets:\n                    # clear previous assets if specified\n                    delete_ext_safe(\n                        os.path.dirname(abs_path),\n                        assets_exts,\n                        logging=self.__logging,\n                    )\n                # check if path has valid file extension\n                assert abs_path.endswith(\n                    valid_extension\n                ), \"Given `{}` path has invalid file-extension w.r.t selected format: `{}`!\".format(\n                    output, self.__format.upper()\n                )\n                self.__logging and logger.debug(\n                    \"Output Path:`{}` is successfully configured for generating streaming assets.\".format(\n                        abs_path\n                    )\n                )\n                # workaround patch for Windows only,\n                # others platforms will not be affected\n                self.__out_file = abs_path.replace(\"\\\\\", \"/\")\n            # check if given output is a valid URL\n            elif is_valid_url(self.__ffmpeg, url=output, logging=self.__logging):\n                self.__logging and logger.debug(\n                    \"URL:`{}` is valid and successfully configured for generating streaming assets.\".format(\n                        output\n                    )\n                )\n                self.__out_file = output\n            # raise ValueError otherwise\n            else:\n                raise ValueError(\n                    \"[StreamGear:ERROR] :: The output parameter value:`{}` is not valid/supported!\".format(\n                        output\n                    )\n                )\n        else:\n            # raise ValueError otherwise\n            raise ValueError(\n                \"[StreamGear:ERROR] :: Kindly provide a valid `output` parameter value. Refer Docs for more information.\"\n            )\n\n        # log Mode of operation\n        self.__video_source and logger.info(\n            \"StreamGear has been successfully configured for {} Mode.\".format(\n                \"Single-Source\" if self.__video_source else \"Real-time Frames\"\n            )\n        )\n\n    @deprecated(\n        parameter=\"rgb_mode\",\n        message=\"The `rgb_mode` parameter is deprecated and will be removed in a future version. Only BGR format frames will be supported going forward.\",\n    )\n    def stream(self, frame, rgb_mode=False):\n        \"\"\"\n        Pipes `ndarray` frames to FFmpeg Pipeline for transcoding them into chunked-encoded media segments of\n        streaming formats such as MPEG-DASH and HLS.\n\n        !!! warning \"[DEPRECATION NOTICE]: The `rgb_mode` parameter is deprecated and will be removed in a future version.\"\n\n        Parameters:\n            frame (ndarray): a valid numpy frame\n            rgb_mode (boolean): enable this flag to activate RGB mode _(i.e. specifies that incoming frames are of RGB format instead of default BGR)_.\n        \"\"\"\n        # check if function is called in correct context\n        if self.__video_source:\n            raise RuntimeError(\n                \"[StreamGear:ERROR] :: The `stream()` method cannot be used when streaming from a `-video_source` input file. Kindly refer vidgear docs!\"\n            )\n        # None-Type frames will be skipped\n        if frame is None:\n            return\n        # extract height, width and number of channels of frame\n        height, width = frame.shape[:2]\n        channels = frame.shape[-1] if frame.ndim == 3 else 1\n        # assign values to class variables on first run\n        if self.__initiate_stream:\n            self.__inputheight = height\n            self.__inputwidth = width\n            self.__inputchannels = channels\n            self.__sourceframerate = (\n                25.0 if not (self.__inputframerate) else self.__inputframerate\n            )\n            self.__logging and logger.debug(\n                \"InputFrame => Height:{} Width:{} Channels:{}\".format(\n                    self.__inputheight, self.__inputwidth, self.__inputchannels\n                )\n            )\n        # validate size of frame\n        if height != self.__inputheight or width != self.__inputwidth:\n            raise ValueError(\"[StreamGear:ERROR] :: All frames must have same size!\")\n        # validate number of channels\n        if channels != self.__inputchannels:\n            raise ValueError(\n                \"[StreamGear:ERROR] :: All frames must have same number of channels!\"\n            )\n        # initiate FFmpeg process on first run\n        if self.__initiate_stream:\n            # launch pre-processing\n            self.__PreProcess(channels=channels, rgb=rgb_mode)\n            # Check status of the process\n            assert self.__process is not None\n\n        # write the frame to pipeline\n        try:\n            self.__process.stdin.write(frame.tobytes())\n        except (OSError, IOError):\n            # log something is wrong!\n            logger.error(\n                \"BrokenPipeError caught, Wrong values passed to FFmpeg Pipe, Kindly Refer Docs!\"\n            )\n            raise ValueError  # for testing purpose only\n\n    def transcode_source(self):\n        \"\"\"\n        Transcodes an entire video file _(with or without audio)_ into chunked-encoded media segments of\n        streaming formats such as MPEG-DASH and HLS.\n        \"\"\"\n        # check if function is called in correct context\n        if not (self.__video_source):\n            raise RuntimeError(\n                \"[StreamGear:ERROR] :: The `transcode_source()` method cannot be used without a valid `-video_source` input. Kindly refer vidgear docs!\"\n            )\n        # assign height, width and framerate\n        self.__inputheight = int(self.__aspect_source[1])\n        self.__inputwidth = int(self.__aspect_source[0])\n        self.__sourceframerate = float(self.__fps_source)\n        # launch pre-processing\n        self.__PreProcess()\n\n    def __PreProcess(self, channels=0, rgb=False):\n        \"\"\"\n        Internal method that pre-processes default FFmpeg parameters before starting pipelining.\n\n        Parameters:\n            channels (int): Number of channels\n            rgb (boolean): activates RGB mode _(if enabled)_.\n        \"\"\"\n        # turn off initiate flag\n        self.__initiate_stream = False\n        # initialize I/O parameters\n        input_parameters = OrderedDict()\n        output_parameters = OrderedDict()\n        # pre-assign default codec parameters (if not assigned by user).\n        default_codec = \"libx264rgb\" if rgb else \"libx264\"\n        output_vcodec = self.__params.pop(\"-vcodec\", default_codec)\n        # enforce default encoder if stream copy specified\n        # in Real-time Frames Mode\n        output_parameters[\"-vcodec\"] = (\n            default_codec\n            if output_vcodec == \"copy\"\n            and (not (self.__video_source) or \"-streams\" in self.__params)\n            else output_vcodec\n        )\n        # enforce compatibility with stream copy\n        if output_parameters[\"-vcodec\"] != \"copy\":\n            # NOTE: these parameters only supported when stream copy not defined\n            output_parameters[\"-vf\"] = self.__params.pop(\"-vf\", \"format=yuv420p\")\n            # Non-essential `-aspect` parameter is removed from the default pipeline.\n        else:\n            # log warnings if stream copy specified in Real-time Frames Mode\n            not (self.__video_source) and logger.error(\n                \"Stream copy is not compatible with Real-time Frames Mode as it require re-encoding of incoming frames. Discarding the `-vcodec copy` parameter!\"\n            )\n            (\"-streams\" in self.__params) and logger.error(\n                \"Stream copying is incompatible with Custom Streams as it require re-encoding for each additional stream. Discarding the `-vcodec copy` parameter!\"\n            )\n            # log warnings for these parameters\n            self.__params.pop(\"-vf\", False) and logger.warning(\n                \"Filtering and stream copy cannot be used together. Discarding specified `-vf` parameter!\"\n            )\n            self.__params.pop(\"-aspect\", False) and logger.warning(\n                \"Overriding aspect ratio with stream copy may produce invalid files. Discarding specified `-aspect` parameter!\"\n            )\n\n        # enable optimizations w.r.t selected codec\n        ### OPTIMIZATION-1 ###\n        if output_parameters[\"-vcodec\"] in [\n            \"libx264\",\n            \"libx264rgb\",\n            \"libx265\",\n            \"libvpx-vp9\",\n        ]:\n            output_parameters[\"-crf\"] = self.__params.pop(\"-crf\", \"20\")\n        ### OPTIMIZATION-2 ###\n        if output_parameters[\"-vcodec\"] == \"libx264\":\n            if not (self.__video_source):\n                output_parameters[\"-profile:v\"] = self.__params.pop(\n                    \"-profile:v\", \"high\"\n                )\n        ### OPTIMIZATION-3 ###\n        if output_parameters[\"-vcodec\"] in [\"libx264\", \"libx264rgb\"]:\n            output_parameters[\"-tune\"] = self.__params.pop(\"-tune\", \"zerolatency\")\n            output_parameters[\"-preset\"] = self.__params.pop(\"-preset\", \"veryfast\")\n        ### OPTIMIZATION-4 ###\n        if output_parameters[\"-vcodec\"] == \"libx265\":\n            output_parameters[\"-x265-params\"] = self.__params.pop(\n                \"-x265-params\", \"lossless=1\"\n            )\n\n        # enable audio (if present)\n        if self.__audio:\n            # validate audio source\n            bitrate = validate_audio(self.__ffmpeg, source=self.__audio)\n            if bitrate:\n                logger.info(\n                    \"Detected External Audio Source is valid, and will be used for generating streams.\"\n                )\n                # assign audio source\n                output_parameters[\n                    \"{}\".format(\n                        \"-core_asource\" if isinstance(self.__audio, list) else \"-i\"\n                    )\n                ] = self.__audio\n                # assign audio codec\n                output_parameters[\"-acodec\"] = self.__params.pop(\"-acodec\", \"aac\")\n                output_parameters[\"a_bitrate\"] = bitrate  # temporary handler\n                output_parameters[\"-core_audio\"] = (\n                    [\"-map\", \"1:a:0\"] if self.__format == \"dash\" else []\n                )\n            else:\n                # discard invalid audio\n                logger.warning(\n                    \"Audio source `{}` is not valid, Skipped!\".format(self.__audio)\n                )\n                self.__audio = False\n        # validate input video's audio source if available\n        elif self.__video_source:\n            bitrate = validate_audio(self.__ffmpeg, source=self.__video_source)\n            if bitrate:\n                logger.info(\"Input video's audio source will be used for this run.\")\n                # assign audio codec\n                output_parameters[\"-acodec\"] = self.__params.pop(\n                    \"-acodec\",\n                    \"aac\" if (\"-streams\" in self.__params) else \"copy\",\n                )\n                if output_parameters[\"-acodec\"] != \"copy\":\n                    output_parameters[\"a_bitrate\"] = bitrate  # temporary handler\n            else:\n                logger.info(\n                    \"No valid audio source available in the input video. Disabling audio while generating streams.\"\n                )\n        else:\n            logger.info(\n                \"No valid audio source provided. Disabling audio while generating streams.\"\n            )\n        # enable audio optimizations based on audio codec\n        if \"-acodec\" in output_parameters and output_parameters[\"-acodec\"] == \"aac\":\n            output_parameters[\"-movflags\"] = \"+faststart\"\n\n        # set input framerate\n        if self.__sourceframerate > 0.0 and not (self.__video_source):\n            # set input framerate\n            self.__logging and logger.debug(\n                \"Setting Input framerate: {}\".format(self.__sourceframerate)\n            )\n            input_parameters[\"-framerate\"] = str(self.__sourceframerate)\n\n        # handle input resolution and pixel format\n        if not (self.__video_source):\n            dimensions = \"{}x{}\".format(self.__inputwidth, self.__inputheight)\n            input_parameters[\"-video_size\"] = str(dimensions)\n            # handles pix_fmt based on channels(HACK)\n            if channels == 1:\n                input_parameters[\"-pix_fmt\"] = \"gray\"\n            elif channels == 2:\n                input_parameters[\"-pix_fmt\"] = \"ya8\"\n            elif channels == 3:\n                input_parameters[\"-pix_fmt\"] = \"rgb24\" if rgb else \"bgr24\"\n            elif channels == 4:\n                input_parameters[\"-pix_fmt\"] = \"rgba\" if rgb else \"bgra\"\n            else:\n                raise ValueError(\n                    \"[StreamGear:ERROR] :: Frames with channels outside range 1-to-4 are not supported!\"\n                )\n        # process assigned format parameters\n        process_params = self.__handle_streams(\n            input_params=input_parameters, output_params=output_parameters\n        )\n        # check if processing completed successfully\n        assert not (\n            process_params is None\n        ), \"[StreamGear:ERROR] :: `{}` stream cannot be initiated properly!\".format(\n            self.__format.upper()\n        )\n        # Finally start FFmpeg pipeline and process everything\n        self.__Build_n_Execute(process_params[0], process_params[1])\n\n    def __handle_streams(self, input_params, output_params):\n        \"\"\"\n        An internal function that parses various streams and its parameters.\n\n        Parameters:\n            input_params (dict): Input FFmpeg parameters\n            output_params (dict): Output FFmpeg parameters\n        \"\"\"\n        # handle bit-per-pixels\n        bpp = self.__params.pop(\"-bpp\", 0.1000)\n        if isinstance(bpp, float) and bpp >= 0.001:\n            bpp = float(bpp)\n        else:\n            # reset to default if invalid\n            bpp = 0.1000\n        # log it\n        bpp and self.__logging and logger.debug(\n            \"Setting bit-per-pixels: {} for this stream.\".format(bpp)\n        )\n\n        # handle gop\n        gop = self.__params.pop(\"-gop\", 2 * int(self.__sourceframerate))\n        if isinstance(gop, (int, float)) and gop >= 0:\n            gop = int(gop)\n        else:\n            # reset to some recommended value\n            gop = 2 * int(self.__sourceframerate)\n        # log it\n        gop and self.__logging and logger.debug(\n            \"Setting GOP: {} for this stream.\".format(gop)\n        )\n\n        # define default stream and its mapping\n        if self.__format == \"hls\":\n            output_params[\"-corev0\"] = [\"-map\", \"0:v\"]\n            if \"-acodec\" in output_params:\n                output_params[\"-corea0\"] = [\n                    \"-map\",\n                    \"{}:a\".format(1 if \"-core_audio\" in output_params else 0),\n                ]\n        else:\n            output_params[\"-map\"] = 0\n\n        # assign default output resolution\n        if \"-s:v:0\" in self.__params:\n            # prevent duplicates\n            del self.__params[\"-s:v:0\"]\n        if output_params[\"-vcodec\"] != \"copy\":\n            output_params[\"-s:v:0\"] = \"{}x{}\".format(\n                self.__inputwidth, self.__inputheight\n            )\n        # assign default output video-bitrate\n        if \"-b:v:0\" in self.__params:\n            # prevent duplicates\n            del self.__params[\"-b:v:0\"]\n        if output_params[\"-vcodec\"] != \"copy\":\n            output_params[\"-b:v:0\"] = (\n                str(\n                    get_video_bitrate(\n                        int(self.__inputwidth),\n                        int(self.__inputheight),\n                        self.__sourceframerate,\n                        bpp,\n                    )\n                )\n                + \"k\"\n            )\n\n        # assign default output audio-bitrate\n        if \"-b:a:0\" in self.__params:\n            # prevent duplicates\n            del self.__params[\"-b:a:0\"]\n        # extract and assign audio-bitrate from temporary handler\n        a_bitrate = output_params.pop(\"a_bitrate\", False)\n        if \"-acodec\" in output_params and a_bitrate:\n            output_params[\"-b:a:0\"] = a_bitrate\n\n        # handle user-defined streams\n        streams = self.__params.pop(\"-streams\", {})\n        output_params = self.__evaluate_streams(streams, output_params, bpp)\n\n        # define additional streams optimization parameters\n        if output_params[\"-vcodec\"] in [\"libx264\", \"libx264rgb\"]:\n            if not \"-bf\" in self.__params:\n                output_params[\"-bf\"] = 1\n            if not \"-sc_threshold\" in self.__params:\n                output_params[\"-sc_threshold\"] = 0\n            if not \"-keyint_min\" in self.__params:\n                output_params[\"-keyint_min\"] = gop\n        if (\n            output_params[\"-vcodec\"] in [\"libx264\", \"libx264rgb\", \"libvpx-vp9\"]\n            and not \"-g\" in self.__params\n        ):\n            output_params[\"-g\"] = gop\n        if output_params[\"-vcodec\"] == \"libx265\":\n            output_params[\"-core_x265\"] = [\n                \"-x265-params\",\n                \"keyint={}:min-keyint={}\".format(gop, gop),\n            ]\n\n        # process given dash/hls stream and return it\n        if self.__format == \"dash\":\n            processed_params = self.__generate_dash_stream(\n                input_params=input_params,\n                output_params=output_params,\n            )\n        else:\n            processed_params = self.__generate_hls_stream(\n                input_params=input_params,\n                output_params=output_params,\n            )\n        return processed_params\n\n    def __evaluate_streams(self, streams, output_params, bpp):\n        \"\"\"\n        Internal function that Extracts, Evaluates & Validates user-defined streams\n\n        Parameters:\n            streams (dict): Individual streams formatted as list of dict.\n            output_params (dict): Output FFmpeg parameters\n        \"\"\"\n        # temporary streams count variable\n        output_params[\"stream_count\"] = 1  # default is 1\n\n        # check if streams are empty\n        if not streams:\n            logger.info(\"No additional `-streams` are provided.\")\n            return output_params\n\n        # check if streams are valid\n        if isinstance(streams, list) and all(isinstance(x, dict) for x in streams):\n            # keep track of streams\n            stream_count = 1\n            # calculate source aspect-ratio\n            source_aspect_ratio = self.__inputwidth / self.__inputheight\n            # log the process\n            self.__logging and logger.debug(\n                \"Processing {} streams.\".format(len(streams))\n            )\n            # iterate over given streams\n            for idx, stream in enumerate(streams):\n                # log stream processing\n                self.__logging and logger.debug(\"Processing Stream: #{}\".format(idx))\n                # make copy\n                stream_copy = stream.copy()\n                # handle intermediate stream data as dictionary\n                intermediate_dict = {}\n                # define and map stream to intermediate dict\n                if self.__format == \"hls\":\n                    intermediate_dict[\"-corev{}\".format(stream_count)] = [\"-map\", \"0:v\"]\n                    if \"-acodec\" in output_params:\n                        intermediate_dict[\"-corea{}\".format(stream_count)] = [\n                            \"-map\",\n                            \"{}:a\".format(1 if \"-core_audio\" in output_params else 0),\n                        ]\n                else:\n                    intermediate_dict[\"-core{}\".format(stream_count)] = [\"-map\", \"0\"]\n\n                # extract resolution & individual dimension of stream\n                resolution = stream.pop(\"-resolution\", \"\")\n                dimensions = (\n                    resolution.lower().split(\"x\")\n                    if (resolution and isinstance(resolution, str))\n                    else []\n                )\n                # validate resolution\n                if (\n                    len(dimensions) == 2\n                    and dimensions[0].isnumeric()\n                    and dimensions[1].isnumeric()\n                ):\n                    # verify resolution is w.r.t source aspect-ratio\n                    expected_width = math.floor(\n                        int(dimensions[1]) * source_aspect_ratio\n                    )\n                    if int(dimensions[0]) != expected_width:\n                        logger.warning(\n                            \"The provided stream resolution '{}' does not align with the source aspect ratio. Output stream may appear distorted!\".format(\n                                resolution\n                            )\n                        )\n                    # assign stream resolution to intermediate dict\n                    intermediate_dict[\"-s:v:{}\".format(stream_count)] = resolution\n                else:\n                    # otherwise log error and skip stream\n                    logger.error(\n                        \"Missing `-resolution` value. Invalid stream `{}` Skipped!\".format(\n                            stream_copy\n                        )\n                    )\n                    continue\n\n                # verify given stream video-bitrate\n                video_bitrate = stream.pop(\"-video_bitrate\", \"\")\n                if (\n                    video_bitrate\n                    and isinstance(video_bitrate, str)\n                    and video_bitrate.endswith((\"k\", \"M\"))\n                ):\n                    # assign it\n                    intermediate_dict[\"-b:v:{}\".format(stream_count)] = video_bitrate\n                else:\n                    # otherwise calculate video-bitrate\n                    fps = stream.pop(\"-framerate\", 0.0)\n                    if dimensions and isinstance(fps, (float, int)) and fps > 0:\n                        intermediate_dict[\"-b:v:{}\".format(stream_count)] = (\n                            \"{}k\".format(\n                                get_video_bitrate(\n                                    int(dimensions[0]), int(dimensions[1]), fps, bpp\n                                )\n                            )\n                        )\n                    else:\n                        # If everything fails, log and skip the stream!\n                        logger.error(\n                            \"Unable to determine Video-Bitrate for the stream `{}`. Skipped!\".format(\n                                stream_copy\n                            )\n                        )\n                        continue\n                # verify given stream audio-bitrate\n                audio_bitrate = stream.pop(\"-audio_bitrate\", \"\")\n                if \"-acodec\" in output_params:\n                    if audio_bitrate and audio_bitrate.endswith((\"k\", \"M\")):\n                        intermediate_dict[\"-b:a:{}\".format(stream_count)] = (\n                            audio_bitrate\n                        )\n                    else:\n                        # otherwise calculate audio-bitrate\n                        if dimensions:\n                            aspect_width = int(dimensions[0])\n                            intermediate_dict[\"-b:a:{}\".format(stream_count)] = (\n                                \"{}k\".format(128 if (aspect_width > 800) else 96)\n                            )\n                # update output parameters\n                output_params.update(intermediate_dict)\n                # clear intermediate dict\n                intermediate_dict.clear()\n                # clear stream copy\n                stream_copy.clear()\n                # increment to next stream\n                stream_count += 1\n                # log stream processing\n                self.__logging and logger.debug(\n                    \"Processed #{} stream successfully.\".format(idx)\n                )\n            # store stream count\n            output_params[\"stream_count\"] = stream_count\n            # log streams processing\n            self.__logging and logger.debug(\"All streams processed successfully!\")\n        else:\n            # skip and log\n            logger.warning(\"Invalid type `-streams` skipped!\")\n\n        return output_params\n\n    def __generate_hls_stream(self, input_params, output_params):\n        \"\"\"\n        An internal function that parses user-defined parameters and generates\n        suitable FFmpeg Terminal Command for transcoding input into HLS Stream.\n\n        Parameters:\n            input_params (dict): Input FFmpeg parameters\n            output_params (dict): Output FFmpeg parameters\n        \"\"\"\n        # validate `hls_segment_type`\n        default_hls_segment_type = self.__params.pop(\"-hls_segment_type\", \"mpegts\")\n        if isinstance(\n            default_hls_segment_type, str\n        ) and default_hls_segment_type.strip() in [\"fmp4\", \"mpegts\"]:\n            output_params[\"-hls_segment_type\"] = default_hls_segment_type.strip()\n        else:\n            # otherwise reset to default\n            logger.warning(\"Invalid `-hls_segment_type` value skipped!\")\n            output_params[\"-hls_segment_type\"] = \"mpegts\"\n        # gather required parameters\n        if self.__livestreaming:\n            # `hls_list_size` must be greater than or equal to 0\n            default_hls_list_size = self.__params.pop(\"-hls_list_size\", 6)\n            if isinstance(default_hls_list_size, int) and default_hls_list_size >= 0:\n                output_params[\"-hls_list_size\"] = default_hls_list_size\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-hls_list_size` value skipped!\")\n                output_params[\"-hls_list_size\"] = 6\n            # `hls_init_time` must be greater than or equal to 0\n            default_hls_init_time = self.__params.pop(\"-hls_init_time\", 4)\n            if isinstance(default_hls_init_time, int) and default_hls_init_time >= 0:\n                output_params[\"-hls_init_time\"] = default_hls_init_time\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-hls_init_time` value skipped!\")\n                output_params[\"-hls_init_time\"] = 4\n            # `hls_time` must be greater than or equal to 0\n            default_hls_time = self.__params.pop(\"-hls_time\", 4)\n            if isinstance(default_hls_time, int) and default_hls_time >= 0:\n                output_params[\"-hls_time\"] = default_hls_time\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-hls_time` value skipped!\")\n                output_params[\"-hls_time\"] = 6\n            # `hls_flags` must be string\n            default_hls_flags = self.__params.pop(\n                \"-hls_flags\", \"delete_segments+discont_start+split_by_time\"\n            )\n            if isinstance(default_hls_flags, str):\n                output_params[\"-hls_flags\"] = default_hls_flags\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-hls_flags` value skipped!\")\n                output_params[\"-hls_flags\"] = (\n                    \"delete_segments+discont_start+split_by_time\"\n                )\n            # clean everything at exit?\n            remove_at_exit = self.__params.pop(\"-remove_at_exit\", 0)\n            if isinstance(remove_at_exit, int) and remove_at_exit in [\n                0,\n                1,\n            ]:\n                output_params[\"-remove_at_exit\"] = remove_at_exit\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-remove_at_exit` value skipped!\")\n                output_params[\"-remove_at_exit\"] = 0\n        else:\n            # enforce \"contain all the segments\"\n            output_params[\"-hls_list_size\"] = 0\n            output_params[\"-hls_playlist_type\"] = \"vod\"\n\n        # handle base URL for absolute paths\n        hls_base_url = self.__params.pop(\"-hls_base_url\", \"\")\n        if isinstance(hls_base_url, str):\n            output_params[\"-hls_base_url\"] = hls_base_url\n        else:\n            # otherwise reset to default\n            logger.warning(\"Invalid `-hls_base_url` value skipped!\")\n            output_params[\"-hls_base_url\"] = \"\"\n\n        # Hardcoded HLS parameters (Refer FFmpeg docs for more info.)\n        output_params[\"-allowed_extensions\"] = \"ALL\"\n        # Handling <hls_segment_filename>\n        # Here filename will be based on `stream_count` dict parameter that\n        # would be used to check whether stream is multi-variant(>1) or single(0-1)\n        segment_template = (\n            \"{}-stream%v-%03d.{}\"\n            if output_params[\"stream_count\"] > 1\n            else \"{}-stream-%03d.{}\"\n        )\n        output_params[\"-hls_segment_filename\"] = segment_template.format(\n            os.path.join(os.path.dirname(self.__out_file), \"chunk\"),\n            \"m4s\" if output_params[\"-hls_segment_type\"] == \"fmp4\" else \"ts\",\n        )\n        # Hardcoded HLS parameters (Refer FFmpeg docs for more info.)\n        output_params[\"-hls_allow_cache\"] = 0\n        # enable hls formatting\n        output_params[\"-f\"] = \"hls\"\n        # return HLS params\n        return (input_params, output_params)\n\n    def __generate_dash_stream(self, input_params, output_params):\n        \"\"\"\n        An internal function that parses user-defined parameters and generates\n        suitable FFmpeg Terminal Command for transcoding input into MPEG-dash Stream.\n\n        Parameters:\n            input_params (dict): Input FFmpeg parameters\n            output_params (dict): Output FFmpeg parameters\n        \"\"\"\n\n        # Check if live-streaming or not?\n        if self.__livestreaming:\n            # `extra_window_size` must be greater than or equal to 0\n            window_size = self.__params.pop(\"-window_size\", 5)\n            if isinstance(window_size, int) and window_size >= 0:\n                output_params[\"-window_size\"] = window_size\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-window_size` value skipped!\")\n                output_params[\"-window_size\"] = 5\n            # `extra_window_size` must be greater than or equal to 0\n            extra_window_size = self.__params.pop(\"-extra_window_size\", 5)\n            if isinstance(extra_window_size, int) and extra_window_size >= 0:\n                output_params[\"-extra_window_size\"] = window_size\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-extra_window_size` value skipped!\")\n                output_params[\"-extra_window_size\"] = 5\n            # clean everything at exit?\n            remove_at_exit = self.__params.pop(\"-remove_at_exit\", 0)\n            if isinstance(remove_at_exit, int) and remove_at_exit in [\n                0,\n                1,\n            ]:\n                output_params[\"-remove_at_exit\"] = remove_at_exit\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-remove_at_exit` value skipped!\")\n                output_params[\"-remove_at_exit\"] = 0\n            # `seg_duration` must be greater than or equal to 0\n            seg_duration = self.__params.pop(\"-seg_duration\", 20)\n            if isinstance(seg_duration, int) and seg_duration >= 0:\n                output_params[\"-seg_duration\"] = seg_duration\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-seg_duration` value skipped!\")\n                output_params[\"-seg_duration\"] = 20\n            # Disable (0) the use of a SegmentTimeline inside a SegmentTemplate.\n            output_params[\"-use_timeline\"] = 0\n        else:\n            # `seg_duration` must be greater than or equal to 0\n            seg_duration = self.__params.pop(\"-seg_duration\", 5)\n            if isinstance(seg_duration, int) and seg_duration >= 0:\n                output_params[\"-seg_duration\"] = seg_duration\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-seg_duration` value skipped!\")\n                output_params[\"-seg_duration\"] = 5\n            # Enable (1) the use of a SegmentTimeline inside a SegmentTemplate.\n            output_params[\"-use_timeline\"] = 1\n\n        # Finally, some hardcoded DASH parameters (Refer FFmpeg docs for more info.)\n        output_params[\"-use_template\"] = 1\n        output_params[\"-adaptation_sets\"] = \"id=0,streams=v {}\".format(\n            \"id=1,streams=a\" if (\"-acodec\" in output_params) else \"\"\n        )\n        # enable dash formatting\n        output_params[\"-f\"] = \"dash\"\n        # return DASH params\n        return (input_params, output_params)\n\n    def __Build_n_Execute(self, input_params, output_params):\n        \"\"\"\n        An Internal function that launches FFmpeg subprocess and pipelines commands.\n\n        Parameters:\n            input_params (dict): Input FFmpeg parameters\n            output_params (dict): Output FFmpeg parameters\n        \"\"\"\n        # handle audio source if present\n        \"-core_asource\" in output_params and output_params.move_to_end(\n            \"-core_asource\", last=False\n        )\n        # handle `-i` parameter\n        \"-i\" in output_params and output_params.move_to_end(\"-i\", last=False)\n        # copy streams count\n        stream_count = output_params.pop(\"stream_count\", 1)\n\n        # convert input parameters to list\n        input_commands = dict2Args(input_params)\n        # convert output parameters to list\n        output_commands = dict2Args(output_params)\n        # convert any additional parameters to list\n        stream_commands = dict2Args(self.__params)\n\n        # create exclusive HLS params\n        hls_commands = []\n        # handle HLS multi-variant streams\n        if self.__format == \"hls\" and stream_count > 1:\n            stream_map = \"\"\n            for count in range(0, stream_count):\n                stream_map += \"v:{}{} \".format(\n                    count, \",a:{}\".format(count) if \"-acodec\" in output_params else \",\"\n                )\n            hls_commands += [\n                \"-master_pl_name\",\n                os.path.basename(self.__out_file),\n                \"-var_stream_map\",\n                stream_map.strip(),\n                os.path.join(os.path.dirname(self.__out_file), \"stream_%v.m3u8\"),\n            ]\n\n        # log it if enabled\n        self.__logging and logger.debug(\n            \"User-Defined Output parameters: `{}`\".format(\n                \" \".join(output_commands) if output_commands else None\n            )\n        )\n        self.__logging and logger.debug(\n            \"Additional parameters: `{}`\".format(\n                \" \".join(stream_commands) if stream_commands else None\n            )\n        )\n        # build FFmpeg command from parameters\n        ffmpeg_cmd = None\n        # ensuring less cluttering if silent mode\n        hide_banner = [] if self.__logging else [\"-hide_banner\"]\n        # format commands\n        if self.__video_source:\n            ffmpeg_cmd = (\n                [self.__ffmpeg, \"-y\"]\n                + ([\"-re\"] if self.__livestreaming else [])  # pseudo live-streaming\n                + hide_banner\n                + [\"-i\", self.__video_source]\n                + input_commands\n                + output_commands\n                + stream_commands\n            )\n        else:\n            ffmpeg_cmd = (\n                [self.__ffmpeg, \"-y\"]\n                + hide_banner\n                + [\"-f\", \"rawvideo\", \"-vcodec\", \"rawvideo\"]\n                + input_commands\n                + [\"-i\", \"-\"]\n                + output_commands\n                + stream_commands\n            )\n        # format outputs\n        ffmpeg_cmd.extend([self.__out_file] if not (hls_commands) else hls_commands)\n        # Launch the FFmpeg pipeline with built command\n        logger.critical(\"Transcoding streaming chunks. Please wait...\")  # log it\n        self.__process = sp.Popen(\n            ffmpeg_cmd,\n            stdin=sp.PIPE,\n            stdout=(\n                sp.DEVNULL\n                if (not self.__video_source and not self.__logging)\n                else sp.PIPE\n            ),\n            stderr=None if self.__logging else sp.STDOUT,\n        )\n        # post handle progress bar and runtime errors in case of video_source\n        if self.__video_source:\n            return_code = 0\n            pbar = None\n            sec_prev = 0\n            if self.__logging:\n                self.__process.communicate()\n                return_code = self.__process.returncode\n            else:\n                # iterate until stdout runs out\n                while True:\n                    # read and process data\n                    data = self.__process.stdout.readline()\n                    if data:\n                        data = data.decode(\"utf-8\")\n                        # extract duration and time-left\n                        if pbar is None and \"Duration:\" in data:\n                            # extract time in seconds\n                            sec_duration = extract_time(data)\n                            # initiate progress bar\n                            pbar = tqdm(\n                                total=sec_duration,\n                                desc=\"Processing Frames\",\n                                unit=\"frame\",\n                            )\n                        elif \"time=\" in data:\n                            # extract time in seconds\n                            sec_current = extract_time(data)\n                            # update progress bar\n                            if sec_current:\n                                pbar.update(sec_current - sec_prev)\n                                sec_prev = sec_current\n                    else:\n                        # poll if no data\n                        if self.__process.poll() is not None:\n                            break\n                return_code = self.__process.poll()\n            # close progress bar\n            not (pbar is None) and pbar.close()\n            # handle return_code\n            if return_code != 0:\n                # log and raise error if return_code is `1`\n                logger.error(\n                    \"StreamGear failed to initiate stream for this video source!\"\n                )\n                raise sp.CalledProcessError(return_code, ffmpeg_cmd)\n            else:\n                # log if successful\n                logger.critical(\n                    \"Transcoding Ended. {} Streaming assets are successfully generated at specified path.\".format(\n                        self.__format.upper()\n                    )\n                )\n\n    def __enter__(self):\n        \"\"\"\n        Handles entry with the `with` statement. See [PEP343 -- The 'with' statement'](https://peps.python.org/pep-0343/).\n\n        **Returns:** Returns a reference to the StreamGear Class\n        \"\"\"\n        return self\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        \"\"\"\n        Handles exit with the `with` statement. See [PEP343 -- The 'with' statement'](https://peps.python.org/pep-0343/).\n        \"\"\"\n        self.close()\n\n    @deprecated(\n        message=\"The `terminate()` method will be removed in the next release. Kindly use `close()` method instead.\"\n    )\n    def terminate(self):\n        \"\"\"\n        !!! warning \"[DEPRECATION NOTICE]: This method is now deprecated and will be removed in a future release.\"\n\n        This function ensures backward compatibility for the `terminate()` method to maintain the API on existing systems.\n        It achieves this by calling the new `close()` method to terminate various\n        StreamGear processes.\n        \"\"\"\n\n        self.close()\n\n    def close(self):\n        \"\"\"\n        Safely terminates various StreamGear process.\n        \"\"\"\n        # log termination\n        self.__logging and logger.debug(\"Terminating StreamGear Processes.\")\n\n        # return if no process was initiated at first place\n        if self.__process is None or not (self.__process.poll() is None):\n            return\n        # close `stdin` output\n        self.__process.stdin and self.__process.stdin.close()\n        # close `stdout` output\n        self.__process.stdout and self.__process.stdout.close()\n        # forced termination if specified.\n        if self.__forced_termination:\n            self.__process.terminate()\n        # handle device audio streams\n        elif self.__audio and isinstance(self.__audio, list):\n            # send `CTRL_BREAK_EVENT` signal if Windows else `SIGINT`\n            self.__process.send_signal(\n                signal.CTRL_BREAK_EVENT if self.__os_windows else signal.SIGINT\n            )\n        # wait if process is still processing\n        self.__process.wait()\n        # discard process\n        self.__process = None\n

"},{"location":"bonus/reference/streamgear/#vidgear.gears.streamgear.StreamGear.__enter__","title":"__enter__(self) special","text":"

Handles entry with the with statement. See PEP343 -- The 'with' statement'.

Returns: Returns a reference to the StreamGear Class

Source code in vidgear/gears/streamgear.py
def __enter__(self):\n    \"\"\"\n    Handles entry with the `with` statement. See [PEP343 -- The 'with' statement'](https://peps.python.org/pep-0343/).\n\n    **Returns:** Returns a reference to the StreamGear Class\n    \"\"\"\n    return self\n
"},{"location":"bonus/reference/streamgear/#vidgear.gears.streamgear.StreamGear.__exit__","title":"__exit__(self, exc_type, exc_val, exc_tb) special","text":"

Handles exit with the with statement. See PEP343 -- The 'with' statement'.

Source code in vidgear/gears/streamgear.py
def __exit__(self, exc_type, exc_val, exc_tb):\n    \"\"\"\n    Handles exit with the `with` statement. See [PEP343 -- The 'with' statement'](https://peps.python.org/pep-0343/).\n    \"\"\"\n    self.close()\n
"},{"location":"bonus/reference/streamgear/#vidgear.gears.streamgear.StreamGear.__init__","title":"__init__(self, output='', format='dash', custom_ffmpeg='', logging=False, **stream_params) special","text":"

This constructor method initializes the object state and attributes of the StreamGear class.

Parameters:

Name Type Description Default output str

sets the valid filename/path for generating the StreamGear assets.

'' format str

select the adaptive HTTP streaming format(DASH and HLS).

'dash' custom_ffmpeg str

assigns the location of custom path/directory for custom FFmpeg executables.

'' logging bool

enables/disables logging.

False stream_params dict

provides the flexibility to control supported internal parameters and FFmpeg properties.

{} Source code in vidgear/gears/streamgear.py
def __init__(\n    self, output=\"\", format=\"dash\", custom_ffmpeg=\"\", logging=False, **stream_params\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the StreamGear class.\n\n    Parameters:\n        output (str): sets the valid filename/path for generating the StreamGear assets.\n        format (str): select the adaptive HTTP streaming format(DASH and HLS).\n        custom_ffmpeg (str): assigns the location of custom path/directory for custom FFmpeg executables.\n        logging (bool): enables/disables logging.\n        stream_params (dict): provides the flexibility to control supported internal parameters and FFmpeg properties.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # checks if machine in-use is running windows os or not\n    self.__os_windows = True if os.name == \"nt\" else False\n\n    # initialize various class variables\n    # handles user-defined parameters\n    self.__params = {}\n    # handle input video/frame resolution and channels\n    self.__inputheight = None\n    self.__inputwidth = None\n    self.__inputchannels = None\n    self.__sourceframerate = None\n    # handle process to be frames written\n    self.__process = None\n    # handle valid FFmpeg assets location\n    self.__ffmpeg = \"\"\n    # handle one time process for valid process initialization\n    self.__initiate_stream = True\n\n    # cleans and reformat user-defined parameters\n    self.__params = {\n        str(k).strip(): (v.strip() if isinstance(v, str) else v)\n        for k, v in stream_params.items()\n    }\n\n    # handle where to save the downloaded FFmpeg Static assets on Windows(if specified)\n    __ffmpeg_download_path = self.__params.pop(\"-ffmpeg_download_path\", \"\")\n    if not isinstance(__ffmpeg_download_path, (str)):\n        # reset improper values\n        __ffmpeg_download_path = \"\"\n\n    # validate the FFmpeg assets and return location (also downloads static assets on windows)\n    self.__ffmpeg = get_valid_ffmpeg_path(\n        str(custom_ffmpeg),\n        self.__os_windows,\n        ffmpeg_download_path=__ffmpeg_download_path,\n        logging=self.__logging,\n    )\n\n    # check if valid FFmpeg path returned\n    if self.__ffmpeg:\n        self.__logging and logger.debug(\n            \"Found valid FFmpeg executables: `{}`.\".format(self.__ffmpeg)\n        )\n    else:\n        # else raise error\n        raise RuntimeError(\n            \"[StreamGear:ERROR] :: Failed to find FFmpeg assets on this system. Kindly compile/install FFmpeg or provide a valid custom FFmpeg binary path!\"\n        )\n\n    # handle streaming format\n    supported_formats = [\"dash\", \"hls\"]  # TODO will be extended in future\n    if format and isinstance(format, str):\n        _format = format.strip().lower()\n        if _format in supported_formats:\n            self.__format = _format\n            logger.info(\n                \"StreamGear will generate asset files for {} streaming format.\".format(\n                    self.__format.upper()\n                )\n            )\n        elif difflib.get_close_matches(_format, supported_formats):\n            raise ValueError(\n                \"[StreamGear:ERROR] :: Incorrect `format` parameter value! Did you mean `{}`?\".format(\n                    difflib.get_close_matches(_format, supported_formats)[0]\n                )\n            )\n        else:\n            raise ValueError(\n                \"[StreamGear:ERROR] :: The `format` parameter value `{}` not valid/supported!\".format(\n                    format\n                )\n            )\n    else:\n        raise ValueError(\n            \"[StreamGear:ERROR] :: The `format` parameter value is Missing or Invalid!\"\n        )\n\n    # handle Audio-Input\n    audio = self.__params.pop(\"-audio\", False)\n    if audio and isinstance(audio, str):\n        if os.path.isfile(audio):\n            self.__audio = os.path.abspath(audio)\n        elif is_valid_url(self.__ffmpeg, url=audio, logging=self.__logging):\n            self.__audio = audio\n        else:\n            self.__audio = False\n    elif audio and isinstance(audio, list):\n        self.__audio = audio\n    else:\n        self.__audio = False\n    # log external audio source\n    self.__audio and self.__logging and logger.debug(\n        \"External audio source `{}` detected.\".format(self.__audio)\n    )\n\n    # handle Video-Source input\n    source = self.__params.pop(\"-video_source\", False)\n    # Check if input is valid string\n    if source and isinstance(source, str) and len(source) > 1:\n        # Differentiate input\n        if os.path.isfile(source):\n            self.__video_source = os.path.abspath(source)\n        elif is_valid_url(self.__ffmpeg, url=source, logging=self.__logging):\n            self.__video_source = source\n        else:\n            # discard the value otherwise\n            self.__video_source = False\n\n        # Validate input\n        if self.__video_source:\n            validation_results = validate_video(\n                self.__ffmpeg, video_path=self.__video_source\n            )\n            assert not (\n                validation_results is None\n            ), \"[StreamGear:ERROR] :: Given `{}` video_source is Invalid, Check Again!\".format(\n                self.__video_source\n            )\n            self.__aspect_source = validation_results[\"resolution\"]\n            self.__fps_source = validation_results[\"framerate\"]\n            # log it\n            self.__logging and logger.debug(\n                \"Given video_source is valid and has {}x{} resolution, and a framerate of {} fps.\".format(\n                    self.__aspect_source[0],\n                    self.__aspect_source[1],\n                    self.__fps_source,\n                )\n            )\n        else:\n            # log warning\n            logger.warning(\"Discarded invalid `-video_source` value provided.\")\n    else:\n        if source:\n            # log warning if source provided\n            logger.warning(\"Invalid `-video_source` value provided.\")\n        else:\n            # log normally\n            logger.info(\"No `-video_source` value provided.\")\n        # discard the value otherwise\n        self.__video_source = False\n\n    # handle user-defined framerate\n    self.__inputframerate = self.__params.pop(\"-input_framerate\", 0.0)\n    if isinstance(self.__inputframerate, (float, int)):\n        # must be float\n        self.__inputframerate = float(self.__inputframerate)\n    else:\n        # reset improper values\n        self.__inputframerate = 0.0\n\n    # handle old assets\n    clear_assets = self.__params.pop(\"-clear_prev_assets\", False)\n    if isinstance(clear_assets, bool):\n        self.__clear_assets = clear_assets\n        # log if clearing assets is enabled\n        clear_assets and logger.info(\n            \"The `-clear_prev_assets` parameter is enabled successfully. All previous StreamGear API assets for `{}` format will be removed for this run.\".format(\n                self.__format.upper()\n            )\n        )\n    else:\n        # reset improper values\n        self.__clear_assets = False\n\n    # handle whether to livestream?\n    livestreaming = self.__params.pop(\"-livestream\", False)\n    if isinstance(livestreaming, bool) and livestreaming:\n        # NOTE:  `livestream` is only available with real-time mode.\n        self.__livestreaming = livestreaming if not (self.__video_source) else False\n        if self.__video_source:\n            logger.error(\n                \"Live-Streaming is only available with Real-time Mode. Refer docs for more information.\"\n            )\n        else:\n            # log if live streaming is enabled\n            livestreaming and logger.info(\n                \"Live-Streaming is successfully enabled for this run.\"\n            )\n    else:\n        # reset improper values\n        self.__livestreaming = False\n\n    # handle the special-case of forced-termination\n    enable_force_termination = self.__params.pop(\"-enable_force_termination\", False)\n    # check if value is valid\n    if isinstance(enable_force_termination, bool):\n        self.__forced_termination = enable_force_termination\n        # log if forced termination is enabled\n        self.__forced_termination and logger.warning(\n            \"Forced termination is enabled for this run. This may result in corrupted output in certain scenarios!\"\n        )\n    else:\n        # handle improper values\n        self.__forced_termination = False\n\n    # handle streaming format\n    supported_formats = [\"dash\", \"hls\"]  # TODO will be extended in future\n    if format and isinstance(format, str):\n        _format = format.strip().lower()\n        if _format in supported_formats:\n            self.__format = _format\n            logger.info(\n                \"StreamGear will generate asset files for {} streaming format.\".format(\n                    self.__format.upper()\n                )\n            )\n        elif difflib.get_close_matches(_format, supported_formats):\n            raise ValueError(\n                \"[StreamGear:ERROR] :: Incorrect `format` parameter value! Did you mean `{}`?\".format(\n                    difflib.get_close_matches(_format, supported_formats)[0]\n                )\n            )\n        else:\n            raise ValueError(\n                \"[StreamGear:ERROR] :: The `format` parameter value `{}` not valid/supported!\".format(\n                    format\n                )\n            )\n    else:\n        raise ValueError(\n            \"[StreamGear:ERROR] :: The `format` parameter value is Missing or Invalid!\"\n        )\n\n    # handles output asset filenames\n    if output:\n        # validate this class has the access rights to specified directory or not\n        abs_path = os.path.abspath(output)\n        # check if given output is a valid system path\n        if check_WriteAccess(\n            os.path.dirname(abs_path),\n            is_windows=self.__os_windows,\n            logging=self.__logging,\n        ):\n            # get all assets extensions\n            valid_extension = \"mpd\" if self.__format == \"dash\" else \"m3u8\"\n            assets_exts = [\n                (\"chunk-stream\", \".m4s\"),  # filename prefix, extension\n                (\"chunk-stream\", \".ts\"),  # filename prefix, extension\n                \".{}\".format(valid_extension),\n            ]\n            # add source file extension too\n            self.__video_source and assets_exts.append(\n                (\n                    \"chunk-stream\",\n                    os.path.splitext(self.__video_source)[1],\n                )  # filename prefix, extension\n            )\n            # handle output\n            # check if path is a directory\n            if os.path.isdir(abs_path):\n                # clear previous assets if specified\n                self.__clear_assets and delete_ext_safe(\n                    abs_path, assets_exts, logging=self.__logging\n                )\n                # auto-assign valid name and adds it to path\n                abs_path = os.path.join(\n                    abs_path,\n                    \"{}-{}.{}\".format(\n                        self.__format,\n                        time.strftime(\"%Y%m%d-%H%M%S\"),\n                        valid_extension,\n                    ),\n                )\n            # or check if path is a file\n            elif os.path.isfile(abs_path) and self.__clear_assets:\n                # clear previous assets if specified\n                delete_ext_safe(\n                    os.path.dirname(abs_path),\n                    assets_exts,\n                    logging=self.__logging,\n                )\n            # check if path has valid file extension\n            assert abs_path.endswith(\n                valid_extension\n            ), \"Given `{}` path has invalid file-extension w.r.t selected format: `{}`!\".format(\n                output, self.__format.upper()\n            )\n            self.__logging and logger.debug(\n                \"Output Path:`{}` is successfully configured for generating streaming assets.\".format(\n                    abs_path\n                )\n            )\n            # workaround patch for Windows only,\n            # others platforms will not be affected\n            self.__out_file = abs_path.replace(\"\\\\\", \"/\")\n        # check if given output is a valid URL\n        elif is_valid_url(self.__ffmpeg, url=output, logging=self.__logging):\n            self.__logging and logger.debug(\n                \"URL:`{}` is valid and successfully configured for generating streaming assets.\".format(\n                    output\n                )\n            )\n            self.__out_file = output\n        # raise ValueError otherwise\n        else:\n            raise ValueError(\n                \"[StreamGear:ERROR] :: The output parameter value:`{}` is not valid/supported!\".format(\n                    output\n                )\n            )\n    else:\n        # raise ValueError otherwise\n        raise ValueError(\n            \"[StreamGear:ERROR] :: Kindly provide a valid `output` parameter value. Refer Docs for more information.\"\n        )\n\n    # log Mode of operation\n    self.__video_source and logger.info(\n        \"StreamGear has been successfully configured for {} Mode.\".format(\n            \"Single-Source\" if self.__video_source else \"Real-time Frames\"\n        )\n    )\n
"},{"location":"bonus/reference/streamgear/#vidgear.gears.streamgear.StreamGear.close","title":"close(self)","text":"

Safely terminates various StreamGear process.

Source code in vidgear/gears/streamgear.py
def close(self):\n    \"\"\"\n    Safely terminates various StreamGear process.\n    \"\"\"\n    # log termination\n    self.__logging and logger.debug(\"Terminating StreamGear Processes.\")\n\n    # return if no process was initiated at first place\n    if self.__process is None or not (self.__process.poll() is None):\n        return\n    # close `stdin` output\n    self.__process.stdin and self.__process.stdin.close()\n    # close `stdout` output\n    self.__process.stdout and self.__process.stdout.close()\n    # forced termination if specified.\n    if self.__forced_termination:\n        self.__process.terminate()\n    # handle device audio streams\n    elif self.__audio and isinstance(self.__audio, list):\n        # send `CTRL_BREAK_EVENT` signal if Windows else `SIGINT`\n        self.__process.send_signal(\n            signal.CTRL_BREAK_EVENT if self.__os_windows else signal.SIGINT\n        )\n    # wait if process is still processing\n    self.__process.wait()\n    # discard process\n    self.__process = None\n
"},{"location":"bonus/reference/streamgear/#vidgear.gears.streamgear.StreamGear.stream","title":"stream(self, frame, rgb_mode=False)","text":"

Pipes ndarray frames to FFmpeg Pipeline for transcoding them into chunked-encoded media segments of streaming formats such as MPEG-DASH and HLS.

[DEPRECATION NOTICE]: The rgb_mode parameter is deprecated and will be removed in a future version.

Parameters:

Name Type Description Default frame ndarray

a valid numpy frame

required rgb_mode boolean

enable this flag to activate RGB mode (i.e. specifies that incoming frames are of RGB format instead of default BGR).

False Source code in vidgear/gears/streamgear.py
@deprecated(\n    parameter=\"rgb_mode\",\n    message=\"The `rgb_mode` parameter is deprecated and will be removed in a future version. Only BGR format frames will be supported going forward.\",\n)\ndef stream(self, frame, rgb_mode=False):\n    \"\"\"\n    Pipes `ndarray` frames to FFmpeg Pipeline for transcoding them into chunked-encoded media segments of\n    streaming formats such as MPEG-DASH and HLS.\n\n    !!! warning \"[DEPRECATION NOTICE]: The `rgb_mode` parameter is deprecated and will be removed in a future version.\"\n\n    Parameters:\n        frame (ndarray): a valid numpy frame\n        rgb_mode (boolean): enable this flag to activate RGB mode _(i.e. specifies that incoming frames are of RGB format instead of default BGR)_.\n    \"\"\"\n    # check if function is called in correct context\n    if self.__video_source:\n        raise RuntimeError(\n            \"[StreamGear:ERROR] :: The `stream()` method cannot be used when streaming from a `-video_source` input file. Kindly refer vidgear docs!\"\n        )\n    # None-Type frames will be skipped\n    if frame is None:\n        return\n    # extract height, width and number of channels of frame\n    height, width = frame.shape[:2]\n    channels = frame.shape[-1] if frame.ndim == 3 else 1\n    # assign values to class variables on first run\n    if self.__initiate_stream:\n        self.__inputheight = height\n        self.__inputwidth = width\n        self.__inputchannels = channels\n        self.__sourceframerate = (\n            25.0 if not (self.__inputframerate) else self.__inputframerate\n        )\n        self.__logging and logger.debug(\n            \"InputFrame => Height:{} Width:{} Channels:{}\".format(\n                self.__inputheight, self.__inputwidth, self.__inputchannels\n            )\n        )\n    # validate size of frame\n    if height != self.__inputheight or width != self.__inputwidth:\n        raise ValueError(\"[StreamGear:ERROR] :: All frames must have same size!\")\n    # validate number of channels\n    if channels != self.__inputchannels:\n        raise ValueError(\n            \"[StreamGear:ERROR] :: All frames must have same number of channels!\"\n        )\n    # initiate FFmpeg process on first run\n    if self.__initiate_stream:\n        # launch pre-processing\n        self.__PreProcess(channels=channels, rgb=rgb_mode)\n        # Check status of the process\n        assert self.__process is not None\n\n    # write the frame to pipeline\n    try:\n        self.__process.stdin.write(frame.tobytes())\n    except (OSError, IOError):\n        # log something is wrong!\n        logger.error(\n            \"BrokenPipeError caught, Wrong values passed to FFmpeg Pipe, Kindly Refer Docs!\"\n        )\n        raise ValueError  # for testing purpose only\n
"},{"location":"bonus/reference/streamgear/#vidgear.gears.streamgear.StreamGear.terminate","title":"terminate(self)","text":"

[DEPRECATION NOTICE]: This method is now deprecated and will be removed in a future release.

This function ensures backward compatibility for the terminate() method to maintain the API on existing systems. It achieves this by calling the new close() method to terminate various StreamGear processes.

Source code in vidgear/gears/streamgear.py
@deprecated(\n    message=\"The `terminate()` method will be removed in the next release. Kindly use `close()` method instead.\"\n)\ndef terminate(self):\n    \"\"\"\n    !!! warning \"[DEPRECATION NOTICE]: This method is now deprecated and will be removed in a future release.\"\n\n    This function ensures backward compatibility for the `terminate()` method to maintain the API on existing systems.\n    It achieves this by calling the new `close()` method to terminate various\n    StreamGear processes.\n    \"\"\"\n\n    self.close()\n
"},{"location":"bonus/reference/streamgear/#vidgear.gears.streamgear.StreamGear.transcode_source","title":"transcode_source(self)","text":"

Transcodes an entire video file (with or without audio) into chunked-encoded media segments of streaming formats such as MPEG-DASH and HLS.

Source code in vidgear/gears/streamgear.py
def transcode_source(self):\n    \"\"\"\n    Transcodes an entire video file _(with or without audio)_ into chunked-encoded media segments of\n    streaming formats such as MPEG-DASH and HLS.\n    \"\"\"\n    # check if function is called in correct context\n    if not (self.__video_source):\n        raise RuntimeError(\n            \"[StreamGear:ERROR] :: The `transcode_source()` method cannot be used without a valid `-video_source` input. Kindly refer vidgear docs!\"\n        )\n    # assign height, width and framerate\n    self.__inputheight = int(self.__aspect_source[1])\n    self.__inputwidth = int(self.__aspect_source[0])\n    self.__sourceframerate = float(self.__fps_source)\n    # launch pre-processing\n    self.__PreProcess()\n
"},{"location":"bonus/reference/videogear/","title":"VideoGear API References","text":"

VideoGear API usage examples can be found here \u27b6

VideoGear API parameters are explained here \u27b6

VideoGear API provides a special internal wrapper around VidGear's exclusive Video Stabilizer class. VideoGear also acts as a Common Video-Capture API that provides internal access for both CamGear and PiGear APIs and their parameters with an exclusive enablePiCamera boolean flag.

VideoGear is ideal when you need to switch to different video sources without changing your code much. Also, it enables easy stabilization for various video-streams (real-time or not) with minimum effort and writing way fewer lines of code.

Source code in vidgear/gears/videogear.py
class VideoGear:\n    \"\"\"\n    VideoGear API provides a special internal wrapper around VidGear's exclusive Video Stabilizer class.\n    VideoGear also acts as a Common Video-Capture API that provides internal access for both CamGear and PiGear APIs and\n    their parameters with an exclusive enablePiCamera boolean flag.\n\n    VideoGear is ideal when you need to switch to different video sources without changing your code much. Also, it enables\n    easy stabilization for various video-streams (real-time or not)\n    with minimum effort and writing way fewer lines of code.\n    \"\"\"\n\n    def __init__(\n        self,\n        # VideoGear parameters\n        enablePiCamera=False,\n        stabilize=False,\n        # PiGear parameters\n        camera_num=0,\n        resolution=(640, 480),\n        framerate=30,\n        # CamGear parameters\n        source=0,\n        stream_mode=False,\n        backend=0,\n        # common parameters\n        time_delay=0,\n        colorspace=None,\n        logging=False,\n        **options\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the VideoGear class.\n\n        Parameters:\n            enablePiCamera (bool): provide access to PiGear(if True) or CamGear(if False) APIs respectively.\n            stabilize (bool): enable access to Stabilizer Class for stabilizing frames.\n            camera_num (int): selects the camera module index which will be used as Rpi source.\n            resolution (tuple): sets the resolution (i.e. `(width,height)`) of the Rpi source.\n            framerate (int/float): sets the framerate of the Rpi source.\n            source (based on input): defines the source for the input stream.\n            stream_mode (bool): controls the exclusive YouTube Mode.\n            backend (int): selects the backend for OpenCV's VideoCapture class.\n            colorspace (str): selects the colorspace of the input stream.\n            logging (bool): enables/disables logging.\n            time_delay (int): time delay (in sec) before start reading the frames.\n            options (dict): provides ability to alter Tweak Parameters of CamGear, PiGear & Stabilizer.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # initialize stabilizer\n        self.__stabilization_mode = stabilize\n\n        # reformat dictionary\n        options = {str(k).strip(): v for k, v in options.items()}\n\n        if self.__stabilization_mode:\n            from .stabilizer import Stabilizer\n\n            s_radius = options.pop(\"SMOOTHING_RADIUS\", 25)\n            if not isinstance(s_radius, int):\n                s_radius = 25\n\n            border_size = options.pop(\"BORDER_SIZE\", 0)\n            if not isinstance(border_size, int):\n                border_size = 0\n\n            border_type = options.pop(\"BORDER_TYPE\", \"black\")\n            if not isinstance(border_type, str):\n                border_type = \"black\"\n\n            crop_n_zoom = options.pop(\"CROP_N_ZOOM\", False)\n            if not isinstance(crop_n_zoom, bool):\n                crop_n_zoom = False\n\n            self.__stabilizer_obj = Stabilizer(\n                smoothing_radius=s_radius,\n                border_type=border_type,\n                border_size=border_size,\n                crop_n_zoom=crop_n_zoom,\n                logging=logging,\n            )\n            self.__logging and logger.debug(\n                \"Enabling Stabilization Mode for the current video source!\"\n            )  # log info\n\n        if enablePiCamera:\n            # only import the pigear module only if required\n            from .pigear import PiGear\n\n            # initialize the picamera stream by enabling PiGear API\n            self.stream = PiGear(\n                camera_num=camera_num,\n                resolution=resolution,\n                framerate=framerate,\n                colorspace=colorspace,\n                logging=logging,\n                time_delay=time_delay,\n                **options\n            )\n        else:\n            # otherwise, we are using OpenCV so initialize the webcam\n            # stream by activating CamGear API\n            self.stream = CamGear(\n                source=source,\n                stream_mode=stream_mode,\n                backend=backend,\n                colorspace=colorspace,\n                logging=logging,\n                time_delay=time_delay,\n                **options\n            )\n\n        # initialize framerate variable\n        self.framerate = self.stream.framerate\n\n    def start(self):\n        \"\"\"\n        Launches the internal *Threaded Frames Extractor* daemon of API in use.\n\n        **Returns:** A reference to the selected class object.\n        \"\"\"\n        self.stream.start()\n        return self\n\n    def read(self):\n        \"\"\"\n        Extracts frames synchronously from selected API's monitored deque, while maintaining a fixed-length frame\n        buffer in the memory, and blocks the thread if the deque is full.\n\n        **Returns:** A n-dimensional numpy array.\n        \"\"\"\n        while self.__stabilization_mode:\n            frame = self.stream.read()\n            if frame is None:\n                break\n            frame_stab = self.__stabilizer_obj.stabilize(frame)\n            if not (frame_stab is None):\n                return frame_stab\n        return self.stream.read()\n\n    def stop(self):\n        \"\"\"\n        Safely terminates the thread, and release the respective multi-threaded resources.\n        \"\"\"\n        self.stream.stop()\n        # logged\n        self.__logging and logger.debug(\"Terminating VideoGear.\")\n        # clean queue\n        self.__stabilization_mode and self.__stabilizer_obj.clean()\n

"},{"location":"bonus/reference/videogear/#vidgear.gears.videogear.VideoGear.__init__","title":"__init__(self, enablePiCamera=False, stabilize=False, camera_num=0, resolution=(640, 480), framerate=30, source=0, stream_mode=False, backend=0, time_delay=0, colorspace=None, logging=False, **options) special","text":"

This constructor method initializes the object state and attributes of the VideoGear class.

Parameters:

Name Type Description Default enablePiCamera bool

provide access to PiGear(if True) or CamGear(if False) APIs respectively.

False stabilize bool

enable access to Stabilizer Class for stabilizing frames.

False camera_num int

selects the camera module index which will be used as Rpi source.

0 resolution tuple

sets the resolution (i.e. (width,height)) of the Rpi source.

(640, 480) framerate int/float

sets the framerate of the Rpi source.

30 source based on input

defines the source for the input stream.

0 stream_mode bool

controls the exclusive YouTube Mode.

False backend int

selects the backend for OpenCV's VideoCapture class.

0 colorspace str

selects the colorspace of the input stream.

None logging bool

enables/disables logging.

False time_delay int

time delay (in sec) before start reading the frames.

0 options dict

provides ability to alter Tweak Parameters of CamGear, PiGear & Stabilizer.

{} Source code in vidgear/gears/videogear.py
def __init__(\n    self,\n    # VideoGear parameters\n    enablePiCamera=False,\n    stabilize=False,\n    # PiGear parameters\n    camera_num=0,\n    resolution=(640, 480),\n    framerate=30,\n    # CamGear parameters\n    source=0,\n    stream_mode=False,\n    backend=0,\n    # common parameters\n    time_delay=0,\n    colorspace=None,\n    logging=False,\n    **options\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the VideoGear class.\n\n    Parameters:\n        enablePiCamera (bool): provide access to PiGear(if True) or CamGear(if False) APIs respectively.\n        stabilize (bool): enable access to Stabilizer Class for stabilizing frames.\n        camera_num (int): selects the camera module index which will be used as Rpi source.\n        resolution (tuple): sets the resolution (i.e. `(width,height)`) of the Rpi source.\n        framerate (int/float): sets the framerate of the Rpi source.\n        source (based on input): defines the source for the input stream.\n        stream_mode (bool): controls the exclusive YouTube Mode.\n        backend (int): selects the backend for OpenCV's VideoCapture class.\n        colorspace (str): selects the colorspace of the input stream.\n        logging (bool): enables/disables logging.\n        time_delay (int): time delay (in sec) before start reading the frames.\n        options (dict): provides ability to alter Tweak Parameters of CamGear, PiGear & Stabilizer.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # initialize stabilizer\n    self.__stabilization_mode = stabilize\n\n    # reformat dictionary\n    options = {str(k).strip(): v for k, v in options.items()}\n\n    if self.__stabilization_mode:\n        from .stabilizer import Stabilizer\n\n        s_radius = options.pop(\"SMOOTHING_RADIUS\", 25)\n        if not isinstance(s_radius, int):\n            s_radius = 25\n\n        border_size = options.pop(\"BORDER_SIZE\", 0)\n        if not isinstance(border_size, int):\n            border_size = 0\n\n        border_type = options.pop(\"BORDER_TYPE\", \"black\")\n        if not isinstance(border_type, str):\n            border_type = \"black\"\n\n        crop_n_zoom = options.pop(\"CROP_N_ZOOM\", False)\n        if not isinstance(crop_n_zoom, bool):\n            crop_n_zoom = False\n\n        self.__stabilizer_obj = Stabilizer(\n            smoothing_radius=s_radius,\n            border_type=border_type,\n            border_size=border_size,\n            crop_n_zoom=crop_n_zoom,\n            logging=logging,\n        )\n        self.__logging and logger.debug(\n            \"Enabling Stabilization Mode for the current video source!\"\n        )  # log info\n\n    if enablePiCamera:\n        # only import the pigear module only if required\n        from .pigear import PiGear\n\n        # initialize the picamera stream by enabling PiGear API\n        self.stream = PiGear(\n            camera_num=camera_num,\n            resolution=resolution,\n            framerate=framerate,\n            colorspace=colorspace,\n            logging=logging,\n            time_delay=time_delay,\n            **options\n        )\n    else:\n        # otherwise, we are using OpenCV so initialize the webcam\n        # stream by activating CamGear API\n        self.stream = CamGear(\n            source=source,\n            stream_mode=stream_mode,\n            backend=backend,\n            colorspace=colorspace,\n            logging=logging,\n            time_delay=time_delay,\n            **options\n        )\n\n    # initialize framerate variable\n    self.framerate = self.stream.framerate\n
"},{"location":"bonus/reference/videogear/#vidgear.gears.videogear.VideoGear.read","title":"read(self)","text":"

Extracts frames synchronously from selected API's monitored deque, while maintaining a fixed-length frame buffer in the memory, and blocks the thread if the deque is full.

Returns: A n-dimensional numpy array.

Source code in vidgear/gears/videogear.py
def read(self):\n    \"\"\"\n    Extracts frames synchronously from selected API's monitored deque, while maintaining a fixed-length frame\n    buffer in the memory, and blocks the thread if the deque is full.\n\n    **Returns:** A n-dimensional numpy array.\n    \"\"\"\n    while self.__stabilization_mode:\n        frame = self.stream.read()\n        if frame is None:\n            break\n        frame_stab = self.__stabilizer_obj.stabilize(frame)\n        if not (frame_stab is None):\n            return frame_stab\n    return self.stream.read()\n
"},{"location":"bonus/reference/videogear/#vidgear.gears.videogear.VideoGear.start","title":"start(self)","text":"

Launches the internal Threaded Frames Extractor daemon of API in use.

Returns: A reference to the selected class object.

Source code in vidgear/gears/videogear.py
def start(self):\n    \"\"\"\n    Launches the internal *Threaded Frames Extractor* daemon of API in use.\n\n    **Returns:** A reference to the selected class object.\n    \"\"\"\n    self.stream.start()\n    return self\n
"},{"location":"bonus/reference/videogear/#vidgear.gears.videogear.VideoGear.stop","title":"stop(self)","text":"

Safely terminates the thread, and release the respective multi-threaded resources.

Source code in vidgear/gears/videogear.py
def stop(self):\n    \"\"\"\n    Safely terminates the thread, and release the respective multi-threaded resources.\n    \"\"\"\n    self.stream.stop()\n    # logged\n    self.__logging and logger.debug(\"Terminating VideoGear.\")\n    # clean queue\n    self.__stabilization_mode and self.__stabilizer_obj.clean()\n
"},{"location":"bonus/reference/webgear/","title":"WebGear API References","text":"

WebGear API usage examples can be found here \u27b6

WebGear API parameters are explained here \u27b6

WebGear is a powerful ASGI Video-Broadcaster API ideal for transmitting Motion-JPEG-frames from a single source to multiple recipients via the browser.

WebGear API works on Starlette's ASGI application and provides a highly extensible and flexible async wrapper around its complete framework. WebGear can flexibly interact with Starlette's ecosystem of shared middleware, mountable applications, Response classes, Routing tables, Static Files, Templating engine(with Jinja2), etc.

WebGear API uses an intraframe-only compression scheme under the hood where the sequence of video-frames are first encoded as JPEG-DIB (JPEG with Device-Independent Bit compression) and then streamed over HTTP using Starlette's Multipart Streaming Response and a Uvicorn ASGI Server. This method imposes lower processing and memory requirements, but the quality is not the best, since JPEG compression is not very efficient for motion video.

In layman's terms, WebGear acts as a powerful Video Broadcaster that transmits live video-frames to any web-browser in the network. Additionally, WebGear API also provides internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs, thereby granting it exclusive power for transferring frames incoming from any source to the network.

Source code in vidgear/gears/asyncio/webgear.py
class WebGear:\n    \"\"\"\n    WebGear is a powerful ASGI Video-Broadcaster API ideal for transmitting Motion-JPEG-frames from a single source to multiple recipients via the browser.\n\n    WebGear API works on Starlette's ASGI application and provides a highly extensible and flexible async wrapper around its complete framework. WebGear can\n    flexibly interact with Starlette's ecosystem of shared middleware, mountable applications, Response classes, Routing tables, Static Files, Templating\n    engine(with Jinja2), etc.\n\n    WebGear API uses an intraframe-only compression scheme under the hood where the sequence of video-frames are first encoded as JPEG-DIB (JPEG with Device-Independent Bit compression)\n    and then streamed over HTTP using Starlette's Multipart Streaming Response and a Uvicorn ASGI Server. This method imposes lower processing and memory requirements, but the quality\n    is not the best, since JPEG compression is not very efficient for motion video.\n\n    In layman's terms, WebGear acts as a powerful Video Broadcaster that transmits live video-frames to any web-browser in the network. Additionally, WebGear API also provides internal\n    wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs, thereby granting it exclusive power for transferring frames incoming from any source to the network.\n    \"\"\"\n\n    def __init__(\n        self,\n        enablePiCamera=False,\n        stabilize=False,\n        source=None,\n        camera_num=0,\n        stream_mode=False,\n        backend=0,\n        colorspace=None,\n        resolution=(640, 480),\n        framerate=25,\n        logging=False,\n        time_delay=0,\n        **options\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the WebGear class.\n\n        Parameters:\n            enablePiCamera (bool): provide access to PiGear(if True) or CamGear(if False) APIs respectively.\n            stabilize (bool): enable access to Stabilizer Class for stabilizing frames.\n            camera_num (int): selects the camera module index which will be used as Rpi source.\n            resolution (tuple): sets the resolution (i.e. `(width,height)`) of the Rpi source.\n            framerate (int/float): sets the framerate of the Rpi source.\n            source (based on input): defines the source for the input stream.\n            stream_mode (bool): controls the exclusive YouTube Mode.\n            backend (int): selects the backend for OpenCV's VideoCapture class.\n            colorspace (str): selects the colorspace of the input stream.\n            logging (bool): enables/disables logging.\n            time_delay (int): time delay (in sec) before start reading the frames.\n            options (dict): provides ability to alter Tweak Parameters of WebGear, CamGear, PiGear & Stabilizer.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # raise error(s) for critical Class imports\n        import_dependency_safe(\"starlette\" if starlette is None else \"\")\n        import_dependency_safe(\n            \"simplejpeg\" if simplejpeg is None else \"\", min_version=\"1.6.1\"\n        )\n\n        # initialize global params\n        self.__skip_generate_webdata = False  # generate webgear data by default\n        # define frame-compression handler\n        self.__jpeg_compression_quality = 90  # 90% quality\n        self.__jpeg_compression_fastdct = True  # fastest DCT on by default\n        self.__jpeg_compression_fastupsample = False  # fastupsample off by default\n        self.__jpeg_compression_colorspace = \"BGR\"  # use BGR colorspace by default\n        self.__frame_size_reduction = 25  # use 25% reduction\n        # retrieve interpolation for reduction\n        self.__interpolation = retrieve_best_interpolation(\n            [\"INTER_LINEAR_EXACT\", \"INTER_LINEAR\", \"INTER_AREA\"]\n        )\n\n        custom_video_endpoint = \"\"  # custom video endpoint path\n        custom_data_location = \"\"  # path to save data-files to custom location\n        data_path = \"\"  # path to WebGear data-files\n        overwrite_default = False\n        self.__enable_inf = False  # continue frames even when video ends.\n\n        # reformat dictionary\n        options = {str(k).strip(): v for k, v in options.items()}\n\n        # assign values to global variables if specified and valid\n        if options:\n            # check whether to disable Data-Files Auto-Generation WorkFlow\n            if \"skip_generate_webdata\" in options:\n                value = options[\"skip_generate_webdata\"]\n                # enable jpeg fastdct\n                if isinstance(value, bool):\n                    self.__skip_generate_webdata = value\n                else:\n                    logger.warning(\"Skipped invalid `skip_generate_webdata` value!\")\n                del options[\"skip_generate_webdata\"]  # clean\n\n            if \"jpeg_compression_colorspace\" in options:\n                value = options[\"jpeg_compression_colorspace\"]\n                if isinstance(value, str) and value.strip().upper() in [\n                    \"RGB\",\n                    \"BGR\",\n                    \"RGBX\",\n                    \"BGRX\",\n                    \"XBGR\",\n                    \"XRGB\",\n                    \"GRAY\",\n                    \"RGBA\",\n                    \"BGRA\",\n                    \"ABGR\",\n                    \"ARGB\",\n                    \"CMYK\",\n                ]:\n                    # set encoding colorspace\n                    self.__jpeg_compression_colorspace = value.strip().upper()\n                else:\n                    logger.warning(\n                        \"Skipped invalid `jpeg_compression_colorspace` value!\"\n                    )\n                del options[\"jpeg_compression_colorspace\"]  # clean\n\n            if \"jpeg_compression_quality\" in options:\n                value = options[\"jpeg_compression_quality\"]\n                # set valid jpeg quality\n                if isinstance(value, (int, float)) and value >= 10 and value <= 100:\n                    self.__jpeg_compression_quality = int(value)\n                else:\n                    logger.warning(\"Skipped invalid `jpeg_compression_quality` value!\")\n                del options[\"jpeg_compression_quality\"]  # clean\n\n            if \"jpeg_compression_fastdct\" in options:\n                value = options[\"jpeg_compression_fastdct\"]\n                # enable jpeg fastdct\n                if isinstance(value, bool):\n                    self.__jpeg_compression_fastdct = value\n                else:\n                    logger.warning(\"Skipped invalid `jpeg_compression_fastdct` value!\")\n                del options[\"jpeg_compression_fastdct\"]  # clean\n\n            if \"jpeg_compression_fastupsample\" in options:\n                value = options[\"jpeg_compression_fastupsample\"]\n                # enable jpeg  fastupsample\n                if isinstance(value, bool):\n                    self.__jpeg_compression_fastupsample = value\n                else:\n                    logger.warning(\n                        \"Skipped invalid `jpeg_compression_fastupsample` value!\"\n                    )\n                del options[\"jpeg_compression_fastupsample\"]  # clean\n\n            if \"frame_size_reduction\" in options:\n                value = options[\"frame_size_reduction\"]\n                if isinstance(value, (int, float)) and value >= 0 and value <= 90:\n                    self.__frame_size_reduction = value\n                else:\n                    logger.warning(\"Skipped invalid `frame_size_reduction` value!\")\n                del options[\"frame_size_reduction\"]  # clean\n\n            if \"custom_video_endpoint\" in options:\n                value = options[\"custom_video_endpoint\"]\n                if value and isinstance(value, str) and value.strip().isalnum():\n                    custom_video_endpoint = value.strip()\n                    logging and logger.critical(\n                        \"Using custom video endpoint path: `/{}`\".format(\n                            custom_video_endpoint\n                        )\n                    )\n                else:\n                    logger.warning(\"Skipped invalid `custom_video_endpoint` value!\")\n                del options[\"custom_video_endpoint\"]  # clean\n\n            if \"custom_data_location\" in options:\n                value = options[\"custom_data_location\"]\n                if value and isinstance(value, str):\n                    assert os.access(\n                        value, os.W_OK\n                    ), \"[WebGear:ERROR] :: Permission Denied!, cannot write WebGear data-files to '{}' directory!\".format(\n                        value\n                    )\n                    assert os.path.isdir(\n                        os.path.abspath(value)\n                    ), \"[WebGear:ERROR] :: `custom_data_location` value must be the path to a directory and not to a file!\"\n                    custom_data_location = os.path.abspath(value)\n                else:\n                    logger.warning(\"Skipped invalid `custom_data_location` value!\")\n                del options[\"custom_data_location\"]  # clean\n\n            if \"overwrite_default_files\" in options:\n                value = options[\"overwrite_default_files\"]\n                if isinstance(value, bool):\n                    overwrite_default = value\n                else:\n                    logger.warning(\"Skipped invalid `overwrite_default_files` value!\")\n                del options[\"overwrite_default_files\"]  # clean\n\n            if \"enable_infinite_frames\" in options:\n                value = options[\"enable_infinite_frames\"]\n                if isinstance(value, bool):\n                    self.__enable_inf = value\n                else:\n                    logger.warning(\"Skipped invalid `enable_infinite_frames` value!\")\n                del options[\"enable_infinite_frames\"]  # clean\n\n        # check if disable Data-Files Auto-Generation WorkFlow is disabled\n        if not self.__skip_generate_webdata:\n            # check if custom data path is specified\n            if custom_data_location:\n                data_path = generate_webdata(\n                    custom_data_location,\n                    c_name=\"webgear\",\n                    overwrite_default=overwrite_default,\n                    logging=logging,\n                )\n            else:\n                # otherwise generate suitable path\n                data_path = generate_webdata(\n                    os.path.join(expanduser(\"~\"), \".vidgear\"),\n                    c_name=\"webgear\",\n                    overwrite_default=overwrite_default,\n                    logging=logging,\n                )\n\n            # log it\n            self.__logging and logger.debug(\n                \"`{}` is the default location for saving WebGear data-files.\".format(\n                    data_path\n                )\n            )\n            # define Jinja2 templates handler\n            self.__templates = Jinja2Templates(\n                directory=\"{}/templates\".format(data_path)\n            )\n            # define routing tables\n            self.routes = [\n                Route(\"/\", endpoint=self.__homepage),\n                Route(\n                    \"/{}\".format(\n                        custom_video_endpoint if custom_video_endpoint else \"video\"\n                    ),\n                    endpoint=self.__video,\n                ),\n                Mount(\n                    \"/static\",\n                    app=StaticFiles(directory=\"{}/static\".format(data_path)),\n                    name=\"static\",\n                ),\n            ]\n        else:\n            # log it\n            self.__logging and logger.critical(\n                \"WebGear Data-Files Auto-Generation WorkFlow has been manually disabled.\"\n            )\n            # define routing tables\n            self.routes = [\n                Route(\n                    \"/{}\".format(\n                        custom_video_endpoint if custom_video_endpoint else \"video\"\n                    ),\n                    endpoint=self.__video,\n                ),\n            ]\n            # log exceptions\n            self.__logging and logger.warning(\n                \"Only `/video` route is available for this instance.\"\n            )\n\n        # define custom exception handlers\n        self.__exception_handlers = {404: self.__not_found, 500: self.__server_error}\n        # define middleware support\n        self.middleware = []\n        # Handle video source\n        if source is None:\n            self.config = {\"generator\": None}\n            self.__stream = None\n        else:\n            # define stream with necessary params\n            self.__stream = VideoGear(\n                enablePiCamera=enablePiCamera,\n                stabilize=stabilize,\n                source=source,\n                camera_num=camera_num,\n                stream_mode=stream_mode,\n                backend=backend,\n                colorspace=colorspace,\n                resolution=resolution,\n                framerate=framerate,\n                logging=logging,\n                time_delay=time_delay,\n                **options\n            )\n            # define default frame generator in configuration\n            self.config = {\"generator\": self.__producer}\n\n        # log if specified\n        if self.__logging:\n            if source is None:\n                logger.warning(\n                    \"Given source is of NoneType. Therefore, JPEG Frame-Compression is disabled!\"\n                )\n            else:\n                logger.debug(\n                    \"Enabling JPEG Frame-Compression with Colorspace:`{}`, Quality:`{}`%, Fastdct:`{}`, and Fastupsample:`{}`.\".format(\n                        self.__jpeg_compression_colorspace,\n                        self.__jpeg_compression_quality,\n                        \"enabled\" if self.__jpeg_compression_fastdct else \"disabled\",\n                        (\n                            \"enabled\"\n                            if self.__jpeg_compression_fastupsample\n                            else \"disabled\"\n                        ),\n                    )\n                )\n\n        # copying original routing tables for further validation\n        self.__rt_org_copy = self.routes[:]\n        # initialize blank frame\n        self.blank_frame = None\n        # keeps check if producer loop should be running\n        self.__isrunning = True\n\n    def __call__(self):\n        \"\"\"\n        Implements a custom Callable method for WebGear application.\n        \"\"\"\n        # validate routing tables\n        assert not (self.routes is None), \"Routing tables are NoneType!\"\n        if not isinstance(self.routes, list) or not all(\n            x in self.routes for x in self.__rt_org_copy\n        ):\n            raise RuntimeError(\"[WebGear:ERROR] :: Routing tables are not valid!\")\n\n        # validate middlewares\n        assert not (self.middleware is None), \"Middlewares are NoneType!\"\n        if self.middleware and (\n            not isinstance(self.middleware, list)\n            or not all(isinstance(x, Middleware) for x in self.middleware)\n        ):\n            raise RuntimeError(\"[WebGear:ERROR] :: Middlewares are not valid!\")\n\n        # validate assigned frame generator in WebGear configuration\n        if isinstance(self.config, dict) and \"generator\" in self.config:\n            # check if its  assigned value is a asynchronous generator\n            if self.config[\"generator\"] is None or not inspect.isasyncgen(\n                self.config[\"generator\"]()\n            ):\n                # otherwise raise error\n                raise ValueError(\n                    \"[WebGear:ERROR] :: Invalid configuration. Assigned generator must be a asynchronous generator function/method only!\"\n                )\n        else:\n            # raise error if validation fails\n            raise RuntimeError(\"[WebGear:ERROR] :: Assigned configuration is invalid!\")\n\n        # initiate stream\n        self.__logging and logger.debug(\"Initiating Video Streaming.\")\n        if not (self.__stream is None):\n            self.__stream.start()\n        # return Starlette application\n        self.__logging and logger.debug(\"Running Starlette application.\")\n        return Starlette(\n            debug=(True if self.__logging else False),\n            routes=self.routes,\n            middleware=self.middleware,\n            exception_handlers=self.__exception_handlers,\n            lifespan=self.__lifespan,\n        )\n\n    async def __producer(self):\n        \"\"\"\n        WebGear's default asynchronous frame producer/generator.\n        \"\"\"\n        # loop over frames\n        while self.__isrunning:\n            # read frame\n            frame = self.__stream.read()\n\n            # display blank if NoneType\n            if frame is None:\n                frame = (\n                    self.blank_frame\n                    if self.blank_frame is None\n                    else self.blank_frame[:]\n                )\n                if not self.__enable_inf:\n                    self.__isrunning = False\n            else:\n                # create blank\n                if self.blank_frame is None:\n                    self.blank_frame = create_blank_frame(\n                        frame=frame,\n                        text=\"No Input\" if self.__enable_inf else \"The End\",\n                        logging=self.__logging,\n                    )\n\n            # reducer frames size if specified\n            if self.__frame_size_reduction:\n                frame = await reducer(\n                    frame,\n                    percentage=self.__frame_size_reduction,\n                    interpolation=self.__interpolation,\n                )\n\n            # handle JPEG encoding\n            if self.__jpeg_compression_colorspace == \"GRAY\":\n                if frame.ndim == 2:\n                    # patch for https://gitlab.com/jfolz/simplejpeg/-/issues/11\n                    frame = np.expand_dims(frame, axis=2)\n                encodedImage = simplejpeg.encode_jpeg(\n                    frame,\n                    quality=self.__jpeg_compression_quality,\n                    colorspace=self.__jpeg_compression_colorspace,\n                    fastdct=self.__jpeg_compression_fastdct,\n                )\n            else:\n                encodedImage = simplejpeg.encode_jpeg(\n                    frame,\n                    quality=self.__jpeg_compression_quality,\n                    colorspace=self.__jpeg_compression_colorspace,\n                    colorsubsampling=\"422\",\n                    fastdct=self.__jpeg_compression_fastdct,\n                )\n\n            # yield frame in byte format\n            yield (\n                b\"--frame\\r\\nContent-Type:image/jpeg\\r\\n\\r\\n\" + encodedImage + b\"\\r\\n\"\n            )\n            # sleep for sometime.\n            await asyncio.sleep(0)\n\n    async def __video(self, scope):\n        \"\"\"\n        Returns a async video streaming response.\n        \"\"\"\n        assert scope[\"type\"] in [\"http\", \"https\"]\n        return StreamingResponse(\n            self.config[\"generator\"](),\n            media_type=\"multipart/x-mixed-replace; boundary=frame\",\n        )\n\n    async def __homepage(self, request):\n        \"\"\"\n        Returns an HTML index page.\n        \"\"\"\n        return (\n            self.__templates.TemplateResponse(request, \"index.html\")\n            if not self.__skip_generate_webdata\n            else JSONResponse(\n                {\n                    \"detail\": \"MESSAGE : WebGear Data-Files Auto-Generation WorkFlow is disabled!\"\n                },\n                status_code=404,\n            )\n        )\n\n    async def __not_found(self, request, exc):\n        \"\"\"\n        Returns an HTML 404 page.\n        \"\"\"\n        return (\n            self.__templates.TemplateResponse(request, \"404.html\", status_code=404)\n            if not self.__skip_generate_webdata\n            else JSONResponse(\n                {\n                    \"detail\": \"ERROR : {} :: MESSAGE : WebGear Data-Files Auto-Generation WorkFlow is disabled.\".format(\n                        exc.detail\n                    )\n                },\n                status_code=404,\n            )\n        )\n\n    async def __server_error(self, request, exc):\n        \"\"\"\n        Returns an HTML 500 page.\n        \"\"\"\n        return (\n            self.__templates.TemplateResponse(request, \"500.html\", status_code=500)\n            if not self.__skip_generate_webdata\n            else JSONResponse(\n                {\n                    \"detail\": \"ERROR : {} :: MESSAGE : WebGear Data-Files Auto-Generation WorkFlow is disabled.\".format(\n                        exc.detail if hasattr(exc, \"detail\") else repr(exc)\n                    )\n                },\n                status_code=500,\n            )\n        )\n\n    @contextlib.asynccontextmanager\n    async def __lifespan(self, context):\n        try:\n            yield\n        finally:\n            # close Video Server\n            self.shutdown()\n\n    def shutdown(self):\n        \"\"\"\n        Implements a Callable to be run on application shutdown\n        \"\"\"\n        if not (self.__stream is None):\n            self.__logging and logger.debug(\"Closing Video Streaming.\")\n            # stops producer\n            self.__isrunning = False\n            # stops VideoGear stream\n            self.__stream.stop()\n            # prevent any re-iteration\n            self.__stream = None\n

"},{"location":"bonus/reference/webgear/#vidgear.gears.asyncio.webgear.WebGear.__call__","title":"__call__(self) special","text":"

Implements a custom Callable method for WebGear application.

Source code in vidgear/gears/asyncio/webgear.py
def __call__(self):\n    \"\"\"\n    Implements a custom Callable method for WebGear application.\n    \"\"\"\n    # validate routing tables\n    assert not (self.routes is None), \"Routing tables are NoneType!\"\n    if not isinstance(self.routes, list) or not all(\n        x in self.routes for x in self.__rt_org_copy\n    ):\n        raise RuntimeError(\"[WebGear:ERROR] :: Routing tables are not valid!\")\n\n    # validate middlewares\n    assert not (self.middleware is None), \"Middlewares are NoneType!\"\n    if self.middleware and (\n        not isinstance(self.middleware, list)\n        or not all(isinstance(x, Middleware) for x in self.middleware)\n    ):\n        raise RuntimeError(\"[WebGear:ERROR] :: Middlewares are not valid!\")\n\n    # validate assigned frame generator in WebGear configuration\n    if isinstance(self.config, dict) and \"generator\" in self.config:\n        # check if its  assigned value is a asynchronous generator\n        if self.config[\"generator\"] is None or not inspect.isasyncgen(\n            self.config[\"generator\"]()\n        ):\n            # otherwise raise error\n            raise ValueError(\n                \"[WebGear:ERROR] :: Invalid configuration. Assigned generator must be a asynchronous generator function/method only!\"\n            )\n    else:\n        # raise error if validation fails\n        raise RuntimeError(\"[WebGear:ERROR] :: Assigned configuration is invalid!\")\n\n    # initiate stream\n    self.__logging and logger.debug(\"Initiating Video Streaming.\")\n    if not (self.__stream is None):\n        self.__stream.start()\n    # return Starlette application\n    self.__logging and logger.debug(\"Running Starlette application.\")\n    return Starlette(\n        debug=(True if self.__logging else False),\n        routes=self.routes,\n        middleware=self.middleware,\n        exception_handlers=self.__exception_handlers,\n        lifespan=self.__lifespan,\n    )\n
"},{"location":"bonus/reference/webgear/#vidgear.gears.asyncio.webgear.WebGear.__init__","title":"__init__(self, enablePiCamera=False, stabilize=False, source=None, camera_num=0, stream_mode=False, backend=0, colorspace=None, resolution=(640, 480), framerate=25, logging=False, time_delay=0, **options) special","text":"

This constructor method initializes the object state and attributes of the WebGear class.

Parameters:

Name Type Description Default enablePiCamera bool

provide access to PiGear(if True) or CamGear(if False) APIs respectively.

False stabilize bool

enable access to Stabilizer Class for stabilizing frames.

False camera_num int

selects the camera module index which will be used as Rpi source.

0 resolution tuple

sets the resolution (i.e. (width,height)) of the Rpi source.

(640, 480) framerate int/float

sets the framerate of the Rpi source.

25 source based on input

defines the source for the input stream.

None stream_mode bool

controls the exclusive YouTube Mode.

False backend int

selects the backend for OpenCV's VideoCapture class.

0 colorspace str

selects the colorspace of the input stream.

None logging bool

enables/disables logging.

False time_delay int

time delay (in sec) before start reading the frames.

0 options dict

provides ability to alter Tweak Parameters of WebGear, CamGear, PiGear & Stabilizer.

{} Source code in vidgear/gears/asyncio/webgear.py
def __init__(\n    self,\n    enablePiCamera=False,\n    stabilize=False,\n    source=None,\n    camera_num=0,\n    stream_mode=False,\n    backend=0,\n    colorspace=None,\n    resolution=(640, 480),\n    framerate=25,\n    logging=False,\n    time_delay=0,\n    **options\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the WebGear class.\n\n    Parameters:\n        enablePiCamera (bool): provide access to PiGear(if True) or CamGear(if False) APIs respectively.\n        stabilize (bool): enable access to Stabilizer Class for stabilizing frames.\n        camera_num (int): selects the camera module index which will be used as Rpi source.\n        resolution (tuple): sets the resolution (i.e. `(width,height)`) of the Rpi source.\n        framerate (int/float): sets the framerate of the Rpi source.\n        source (based on input): defines the source for the input stream.\n        stream_mode (bool): controls the exclusive YouTube Mode.\n        backend (int): selects the backend for OpenCV's VideoCapture class.\n        colorspace (str): selects the colorspace of the input stream.\n        logging (bool): enables/disables logging.\n        time_delay (int): time delay (in sec) before start reading the frames.\n        options (dict): provides ability to alter Tweak Parameters of WebGear, CamGear, PiGear & Stabilizer.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # raise error(s) for critical Class imports\n    import_dependency_safe(\"starlette\" if starlette is None else \"\")\n    import_dependency_safe(\n        \"simplejpeg\" if simplejpeg is None else \"\", min_version=\"1.6.1\"\n    )\n\n    # initialize global params\n    self.__skip_generate_webdata = False  # generate webgear data by default\n    # define frame-compression handler\n    self.__jpeg_compression_quality = 90  # 90% quality\n    self.__jpeg_compression_fastdct = True  # fastest DCT on by default\n    self.__jpeg_compression_fastupsample = False  # fastupsample off by default\n    self.__jpeg_compression_colorspace = \"BGR\"  # use BGR colorspace by default\n    self.__frame_size_reduction = 25  # use 25% reduction\n    # retrieve interpolation for reduction\n    self.__interpolation = retrieve_best_interpolation(\n        [\"INTER_LINEAR_EXACT\", \"INTER_LINEAR\", \"INTER_AREA\"]\n    )\n\n    custom_video_endpoint = \"\"  # custom video endpoint path\n    custom_data_location = \"\"  # path to save data-files to custom location\n    data_path = \"\"  # path to WebGear data-files\n    overwrite_default = False\n    self.__enable_inf = False  # continue frames even when video ends.\n\n    # reformat dictionary\n    options = {str(k).strip(): v for k, v in options.items()}\n\n    # assign values to global variables if specified and valid\n    if options:\n        # check whether to disable Data-Files Auto-Generation WorkFlow\n        if \"skip_generate_webdata\" in options:\n            value = options[\"skip_generate_webdata\"]\n            # enable jpeg fastdct\n            if isinstance(value, bool):\n                self.__skip_generate_webdata = value\n            else:\n                logger.warning(\"Skipped invalid `skip_generate_webdata` value!\")\n            del options[\"skip_generate_webdata\"]  # clean\n\n        if \"jpeg_compression_colorspace\" in options:\n            value = options[\"jpeg_compression_colorspace\"]\n            if isinstance(value, str) and value.strip().upper() in [\n                \"RGB\",\n                \"BGR\",\n                \"RGBX\",\n                \"BGRX\",\n                \"XBGR\",\n                \"XRGB\",\n                \"GRAY\",\n                \"RGBA\",\n                \"BGRA\",\n                \"ABGR\",\n                \"ARGB\",\n                \"CMYK\",\n            ]:\n                # set encoding colorspace\n                self.__jpeg_compression_colorspace = value.strip().upper()\n            else:\n                logger.warning(\n                    \"Skipped invalid `jpeg_compression_colorspace` value!\"\n                )\n            del options[\"jpeg_compression_colorspace\"]  # clean\n\n        if \"jpeg_compression_quality\" in options:\n            value = options[\"jpeg_compression_quality\"]\n            # set valid jpeg quality\n            if isinstance(value, (int, float)) and value >= 10 and value <= 100:\n                self.__jpeg_compression_quality = int(value)\n            else:\n                logger.warning(\"Skipped invalid `jpeg_compression_quality` value!\")\n            del options[\"jpeg_compression_quality\"]  # clean\n\n        if \"jpeg_compression_fastdct\" in options:\n            value = options[\"jpeg_compression_fastdct\"]\n            # enable jpeg fastdct\n            if isinstance(value, bool):\n                self.__jpeg_compression_fastdct = value\n            else:\n                logger.warning(\"Skipped invalid `jpeg_compression_fastdct` value!\")\n            del options[\"jpeg_compression_fastdct\"]  # clean\n\n        if \"jpeg_compression_fastupsample\" in options:\n            value = options[\"jpeg_compression_fastupsample\"]\n            # enable jpeg  fastupsample\n            if isinstance(value, bool):\n                self.__jpeg_compression_fastupsample = value\n            else:\n                logger.warning(\n                    \"Skipped invalid `jpeg_compression_fastupsample` value!\"\n                )\n            del options[\"jpeg_compression_fastupsample\"]  # clean\n\n        if \"frame_size_reduction\" in options:\n            value = options[\"frame_size_reduction\"]\n            if isinstance(value, (int, float)) and value >= 0 and value <= 90:\n                self.__frame_size_reduction = value\n            else:\n                logger.warning(\"Skipped invalid `frame_size_reduction` value!\")\n            del options[\"frame_size_reduction\"]  # clean\n\n        if \"custom_video_endpoint\" in options:\n            value = options[\"custom_video_endpoint\"]\n            if value and isinstance(value, str) and value.strip().isalnum():\n                custom_video_endpoint = value.strip()\n                logging and logger.critical(\n                    \"Using custom video endpoint path: `/{}`\".format(\n                        custom_video_endpoint\n                    )\n                )\n            else:\n                logger.warning(\"Skipped invalid `custom_video_endpoint` value!\")\n            del options[\"custom_video_endpoint\"]  # clean\n\n        if \"custom_data_location\" in options:\n            value = options[\"custom_data_location\"]\n            if value and isinstance(value, str):\n                assert os.access(\n                    value, os.W_OK\n                ), \"[WebGear:ERROR] :: Permission Denied!, cannot write WebGear data-files to '{}' directory!\".format(\n                    value\n                )\n                assert os.path.isdir(\n                    os.path.abspath(value)\n                ), \"[WebGear:ERROR] :: `custom_data_location` value must be the path to a directory and not to a file!\"\n                custom_data_location = os.path.abspath(value)\n            else:\n                logger.warning(\"Skipped invalid `custom_data_location` value!\")\n            del options[\"custom_data_location\"]  # clean\n\n        if \"overwrite_default_files\" in options:\n            value = options[\"overwrite_default_files\"]\n            if isinstance(value, bool):\n                overwrite_default = value\n            else:\n                logger.warning(\"Skipped invalid `overwrite_default_files` value!\")\n            del options[\"overwrite_default_files\"]  # clean\n\n        if \"enable_infinite_frames\" in options:\n            value = options[\"enable_infinite_frames\"]\n            if isinstance(value, bool):\n                self.__enable_inf = value\n            else:\n                logger.warning(\"Skipped invalid `enable_infinite_frames` value!\")\n            del options[\"enable_infinite_frames\"]  # clean\n\n    # check if disable Data-Files Auto-Generation WorkFlow is disabled\n    if not self.__skip_generate_webdata:\n        # check if custom data path is specified\n        if custom_data_location:\n            data_path = generate_webdata(\n                custom_data_location,\n                c_name=\"webgear\",\n                overwrite_default=overwrite_default,\n                logging=logging,\n            )\n        else:\n            # otherwise generate suitable path\n            data_path = generate_webdata(\n                os.path.join(expanduser(\"~\"), \".vidgear\"),\n                c_name=\"webgear\",\n                overwrite_default=overwrite_default,\n                logging=logging,\n            )\n\n        # log it\n        self.__logging and logger.debug(\n            \"`{}` is the default location for saving WebGear data-files.\".format(\n                data_path\n            )\n        )\n        # define Jinja2 templates handler\n        self.__templates = Jinja2Templates(\n            directory=\"{}/templates\".format(data_path)\n        )\n        # define routing tables\n        self.routes = [\n            Route(\"/\", endpoint=self.__homepage),\n            Route(\n                \"/{}\".format(\n                    custom_video_endpoint if custom_video_endpoint else \"video\"\n                ),\n                endpoint=self.__video,\n            ),\n            Mount(\n                \"/static\",\n                app=StaticFiles(directory=\"{}/static\".format(data_path)),\n                name=\"static\",\n            ),\n        ]\n    else:\n        # log it\n        self.__logging and logger.critical(\n            \"WebGear Data-Files Auto-Generation WorkFlow has been manually disabled.\"\n        )\n        # define routing tables\n        self.routes = [\n            Route(\n                \"/{}\".format(\n                    custom_video_endpoint if custom_video_endpoint else \"video\"\n                ),\n                endpoint=self.__video,\n            ),\n        ]\n        # log exceptions\n        self.__logging and logger.warning(\n            \"Only `/video` route is available for this instance.\"\n        )\n\n    # define custom exception handlers\n    self.__exception_handlers = {404: self.__not_found, 500: self.__server_error}\n    # define middleware support\n    self.middleware = []\n    # Handle video source\n    if source is None:\n        self.config = {\"generator\": None}\n        self.__stream = None\n    else:\n        # define stream with necessary params\n        self.__stream = VideoGear(\n            enablePiCamera=enablePiCamera,\n            stabilize=stabilize,\n            source=source,\n            camera_num=camera_num,\n            stream_mode=stream_mode,\n            backend=backend,\n            colorspace=colorspace,\n            resolution=resolution,\n            framerate=framerate,\n            logging=logging,\n            time_delay=time_delay,\n            **options\n        )\n        # define default frame generator in configuration\n        self.config = {\"generator\": self.__producer}\n\n    # log if specified\n    if self.__logging:\n        if source is None:\n            logger.warning(\n                \"Given source is of NoneType. Therefore, JPEG Frame-Compression is disabled!\"\n            )\n        else:\n            logger.debug(\n                \"Enabling JPEG Frame-Compression with Colorspace:`{}`, Quality:`{}`%, Fastdct:`{}`, and Fastupsample:`{}`.\".format(\n                    self.__jpeg_compression_colorspace,\n                    self.__jpeg_compression_quality,\n                    \"enabled\" if self.__jpeg_compression_fastdct else \"disabled\",\n                    (\n                        \"enabled\"\n                        if self.__jpeg_compression_fastupsample\n                        else \"disabled\"\n                    ),\n                )\n            )\n\n    # copying original routing tables for further validation\n    self.__rt_org_copy = self.routes[:]\n    # initialize blank frame\n    self.blank_frame = None\n    # keeps check if producer loop should be running\n    self.__isrunning = True\n
"},{"location":"bonus/reference/webgear/#vidgear.gears.asyncio.webgear.WebGear.shutdown","title":"shutdown(self)","text":"

Implements a Callable to be run on application shutdown

Source code in vidgear/gears/asyncio/webgear.py
def shutdown(self):\n    \"\"\"\n    Implements a Callable to be run on application shutdown\n    \"\"\"\n    if not (self.__stream is None):\n        self.__logging and logger.debug(\"Closing Video Streaming.\")\n        # stops producer\n        self.__isrunning = False\n        # stops VideoGear stream\n        self.__stream.stop()\n        # prevent any re-iteration\n        self.__stream = None\n
"},{"location":"bonus/reference/webgear_rtc/","title":"WebGear_RTC API References","text":"

WebGear_RTC API usage examples can be found here \u27b6

WebGear_RTC API parameters are explained here \u27b6

WebGear_RTC is similar to WeGear API in many aspects but utilizes WebRTC technology under the hood instead of Motion JPEG, which makes it suitable for building powerful video-streaming solutions for all modern browsers as well as native clients available on all major platforms.

WebGear_RTC is implemented with the help of aiortc library which is built on top of asynchronous I/O framework for Web Real-Time Communication (WebRTC) and Object Real-Time Communication (ORTC) and supports many features like SDP generation/parsing, Interactive Connectivity Establishment with half-trickle and mDNS support, DTLS key and certificate generation, DTLS handshake, etc.

WebGear_RTC can handle multiple consumers seamlessly and provides native support for ICE (Interactive Connectivity Establishment) protocol, STUN (Session Traversal Utilities for NAT), and TURN (Traversal Using Relays around NAT) servers that help us to easily establish direct media connection with the remote peers for uninterrupted data flow. It also allows us to define our custom Server as a source to transform frames easily before sending them across the network(see this doc example).

WebGear_RTC API works in conjunction with Starlette ASGI application and can also flexibly interact with Starlette's ecosystem of shared middleware, mountable applications, Response classes, Routing tables, Static Files, Templating engine(with Jinja2), etc.

Additionally, WebGear_RTC API also provides internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs.

Source code in vidgear/gears/asyncio/webgear_rtc.py
class WebGear_RTC:\n    \"\"\"\n    WebGear_RTC is similar to WeGear API in many aspects but utilizes WebRTC technology under the hood instead of Motion JPEG, which\n    makes it suitable for building powerful video-streaming solutions for all modern browsers as well as native clients available on\n    all major platforms.\n\n    WebGear_RTC is implemented with the help of aiortc library which is built on top of asynchronous I/O framework for Web Real-Time\n    Communication (WebRTC) and Object Real-Time Communication (ORTC) and supports many features like SDP generation/parsing, Interactive\n    Connectivity Establishment with half-trickle and mDNS support, DTLS key and certificate generation, DTLS handshake, etc.\n\n    WebGear_RTC can handle multiple consumers seamlessly and provides native support for ICE (Interactive Connectivity Establishment)\n    protocol, STUN (Session Traversal Utilities for NAT), and TURN (Traversal Using Relays around NAT) servers that help us to easily\n    establish direct media connection with the remote peers for uninterrupted data flow. It also allows us to define our custom Server\n    as a source to transform frames easily before sending them across the network(see this doc example).\n\n    WebGear_RTC API works in conjunction with Starlette ASGI application and can also flexibly interact with Starlette's ecosystem of\n    shared middleware, mountable applications, Response classes, Routing tables, Static Files, Templating engine(with Jinja2), etc.\n\n    Additionally, WebGear_RTC API also provides internal wrapper around VideoGear, which itself provides internal access to both\n    CamGear and PiGear APIs.\n    \"\"\"\n\n    def __init__(\n        self,\n        enablePiCamera=False,\n        stabilize=False,\n        source=None,\n        camera_num=0,\n        stream_mode=False,\n        backend=0,\n        colorspace=None,\n        resolution=(640, 480),\n        framerate=25,\n        logging=False,\n        time_delay=0,\n        **options\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the WebGear_RTC class.\n\n        Parameters:\n            enablePiCamera (bool): provide access to PiGear(if True) or CamGear(if False) APIs respectively.\n            stabilize (bool): enable access to Stabilizer Class for stabilizing frames.\n            camera_num (int): selects the camera module index which will be used as Rpi source.\n            resolution (tuple): sets the resolution (i.e. `(width,height)`) of the Rpi source.\n            framerate (int/float): sets the framerate of the Rpi source.\n            source (based on input): defines the source for the input stream.\n            stream_mode (bool): controls the exclusive YouTube Mode.\n            backend (int): selects the backend for OpenCV's VideoCapture class.\n            colorspace (str): selects the colorspace of the input stream.\n            logging (bool): enables/disables logging.\n            time_delay (int): time delay (in sec) before start reading the frames.\n            options (dict): provides ability to alter Tweak Parameters of WebGear_RTC, CamGear, PiGear & Stabilizer.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # raise error(s) for critical Class imports\n        import_dependency_safe(\"starlette\" if starlette is None else \"\")\n        import_dependency_safe(\"aiortc\" if aiortc is None else \"\")\n\n        # initialize global params\n        custom_data_location = \"\"  # path to save data-files to custom location\n        data_path = \"\"  # path to WebGear_RTC data-files\n        overwrite_default = False\n        self.__relay = None  # act as broadcaster\n\n        # reformat dictionary\n        options = {str(k).strip(): v for k, v in options.items()}\n\n        # assign values to global variables if specified and valid\n        if options:\n            if \"custom_data_location\" in options:\n                value = options[\"custom_data_location\"]\n                if isinstance(value, str):\n                    assert os.access(\n                        value, os.W_OK\n                    ), \"[WebGear_RTC:ERROR] :: Permission Denied!, cannot write WebGear_RTC data-files to '{}' directory!\".format(\n                        value\n                    )\n                    assert os.path.isdir(\n                        os.path.abspath(value)\n                    ), \"[WebGear_RTC:ERROR] :: `custom_data_location` value must be the path to a directory and not to a file!\"\n                    custom_data_location = os.path.abspath(value)\n                else:\n                    logger.warning(\"Skipped invalid `custom_data_location` value!\")\n                del options[\"custom_data_location\"]  # clean\n\n            if \"overwrite_default_files\" in options:\n                value = options[\"overwrite_default_files\"]\n                if isinstance(value, bool):\n                    overwrite_default = value\n                else:\n                    logger.warning(\"Skipped invalid `overwrite_default_files` value!\")\n                del options[\"overwrite_default_files\"]  # clean\n\n            if \"enable_live_broadcast\" in options:\n                value = options[\"enable_live_broadcast\"]\n                if isinstance(value, bool):\n                    if value:\n                        self.__relay = MediaRelay()\n                        options[\"enable_infinite_frames\"] = (\n                            True  # enforce infinite frames\n                        )\n                        logger.critical(\n                            \"Enabled live broadcasting for Peer connection(s).\"\n                        )\n                    else:\n                        None\n                else:\n                    logger.warning(\"Skipped invalid `enable_live_broadcast` value!\")\n                del options[\"enable_live_broadcast\"]  # clean\n\n        # check if custom certificates path is specified\n        if custom_data_location:\n            data_path = generate_webdata(\n                custom_data_location,\n                c_name=\"webgear_rtc\",\n                overwrite_default=overwrite_default,\n                logging=logging,\n            )\n        else:\n            # otherwise generate suitable path\n            data_path = generate_webdata(\n                os.path.join(expanduser(\"~\"), \".vidgear\"),\n                c_name=\"webgear_rtc\",\n                overwrite_default=overwrite_default,\n                logging=logging,\n            )\n\n        # log it\n        self.__logging and logger.debug(\n            \"`{}` is the default location for saving WebGear_RTC data-files.\".format(\n                data_path\n            )\n        )\n\n        # define Jinja2 templates handler\n        self.__templates = Jinja2Templates(directory=\"{}/templates\".format(data_path))\n\n        # define custom exception handlers\n        self.__exception_handlers = {404: self.__not_found, 500: self.__server_error}\n        # define routing tables\n        self.routes = [\n            Route(\"/\", endpoint=self.__homepage),\n            Route(\"/offer\", self.__offer, methods=[\"GET\", \"POST\"]),\n            Mount(\n                \"/static\",\n                app=StaticFiles(directory=\"{}/static\".format(data_path)),\n                name=\"static\",\n            ),\n        ]\n\n        # define middleware support\n        self.middleware = []\n\n        # Handle RTC video server\n        if \"custom_stream\" in options or not (source is None):\n            # Handle video source\n            self.__default_rtc_server = RTC_VideoServer(\n                enablePiCamera=enablePiCamera,\n                stabilize=stabilize,\n                source=source,\n                camera_num=camera_num,\n                stream_mode=stream_mode,\n                backend=backend,\n                colorspace=colorspace,\n                resolution=resolution,\n                framerate=framerate,\n                logging=logging,\n                time_delay=time_delay,\n                **options\n            )\n            # add exclusive reset connection node\n            self.routes.append(\n                Route(\"/close_connection\", self.__reset_connections, methods=[\"POST\"])\n            )\n        else:\n            raise ValueError(\n                \"[WebGear_RTC:ERROR] :: Source cannot be NoneType without Custom Stream(`custom_stream`) defined!\"\n            )\n\n        # copying original routing tables for further validation\n        self.__rt_org_copy = self.routes[:]\n        # collects peer RTC connections\n        self.__pcs = set()\n\n    def __call__(self):\n        \"\"\"\n        Implements a custom Callable method for WebGear_RTC application.\n        \"\"\"\n        # validate routing tables\n        assert not (self.routes is None), \"Routing tables are NoneType!\"\n        if not isinstance(self.routes, list) or not all(\n            x in self.routes for x in self.__rt_org_copy\n        ):\n            raise RuntimeError(\"[WebGear_RTC:ERROR] :: Routing tables are not valid!\")\n\n        # validate middlewares\n        assert not (self.middleware is None), \"Middlewares are NoneType!\"\n        if self.middleware and (\n            not isinstance(self.middleware, list)\n            or not all(isinstance(x, Middleware) for x in self.middleware)\n        ):\n            raise RuntimeError(\"[WebGear_RTC:ERROR] :: Middlewares are not valid!\")\n\n        # return Starlette application\n        self.__logging and logger.debug(\"Running Starlette application.\")\n        return Starlette(\n            debug=(True if self.__logging else False),\n            routes=self.routes,\n            middleware=self.middleware,\n            exception_handlers=self.__exception_handlers,\n            lifespan=self.__lifespan,\n        )\n\n    async def __offer(self, request):\n        \"\"\"\n        Generates JSON Response with a WebRTC Peer Connection of Video Server.\n        \"\"\"\n        # get offer from params\n        params = await request.json()\n        offer = RTCSessionDescription(sdp=params[\"sdp\"], type=params[\"type\"])\n\n        # initiate stream\n        if not (self.__default_rtc_server is None) and not (\n            self.__default_rtc_server.is_launched\n        ):\n            self.__logging and logger.debug(\"Initiating Video Streaming.\")\n            self.__default_rtc_server.launch()\n\n        # setup RTC peer connection - interface represents a WebRTC connection\n        # between the local computer and a remote peer.\n        pc = RTCPeerConnection()\n        self.__pcs.add(pc)\n        self.__logging and logger.info(\"Created WebRTC Peer Connection.\")\n\n        # track ICE connection state changes\n        @pc.on(\"iceconnectionstatechange\")\n        async def on_iceconnectionstatechange():\n            if pc.iceConnectionState == \"failed\":\n                logger.error(\"ICE connection state failed.\")\n                # check if Live Broadcasting is enabled\n                if self.__relay is None:\n                    # if not, close connection.\n                    await pc.close()\n                    self.__pcs.discard(pc)\n            else:\n                logger.debug(\"ICE connection state is %s\" % pc.iceConnectionState)\n\n        # Change the remote description associated with the connection.\n        await pc.setRemoteDescription(offer)\n        # retrieve list of RTCRtpTransceiver objects that are currently attached to the connection\n        for t in pc.getTransceivers():\n            # Increments performance significantly, IDK why this works as H265 codec is not even supported :D\n            capabilities = RTCRtpSender.getCapabilities(\"video\")\n            preferences = list(filter(lambda x: x.name == \"H265\", capabilities.codecs))\n            t.setCodecPreferences(preferences)\n            # add video server to peer track\n            if t.kind == \"video\":\n                pc.addTrack(\n                    self.__relay.subscribe(self.__default_rtc_server)\n                    if not (self.__relay is None)\n                    else self.__default_rtc_server\n                )\n\n        # Create an SDP answer to an offer received from a remote peer\n        answer = await pc.createAnswer()\n\n        # Change the local description for the answer\n        await pc.setLocalDescription(answer)\n\n        # return Starlette json response\n        return JSONResponse(\n            {\"sdp\": pc.localDescription.sdp, \"type\": pc.localDescription.type}\n        )\n\n    async def __homepage(self, request):\n        \"\"\"\n        Return an HTML index page.\n        \"\"\"\n        return self.__templates.TemplateResponse(request, \"index.html\")\n\n    async def __not_found(self, request, exc):\n        \"\"\"\n        Return an HTML 404 page.\n        \"\"\"\n        return self.__templates.TemplateResponse(request, \"404.html\", status_code=404)\n\n    async def __server_error(self, request, exc):\n        \"\"\"\n        Return an HTML 500 page.\n        \"\"\"\n        return self.__templates.TemplateResponse(request, \"500.html\", status_code=500)\n\n    async def __reset_connections(self, request):\n        \"\"\"\n        Resets all connections and recreates VideoServer timestamps\n        \"\"\"\n        # get additional parameter\n        parameter = await request.json()\n        # check if Live Broadcasting is enabled\n        if (\n            self.__relay is None\n            and not (self.__default_rtc_server is None)\n            and (self.__default_rtc_server.is_running)\n        ):\n            logger.critical(\"Resetting Server\")\n            # close old peer connections\n            if parameter != 0:  # disable if specified explicitly\n                coros = [\n                    pc.close() for pc in self.__pcs if pc.iceConnectionState != \"closed\"\n                ]\n                await asyncio.gather(*coros)\n                self.__pcs.clear()\n            await self.__default_rtc_server.reset()\n            return PlainTextResponse(\"OK\")\n        else:\n            # if does, then do nothing\n            return PlainTextResponse(\"DISABLED\")\n\n    @contextlib.asynccontextmanager\n    async def __lifespan(self, context):\n        try:\n            yield\n        finally:\n            # close Video Server\n            self.shutdown()\n            # collects peer RTC connections\n            coros = [\n                pc.close() for pc in self.__pcs if pc.iceConnectionState != \"closed\"\n            ]\n            await asyncio.gather(*coros)\n            self.__pcs.clear()\n\n    def shutdown(self):\n        \"\"\"\n        Gracefully shutdown video-server\n        \"\"\"\n        if not (self.__default_rtc_server is None):\n            self.__logging and logger.debug(\"Closing Video Server.\")\n            self.__default_rtc_server.terminate()\n            self.__default_rtc_server = None\n        # terminate internal server aswell.\n        self.__default_rtc_server = None\n

"},{"location":"bonus/reference/webgear_rtc/#vidgear.gears.asyncio.webgear_rtc.WebGear_RTC.__call__","title":"__call__(self) special","text":"

Implements a custom Callable method for WebGear_RTC application.

Source code in vidgear/gears/asyncio/webgear_rtc.py
def __call__(self):\n    \"\"\"\n    Implements a custom Callable method for WebGear_RTC application.\n    \"\"\"\n    # validate routing tables\n    assert not (self.routes is None), \"Routing tables are NoneType!\"\n    if not isinstance(self.routes, list) or not all(\n        x in self.routes for x in self.__rt_org_copy\n    ):\n        raise RuntimeError(\"[WebGear_RTC:ERROR] :: Routing tables are not valid!\")\n\n    # validate middlewares\n    assert not (self.middleware is None), \"Middlewares are NoneType!\"\n    if self.middleware and (\n        not isinstance(self.middleware, list)\n        or not all(isinstance(x, Middleware) for x in self.middleware)\n    ):\n        raise RuntimeError(\"[WebGear_RTC:ERROR] :: Middlewares are not valid!\")\n\n    # return Starlette application\n    self.__logging and logger.debug(\"Running Starlette application.\")\n    return Starlette(\n        debug=(True if self.__logging else False),\n        routes=self.routes,\n        middleware=self.middleware,\n        exception_handlers=self.__exception_handlers,\n        lifespan=self.__lifespan,\n    )\n
"},{"location":"bonus/reference/webgear_rtc/#vidgear.gears.asyncio.webgear_rtc.WebGear_RTC.__init__","title":"__init__(self, enablePiCamera=False, stabilize=False, source=None, camera_num=0, stream_mode=False, backend=0, colorspace=None, resolution=(640, 480), framerate=25, logging=False, time_delay=0, **options) special","text":"

This constructor method initializes the object state and attributes of the WebGear_RTC class.

Parameters:

Name Type Description Default enablePiCamera bool

provide access to PiGear(if True) or CamGear(if False) APIs respectively.

False stabilize bool

enable access to Stabilizer Class for stabilizing frames.

False camera_num int

selects the camera module index which will be used as Rpi source.

0 resolution tuple

sets the resolution (i.e. (width,height)) of the Rpi source.

(640, 480) framerate int/float

sets the framerate of the Rpi source.

25 source based on input

defines the source for the input stream.

None stream_mode bool

controls the exclusive YouTube Mode.

False backend int

selects the backend for OpenCV's VideoCapture class.

0 colorspace str

selects the colorspace of the input stream.

None logging bool

enables/disables logging.

False time_delay int

time delay (in sec) before start reading the frames.

0 options dict

provides ability to alter Tweak Parameters of WebGear_RTC, CamGear, PiGear & Stabilizer.

{} Source code in vidgear/gears/asyncio/webgear_rtc.py
def __init__(\n    self,\n    enablePiCamera=False,\n    stabilize=False,\n    source=None,\n    camera_num=0,\n    stream_mode=False,\n    backend=0,\n    colorspace=None,\n    resolution=(640, 480),\n    framerate=25,\n    logging=False,\n    time_delay=0,\n    **options\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the WebGear_RTC class.\n\n    Parameters:\n        enablePiCamera (bool): provide access to PiGear(if True) or CamGear(if False) APIs respectively.\n        stabilize (bool): enable access to Stabilizer Class for stabilizing frames.\n        camera_num (int): selects the camera module index which will be used as Rpi source.\n        resolution (tuple): sets the resolution (i.e. `(width,height)`) of the Rpi source.\n        framerate (int/float): sets the framerate of the Rpi source.\n        source (based on input): defines the source for the input stream.\n        stream_mode (bool): controls the exclusive YouTube Mode.\n        backend (int): selects the backend for OpenCV's VideoCapture class.\n        colorspace (str): selects the colorspace of the input stream.\n        logging (bool): enables/disables logging.\n        time_delay (int): time delay (in sec) before start reading the frames.\n        options (dict): provides ability to alter Tweak Parameters of WebGear_RTC, CamGear, PiGear & Stabilizer.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # raise error(s) for critical Class imports\n    import_dependency_safe(\"starlette\" if starlette is None else \"\")\n    import_dependency_safe(\"aiortc\" if aiortc is None else \"\")\n\n    # initialize global params\n    custom_data_location = \"\"  # path to save data-files to custom location\n    data_path = \"\"  # path to WebGear_RTC data-files\n    overwrite_default = False\n    self.__relay = None  # act as broadcaster\n\n    # reformat dictionary\n    options = {str(k).strip(): v for k, v in options.items()}\n\n    # assign values to global variables if specified and valid\n    if options:\n        if \"custom_data_location\" in options:\n            value = options[\"custom_data_location\"]\n            if isinstance(value, str):\n                assert os.access(\n                    value, os.W_OK\n                ), \"[WebGear_RTC:ERROR] :: Permission Denied!, cannot write WebGear_RTC data-files to '{}' directory!\".format(\n                    value\n                )\n                assert os.path.isdir(\n                    os.path.abspath(value)\n                ), \"[WebGear_RTC:ERROR] :: `custom_data_location` value must be the path to a directory and not to a file!\"\n                custom_data_location = os.path.abspath(value)\n            else:\n                logger.warning(\"Skipped invalid `custom_data_location` value!\")\n            del options[\"custom_data_location\"]  # clean\n\n        if \"overwrite_default_files\" in options:\n            value = options[\"overwrite_default_files\"]\n            if isinstance(value, bool):\n                overwrite_default = value\n            else:\n                logger.warning(\"Skipped invalid `overwrite_default_files` value!\")\n            del options[\"overwrite_default_files\"]  # clean\n\n        if \"enable_live_broadcast\" in options:\n            value = options[\"enable_live_broadcast\"]\n            if isinstance(value, bool):\n                if value:\n                    self.__relay = MediaRelay()\n                    options[\"enable_infinite_frames\"] = (\n                        True  # enforce infinite frames\n                    )\n                    logger.critical(\n                        \"Enabled live broadcasting for Peer connection(s).\"\n                    )\n                else:\n                    None\n            else:\n                logger.warning(\"Skipped invalid `enable_live_broadcast` value!\")\n            del options[\"enable_live_broadcast\"]  # clean\n\n    # check if custom certificates path is specified\n    if custom_data_location:\n        data_path = generate_webdata(\n            custom_data_location,\n            c_name=\"webgear_rtc\",\n            overwrite_default=overwrite_default,\n            logging=logging,\n        )\n    else:\n        # otherwise generate suitable path\n        data_path = generate_webdata(\n            os.path.join(expanduser(\"~\"), \".vidgear\"),\n            c_name=\"webgear_rtc\",\n            overwrite_default=overwrite_default,\n            logging=logging,\n        )\n\n    # log it\n    self.__logging and logger.debug(\n        \"`{}` is the default location for saving WebGear_RTC data-files.\".format(\n            data_path\n        )\n    )\n\n    # define Jinja2 templates handler\n    self.__templates = Jinja2Templates(directory=\"{}/templates\".format(data_path))\n\n    # define custom exception handlers\n    self.__exception_handlers = {404: self.__not_found, 500: self.__server_error}\n    # define routing tables\n    self.routes = [\n        Route(\"/\", endpoint=self.__homepage),\n        Route(\"/offer\", self.__offer, methods=[\"GET\", \"POST\"]),\n        Mount(\n            \"/static\",\n            app=StaticFiles(directory=\"{}/static\".format(data_path)),\n            name=\"static\",\n        ),\n    ]\n\n    # define middleware support\n    self.middleware = []\n\n    # Handle RTC video server\n    if \"custom_stream\" in options or not (source is None):\n        # Handle video source\n        self.__default_rtc_server = RTC_VideoServer(\n            enablePiCamera=enablePiCamera,\n            stabilize=stabilize,\n            source=source,\n            camera_num=camera_num,\n            stream_mode=stream_mode,\n            backend=backend,\n            colorspace=colorspace,\n            resolution=resolution,\n            framerate=framerate,\n            logging=logging,\n            time_delay=time_delay,\n            **options\n        )\n        # add exclusive reset connection node\n        self.routes.append(\n            Route(\"/close_connection\", self.__reset_connections, methods=[\"POST\"])\n        )\n    else:\n        raise ValueError(\n            \"[WebGear_RTC:ERROR] :: Source cannot be NoneType without Custom Stream(`custom_stream`) defined!\"\n        )\n\n    # copying original routing tables for further validation\n    self.__rt_org_copy = self.routes[:]\n    # collects peer RTC connections\n    self.__pcs = set()\n
"},{"location":"bonus/reference/webgear_rtc/#vidgear.gears.asyncio.webgear_rtc.WebGear_RTC.shutdown","title":"shutdown(self)","text":"

Gracefully shutdown video-server

Source code in vidgear/gears/asyncio/webgear_rtc.py
def shutdown(self):\n    \"\"\"\n    Gracefully shutdown video-server\n    \"\"\"\n    if not (self.__default_rtc_server is None):\n        self.__logging and logger.debug(\"Closing Video Server.\")\n        self.__default_rtc_server.terminate()\n        self.__default_rtc_server = None\n    # terminate internal server aswell.\n    self.__default_rtc_server = None\n
"},{"location":"bonus/reference/writegear/","title":"WriteGear API References","text":"

WriteGear API usage examples for: Compression Mode \u27b6 and Non-Compression Mode \u27b6

WriteGear API parameters are explained for: Compression Mode \u27b6 and Non-Compression Mode \u27b6

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 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 third-party API.

In addition to this, WriteGear also provides flexible access to OpenCV's VideoWriter API tools for video-frames encoding without compression.

Modes of Operation

WriteGear primarily operates in following modes:

Source code in vidgear/gears/writegear.py
class WriteGear:\n    \"\"\"\n    WriteGear handles various powerful Video-Writer Tools that provide us the freedom to do almost anything imaginable with multimedia data.\n\n    WriteGear API provides a complete, flexible, and robust wrapper around FFmpeg, a leading multimedia framework. WriteGear can process real-time frames into a lossless\n    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\n    Live-Streaming (such as for Twitch) and Multiplexing Video-Audio with real-time frames in way fewer lines of code.\n\n    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\n    third-party API.\n\n    In addition to this, WriteGear also provides flexible access to OpenCV's VideoWriter API tools for video-frames encoding without compression.\n\n    ??? tip \"Modes of Operation\"\n\n        WriteGear primarily operates in following modes:\n\n        * **Compression Mode**: In this mode, WriteGear utilizes powerful **FFmpeg** inbuilt encoders to encode lossless multimedia files.\n                                This mode provides us the ability to exploit almost any parameter available within FFmpeg, effortlessly and flexibly,\n                                and while doing that it robustly handles all errors/warnings quietly.\n\n        * **Non-Compression Mode**: In this mode, WriteGear utilizes basic **OpenCV's inbuilt VideoWriter API** tools. This mode also supports all\n                                    parameters manipulation available within VideoWriter API, but it lacks the ability to manipulate encoding parameters\n                                    and other important features like video compression, audio encoding, etc.\n\n    \"\"\"\n\n    def __init__(\n        self,\n        output=\"\",\n        compression_mode=True,\n        custom_ffmpeg=\"\",\n        logging=False,\n        **output_params\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the WriteGear class.\n\n        Parameters:\n            output (str): sets the valid filename/path/URL for encoding.\n            compression_mode (bool): selects the WriteGear's Primary Mode of Operation.\n            custom_ffmpeg (str): assigns the location of custom path/directory for custom FFmpeg executables.\n            logging (bool): enables/disables logging.\n            output_params (dict): provides the flexibility to control supported internal parameters and FFmpeg properties.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # check if user not using depreciated `output_filename` parameter\n        assert (\n            not \"output_filename\" in output_params\n        ), \"[WriteGear:ERROR] :: The `output_filename` parameter has been renamed to `output`. Refer Docs for more info.\"\n\n        # assign parameter values to class variables\n        # enables compression if enabled\n        self.__compression = (\n            compression_mode if isinstance(compression_mode, bool) else False\n        )\n        # specifies if machine in-use is running Windows OS or not\n        self.__os_windows = True if os.name == \"nt\" else False\n\n        # initialize various important class variables\n        self.__output_parameters = {}  # handles output parameters\n        self.__inputheight = None  # handles input frames height\n        self.__inputwidth = None  # handles input frames width\n        self.__inputchannels = None  # handles input frames channels\n        self.__inputdtype = None  # handles input frames dtype\n        self.__process = None  # handles Encoding class/process\n        self.__ffmpeg = \"\"  # handles valid FFmpeg binaries location\n        self.__initiate_process = (\n            True  # handles initiate one-time process for generating pipeline\n        )\n        self.__ffmpeg_window_disabler_patch = (\n            False  # handles disabling window for ffmpeg subprocess on Windows\n        )\n        self.__out_file = None  # handles output\n        gstpipeline_mode = False  # handles GStreamer Pipeline Mode\n\n        # handles output\n        if not output:\n            # raise error otherwise\n            raise ValueError(\n                \"[WriteGear:ERROR] :: Kindly provide a valid `output` value. Refer Docs for more info.\"\n            )\n        else:\n            # validate output is a system file/directory\n            # and Whether WriteGear has the write rights\n            # to specified file/directory or not\n            abs_path = os.path.abspath(output)\n            if check_WriteAccess(\n                os.path.dirname(abs_path),\n                is_windows=self.__os_windows,\n                logging=self.__logging,\n            ):\n                # check if given path is directory\n                if os.path.isdir(abs_path):\n                    # then, auto-assign valid name and adds it to path\n                    abs_path = os.path.join(\n                        abs_path,\n                        \"VidGear-{}.mp4\".format(time.strftime(\"%Y%m%d-%H%M%S\")),\n                    )\n                # assign output file absolute\n                # path to class variable if valid\n                self.__out_file = abs_path\n            else:\n                # log note otherwise\n                logger.info(\n                    \"`{}` isn't a valid system path or directory. Skipped!\".format(\n                        output\n                    )\n                )\n\n        # cleans and reformat output parameters\n        self.__output_parameters = {\n            str(k).strip(): (v.strip() if isinstance(v, str) else v)\n            for k, v in output_params.items()\n        }\n        # log it if specified\n        self.__logging and logger.debug(\n            \"Output Parameters: `{}`\".format(self.__output_parameters)\n        )\n\n        # handles FFmpeg binaries validity\n        # in Compression mode\n        if self.__compression:\n            # log it if specified\n            self.__logging and logger.debug(\n                \"Compression Mode is enabled therefore checking for valid FFmpeg executable.\"\n            )\n\n            # handles where to save the downloaded FFmpeg Static Binaries\n            # on Windows(if specified)\n            __ffmpeg_download_path = self.__output_parameters.pop(\n                \"-ffmpeg_download_path\", \"\"\n            )\n            # check if value is valid\n            if not isinstance(__ffmpeg_download_path, (str)):\n                # reset improper values\n                __ffmpeg_download_path = \"\"\n\n            # handle user-defined output resolution (must be a tuple or list)\n            # in Compression Mode only.\n            self.__output_dimensions = self.__output_parameters.pop(\n                \"-output_dimensions\", None\n            )\n            # check if value is valid\n            if not isinstance(self.__output_dimensions, (list, tuple)):\n                # reset improper values\n                self.__output_dimensions = None\n\n            # handle user defined input framerate of encoding pipeline\n            # in Compression Mode only.\n            self.__inputframerate = self.__output_parameters.pop(\n                \"-input_framerate\", 0.0\n            )\n            # check if value is valid\n            if not isinstance(self.__inputframerate, (float, int)):\n                # reset improper values\n                self.__inputframerate = 0.0\n            else:\n                # must be float\n                self.__inputframerate = float(self.__inputframerate)\n\n            # handle user-defined input frames pixel-format in Compression Mode only.\n            self.__inputpixfmt = self.__output_parameters.pop(\"-input_pixfmt\", None)\n            # check if value is valid\n            if not isinstance(self.__inputpixfmt, str):\n                # reset improper values\n                self.__inputpixfmt = None\n            else:\n                # must be exact\n                self.__inputpixfmt = self.__inputpixfmt.strip()\n\n            # handle user-defined FFmpeg command pre-headers(must be a list)\n            # in Compression Mode only.\n            self.__ffmpeg_preheaders = self.__output_parameters.pop(\"-ffpreheaders\", [])\n            # check if value is valid\n            if not isinstance(self.__ffmpeg_preheaders, list):\n                # reset improper values\n                self.__ffmpeg_preheaders = []\n\n            # handle the special-case of forced-termination (only for Compression mode)\n            disable_force_termination = self.__output_parameters.pop(\n                \"-disable_force_termination\",\n                False if (\"-i\" in self.__output_parameters) else True,\n            )\n            # check if value is valid\n            if isinstance(disable_force_termination, bool):\n                self.__forced_termination = not (disable_force_termination)\n            else:\n                # handle improper values\n                self.__forced_termination = (\n                    True if (\"-i\" in self.__output_parameters) else False\n                )\n\n            # handles disabling window for ffmpeg subprocess on Windows OS (only for Compression mode)\n            # this patch prevents ffmpeg creation window from opening when building exe files\n            ffmpeg_window_disabler_patch = self.__output_parameters.pop(\n                \"-disable_ffmpeg_window\", False\n            )\n            # check if value is valid\n            if not self.__os_windows or logging:\n                logger.warning(\n                    \"Optional `-disable_ffmpeg_window` flag is only available on Windows OS with `logging=False`. Discarding!\"\n                )\n            elif isinstance(ffmpeg_window_disabler_patch, bool):\n                self.__ffmpeg_window_disabler_patch = ffmpeg_window_disabler_patch\n            else:\n                # handle improper values\n                self.__ffmpeg_window_disabler_patch = False\n\n            # validate the FFmpeg path/binaries and returns valid executable FFmpeg\n            # location/path (also auto-downloads static binaries on Windows OS)\n            self.__ffmpeg = get_valid_ffmpeg_path(\n                custom_ffmpeg,\n                self.__os_windows,\n                ffmpeg_download_path=__ffmpeg_download_path,\n                logging=self.__logging,\n            )\n            # check if valid executable FFmpeg location/path\n            if self.__ffmpeg:\n                # log it if found\n                self.__logging and logger.debug(\n                    \"Found valid FFmpeg executable: `{}`.\".format(self.__ffmpeg)\n                )\n            else:\n                # otherwise disable Compression Mode\n                # and switch to Non-compression mode\n                logger.warning(\n                    \"Disabling Compression Mode since no valid FFmpeg executable found on this machine!\"\n                )\n                if self.__logging and not self.__os_windows:\n                    logger.debug(\n                        \"Kindly install a working FFmpeg module or provide a valid custom FFmpeg binary path. See docs for more info.\"\n                    )\n                # compression mode disabled\n                self.__compression = False\n        else:\n            # handle GStreamer Pipeline Mode (only for Non-compression mode)\n            if \"-gst_pipeline_mode\" in self.__output_parameters:\n                # check if value is valid\n                if isinstance(self.__output_parameters[\"-gst_pipeline_mode\"], bool):\n                    gstpipeline_mode = self.__output_parameters[\n                        \"-gst_pipeline_mode\"\n                    ] and check_gstreamer_support(logging=logging)\n                    self.__logging and logger.debug(\n                        \"GStreamer Pipeline Mode successfully activated!\"\n                    )\n                else:\n                    # reset improper values\n                    gstpipeline_mode = False\n                    # log it\n                    self.__logging and logger.warning(\n                        \"GStreamer Pipeline Mode failed to activate!\"\n                    )\n\n        # handle output differently in Compression/Non-compression Modes\n        if self.__compression and self.__ffmpeg:\n            # check if output falls in exclusive cases\n            if self.__out_file is None:\n                if (\n                    platform.system() == \"Linux\"\n                    and pathlib.Path(output).is_char_device()\n                ):\n                    # check whether output is a Linux video device path (such as `/dev/video0`)\n                    self.__logging and logger.debug(\n                        \"Path:`{}` is a valid Linux Video Device path.\".format(output)\n                    )\n                    self.__out_file = output\n                elif is_valid_url(self.__ffmpeg, url=output, logging=self.__logging):\n                    # check whether output is a valid URL instead\n                    self.__logging and logger.debug(\n                        \"URL:`{}` is valid and successfully configured for streaming.\".format(\n                            output\n                        )\n                    )\n                    self.__out_file = output\n                else:\n                    # raise error otherwise\n                    raise ValueError(\n                        \"[WriteGear:ERROR] :: output value:`{}` is not supported in Compression Mode.\".format(\n                            output\n                        )\n                    )\n            # log if forced termination is enabled\n            self.__forced_termination and logger.debug(\n                \"Forced termination is enabled for this FFmpeg process.\"\n            )\n            # log Compression is enabled\n            self.__logging and logger.debug(\n                \"Compression Mode with FFmpeg backend is configured properly.\"\n            )\n        else:\n            # raise error if not valid input\n            if self.__out_file is None and not gstpipeline_mode:\n                raise ValueError(\n                    \"[WriteGear:ERROR] :: output value:`{}` is not supported in Non-Compression Mode.\".format(\n                        output\n                    )\n                )\n\n            # check if GStreamer Pipeline Mode is enabled\n            if gstpipeline_mode:\n                # enforce GStreamer backend\n                self.__output_parameters[\"-backend\"] = \"CAP_GSTREAMER\"\n                # enforce original output value\n                self.__out_file = output\n\n            # log it\n            self.__logging and logger.debug(\n                \"Non-Compression Mode is successfully configured in GStreamer Pipeline Mode.\"\n            )\n\n            # log if Compression is disabled\n            logger.critical(\n                \"Compression Mode is disabled, Activating OpenCV built-in Writer!\"\n            )\n\n    def write(self, frame, rgb_mode=False):\n        \"\"\"\n        Pipelines `ndarray` frames to respective API _(**FFmpeg** in Compression Mode & **OpenCV's VideoWriter API** in Non-Compression Mode)_.\n\n        Parameters:\n            frame (ndarray): a valid numpy frame\n            rgb_mode (boolean): enable this flag to activate RGB mode _(i.e. specifies that incoming frames are of RGB format(instead of default BGR)_.\n\n        \"\"\"\n        if frame is None:  # None-Type frames will be skipped\n            return\n\n        # get height, width, number of channels, and dtype of current frame\n        height, width = frame.shape[:2]\n        channels = frame.shape[-1] if frame.ndim == 3 else 1\n        dtype = frame.dtype\n\n        # assign values to class variables on first run\n        if self.__initiate_process:\n            self.__inputheight = height\n            self.__inputwidth = width\n            self.__inputchannels = channels\n            self.__inputdtype = dtype\n            self.__logging and logger.debug(\n                \"InputFrame => Height:{} Width:{} Channels:{} Datatype:{}\".format(\n                    self.__inputheight,\n                    self.__inputwidth,\n                    self.__inputchannels,\n                    self.__inputdtype,\n                )\n            )\n\n        # validate frame size\n        if height != self.__inputheight or width != self.__inputwidth:\n            raise ValueError(\n                \"[WriteGear:ERROR] :: All video-frames must have same size!\"\n            )\n        # validate number of channels in frame\n        if channels != self.__inputchannels:\n            raise ValueError(\n                \"[WriteGear:ERROR] :: All video-frames must have same number of channels!\"\n            )\n        # validate frame datatype\n        if dtype != self.__inputdtype:\n            raise ValueError(\n                \"[WriteGear:ERROR] :: All video-frames must have same datatype!\"\n            )\n\n        # checks if compression mode is enabled\n        if self.__compression:\n            # initiate FFmpeg process on first run\n            if self.__initiate_process:\n                # start pre-processing of FFmpeg parameters, and initiate process\n                self.__PreprocessFFParams(channels, dtype=dtype, rgb=rgb_mode)\n                # Check status of the process\n                assert self.__process is not None\n            try:\n                # try writing the frame bytes to the subprocess pipeline\n                self.__process.stdin.write(frame.tobytes())\n            except (OSError, IOError):\n                # log if something is wrong!\n                logger.error(\n                    \"BrokenPipeError caught, Wrong values passed to FFmpeg Pipe. Kindly Refer Docs!\"\n                )\n                raise ValueError  # for testing purpose only\n        else:\n            # otherwise initiate OpenCV's VideoWriter Class process\n            if self.__initiate_process:\n                # start VideoWriter Class process\n                self.__start_CVProcess()\n                # Check status of the process\n                assert self.__process is not None\n                # log one-time OpenCV warning\n                self.__logging and logger.info(\n                    \"RGBA and 16-bit grayscale video frames are not supported by OpenCV yet. Kindly switch on `compression_mode` to use them!\"\n                )\n            # write frame directly to\n            # VideoWriter Class process\n            self.__process.write(frame)\n\n    def __PreprocessFFParams(self, channels, dtype=None, rgb=False):\n        \"\"\"\n        Internal method that pre-processes FFmpeg Parameters before beginning to pipeline frames.\n\n        Parameters:\n            channels (int): Number of channels in input frame.\n            dtype (str): Datatype of input frame.\n            rgb_mode (boolean): Whether to activate `RGB mode`?\n        \"\"\"\n        # turn off initiate flag\n        self.__initiate_process = False\n        # initialize input parameters\n        input_parameters = {}\n\n        # handle output frames dimensions\n        dimensions = \"\"\n        if self.__output_dimensions is None:  # check if dimensions are given\n            dimensions += \"{}x{}\".format(\n                self.__inputwidth, self.__inputheight\n            )  # auto derive from frame\n        else:\n            dimensions += \"{}x{}\".format(\n                self.__output_dimensions[0], self.__output_dimensions[1]\n            )  # apply if defined\n        input_parameters[\"-s\"] = str(dimensions)\n\n        # handles user-defined and auto-assigned input pixel-formats\n        if not (\n            self.__inputpixfmt is None\n        ) and self.__inputpixfmt in get_supported_pixfmts(self.__ffmpeg):\n            # assign directly if valid\n            input_parameters[\"-pix_fmt\"] = self.__inputpixfmt\n        else:\n            # handles pix_fmt based on channels and dtype(HACK)\n            if dtype.kind == \"u\" and dtype.itemsize == 2:\n                # handle pix_fmt for frames with higher than 8-bit depth\n                pix_fmt = None\n                if channels == 1:\n                    pix_fmt = \"gray16\"\n                elif channels == 2:\n                    pix_fmt = \"ya16\"\n                elif channels == 3:\n                    pix_fmt = \"rgb48\" if rgb else \"bgr48\"\n                elif channels == 4:\n                    pix_fmt = \"rgba64\" if rgb else \"bgra64\"\n                else:\n                    # raise error otherwise\n                    raise ValueError(\n                        \"[WriteGear:ERROR] :: Frames with channels outside range 1-to-4 are not supported!\"\n                    )\n                # Add endianness suffix (w.r.t byte-order)\n                input_parameters[\"-pix_fmt\"] = pix_fmt + (\n                    \"be\" if dtype.byteorder == \">\" else \"le\"\n                )\n            else:\n                # handle pix_fmt for frames with exactly 8-bit depth(`uint8`)\n                if channels == 1:\n                    input_parameters[\"-pix_fmt\"] = \"gray\"\n                elif channels == 2:\n                    input_parameters[\"-pix_fmt\"] = \"ya8\"\n                elif channels == 3:\n                    input_parameters[\"-pix_fmt\"] = \"rgb24\" if rgb else \"bgr24\"\n                elif channels == 4:\n                    input_parameters[\"-pix_fmt\"] = \"rgba\" if rgb else \"bgra\"\n                else:\n                    # raise error otherwise\n                    raise ValueError(\n                        \"[WriteGear:ERROR] :: Frames with channels outside range 1-to-4 are not supported!\"\n                    )\n\n        # handles user-defined output video framerate\n        if self.__inputframerate > 0.0:\n            # assign input framerate if valid\n            self.__logging and logger.debug(\n                \"Setting Input framerate: {}\".format(self.__inputframerate)\n            )\n            input_parameters[\"-framerate\"] = str(self.__inputframerate)\n\n        # initiate FFmpeg process\n        self.__start_FFProcess(\n            input_params=input_parameters, output_params=self.__output_parameters\n        )\n\n    def __start_FFProcess(self, input_params, output_params):\n        \"\"\"\n        An Internal method that launches FFmpeg subprocess pipeline in Compression Mode\n        for pipelining frames to `stdin`.\n\n        Parameters:\n            input_params (dict): Input FFmpeg parameters\n            output_params (dict): Output FFmpeg parameters\n        \"\"\"\n        # convert input parameters to argument list\n        input_parameters = dict2Args(input_params)\n\n        # handle output video encoder.\n        # get list of supported video-encoders\n        supported_vcodecs = get_supported_vencoders(self.__ffmpeg)\n        # dynamically select default encoder\n        default_vcodec = [\n            vcodec\n            for vcodec in [\"libx264\", \"libx265\", \"libxvid\", \"mpeg4\"]\n            if vcodec in supported_vcodecs\n        ][0] or \"unknown\"\n        # extract any user-defined encoder\n        if \"-c:v\" in output_params:\n            # assign it to the pipeline\n            output_params[\"-vcodec\"] = output_params.pop(\"-c:v\", default_vcodec)\n        if not \"-vcodec\" in output_params:\n            # auto-assign default video-encoder (if not assigned by user).\n            output_params[\"-vcodec\"] = default_vcodec\n        if (\n            default_vcodec != \"unknown\"\n            and not output_params[\"-vcodec\"] in supported_vcodecs\n        ):\n            # reset to default if not supported\n            logger.critical(\n                \"Provided FFmpeg does not support `{}` video-encoder. Switching to default supported `{}` encoder!\".format(\n                    output_params[\"-vcodec\"], default_vcodec\n                )\n            )\n            output_params[\"-vcodec\"] = default_vcodec\n\n        # assign optimizations based on selected video encoder(if any)\n        if output_params[\"-vcodec\"] in supported_vcodecs:\n            if output_params[\"-vcodec\"] in [\"libx265\", \"libx264\"]:\n                if not \"-crf\" in output_params:\n                    output_params[\"-crf\"] = \"18\"\n                if not \"-preset\" in output_params:\n                    output_params[\"-preset\"] = \"fast\"\n            if output_params[\"-vcodec\"] in [\"libxvid\", \"mpeg4\"]:\n                if not \"-qscale:v\" in output_params:\n                    output_params[\"-qscale:v\"] = \"3\"\n        else:\n            # raise error otherwise\n            raise RuntimeError(\n                \"[WriteGear:ERROR] :: Provided FFmpeg does not support any suitable/usable video-encoders for compression.\"\n                \" Kindly disable compression mode or switch to another FFmpeg binaries(if available).\"\n            )\n\n        # convert output parameters to argument list\n        output_parameters = dict2Args(output_params)\n\n        # format FFmpeg command\n        cmd = (\n            [self.__ffmpeg, \"-y\"]\n            + self.__ffmpeg_preheaders\n            + [\"-f\", \"rawvideo\", \"-vcodec\", \"rawvideo\"]\n            + input_parameters\n            + [\"-i\", \"-\"]\n            + output_parameters\n            + [self.__out_file]\n        )\n        # Launch the process with FFmpeg command\n        if self.__logging:\n            # log command in logging mode\n            logger.debug(\"Executing FFmpeg command: `{}`\".format(\" \".join(cmd)))\n            # In logging mode\n            self.__process = sp.Popen(cmd, stdin=sp.PIPE, stdout=sp.PIPE, stderr=None)\n        else:\n            # In silent mode\n            self.__process = sp.Popen(\n                cmd,\n                stdin=sp.PIPE,\n                stdout=sp.DEVNULL,\n                stderr=sp.STDOUT,\n                creationflags=(  # this prevents ffmpeg creation window from opening when building exe files on Windows\n                    sp.DETACHED_PROCESS if self.__ffmpeg_window_disabler_patch else 0\n                ),\n            )\n\n    def __enter__(self):\n        \"\"\"\n        Handles entry with the `with` statement. See [PEP343 -- The 'with' statement'](https://peps.python.org/pep-0343/).\n\n        **Returns:** Returns a reference to the WriteGear Class\n        \"\"\"\n        return self\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        \"\"\"\n        Handles exit with the `with` statement. See [PEP343 -- The 'with' statement'](https://peps.python.org/pep-0343/).\n        \"\"\"\n        self.close()\n\n    def execute_ffmpeg_cmd(self, command=None):\n        \"\"\"\n\n        Executes user-defined FFmpeg Terminal command, formatted as a python list(in Compression Mode only).\n\n        Parameters:\n            command (list): inputs list data-type command.\n\n        \"\"\"\n        # check if valid command\n        if command is None or not (command):\n            logger.warning(\"Input command is empty, Nothing to execute!\")\n            return\n        else:\n            if not (isinstance(command, list)):\n                raise ValueError(\n                    \"[WriteGear:ERROR] :: Invalid input command datatype! Kindly read docs.\"\n                )\n\n        # check if Compression Mode is enabled\n        if not (self.__compression):\n            # raise error otherwise\n            raise RuntimeError(\n                \"[WriteGear:ERROR] :: Compression Mode is disabled, Kindly enable it to access this function.\"\n            )\n\n        # add configured FFmpeg path\n        cmd = [self.__ffmpeg] + command\n\n        try:\n            # write frames to pipeline\n            if self.__logging:\n                # log command in logging mode\n                logger.debug(\"Executing FFmpeg command: `{}`\".format(\" \".join(cmd)))\n                # In logging mode\n                sp.run(cmd, stdin=sp.PIPE, stdout=sp.PIPE, stderr=None)\n            else:\n                # In silent mode\n                sp.run(cmd, stdin=sp.PIPE, stdout=sp.DEVNULL, stderr=sp.STDOUT)\n        except (OSError, IOError) as e:\n            # re-raise error\n            if self.__logging:\n                raise ValueError(\n                    \"BrokenPipeError caught, Wrong command passed to FFmpeg Pipe, Kindly Refer Docs!\"\n                ) from None\n            else:\n                raise ValueError(\n                    \"BrokenPipeError caught, Wrong command passed to FFmpeg Pipe, Kindly Refer Docs!\"\n                ) from e\n\n    def __start_CVProcess(self):\n        \"\"\"\n        An Internal method that launches OpenCV VideoWriter process in Non-Compression\n        Mode with given settings.\n        \"\"\"\n        # turn off initiate flag\n        self.__initiate_process = False\n\n        # initialize essential variables\n        FPS = 0\n        BACKEND = \"\"\n        FOURCC = 0\n        COLOR = True\n\n        # pre-assign default parameters (if not assigned by user).\n        if \"-fourcc\" not in self.__output_parameters:\n            FOURCC = cv2.VideoWriter_fourcc(*\"MJPG\")\n        if \"-fps\" not in self.__output_parameters:\n            FPS = 25\n\n        # auto-assign frame dimensions\n        HEIGHT = self.__inputheight\n        WIDTH = self.__inputwidth\n\n        # assign dict parameter values to variables\n        try:\n            for key, value in self.__output_parameters.items():\n                if key == \"-fourcc\":\n                    FOURCC = cv2.VideoWriter_fourcc(*(value.upper()))\n                elif key == \"-fps\":\n                    FPS = int(value)\n                elif key == \"-backend\":\n                    BACKEND = capPropId(value.upper())\n                elif key == \"-color\":\n                    COLOR = bool(value)\n                else:\n                    pass\n        except Exception as e:\n            # log and raise error if something is wrong\n            self.__logging and logger.exception(str(e))\n            raise ValueError(\n                \"[WriteGear:ERROR] :: Wrong Values passed to OpenCV Writer, Kindly Refer Docs!\"\n            )\n\n        # log values for debugging\n        self.__logging and logger.debug(\n            \"FILE_PATH: {}, FOURCC = {}, FPS = {}, WIDTH = {}, HEIGHT = {}, BACKEND = {}\".format(\n                self.__out_file, FOURCC, FPS, WIDTH, HEIGHT, BACKEND\n            )\n        )\n        # start different OpenCV VideoCapture processes\n        # for with and without Backend.\n        if BACKEND:\n            self.__process = cv2.VideoWriter(\n                self.__out_file,\n                apiPreference=BACKEND,\n                fourcc=FOURCC,\n                fps=FPS,\n                frameSize=(WIDTH, HEIGHT),\n                isColor=COLOR,\n            )\n        else:\n            self.__process = cv2.VideoWriter(\n                self.__out_file,\n                fourcc=FOURCC,\n                fps=FPS,\n                frameSize=(WIDTH, HEIGHT),\n                isColor=COLOR,\n            )\n        # check if OpenCV VideoCapture is opened successfully\n        assert (\n            self.__process.isOpened()\n        ), \"[WriteGear:ERROR] :: Failed to initialize OpenCV Writer!\"\n\n    def close(self):\n        \"\"\"\n        Safely terminates various WriteGear process.\n        \"\"\"\n        # log termination\n        self.__logging and logger.debug(\"Terminating WriteGear Processes.\")\n        # handle termination separately\n        if self.__compression:\n            # when Compression Mode is enabled\n            if self.__process is None or not (self.__process.poll() is None):\n                # return if no process initiated\n                # at first place\n                return\n            # close `stdin` output\n            self.__process.stdin and self.__process.stdin.close()\n            # close `stdout` output\n            self.__process.stdout and self.__process.stdout.close()\n            # forced termination if specified.\n            self.__forced_termination and self.__process.terminate()\n            # wait if process is still processing\n            self.__process.wait()\n        else:\n            # when Compression Mode is disabled\n            if self.__process is None:\n                # return if no process initiated\n                # at first place\n                return\n            # close it\n            self.__process.release()\n        # discard process\n        self.__process = None\n

"},{"location":"bonus/reference/writegear/#vidgear.gears.writegear.WriteGear.__enter__","title":"__enter__(self) special","text":"

Handles entry with the with statement. See PEP343 -- The 'with' statement'.

Returns: Returns a reference to the WriteGear Class

Source code in vidgear/gears/writegear.py
def __enter__(self):\n    \"\"\"\n    Handles entry with the `with` statement. See [PEP343 -- The 'with' statement'](https://peps.python.org/pep-0343/).\n\n    **Returns:** Returns a reference to the WriteGear Class\n    \"\"\"\n    return self\n
"},{"location":"bonus/reference/writegear/#vidgear.gears.writegear.WriteGear.__exit__","title":"__exit__(self, exc_type, exc_val, exc_tb) special","text":"

Handles exit with the with statement. See PEP343 -- The 'with' statement'.

Source code in vidgear/gears/writegear.py
def __exit__(self, exc_type, exc_val, exc_tb):\n    \"\"\"\n    Handles exit with the `with` statement. See [PEP343 -- The 'with' statement'](https://peps.python.org/pep-0343/).\n    \"\"\"\n    self.close()\n
"},{"location":"bonus/reference/writegear/#vidgear.gears.writegear.WriteGear.__init__","title":"__init__(self, output='', compression_mode=True, custom_ffmpeg='', logging=False, **output_params) special","text":"

This constructor method initializes the object state and attributes of the WriteGear class.

Parameters:

Name Type Description Default output str

sets the valid filename/path/URL for encoding.

'' compression_mode bool

selects the WriteGear's Primary Mode of Operation.

True custom_ffmpeg str

assigns the location of custom path/directory for custom FFmpeg executables.

'' logging bool

enables/disables logging.

False output_params dict

provides the flexibility to control supported internal parameters and FFmpeg properties.

{} Source code in vidgear/gears/writegear.py
def __init__(\n    self,\n    output=\"\",\n    compression_mode=True,\n    custom_ffmpeg=\"\",\n    logging=False,\n    **output_params\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the WriteGear class.\n\n    Parameters:\n        output (str): sets the valid filename/path/URL for encoding.\n        compression_mode (bool): selects the WriteGear's Primary Mode of Operation.\n        custom_ffmpeg (str): assigns the location of custom path/directory for custom FFmpeg executables.\n        logging (bool): enables/disables logging.\n        output_params (dict): provides the flexibility to control supported internal parameters and FFmpeg properties.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # check if user not using depreciated `output_filename` parameter\n    assert (\n        not \"output_filename\" in output_params\n    ), \"[WriteGear:ERROR] :: The `output_filename` parameter has been renamed to `output`. Refer Docs for more info.\"\n\n    # assign parameter values to class variables\n    # enables compression if enabled\n    self.__compression = (\n        compression_mode if isinstance(compression_mode, bool) else False\n    )\n    # specifies if machine in-use is running Windows OS or not\n    self.__os_windows = True if os.name == \"nt\" else False\n\n    # initialize various important class variables\n    self.__output_parameters = {}  # handles output parameters\n    self.__inputheight = None  # handles input frames height\n    self.__inputwidth = None  # handles input frames width\n    self.__inputchannels = None  # handles input frames channels\n    self.__inputdtype = None  # handles input frames dtype\n    self.__process = None  # handles Encoding class/process\n    self.__ffmpeg = \"\"  # handles valid FFmpeg binaries location\n    self.__initiate_process = (\n        True  # handles initiate one-time process for generating pipeline\n    )\n    self.__ffmpeg_window_disabler_patch = (\n        False  # handles disabling window for ffmpeg subprocess on Windows\n    )\n    self.__out_file = None  # handles output\n    gstpipeline_mode = False  # handles GStreamer Pipeline Mode\n\n    # handles output\n    if not output:\n        # raise error otherwise\n        raise ValueError(\n            \"[WriteGear:ERROR] :: Kindly provide a valid `output` value. Refer Docs for more info.\"\n        )\n    else:\n        # validate output is a system file/directory\n        # and Whether WriteGear has the write rights\n        # to specified file/directory or not\n        abs_path = os.path.abspath(output)\n        if check_WriteAccess(\n            os.path.dirname(abs_path),\n            is_windows=self.__os_windows,\n            logging=self.__logging,\n        ):\n            # check if given path is directory\n            if os.path.isdir(abs_path):\n                # then, auto-assign valid name and adds it to path\n                abs_path = os.path.join(\n                    abs_path,\n                    \"VidGear-{}.mp4\".format(time.strftime(\"%Y%m%d-%H%M%S\")),\n                )\n            # assign output file absolute\n            # path to class variable if valid\n            self.__out_file = abs_path\n        else:\n            # log note otherwise\n            logger.info(\n                \"`{}` isn't a valid system path or directory. Skipped!\".format(\n                    output\n                )\n            )\n\n    # cleans and reformat output parameters\n    self.__output_parameters = {\n        str(k).strip(): (v.strip() if isinstance(v, str) else v)\n        for k, v in output_params.items()\n    }\n    # log it if specified\n    self.__logging and logger.debug(\n        \"Output Parameters: `{}`\".format(self.__output_parameters)\n    )\n\n    # handles FFmpeg binaries validity\n    # in Compression mode\n    if self.__compression:\n        # log it if specified\n        self.__logging and logger.debug(\n            \"Compression Mode is enabled therefore checking for valid FFmpeg executable.\"\n        )\n\n        # handles where to save the downloaded FFmpeg Static Binaries\n        # on Windows(if specified)\n        __ffmpeg_download_path = self.__output_parameters.pop(\n            \"-ffmpeg_download_path\", \"\"\n        )\n        # check if value is valid\n        if not isinstance(__ffmpeg_download_path, (str)):\n            # reset improper values\n            __ffmpeg_download_path = \"\"\n\n        # handle user-defined output resolution (must be a tuple or list)\n        # in Compression Mode only.\n        self.__output_dimensions = self.__output_parameters.pop(\n            \"-output_dimensions\", None\n        )\n        # check if value is valid\n        if not isinstance(self.__output_dimensions, (list, tuple)):\n            # reset improper values\n            self.__output_dimensions = None\n\n        # handle user defined input framerate of encoding pipeline\n        # in Compression Mode only.\n        self.__inputframerate = self.__output_parameters.pop(\n            \"-input_framerate\", 0.0\n        )\n        # check if value is valid\n        if not isinstance(self.__inputframerate, (float, int)):\n            # reset improper values\n            self.__inputframerate = 0.0\n        else:\n            # must be float\n            self.__inputframerate = float(self.__inputframerate)\n\n        # handle user-defined input frames pixel-format in Compression Mode only.\n        self.__inputpixfmt = self.__output_parameters.pop(\"-input_pixfmt\", None)\n        # check if value is valid\n        if not isinstance(self.__inputpixfmt, str):\n            # reset improper values\n            self.__inputpixfmt = None\n        else:\n            # must be exact\n            self.__inputpixfmt = self.__inputpixfmt.strip()\n\n        # handle user-defined FFmpeg command pre-headers(must be a list)\n        # in Compression Mode only.\n        self.__ffmpeg_preheaders = self.__output_parameters.pop(\"-ffpreheaders\", [])\n        # check if value is valid\n        if not isinstance(self.__ffmpeg_preheaders, list):\n            # reset improper values\n            self.__ffmpeg_preheaders = []\n\n        # handle the special-case of forced-termination (only for Compression mode)\n        disable_force_termination = self.__output_parameters.pop(\n            \"-disable_force_termination\",\n            False if (\"-i\" in self.__output_parameters) else True,\n        )\n        # check if value is valid\n        if isinstance(disable_force_termination, bool):\n            self.__forced_termination = not (disable_force_termination)\n        else:\n            # handle improper values\n            self.__forced_termination = (\n                True if (\"-i\" in self.__output_parameters) else False\n            )\n\n        # handles disabling window for ffmpeg subprocess on Windows OS (only for Compression mode)\n        # this patch prevents ffmpeg creation window from opening when building exe files\n        ffmpeg_window_disabler_patch = self.__output_parameters.pop(\n            \"-disable_ffmpeg_window\", False\n        )\n        # check if value is valid\n        if not self.__os_windows or logging:\n            logger.warning(\n                \"Optional `-disable_ffmpeg_window` flag is only available on Windows OS with `logging=False`. Discarding!\"\n            )\n        elif isinstance(ffmpeg_window_disabler_patch, bool):\n            self.__ffmpeg_window_disabler_patch = ffmpeg_window_disabler_patch\n        else:\n            # handle improper values\n            self.__ffmpeg_window_disabler_patch = False\n\n        # validate the FFmpeg path/binaries and returns valid executable FFmpeg\n        # location/path (also auto-downloads static binaries on Windows OS)\n        self.__ffmpeg = get_valid_ffmpeg_path(\n            custom_ffmpeg,\n            self.__os_windows,\n            ffmpeg_download_path=__ffmpeg_download_path,\n            logging=self.__logging,\n        )\n        # check if valid executable FFmpeg location/path\n        if self.__ffmpeg:\n            # log it if found\n            self.__logging and logger.debug(\n                \"Found valid FFmpeg executable: `{}`.\".format(self.__ffmpeg)\n            )\n        else:\n            # otherwise disable Compression Mode\n            # and switch to Non-compression mode\n            logger.warning(\n                \"Disabling Compression Mode since no valid FFmpeg executable found on this machine!\"\n            )\n            if self.__logging and not self.__os_windows:\n                logger.debug(\n                    \"Kindly install a working FFmpeg module or provide a valid custom FFmpeg binary path. See docs for more info.\"\n                )\n            # compression mode disabled\n            self.__compression = False\n    else:\n        # handle GStreamer Pipeline Mode (only for Non-compression mode)\n        if \"-gst_pipeline_mode\" in self.__output_parameters:\n            # check if value is valid\n            if isinstance(self.__output_parameters[\"-gst_pipeline_mode\"], bool):\n                gstpipeline_mode = self.__output_parameters[\n                    \"-gst_pipeline_mode\"\n                ] and check_gstreamer_support(logging=logging)\n                self.__logging and logger.debug(\n                    \"GStreamer Pipeline Mode successfully activated!\"\n                )\n            else:\n                # reset improper values\n                gstpipeline_mode = False\n                # log it\n                self.__logging and logger.warning(\n                    \"GStreamer Pipeline Mode failed to activate!\"\n                )\n\n    # handle output differently in Compression/Non-compression Modes\n    if self.__compression and self.__ffmpeg:\n        # check if output falls in exclusive cases\n        if self.__out_file is None:\n            if (\n                platform.system() == \"Linux\"\n                and pathlib.Path(output).is_char_device()\n            ):\n                # check whether output is a Linux video device path (such as `/dev/video0`)\n                self.__logging and logger.debug(\n                    \"Path:`{}` is a valid Linux Video Device path.\".format(output)\n                )\n                self.__out_file = output\n            elif is_valid_url(self.__ffmpeg, url=output, logging=self.__logging):\n                # check whether output is a valid URL instead\n                self.__logging and logger.debug(\n                    \"URL:`{}` is valid and successfully configured for streaming.\".format(\n                        output\n                    )\n                )\n                self.__out_file = output\n            else:\n                # raise error otherwise\n                raise ValueError(\n                    \"[WriteGear:ERROR] :: output value:`{}` is not supported in Compression Mode.\".format(\n                        output\n                    )\n                )\n        # log if forced termination is enabled\n        self.__forced_termination and logger.debug(\n            \"Forced termination is enabled for this FFmpeg process.\"\n        )\n        # log Compression is enabled\n        self.__logging and logger.debug(\n            \"Compression Mode with FFmpeg backend is configured properly.\"\n        )\n    else:\n        # raise error if not valid input\n        if self.__out_file is None and not gstpipeline_mode:\n            raise ValueError(\n                \"[WriteGear:ERROR] :: output value:`{}` is not supported in Non-Compression Mode.\".format(\n                    output\n                )\n            )\n\n        # check if GStreamer Pipeline Mode is enabled\n        if gstpipeline_mode:\n            # enforce GStreamer backend\n            self.__output_parameters[\"-backend\"] = \"CAP_GSTREAMER\"\n            # enforce original output value\n            self.__out_file = output\n\n        # log it\n        self.__logging and logger.debug(\n            \"Non-Compression Mode is successfully configured in GStreamer Pipeline Mode.\"\n        )\n\n        # log if Compression is disabled\n        logger.critical(\n            \"Compression Mode is disabled, Activating OpenCV built-in Writer!\"\n        )\n
"},{"location":"bonus/reference/writegear/#vidgear.gears.writegear.WriteGear.close","title":"close(self)","text":"

Safely terminates various WriteGear process.

Source code in vidgear/gears/writegear.py
def close(self):\n    \"\"\"\n    Safely terminates various WriteGear process.\n    \"\"\"\n    # log termination\n    self.__logging and logger.debug(\"Terminating WriteGear Processes.\")\n    # handle termination separately\n    if self.__compression:\n        # when Compression Mode is enabled\n        if self.__process is None or not (self.__process.poll() is None):\n            # return if no process initiated\n            # at first place\n            return\n        # close `stdin` output\n        self.__process.stdin and self.__process.stdin.close()\n        # close `stdout` output\n        self.__process.stdout and self.__process.stdout.close()\n        # forced termination if specified.\n        self.__forced_termination and self.__process.terminate()\n        # wait if process is still processing\n        self.__process.wait()\n    else:\n        # when Compression Mode is disabled\n        if self.__process is None:\n            # return if no process initiated\n            # at first place\n            return\n        # close it\n        self.__process.release()\n    # discard process\n    self.__process = None\n
"},{"location":"bonus/reference/writegear/#vidgear.gears.writegear.WriteGear.execute_ffmpeg_cmd","title":"execute_ffmpeg_cmd(self, command=None)","text":"

Executes user-defined FFmpeg Terminal command, formatted as a python list(in Compression Mode only).

Parameters:

Name Type Description Default command list

inputs list data-type command.

None Source code in vidgear/gears/writegear.py
def execute_ffmpeg_cmd(self, command=None):\n    \"\"\"\n\n    Executes user-defined FFmpeg Terminal command, formatted as a python list(in Compression Mode only).\n\n    Parameters:\n        command (list): inputs list data-type command.\n\n    \"\"\"\n    # check if valid command\n    if command is None or not (command):\n        logger.warning(\"Input command is empty, Nothing to execute!\")\n        return\n    else:\n        if not (isinstance(command, list)):\n            raise ValueError(\n                \"[WriteGear:ERROR] :: Invalid input command datatype! Kindly read docs.\"\n            )\n\n    # check if Compression Mode is enabled\n    if not (self.__compression):\n        # raise error otherwise\n        raise RuntimeError(\n            \"[WriteGear:ERROR] :: Compression Mode is disabled, Kindly enable it to access this function.\"\n        )\n\n    # add configured FFmpeg path\n    cmd = [self.__ffmpeg] + command\n\n    try:\n        # write frames to pipeline\n        if self.__logging:\n            # log command in logging mode\n            logger.debug(\"Executing FFmpeg command: `{}`\".format(\" \".join(cmd)))\n            # In logging mode\n            sp.run(cmd, stdin=sp.PIPE, stdout=sp.PIPE, stderr=None)\n        else:\n            # In silent mode\n            sp.run(cmd, stdin=sp.PIPE, stdout=sp.DEVNULL, stderr=sp.STDOUT)\n    except (OSError, IOError) as e:\n        # re-raise error\n        if self.__logging:\n            raise ValueError(\n                \"BrokenPipeError caught, Wrong command passed to FFmpeg Pipe, Kindly Refer Docs!\"\n            ) from None\n        else:\n            raise ValueError(\n                \"BrokenPipeError caught, Wrong command passed to FFmpeg Pipe, Kindly Refer Docs!\"\n            ) from e\n
"},{"location":"bonus/reference/writegear/#vidgear.gears.writegear.WriteGear.write","title":"write(self, frame, rgb_mode=False)","text":"

Pipelines ndarray frames to respective API (FFmpeg in Compression Mode & OpenCV's VideoWriter API in Non-Compression Mode).

Parameters:

Name Type Description Default frame ndarray

a valid numpy frame

required rgb_mode boolean

enable this flag to activate RGB mode (i.e. specifies that incoming frames are of RGB format(instead of default BGR).

False Source code in vidgear/gears/writegear.py
def write(self, frame, rgb_mode=False):\n    \"\"\"\n    Pipelines `ndarray` frames to respective API _(**FFmpeg** in Compression Mode & **OpenCV's VideoWriter API** in Non-Compression Mode)_.\n\n    Parameters:\n        frame (ndarray): a valid numpy frame\n        rgb_mode (boolean): enable this flag to activate RGB mode _(i.e. specifies that incoming frames are of RGB format(instead of default BGR)_.\n\n    \"\"\"\n    if frame is None:  # None-Type frames will be skipped\n        return\n\n    # get height, width, number of channels, and dtype of current frame\n    height, width = frame.shape[:2]\n    channels = frame.shape[-1] if frame.ndim == 3 else 1\n    dtype = frame.dtype\n\n    # assign values to class variables on first run\n    if self.__initiate_process:\n        self.__inputheight = height\n        self.__inputwidth = width\n        self.__inputchannels = channels\n        self.__inputdtype = dtype\n        self.__logging and logger.debug(\n            \"InputFrame => Height:{} Width:{} Channels:{} Datatype:{}\".format(\n                self.__inputheight,\n                self.__inputwidth,\n                self.__inputchannels,\n                self.__inputdtype,\n            )\n        )\n\n    # validate frame size\n    if height != self.__inputheight or width != self.__inputwidth:\n        raise ValueError(\n            \"[WriteGear:ERROR] :: All video-frames must have same size!\"\n        )\n    # validate number of channels in frame\n    if channels != self.__inputchannels:\n        raise ValueError(\n            \"[WriteGear:ERROR] :: All video-frames must have same number of channels!\"\n        )\n    # validate frame datatype\n    if dtype != self.__inputdtype:\n        raise ValueError(\n            \"[WriteGear:ERROR] :: All video-frames must have same datatype!\"\n        )\n\n    # checks if compression mode is enabled\n    if self.__compression:\n        # initiate FFmpeg process on first run\n        if self.__initiate_process:\n            # start pre-processing of FFmpeg parameters, and initiate process\n            self.__PreprocessFFParams(channels, dtype=dtype, rgb=rgb_mode)\n            # Check status of the process\n            assert self.__process is not None\n        try:\n            # try writing the frame bytes to the subprocess pipeline\n            self.__process.stdin.write(frame.tobytes())\n        except (OSError, IOError):\n            # log if something is wrong!\n            logger.error(\n                \"BrokenPipeError caught, Wrong values passed to FFmpeg Pipe. Kindly Refer Docs!\"\n            )\n            raise ValueError  # for testing purpose only\n    else:\n        # otherwise initiate OpenCV's VideoWriter Class process\n        if self.__initiate_process:\n            # start VideoWriter Class process\n            self.__start_CVProcess()\n            # Check status of the process\n            assert self.__process is not None\n            # log one-time OpenCV warning\n            self.__logging and logger.info(\n                \"RGBA and 16-bit grayscale video frames are not supported by OpenCV yet. Kindly switch on `compression_mode` to use them!\"\n            )\n        # write frame directly to\n        # VideoWriter Class process\n        self.__process.write(frame)\n
"},{"location":"contribution/PR/","title":"Submitting Pull Request(PR) Guidelines","text":""},{"location":"contribution/PR/#submitting-pull-requestpr-guidelines","title":"Submitting Pull Request(PR) Guidelines:","text":"

The following guidelines tells you how to submit a valid PR for vidGear:

Working on your first Pull Request for VidGear?

"},{"location":"contribution/PR/#clone-testing-branch","title":"Clone Testing branch","text":"

Base Branch must be testing in your Pull Request

Every PR MUST be pushed against VidGear's testing branch only, in order to trigger must needed CI testing workflows. If your's not, then change the base branch to testing \u27b6

Make sure the testing branch of your Forked repository is up-to-date with VidGear, before starting working on Pull Request.

You can clone your Forked remote git to local and create your PR working branch as a sub-branch of latest testing branch as follows:

Functions of different VidGear's Github Branches

Following are the base branches for VidGear's code in its Github Repository:

Master/MainTestingDevelopment

Branch Features:

Cloning:

# clone your forked repository and `cd` inside\ngit clone https://github.com/abhiTronix/vidgear.git && cd vidgear\n

Branch Features:

Cloning:

# clone your forked repository and `cd` inside\ngit clone https://github.com/abhiTronix/vidgear.git && cd vidgear\n\n# checkout the latest testing branch\ngit checkout testing\n

Branch Features:

Cloning:

# clone your forked repository and `cd` inside\ngit clone https://github.com/abhiTronix/vidgear.git && cd vidgear\n\n# checkout the latest development branch\ngit checkout development\n

Workflow:

Typically any feature/improvement/bug-fix code flows as follows:

# clone your forked repository(change with your username) and get inside\ngit clone https://github.com/{YOUR USERNAME}/vidgear.git && cd vidgear\n\n# pull any recent updates\ngit pull\n\n# checkout the latest testing branch\ngit checkout testing\n\n# Now create your new branch with suitable name(such as \"subbranch_of_testing\")\ngit checkout -b subbranch_of_testing\n

Now after working with this newly created branch for your Pull Request, you can commit and push or merge it locally or remotely as usual.

"},{"location":"contribution/PR/#pr-submission-checklist","title":"PR Submission Checklist","text":"

There are some important checks you need to perform while submitting your Pull Request(s) for VidGear library:

"},{"location":"contribution/PR/#testing-formatting-linting","title":"Testing, Formatting & Linting","text":"

All Pull Request(s) must be tested, formatted & linted against our library standards as discussed below:

"},{"location":"contribution/PR/#requirements","title":"Requirements","text":"

Testing VidGear requires additional test dependencies and dataset, which can be handled manually as follows:

"},{"location":"contribution/PR/#running-tests","title":"Running Tests","text":"

All tests can be run with pytest(in VidGear's root folder) as follows:

 pytest -sv  #-sv for verbose output.\n
"},{"location":"contribution/PR/#formatting-linting","title":"Formatting & Linting","text":"

For formatting and linting, following libraries are used:

"},{"location":"contribution/PR/#frequently-asked-questions","title":"Frequently Asked Questions","text":"

Q1. Why do my changes taking so long to be Reviewed and/or Merged?

Submission Aftermaths

Pull requests will be reviewed by the maintainers and the rationale behind the maintainer\u2019s decision to accept or deny the changes will be posted in the pull request. Please wait for our code review and approval, possibly enhancing your change on request.

Q2. Would you accept a huge Pull Request with Lots of Changes?

First, make sure that the changes are somewhat related. Otherwise, please create separate pull requests. Anyway, before submitting a huge change, it's probably a good idea to open an issue in the VidGear Github repository to ask the maintainers if they agree with your proposed changes. Otherwise, they could refuse your proposal after you put all that hard work into making the changes. We definitely don't want you to waste your time!

"},{"location":"contribution/issue/","title":"Submitting an Issue Guidelines","text":""},{"location":"contribution/issue/#submitting-an-issue-guidelines","title":"Submitting an Issue Guidelines","text":"

If you've found a new bug or you've come up with some new feature which can improve the quality of the VidGear, then related issues are welcomed! But, Before you do, please read the following guidelines:

First Issue on GitHub?

You can easily learn about it from creating an issue wiki.

Info

Please note that your issue will be fixed much faster if you spend about half an hour preparing it, including the exact reproduction steps and a demo. If you're in a hurry or don't feel confident, it's fine to report issues with less details, but this makes it less likely they'll get fixed soon.

"},{"location":"contribution/issue/#search-the-docs-and-previous-issues","title":"Search the Docs and Previous Issues","text":""},{"location":"contribution/issue/#gather-required-information","title":"Gather Required Information","text":""},{"location":"contribution/issue/#follow-the-issue-template","title":"Follow the Issue Template","text":""},{"location":"contribution/issue/#raise-the-issue","title":"Raise the Issue","text":""},{"location":"gears/camgear/overview/","title":"Overview","text":""},{"location":"gears/camgear/overview/#camgear-api","title":"CamGear API","text":"CamGear API's generalized workflow"},{"location":"gears/camgear/overview/#overview","title":"Overview","text":"

CamGear supports a diverse range of video streams which can handle/control video stream almost any IP/USB Cameras, multimedia video file format (upto 4k tested), any network stream URL such as http(s), rtp, rtsp, rtmp, mms, etc. In addition to this, it also supports Gstreamer's RAW pipelines and various live video streaming sites like YouTube, Twitch, Dailymotion etc.

CamGear API provides a flexible, high-level multi-threaded wrapper around OpenCV's VideoCapture API with direct access to almost all of its available parameters. It relies on Threaded Queue mode for threaded, error-free and synchronized frame handling.

CamGear internally implements yt_dlp backend class for seamlessly pipelining live video-frames and metadata from various streaming services like YouTube, Twitch, and many more \u27b6

Helpful Tips

"},{"location":"gears/camgear/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through CamGear Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/camgear/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/camgear/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/camgear/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/camgear/params/","title":"Parameters","text":""},{"location":"gears/camgear/params/#camgear-api-parameters","title":"CamGear API Parameters","text":""},{"location":"gears/camgear/params/#source","title":"source","text":"

CamGear API will throw RuntimeError if source provided is invalid.

This parameter defines the source for the input stream.

Data-Type: Based on input.

Default Value: Its default value is 0.

Its valid input can be one of the following:

"},{"location":"gears/camgear/params/#stream_mode","title":"stream_mode","text":"

This parameter controls the Stream Mode, .i.e if enabled(stream_mode=True), the CamGear API will interpret the given source input as YouTube URL address.

Due to a FFmpeg bug that causes video to freeze frequently in OpenCV, It is advised to always use GStreamer backend for any livestream videos. Checkout this FAQ for compiling OpenCV with GStreamer support.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

Supported Streaming Websites

The complete list of all supported Streaming Websites URLs can be found here \u27b6

CamGear(source='https://youtu.be/bvetuLwJIkA', stream_mode=True)\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/camgear/params/#colorspace","title":"colorspace","text":"

This parameter selects the colorspace of the input stream.

Data-Type: String

Default Value: Its default value is None.

Usage:

All supported colorspace values are given here \u27b6

CamGear(source=0, colorspace=\"COLOR_BGR2HSV\")\n

Its complete usage example is given here \u27b6

"},{"location":"gears/camgear/params/#backend","title":"backend","text":"

This parameter manually selects the backend for OpenCV's VideoCapture class (only if specified).

Data-Type: Integer

Default Value: Its default value is 0

Usage:

All supported backends are listed here \u27b6

Its value can be for e.g. backend = cv2.CAP_DSHOW for selecting Direct Show as backend:

CamGear(source=0, backend = cv2.CAP_DSHOW)\n

"},{"location":"gears/camgear/params/#options","title":"options","text":"

This parameter provides the ability to alter various Source Tweak Parameters available within OpenCV's VideoCapture API properties.

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

All supported parameters are listed here \u27b6

The desired parameters can be passed to CamGear API by formatting them as this parameter's attributes, as follows:

# formatting parameters as dictionary attributes\noptions = {\"CAP_PROP_FRAME_WIDTH\":320, \"CAP_PROP_FRAME_HEIGHT\":240, \"CAP_PROP_FPS\":60}\n# assigning it\nCamGear(source=0, **options)\n

"},{"location":"gears/camgear/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

CamGear(source=0, logging=True)\n

"},{"location":"gears/camgear/params/#time_delay","title":"time_delay","text":"

This parameter set the time delay (in seconds) before the CamGear API start reading the frames. This delay is only required if the source required some warm-up delay before starting up.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

CamGear(source=0, time_delay=1) # set 1 seconds time delay\n

"},{"location":"gears/camgear/usage/","title":"Usage Examples","text":""},{"location":"gears/camgear/usage/#camgear-api-usage-examples","title":"CamGear API Usage Examples:","text":"

After going through following Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/camgear/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with CamGear API:

# import required libraries\nfrom vidgear.gears import CamGear\nimport cv2\n\n\n# open any valid video stream(for e.g `myvideo.avi` file)\nstream = CamGear(source=\"myvideo.avi\").start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/camgear/usage/#using-camgear-with-streaming-websites","title":"Using Camgear with Streaming Websites","text":"

CamGear internally implements yt_dlp backend class for seamlessly pipelining live video-frames and metadata from various streaming services like Twitch, Vimeo, Dailymotion, and many more \u27b6. All you have to do is to provide the desired Video's URL to its source parameter, and enable its stream_mode parameter.

The complete usage example for Dailymotion and Twitch URLs are as follows:

Bug in OpenCV's FFmpeg

To workaround a FFmpeg bug that causes video to freeze frequently in OpenCV, It is advised to always use GStreamer backend for Livestream videos.

Checkout this FAQ \u27b6 for compiling OpenCV with GStreamer support.

Not all resolutions are supported with GStreamer Backend. See issue #244

Exclusive CamGear Attributes for yt_dlp backend

CamGear also provides exclusive attributes:

with its options dictionary parameter. More information can be found here \u27b6

Supported Streaming Websites

The list of all supported Streaming Websites URLs can be found here \u27b6

Accessing Stream's Metadata

CamGear now provides ytv_metadata global parameter for accessing given Video's metadata as JSON Object. It can used as follows:

New in v0.2.4

ytv_metadata global parameter was added in v0.2.4.

# import required libraries\nfrom vidgear.gears import CamGear\n\n# Add YouTube Video URL as input source (for e.g https://www.dailymotion.com/video/x2yrnum)\n# and enable Stream Mode (`stream_mode = True`)\nstream = CamGear(\n    source=\"https://www.dailymotion.com/video/x2yrnum\", stream_mode=True, logging=True, **options\n).start()\n\n# get Video's metadata as JSON object\nvideo_metadata =  stream.ytv_metadata\n\n# print all available keys\nprint(video_metadata.keys())\n\n# get data like `title`\nprint(video_metadata[\"title\"])\n
Dailymotion Twitch
# import required libraries\nfrom vidgear.gears import CamGear\nimport cv2\n\n# set desired quality as 720p\noptions = {\"STREAM_RESOLUTION\": \"720p\"}\n\n# Add any desire Video URL as input source\n# for e.g https://vimeo.com/151666798\n# and enable Stream Mode (`stream_mode = True`)\nstream = CamGear(\n    source=\"https://www.dailymotion.com/video/x2yrnum\",\n    stream_mode=True,\n    logging=True,\n    **options\n).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

If Twitch user is offline, CamGear will throw ValueError.

# import required libraries\nfrom vidgear.gears import CamGear\nimport cv2\n\n# set desired quality as 720p\noptions = {\"STREAM_RESOLUTION\": \"720p\"}\n\n# Add any desire Video URL as input source\n# for e.g hhttps://www.twitch.tv/shroud\n# and enable Stream Mode (`stream_mode = True`)\nstream = CamGear(\n    source=\"https://www.twitch.tv/shroud\",\n    stream_mode=True,\n    logging=True,\n    **options\n).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/camgear/usage/#using-camgear-with-youtube-videos","title":"Using Camgear with Youtube Videos","text":"

CamGear API also provides out-of-the-box support for pipelining live video-frames and metadata from YouTube (Livestream + Normal) Videos.

YouTube Playlists are not supported yet.

The complete usage example is as follows:

Bug in OpenCV's FFmpeg

To workaround a FFmpeg bug that causes video to freeze frequently in OpenCV, It is advised to always use GStreamer backend for Livestream videos.

Checkout this FAQ \u27b6 for compiling OpenCV with GStreamer support.

Not all resolutions are supported with GStreamer Backend. See issue #244

Exclusive CamGear Attributes for yt_dlp backend

CamGear also provides exclusive attributes:

with its options dictionary parameter. More information can be found here \u27b6

Accessing Stream's Metadata

CamGear now provides ytv_metadata global parameter for accessing given Video's metadata as JSON Object. It can used as follows:

New in v0.2.4

ytv_metadata global parameter was added in v0.2.4.

# import required libraries\nfrom vidgear.gears import CamGear\n\n# Add YouTube Video URL as input source (for e.g https://youtu.be/uCy5OuSQnyA)\n# and enable Stream Mode (`stream_mode = True`)\nstream = CamGear(\n    source=\"https://youtu.be/uCy5OuSQnyA\", stream_mode=True, logging=True, **options\n).start()\n\n# get Video's metadata as JSON object\nvideo_metadata =  stream.ytv_metadata\n\n# print all available keys\nprint(video_metadata.keys())\n\n# get data like `title`\nprint(video_metadata[\"title\"])\n
# import required libraries\nfrom vidgear.gears import CamGear\nimport cv2\n\n# Add YouTube Video URL as input source (for e.g https://youtu.be/uCy5OuSQnyA)\n# and enable Stream Mode (`stream_mode = True`)\nstream = CamGear(\n    source=\"https://youtu.be/uCy5OuSQnyA\", \n    stream_mode=True,\n    logging=True\n).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/camgear/usage/#using-camgear-with-variable-camera-properties","title":"Using CamGear with Variable Camera Properties","text":"

CamGear API also flexibly support various Source Tweak Parameters available within OpenCV's VideoCapture API. These tweak parameters can be used to transform input source Camera-Device properties (such as its brightness, saturation, framerate, resolution, gain etc.) seamlessly, and can be easily applied in CamGear API through its options dictionary parameter by formatting them as its attributes.

The complete usage example is as follows:

All the supported Source Tweak Parameters can be found here \u27b6

# import required libraries\nfrom vidgear.gears import CamGear\nimport cv2\n\n\n# define suitable tweak parameters for your stream.\noptions = {\n    \"CAP_PROP_FRAME_WIDTH\": 320, # resolution 320x240\n    \"CAP_PROP_FRAME_HEIGHT\": 240,\n    \"CAP_PROP_FPS\": 60, # framerate 60fps\n}\n\n# To open live video stream on webcam at first index(i.e. 0) \n# device and apply source tweak parameters\nstream = CamGear(source=0, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/camgear/usage/#using-camgear-with-direct-colorspace-manipulation","title":"Using Camgear with Direct Colorspace Manipulation","text":"

CamGear API also supports Direct Colorspace Manipulation, which is ideal for changing source colorspace on the run.

A more detailed information on colorspace manipulation can be found here \u27b6

In following example code, we will start with HSV as source colorspace, and then we will switch to GRAY colorspace when W key is pressed, and then LAB colorspace when E key is pressed, finally default colorspace (i.e. BGR) when S key is pressed. Also, quit when Q key is pressed:

Any incorrect or None-type value, will immediately revert the colorspace to default i.e. BGR.

# import required libraries\nfrom vidgear.gears import CamGear\nimport cv2\n\n# Open any source of your choice, like Webcam first index(i.e. 0)\n# and change its colorspace to `HSV`\nstream = CamGear(source=0, colorspace=\"COLOR_BGR2HSV\", logging=True).start()\n\n# loop over\nwhile True:\n\n    # read HSV frames\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the HSV frame here}\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for key if pressed\n    key = cv2.waitKey(1) & 0xFF\n\n    # check if 'w' key is pressed\n    if key == ord(\"w\"):\n        # directly change colorspace at any instant\n        stream.color_space = cv2.COLOR_BGR2GRAY  # Now colorspace is GRAY\n\n    # check for 'e' key is pressed\n    if key == ord(\"e\"):\n        stream.color_space = cv2.COLOR_BGR2LAB  # Now colorspace is CieLAB\n\n    # check for 's' key is pressed\n    if key == ord(\"s\"):\n        stream.color_space = None  # Now colorspace is default(ie BGR)\n\n    # check for 'q' key is pressed\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/camgear/advanced/source_params/","title":"Source Tweak Parameters","text":""},{"location":"gears/camgear/advanced/source_params/#source-tweak-parameters-for-camgear-api","title":"Source Tweak Parameters for CamGear API","text":""},{"location":"gears/camgear/advanced/source_params/#overview","title":"Overview","text":"

With CamGear's options dictionary parameter, the user has the ability to alter various tweak parameters available within OpenCV's VideoCapture Class by formatting them as its attributes.

These tweak parameters can be used to transform input Camera-Source properties (such as its brightness, saturation, resolution, iso, gain etc.) seamlessly. All parameters supported by CamGear API are disscussed in this document.

"},{"location":"gears/camgear/advanced/source_params/#exclusive-camgear-attributes","title":"Exclusive CamGear Attributes","text":"CamGear's Exclusive Attributes

In addition to Source Tweak Parameters, CamGear also provides some exclusive attributes for its options dictionary parameters.

These attributes are as follows:

"},{"location":"gears/camgear/advanced/source_params/#supported-source-tweak-parameters","title":"Supported Source Tweak Parameters","text":"

All Source Tweak Parameters supported by CamGear API are as follows:

Remember, Not all parameters are supported by all cameras devices, which is one of the most troublesome thing with OpenCV library. Each camera type, from android cameras, to USB cameras , to professional ones, offers a different interface to modify its parameters. Therefore, there are many branches in OpenCV code to support as many of them, but of course, not all possible devices are covered, and thereby works. Furthermore, OpenCV does not return any helpful error messages regarding this problem, so it\u2019s pretty much based on trial and error.

You can easily check parameter values supported by your webcam, by hooking it to a Linux machine, and using the command v4l2-ctl -d 0 --list-formats-ext (where 0 is an index of the given camera) to list the supported video parameters and their values. If that doesn't works, refer to its datasheet (if available).

These parameters can be passed to CamGear's options dictionary parameter by formatting them as its string attributes. Its complete usage example is here \u27b6

Values Description CAP_PROP_POS_MSEC Current position of the video file in milliseconds. CAP_PROP_POS_FRAMES 0-based index of the frame to be decoded/captured next. CAP_PROP_POS_AVI_RATIO Relative position of the video file: 0=start of the film, 1=end of the film. CAP_PROP_FRAME_WIDTH Width of the frames in the video stream. CAP_PROP_FRAME_HEIGHT Height of the frames in the video stream. CAP_PROP_FPS Frame rate. CAP_PROP_FOURCC 4-character code of codec. see VideoWriter::fourcc. CAP_PROP_FRAME_COUNT Number of frames in the video file. CAP_PROP_FORMAT Format of the Mat objects returned by VideoCapture::retrieve(). CAP_PROP_MODE Backend-specific value indicating the current capture mode. CAP_PROP_BRIGHTNESS Brightness of the image (only for those cameras that support). CAP_PROP_CONTRAST Contrast of the image (only for cameras). CAP_PROP_SATURATION Saturation of the image (only for cameras). CAP_PROP_HUE Hue of the image (only for cameras). CAP_PROP_GAIN Gain of the image (only for those cameras that support). CAP_PROP_EXPOSURE Exposure (only for those cameras that support). CAP_PROP_CONVERT_RGB Boolean flags indicating whether images should be converted to RGB. CAP_PROP_WHITE_BALANCE_BLUE_U Currently unsupported. CAP_PROP_RECTIFICATION Rectification flag for stereo cameras (note: only supported by DC1394 v 2.x backend currently). CAP_PROP_MONOCHROME CAP_PROP_SHARPNESS CAP_PROP_AUTO_EXPOSURE DC1394: exposure control done by camera, user can adjust reference level using this feature. CAP_PROP_GAMMA CAP_PROP_TEMPERATURE CAP_PROP_TRIGGER CAP_PROP_TRIGGER_DELAY CAP_PROP_WHITE_BALANCE_RED_V CAP_PROP_ZOOM CAP_PROP_FOCUS CAP_PROP_GUID CAP_PROP_ISO_SPEED CAP_PROP_BACKLIGHT CAP_PROP_PAN CAP_PROP_TILT CAP_PROP_ROLL CAP_PROP_IRIS CAP_PROP_SETTINGS Pop up video/camera filter dialog (note: only supported by DSHOW backend currently. The property value is ignored) CAP_PROP_BUFFERSIZE CAP_PROP_AUTOFOCUS CAP_PROP_SAR_NUM Sample aspect ratio: num/den (num) CAP_PROP_SAR_DEN Sample aspect ratio: num/den (den) CAP_PROP_BACKEND Current backend (enum VideoCapture APIs). Read-only property. CAP_PROP_CHANNEL Video input or Channel Number (only for those cameras that support) CAP_PROP_AUTO_WB enable/ disable auto white-balance CAP_PROP_WB_TEMPERATURE white-balance color temperature

"},{"location":"gears/netgear/overview/","title":"Overview","text":""},{"location":"gears/netgear/overview/#netgear-api","title":"NetGear API","text":"NetGear API generalized"},{"location":"gears/netgear/overview/#overview","title":"Overview","text":"

NetGear is exclusively designed to transfer video frames synchronously and asynchronously between interconnecting systems over the network in real-time.

NetGear implements a high-level wrapper around PyZmQ python library that contains python bindings for ZeroMQ - a high-performance asynchronous distributed messaging library that provides a message queue, but unlike message-oriented middleware, its system can run without a dedicated message broker.

NetGear also enables real-time JPEG Frame Compression capabilities for boosting performance significantly while sending video-frames over the network in real-time.

Lazy Pirate pattern in NetGear API

NetGear API now internally implements robust Lazy Pirate pattern (auto-reconnection) for its synchronous messaging patterns(zmq.PAIR & zmq.REQ/zmq.REP) at both Server and Client ends, where its API instead of doing a blocking receive, will:

Netgear API also provides max_retries and request_timeout like attributes for controlling this polling.

NetGear as of now seamlessly supports three ZeroMQ messaging patterns:

whereas the supported protocol are: tcp and ipc.

"},{"location":"gears/netgear/overview/#modes-of-operation","title":"Modes of Operation","text":""},{"location":"gears/netgear/overview/#primary-modes","title":"Primary Modes","text":"

NetGear API primarily has two modes of operations:

"},{"location":"gears/netgear/overview/#exclusive-modes","title":"Exclusive Modes","text":"

In addition to the primary modes, NetGear API also offers application-specific Exclusive Modes:

Also, checkout this compatibility chart for these modes interoperability.

"},{"location":"gears/netgear/overview/#a-multi-servers-mode","title":"A. Multi-Servers Mode","text":""},{"location":"gears/netgear/overview/#b-multi-clients-mode","title":"B. Multi-Clients Mode","text":""},{"location":"gears/netgear/overview/#c-bidirectional-mode","title":"C. Bidirectional Mode","text":""},{"location":"gears/netgear/overview/#d-ssh-tunneling-mode","title":"D. SSH Tunneling Mode","text":""},{"location":"gears/netgear/overview/#e-secure-mode","title":"E. Secure Mode","text":"

Important Information

"},{"location":"gears/netgear/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through NetGear Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/netgear/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/netgear/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/netgear/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/netgear/params/","title":"Parameters","text":""},{"location":"gears/netgear/params/#netgear-api-parameters","title":"NetGear API Parameters","text":""},{"location":"gears/netgear/params/#address","title":"address","text":"

This parameter sets the valid Network IP address for Server/Client. Network addresses are unique identifiers across the network.

Data-Type: String

Default Value: Its default value is based on selected primary mode, i.e 'localhost' for Send Mode and '*' for Receive Mode on a local machine.

Usage:

NetGear(address=\"192.168.0.145\")\n

"},{"location":"gears/netgear/params/#port","title":"port","text":"

This parameter sets the valid Network Port for Server/Client. Network port is a number that identifies one side of a connection between two devices on the network and is used determine to which process or application a message should be delivered.

Exception for Exclusive Modes

In Multi-Servers Mode:

In Multi-Client Mode:

Data-Type: String or List/Tuple

Default Value: Its default value is '5555'

Usage:

NetGear(port=\"5575\")\n

"},{"location":"gears/netgear/params/#protocol","title":"protocol","text":"

This parameter sets the valid messaging protocol between server and client. A network protocol is a set of established rules that dictates how to format, transmit and receive data so computer network devices - from servers and routers to endpoints - can communicate regardless of the differences in their underlying infrastructures, designs or standards. Supported protocol are: 'tcp' and 'ipc'.

Data-Type: String

Default Value: Its default value is 'tcp'

Usage:

NetGear(protocol=\"ipc\")\n

"},{"location":"gears/netgear/params/#pattern","title":"pattern","text":"

This parameter sets the supported messaging pattern(flow of communication) between server and client. Messaging patterns are the network-oriented architectural pattern that describes the flow of communication between interconnecting systems. NetGear provides access to ZeroMQ's pre-optimized sockets which enables you to take advantage of these patterns.

Data-Type: Integer

Default Value: Its default value is 0 (i.e zmq.PAIR).

Supported ZMQ patterns

All supported ZMQ patterns for NetGear are:

Usage:

NetGear(pattern=1) # sets zmq.REQ/zmq.REP pattern\n

"},{"location":"gears/netgear/params/#receive_mode","title":"receive_mode","text":"

This parameter select the Netgear's Mode of operation. It basically activates Receive Mode(if True) and Send Mode(if False). Furthermore, recv() method will only work when this flag is enabled(i.e. Receive Mode), whereas send() method will only work when this flag is disabled(i.e.Send Mode).

Data-Type: Boolean

Default Value: Its default value is False(i.e. Send Mode is activated by default).

Usage:

NetGear(receive_mode=True) # activates Recieve Mode\n

"},{"location":"gears/netgear/params/#options","title":"options","text":"

This parameter provides the flexibility to alter various NetGear API's internal properties, modes, and some PyZMQ flags.

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

Supported dictionary attributes for NetGear API

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

The desired attributes can be passed to NetGear API as follows:

# formatting parameters as dictionary attributes\noptions = {\n    \"secure_mode\": 2,\n    \"custom_cert_location\": \"/home/foo/foo1/foo2\",\n    \"overwrite_cert\": True,\n    \"flag\": 0, \n    \"copy\": True, \n    \"track\": False\n}\n# assigning it\nNetGear(logging=True, **options)\n

"},{"location":"gears/netgear/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

NetGear_Async(logging=True)\n

"},{"location":"gears/netgear/usage/","title":"Usage Examples","text":""},{"location":"gears/netgear/usage/#netgear-api-usage-examples","title":"NetGear API Usage Examples:","text":"

Important Information

After going through following Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/netgear/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with NetGear API:

"},{"location":"gears/netgear/usage/#servers-end","title":"Server's End","text":"

Open your favorite terminal and execute the following python code:

You can terminate both sides anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\n\n# open any valid video stream(for e.g `test.mp4` file)\nstream = VideoGear(source=\"test.mp4\").start()\n\n# Define Netgear Server with default parameters\nserver = NetGear()\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n
"},{"location":"gears/netgear/usage/#clients-end","title":"Client's End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n\n# define Netgear Client with `receive_mode = True` and default parameter\nclient = NetGear(receive_mode=True)\n\n# loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/usage/#using-netgear-with-variable-parameters","title":"Using NetGear with Variable Parameters","text":""},{"location":"gears/netgear/usage/#clients-end_1","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from the Server) and execute the following python code:

Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# define various tweak flags\noptions = {\"flag\": 0, \"copy\": True, \"track\": False}\n\n# Define Netgear Client at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n
"},{"location":"gears/netgear/usage/#servers-end_1","title":"Server's End","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\n\n# define various tweak flags\noptions = {\"flag\": 0, \"copy\": True, \"track\": False}\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = VideoGear(source=0).start()\n\n# Define Netgear server at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/usage/#using-netgear-with-opencv","title":"Using NetGear with OpenCV","text":"

You can easily use NetGear directly with any Video Processing library such as OpenCV itself. The complete usage example is as follows:

"},{"location":"gears/netgear/usage/#clients-end_2","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from the Server) and execute the following python code:

Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# define tweak flags\noptions = {\"flag\": 0, \"copy\": True, \"track\": False}\n\n# Define Netgear Client at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=0,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the received frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n
"},{"location":"gears/netgear/usage/#servers-end_2","title":"Server's End","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = cv2.VideoCapture(0)\n\n# define tweak flags\noptions = {\"flag\": 0, \"copy\": True, \"track\": False}\n\n# Define Netgear Client at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=0,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        (grabbed, frame) = stream.read()\n\n        # check for frame if not grabbed\n        if not grabbed:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.release()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/usage/#using-netgear-with-other-videocapture-gears","title":"Using NetGear with Other VideoCapture Gears","text":"

You can use any VideoCapture Gear in the similar manner. Let's implement given usage example with ScreenGear:

"},{"location":"gears/netgear/usage/#clients-end_3","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from the Server) and execute the following python code:

Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# define various tweak flags\noptions = {\"flag\": 0, \"copy\": True, \"track\": False}\n\n# Define Netgear Client at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n
"},{"location":"gears/netgear/usage/#servers-end_3","title":"Server's End","text":"

Now, Open the terminal on another Server System (let's say you want to transmit Monitor Screen Frames from a Laptop), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import ScreenGear\nfrom vidgear.gears import NetGear\n\n# define various tweak flags\noptions = {\"flag\": 0, \"copy\": True, \"track\": False}\n\n# Start capturing live Monitor screen frames with default settings\nstream = ScreenGear().start()\n\n# Define Netgear server at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/bidirectional_mode/","title":"Bidirectional Mode","text":""},{"location":"gears/netgear/advanced/bidirectional_mode/#bidirectional-mode-for-netgear-api","title":"Bidirectional Mode for NetGear API","text":"NetGear's Bidirectional Mode"},{"location":"gears/netgear/advanced/bidirectional_mode/#overview","title":"Overview","text":"

Bidirectional Mode enables seamless support for Bidirectional data transmission between Client/Consumer and Sender/Publisher along with video-frames through its synchronous messaging patterns such as zmq.PAIR (ZMQ Pair Pattern) & zmq.REQ/zmq.REP (ZMQ Request/Reply Pattern).

In Bidirectional Mode, we utilizes the NetGear API's message parameter of send() method for sending data from Server-to-Client, and return_data parameter of recv() method to return data back from Client-to-Server all while transferring frames in real-time.

This mode can be easily activated in NetGear through bidirectional_mode attribute of its options dictionary parameter during initialization.

Important Information regarding Bidirectional Mode

"},{"location":"gears/netgear/advanced/bidirectional_mode/#features-of-bidirectional-mode","title":"Features of Bidirectional Mode","text":"

"},{"location":"gears/netgear/advanced/bidirectional_mode/#exclusive-parameters","title":"Exclusive Parameters","text":"

To send data bidirectionally, NetGear API provides two exclusive parameters for its methods:

"},{"location":"gears/netgear/advanced/bidirectional_mode/#usage-examples","title":"Usage Examples","text":""},{"location":"gears/netgear/advanced/bidirectional_mode/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with Bidirectional Mode in NetGear API:

"},{"location":"gears/netgear/advanced/bidirectional_mode/#server-end","title":"Server End","text":"

Open your favorite terminal and execute the following python code:

You can terminate both sides anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\n\n# open any valid video stream(for e.g `test.mp4` file)\nstream = VideoGear(source=\"test.mp4\").start()\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# Define NetGear Server with defined parameters\nserver = NetGear(logging=True, **options)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # send frame & data and also receive data from Client\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # print data just received from Client\n        if not (recv_data is None):\n            print(recv_data)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.
"},{"location":"gears/netgear/advanced/bidirectional_mode/#client-end","title":"Client End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# define NetGear Client with `receive_mode = True` and defined parameter\nclient = NetGear(receive_mode=True, logging=True, **options)\n\n# loop over\nwhile True:\n\n    # prepare data to be sent\n    target_data = \"Hi, I am a Client here.\"\n\n    # receive data from server and also send our data\n    data = client.recv(return_data=target_data)\n\n    # check for data if None\n    if data is None:\n        break\n\n    # extract server_data & frame from data\n    server_data, frame = data\n\n    # again check for frame if None\n    if frame is None:\n        break\n\n    # {do something with the extracted frame and data here}\n\n    # lets print extracted server data\n    if not (server_data is None):\n        print(server_data)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/bidirectional_mode/#using-bidirectional-mode-with-variable-parameters","title":"Using Bidirectional Mode with Variable Parameters","text":""},{"location":"gears/netgear/advanced/bidirectional_mode/#clients-end","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from the Server) and execute the following python code:

Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# Define NetGear Client at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# loop over\nwhile True:\n\n    # prepare data to be sent\n    target_data = \"Hi, I am a Client here.\"\n\n    # receive data from server and also send our data\n    data = client.recv(return_data=target_data)\n\n    # check for data if None\n    if data is None:\n        break\n\n    # extract server_data & frame from data\n    server_data, frame = data\n\n    # again check for frame if None\n    if frame is None:\n        break\n\n    # {do something with the extracted frame and data here}\n\n    # lets print received server data\n    if not (server_data is None):\n        print(server_data)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/bidirectional_mode/#server-end_1","title":"Server End","text":"

Now, Open the terminal on another Server System (a Raspberry Pi with Camera Module), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this backend, otherwise nothing will work.

New Picamera2 backendLegacy Picamera backend
# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import PiGear\nfrom libcamera import Transform\n\n# add various Picamera2 API tweaks\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# Define NetGear server at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # send frame & data and also receive data from Client\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # print data just received from Client\n        if not (recv_data is None):\n            print(recv_data)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import PiGear\n\n# add various Picamera tweak parameters to dictionary\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# Define NetGear server at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # send frame & data and also receive data from Client\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # print data just received from Client\n        if not (recv_data is None):\n            print(recv_data)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.

"},{"location":"gears/netgear/advanced/bidirectional_mode/#using-bidirectional-mode-for-video-frames-transfer","title":"Using Bidirectional Mode for Video-Frames Transfer","text":"

In this example we are going to implement a bare-minimum example, where we will be sending video-frames (3-Dimensional numpy arrays) of the same Video bidirectionally at the same time, for testing the real-time performance and synchronization between the Server and the Client using this(Bidirectional) Mode.

This example is useful for building applications like Real-Time Video Chat.

We're also using reducer() method for reducing frame-size on-the-go for additional performance.

Remember, Sending large HQ video-frames may required more network bandwidth and packet size which may lead to video latency!

"},{"location":"gears/netgear/advanced/bidirectional_mode/#server-end_2","title":"Server End","text":"

Open your favorite terminal and execute the following python code:

You can terminate both side anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears.helper import reducer\nimport numpy as np\nimport cv2\n\n# open any valid video stream(for e.g `test.mp4` file)\nstream = cv2.VideoCapture(\"test.mp4\")\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# Define NetGear Server with defined parameters\nserver = NetGear(pattern=1, logging=True, **options)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        (grabbed, frame) = stream.read()\n\n        # check for frame if not grabbed\n        if not grabbed:\n            break\n\n        # reducer frames size if you want more performance, otherwise comment this line\n        frame = reducer(frame, percentage=30)  # reduce frame by 30%\n\n        # {do something with the frame here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # send frame & data and also receive data from Client\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # check data just received from Client is of numpy datatype\n        if not (recv_data is None) and isinstance(recv_data, np.ndarray):\n\n            # {do something with received numpy array here}\n\n            # Let's show it on output window\n            cv2.imshow(\"Received Frame\", recv_data)\n            key = cv2.waitKey(1) & 0xFF\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.release()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.

"},{"location":"gears/netgear/advanced/bidirectional_mode/#client-end_1","title":"Client End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears.helper import reducer\nimport cv2\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# again open the same video stream\nstream = cv2.VideoCapture(\"test.mp4\")\n\n# define NetGear Client with `receive_mode = True` and defined parameter\nclient = NetGear(receive_mode=True, pattern=1, logging=True, **options)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n        break\n\n    # reducer frames size if you want more performance, otherwise comment this line\n    frame = reducer(frame, percentage=30)  # reduce frame by 30%\n\n    # receive data from server and also send our data\n    data = client.recv(return_data=frame)\n\n    # check for data if None\n    if data is None:\n        break\n\n    # extract server_data & frame from data\n    server_data, frame = data\n\n    # again check for frame if None\n    if frame is None:\n        break\n\n    # {do something with the extracted frame and data here}\n\n    # lets print extracted server data\n    if not (server_data is None):\n        print(server_data)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.release()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/bidirectional_mode/#using-bidirectional-mode-for-video-frames-transfer-with-frame-compression","title":"Using Bidirectional Mode for Video-Frames Transfer with Frame Compression","text":"

This usage examples can be found here \u27b6

  1. Additional data of numpy.ndarray data-type is ONLY SUPPORTED at Client's end with its return_data parameter.

    \u21a9\u21a9

"},{"location":"gears/netgear/advanced/compression/","title":"Frame Compression","text":""},{"location":"gears/netgear/advanced/compression/#frame-compression-for-netgear-api","title":"Frame Compression for NetGear API","text":""},{"location":"gears/netgear/advanced/compression/#overview","title":"Overview","text":"

NetGear API enables real-time JPEG Frame Compression capabilities for optimizing performance significantly while sending frames over the network.

For enabling Frame Compression, NetGear uses powerful simplejpeg library at its backend, which is based on recent versions of libjpeg-turbo JPEG image codec, to accelerate baseline JPEG compression and decompression on all modern systems. NetGear API employs its exposed decode_jpeg and encode_jpeg methods to encode video-frames to JFIF format before sending it at Server, and cleverly decode it at the Client(s) all in real-time, thereby leveraging performance at cost of minor loss in frame quality.

Frame Compression is enabled by default in NetGear, and can be easily controlled through jpeg_compression_quality, jpeg_compression_fastdct, jpeg_compression_fastupsample like attributes of its options dictionary parameter during initialization.

Useful Information about Frame Compression

Frame Compression is primarily controlled by Server end. That means, if Frame Compression is enabled at Server, then Client(s) will automatically enforce the Frame Compression with defined performance attributes. Otherwise if it is disabled, then Client(s) disables it too.

"},{"location":"gears/netgear/advanced/compression/#exclusive-attributes","title":"Exclusive Attributes","text":"

For implementing Frame Compression, NetGear API currently provide following exclusive attribute for its options dictionary parameter to leverage performance with Frame Compression:

"},{"location":"gears/netgear/advanced/compression/#performance-attributes","title":"Performance Attributes","text":""},{"location":"gears/netgear/advanced/compression/#usage-examples","title":"Usage Examples","text":""},{"location":"gears/netgear/advanced/compression/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with Frame Compression in NetGear API:

"},{"location":"gears/netgear/advanced/compression/#server-end","title":"Server End","text":"

Open your favorite terminal and execute the following python code:

You can terminate both sides anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\nimport cv2\n\n# open any valid video stream(for e.g `test.mp4` file)\nstream = VideoGear(source=\"test.mp4\").start()\n\n# activate jpeg encoding and specify other related parameters\noptions = {\n    \"jpeg_compression\": True,\n    \"jpeg_compression_quality\": 90,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# Define NetGear Server with defined parameters\nserver = NetGear(pattern=1, logging=True, **options)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if None-type\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/compression/#client-end","title":"Client End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

You can terminate client anytime by pressing Ctrl+C on your keyboard!

If compression is enabled at Server, then Client will automatically enforce Frame Compression with its performance attributes.

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# define NetGear Client with `receive_mode = True` and defined parameter\nclient = NetGear(receive_mode=True, pattern=1, logging=True)\n\n# loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/compression/#bare-minimum-usage-with-variable-colorspace","title":"Bare-Minimum Usage with Variable Colorspace","text":"

Frame Compression also supports specify incoming frames colorspace with compression. In following bare-minimum code, we will be sending GRAY frames from Server to Client:

New in v0.2.2

This example was added in v0.2.2.

This example works in conjunction with Source ColorSpace manipulation for VideoCapture Gears \u27b6

Supported colorspace values are RGB, BGR, RGBX, BGRX, XBGR, XRGB, GRAY, RGBA, BGRA, ABGR, ARGB, CMYK. More information can be found here \u27b6

"},{"location":"gears/netgear/advanced/compression/#server-end_1","title":"Server End","text":"

Open your favorite terminal and execute the following python code:

You can terminate both sides anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\nimport cv2\n\n# open any valid video stream(for e.g `test.mp4` file) and change its colorspace to grayscale\nstream = VideoGear(source=\"test.mp4\", colorspace=\"COLOR_BGR2GRAY\").start()\n\n# activate jpeg encoding and specify other related parameters\noptions = {\n    \"jpeg_compression\": \"GRAY\", # set grayscale\n    \"jpeg_compression_quality\": 90,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# Define NetGear Server with defined parameters\nserver = NetGear(pattern=1, logging=True, **options)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read grayscale frames from stream\n        frame = stream.read()\n\n        # check for frame if None-type\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send grayscale frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/compression/#client-end_1","title":"Client End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

You can terminate client anytime by pressing Ctrl+C on your keyboard!

If compression is enabled at Server, then Client will automatically enforce Frame Compression with its performance attributes.

Client's end also automatically enforces Server's colorspace, there's no need to define it again.

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# define NetGear Client with `receive_mode = True` and defined parameter\nclient = NetGear(receive_mode=True, pattern=1, logging=True)\n\n# loop over\nwhile True:\n\n    # receive grayscale frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the grayscale frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Grayscale Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/compression/#using-frame-compression-with-variable-parameters","title":"Using Frame Compression with Variable Parameters","text":""},{"location":"gears/netgear/advanced/compression/#clients-end","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from the Server) and execute the following python code:

Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

If compression is enabled at Server, then Client will automatically enforce Frame Compression with its performance attributes.

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n\n# Define NetGear Client at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n#  loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/compression/#server-end_2","title":"Server End","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate jpeg encoding and specify other related parameters\noptions = {\n    \"jpeg_compression\": True,\n    \"jpeg_compression_quality\": 90,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = VideoGear(source=0).start()\n\n# Define NetGear server at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/compression/#using-bidirectional-mode-for-video-frames-transfer-with-frame-compression","title":"Using Bidirectional Mode for Video-Frames Transfer with Frame Compression","text":"

NetGear now supports Dual Frame Compression for transferring video-frames with its exclusive Bidirectional Mode for achieving unmatchable performance bidirectionally. You can easily enable Frame Compression with its performance attributes at both ends to boost performance bidirectionally.

In this example we are going to implement a bare-minimum example, where we will be sending video-frames (3-Dimensional numpy arrays) of the same Video bidirectionally at the same time for testing the real-time performance and synchronization between the Server and Client using Bidirectional Mode. Furthermore, we're going to use optimal Dual Frame Compression Setting for Sending and Receiving frames at both Server and Client end.

This example is great for building applications like Real-time Video Chat System.

This Dual Frame Compression feature also available for Multi-Clients Mode.

We're also using reducer() Helper method for reducing frame-size on-the-go for additional performance.

Remember to define Frame Compression's performance attributes both on Server and Client ends in Dual Frame Compression to boost performance bidirectionally!

"},{"location":"gears/netgear/advanced/compression/#server-end_3","title":"Server End","text":"

Open your favorite terminal and execute the following python code:

You can terminate both side anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears.helper import reducer\nimport numpy as np\nimport cv2\n\n# open any valid video stream(for e.g `test.mp4` file)\nstream = cv2.VideoCapture(\"test.mp4\")\n\n# activate Bidirectional mode and Frame Compression\noptions = {\n    \"bidirectional_mode\": True,\n    \"jpeg_compression\": True,\n    \"jpeg_compression_quality\": 95,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# Define NetGear Server with defined parameters\nserver = NetGear(pattern=1, logging=True, **options)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        (grabbed, frame) = stream.read()\n\n        # check for frame if not grabbed\n        if not grabbed:\n            break\n\n        # reducer frames size if you want even more performance, otherwise comment this line\n        frame = reducer(frame, percentage=20)  # reduce frame by 20%\n\n        # {do something with the frame here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # send frame & data and also receive data from Client\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # check data just received from Client is of numpy datatype\n        if not (recv_data is None) and isinstance(recv_data, np.ndarray):\n\n            # {do something with received numpy array here}\n\n            # Let's show it on output window\n            cv2.imshow(\"Received Frame\", recv_data)\n            key = cv2.waitKey(1) & 0xFF\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.release()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.

"},{"location":"gears/netgear/advanced/compression/#client-end_2","title":"Client End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears.helper import reducer\nimport cv2\n\n# activate Bidirectional mode and Frame Compression\noptions = {\n    \"bidirectional_mode\": True,\n    \"jpeg_compression\": True,\n    \"jpeg_compression_quality\": 95,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# again open the same video stream\nstream = cv2.VideoCapture(\"test.mp4\")\n\n# define NetGear Client with `receive_mode = True` and defined parameter\nclient = NetGear(receive_mode=True, pattern=1, logging=True, **options)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n        break\n\n    # reducer frames size if you want even more performance, otherwise comment this line\n    frame = reducer(frame, percentage=20)  # reduce frame by 20%\n\n    # receive data from server and also send our data\n    data = client.recv(return_data=frame)\n\n    # check for data if None\n    if data is None:\n        break\n\n    # extract server_data & frame from data\n    server_data, frame = data\n\n    # again check for frame if None\n    if frame is None:\n        break\n\n    # {do something with the extracted frame and data here}\n\n    # lets print extracted server data\n    if not (server_data is None):\n        print(server_data)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.release()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/multi_client/","title":"Multi-Clients Mode","text":""},{"location":"gears/netgear/advanced/multi_client/#multi-clients-mode-for-netgear-api","title":"Multi-Clients Mode for NetGear API","text":"NetGear's Multi-Clients Mode"},{"location":"gears/netgear/advanced/multi_client/#overview","title":"Overview","text":"

In Multi-Clients Mode, NetGear robustly handles Multiple Clients at once thereby able to broadcast frames and data across multiple Clients/Consumers in the network at same time. This mode works contrary to Multi-Servers Mode such that every new Client that connects to single Server can be identified by its unique port address on the network.

The supported patterns for this mode are Publish/Subscribe (zmq.PUB/zmq.SUB) and Request/Reply(zmq.REQ/zmq.REP) and can be easily activated in NetGear API through multiclient_mode attribute of its options dictionary parameter during initialization.

Multi-Clients Mode is best for broadcasting Meta-Data with Video-frames to specific limited number of clients in real time. But if you're looking to scale broadcast to a very large pool of clients, then see our WebGear or WebGear_RTC APIs.

Important Information regarding Multi-Clients Mode

"},{"location":"gears/netgear/advanced/multi_client/#features-of-multi-clients-mode","title":"Features of Multi-Clients Mode","text":"

"},{"location":"gears/netgear/advanced/multi_client/#usage-examples","title":"Usage Examples","text":"

Important

"},{"location":"gears/netgear/advanced/multi_client/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

In this example, we will capturing live video-frames from a source (a.k.a Server) with a webcam connected to it. Afterwards, those captured frame will be sent over the network to two independent system (a.k.a Clients) using this Multi-Clients Mode in NetGear API. Finally, both Clients will be displaying received frames in Output Windows in real time.

This example is useful for building applications like Real-Time Video Broadcasting to multiple clients in local network.

"},{"location":"gears/netgear/advanced/multi_client/#servers-end","title":"Server's End","text":"

Now, Open the terminal on a Server System (with a webcam connected to it at index 0). Now execute the following python code:

Important Notes

Frame/Data transmission will NOT START untill all given Client(s) are connected to this Server.

You can terminate streaming anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import CamGear\n\n# Open suitable video stream (webcam on first index in our case)\nstream = CamGear(source=0).start()\n\n# activate multiclient_mode mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple of\n# all unique Server((5577,5578) in our case) and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.x.x\",\n    port=(5567, 5577),\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# Define received data dictionary\ndata_dict = {}\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if not None-type\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame and also receive data from Client(s)\n        recv_data = server.send(frame)\n\n        # check if valid data received\n        if not (recv_data is None):\n            # extract unique port address and its respective data\n            unique_address, data = recv_data\n            # update the extracted data in the data dictionary\n            data_dict[unique_address] = data\n\n        if data_dict:\n            # print data just received from Client(s)\n            for key, value in data_dict.items():\n                print(\"Client at port {} said: {}\".format(key, value))\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#client-1s-end","title":"Client-1's End","text":"

Now, Open a terminal on another Client System (where you want to display the input frames received from Server), let's name it Client-1. Execute the following python code:

Replace the IP address in the following code with Server's IP address you noted earlier and also assign a unique port address (required by Server to identify this system).

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate Multi-Clients mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at Server's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5567\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n) \n\n# loop over\nwhile True:\n    # receive data from server\n    frame = client.recv()\n\n    # check for frame if None\n    if frame is None:\n        break\n\n    # {do something with frame here}\n\n    # Show output window\n    cv2.imshow(\"Client 5567 Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#client-2s-end","title":"Client-2's End","text":"

Finally, Open a terminal on another Client System (where you want to display the input frames received from Server), let's name it Client-2. Execute the following python code:

Replace the IP address in the following code with Server's IP address you noted earlier and also assign a unique port address (required by Server to identify this system).

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate Multi-Clients mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at Server's IP address and assign a unique port address and other parameters\n # !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5577\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# loop over\nwhile True:\n\n    # receive data from server\n    frame = client.recv()\n\n    # check for frame if None\n    if frame is None:\n        break\n\n    # {do something with frame here}\n\n    # Show output window\n    cv2.imshow(\"Client 5577 Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#bare-minimum-usage-with-opencv","title":"Bare-Minimum Usage with OpenCV","text":"

In this example, we will be re-implementing previous bare-minimum example with OpenCV and NetGear API.

"},{"location":"gears/netgear/advanced/multi_client/#servers-end_1","title":"Server's End","text":"

Now, Open the terminal on a Server System (with a webcam connected to it at index 0). Now execute the following python code:

Important Notes

Frame/Data transmission will NOT START untill all given Client(s) are connected to this Server.

You can terminate streaming anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# Open suitable video stream (webcam on first index in our case)\nstream = cv2.VideoCapture(0)\n\n# activate multiclient_mode mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple of all unique Server((5577,5578) in our case) and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.x.x\",\n    port=(5567, 5577),\n    protocol=\"tcp\",\n    pattern=2,\n    logging=True,\n    **options\n)\n\n# Define received data dictionary\ndata_dict = {}\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        (grabbed, frame) = stream.read()\n\n        # check for frame if not grabbed\n        if not grabbed:\n            break\n\n        # {do something with the frame here}\n\n        # send frame and also receive data from Client(s)\n        recv_data = server.send(frame)\n\n        # check if valid data received\n        if not (recv_data is None):\n            # extract unique port address and its respective data\n            unique_address, data = recv_data\n            # update the extracted data in the data dictionary\n            data_dict[unique_address] = data\n\n        if data_dict:\n            # print data just received from Client(s)\n            for key, value in data_dict.items():\n                print(\"Client at port {} said: {}\".format(key, value))\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.release()\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#client-1s-end_1","title":"Client-1's End","text":"

Now, Open a terminal on another Client System (where you want to display the input frames received from Server), let's name it Client-1. Execute the following python code:

Replace the IP address in the following code with Server's IP address you noted earlier and also assign a unique port address (required by Server to identify this system).

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate Multi-Clients mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at Server's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5567\",\n    protocol=\"tcp\",\n    pattern=2,\n    receive_mode=True,\n    logging=True,\n    **options\n) \n\n# loop over\nwhile True:\n    # receive data from server\n    frame = client.recv()\n\n    # check for frame if None\n    if frame is None:\n        break\n\n    # {do something with frame here}\n\n    # Show output window\n    cv2.imshow(\"Client 5567 Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#client-2s-end_1","title":"Client-2's End","text":"

Finally, Open a terminal on another Client System (also, where you want to display the input frames received from Server), let's name it Client-2. Execute the following python code:

Replace the IP address in the following code with Server's IP address you noted earlier and also assign a unique port address (required by Server to identify this system).

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate Multi-Clients mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at Server's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5577\",\n    protocol=\"tcp\",\n    pattern=2,\n    receive_mode=True,\n    logging=True,\n    **options\n) \n\n# loop over\nwhile True:\n    # receive data from server\n    frame = client.recv()\n\n    # check for frame if None\n    if frame is None:\n        break\n\n    # {do something with frame here}\n\n    # Show output window\n    cv2.imshow(\"Client 5577 Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#using-multi-clients-mode-for-unidirectional-custom-data-transfer","title":"Using Multi-Clients Mode for Unidirectional Custom Data Transfer","text":"

Abstract

With Multi-Clients Mode, you can also send additional data of any data-type (such as list, tuple, string, int, ndarray etc.) along with frame, from all connected Clients(s) back to a Server unidirectionally.

In Multi-Clients Mode, unidirectional data transfer ONLY works with pattern 1 (i.e. Request/Reply zmq.REQ/zmq.REP), and NOT with pattern 2 (i.e. Publish/Subscribe zmq.PUB/zmq.SUB)!

In this example, We will be transferring video-frames from a single Server (consisting of Raspberry Pi with Camera Module) over the network to two independent Client for displaying them in real-time. At the same time, we will be sending data (a Text String, for the sake of simplicity) from both the Client(s) back to our Server, which will be printed onto the terminal.

"},{"location":"gears/netgear/advanced/multi_client/#servers-end_2","title":"Server's End","text":"

Now, Open the terminal on a Server System (with a webcam connected to it at index 0). Now execute the following python code:

Important Notes

Frame/Data transmission will NOT START untill all given Client(s) are connected to this Server.

You can terminate streaming anytime by pressing Ctrl+C on your keyboard!

Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this backend, otherwise nothing will work.

New Picamera2 backendLegacy Picamera backend
# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import NetGear\n\n# add various Picamera2 tweak parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate multiclient_mode mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple of all unique Server((5577,5578) in our case) and other parameters\nserver = NetGear(\n    address=\"192.168.x.x\",\n    port=(5577, 5578),\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)  # !!! change following IP address '192.168.x.xxx' with yours !!!\n\n# Define received data dictionary\ndata_dict = {}\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame and also receive data from Client(s)\n        recv_data = server.send(frame)\n\n        # check if valid data received\n        if not (recv_data is None):\n            # extract unique port address and its respective data\n            unique_address, data = recv_data\n            # update the extracted data in the data dictionary\n            data_dict[unique_address] = data\n\n        if data_dict:\n            # print data just received from Client(s)\n            for key, value in data_dict.items():\n                print(\"Client at port {} said: {}\".format(key, value))\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import NetGear\n\n# add various Picamera tweak parameters to dictionary\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate multiclient_mode mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple of all unique Server((5577,5578) in our case) and other parameters\nserver = NetGear(\n    address=\"192.168.x.x\",\n    port=(5577, 5578),\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)  # !!! change following IP address '192.168.x.xxx' with yours !!!\n\n# Define received data dictionary\ndata_dict = {}\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame and also receive data from Client(s)\n        recv_data = server.send(frame)\n\n        # check if valid data received\n        if not (recv_data is None):\n            # extract unique port address and its respective data\n            unique_address, data = recv_data\n            # update the extracted data in the data dictionary\n            data_dict[unique_address] = data\n\n        if data_dict:\n            # print data just received from Client(s)\n            for key, value in data_dict.items():\n                print(\"Client at port {} said: {}\".format(key, value))\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#client-1s-end_2","title":"Client-1's End","text":"

Now, Open a terminal on another Client System (where you want to display the input frames received from Server), let's name it Client-1. Execute the following python code:

Replace the IP address in the following code with Server's IP address you noted earlier and also assign a unique port address (required by Server to identify this system).

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate Multi-Clients mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at Server's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5577\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# loop over\nwhile True:\n\n    # prepare data to be sent\n    target_data = \"Hi, I am 5577 Client here.\"\n\n    # receive data from server and also send our data\n    frame = client.recv(return_data=target_data)\n\n    # check for frame if None\n    if frame is None:\n        break\n\n    # {do something with frame here}\n\n    # Show output window\n    cv2.imshow(\"Client 5577 Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#client-2s-end_2","title":"Client-2's End","text":"

Finally, Open a terminal on another Client System (also, where you want to display the input frames received from Server), let's name it Client-2. Execute the following python code:

Replace the IP address in the following code with Server's IP address you noted earlier and also assign a unique port address (required by Server to identify this system).

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate Multi-Clients mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at Server's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5578\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n) \n\n# loop over\nwhile True:\n\n    # prepare data to be sent\n    target_data = \"Hi, I am 5578 Client here.\"\n\n    # receive data from server and also send our data\n    frame = client.recv(return_data=target_data)\n\n    # check for frame if None\n    if frame is None:\n        break\n\n    # {do something with frame here}\n\n    # Show output window\n    cv2.imshow(\"Client 5578 Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#using-multi-clients-mode-with-bidirectional-mode","title":"Using Multi-Clients Mode with Bidirectional Mode","text":"

Abstract

Multi-Clients Mode now also compatible with Bidirectional Mode, which lets you send additional data of any datatype1 along with frame in real-time bidirectionally between a single Server and all connected Client(s).

Important Information

New in v0.2.5

This example was added in v0.2.5.

In this example, We will be transferring video-frames and data (a Text String, for the sake of simplicity) from a single Server (In this case, Raspberry Pi with Camera Module) over the network to two independent Clients for displaying them both in real-time. At the same time, we will be sending data (a Text String, for the sake of simplicity) back from both the Client(s) to our Server, which will be printed onto the terminal.

"},{"location":"gears/netgear/advanced/multi_client/#servers-end_3","title":"Server's End","text":"

Now, Open the terminal on a Server System (with a webcam connected to it at index 0). Now execute the following python code:

Important Notes

Frame/Data transmission will NOT START untill all given Client(s) are connected to this Server.

You can terminate streaming anytime by pressing Ctrl+C on your keyboard!

Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this backend, otherwise nothing will work.

New Picamera2 backendLegacy Picamera backend
# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import NetGear\nfrom libcamera import Transform\n\n# add various Picamera2 tweak parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate both multiclient and bidirectional modes\noptions = {\"multiclient_mode\": True, \"bidirectional_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple of \n# all unique Server((5577,5578) in our case) and other parameters\nserver = NetGear(\n    address=\"192.168.x.x\",\n    port=(5577, 5578),\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)  # !!! change following IP address '192.168.x.xxx' with yours !!!\n\n# Define received data dictionary\ndata_dict = {}\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # send frame & data and also receive data from Client(s)\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # check if valid data received\n        if not (recv_data is None):\n            # extract unique port address and its respective data\n            unique_address, data = recv_data\n            # update the extracted data in the data dictionary\n            data_dict[unique_address] = data\n\n        if data_dict:\n            # print data just received from Client(s)\n            for key, value in data_dict.items():\n                print(\"Client at port {} said: {}\".format(key, value))\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import NetGear\n\n# add various Picamera tweak parameters to dictionary\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate both multiclient and bidirectional modes\noptions = {\"multiclient_mode\": True, \"bidirectional_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple of \n# all unique Server((5577,5578) in our case) and other parameters\nserver = NetGear(\n    address=\"192.168.x.x\",\n    port=(5577, 5578),\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)  # !!! change following IP address '192.168.x.xxx' with yours !!!\n\n# Define received data dictionary\ndata_dict = {}\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # send frame & data and also receive data from Client(s)\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # check if valid data received\n        if not (recv_data is None):\n            # extract unique port address and its respective data\n            unique_address, data = recv_data\n            # update the extracted data in the data dictionary\n            data_dict[unique_address] = data\n\n        if data_dict:\n            # print data just received from Client(s)\n            for key, value in data_dict.items():\n                print(\"Client at port {} said: {}\".format(key, value))\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.

"},{"location":"gears/netgear/advanced/multi_client/#client-1s-end_3","title":"Client-1's End","text":"

Now, Open a terminal on another Client System (where you want to display the input frames received from Server), let's name it Client-1. Execute the following python code:

Replace the IP address in the following code with Server's IP address you noted earlier and also assign a unique port address (required by Server to identify this system).

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate both multiclient and bidirectional modes\noptions = {\"multiclient_mode\": True, \"bidirectional_mode\": True}\n\n# Define NetGear Client at Server's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5577\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# loop over\nwhile True:\n\n    # prepare data to be sent\n    target_data = \"Hi, I am 5577 Client here.\"\n\n    # receive data from server and also send our data\n    data = client.recv(return_data=target_data)\n\n    # check for data if None\n    if data is None:\n        break\n\n    # extract server_data & frame from data\n    server_data, frame = data\n\n    # again check for frame if None\n    if frame is None:\n        break\n\n    # {do something with the extracted frame and data here}\n\n    # lets print extracted server data\n    if not (server_data is None):\n        print(server_data)\n\n    # Show output window\n    cv2.imshow(\"Client 5577 Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#client-2s-end_3","title":"Client-2's End","text":"

Finally, Open a terminal on another Client System (also, where you want to display the input frames received from Server), let's name it Client-2. Execute the following python code:

Replace the IP address in the following code with Server's IP address you noted earlier and also assign a unique port address (required by Server to identify this system).

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate both multiclient and bidirectional modes\noptions = {\"multiclient_mode\": True, \"bidirectional_mode\": True}\n\n# Define NetGear Client at Server's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5578\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n) \n\n# loop over\nwhile True:\n\n    # prepare data to be sent\n    target_data = \"Hi, I am 5578 Client here.\"\n\n    # receive data from server and also send our data\n    data = client.recv(return_data=target_data)\n\n    # check for data if None\n    if data is None:\n        break\n\n    # extract server_data & frame from data\n    server_data, frame = data\n\n    # again check for frame if None\n    if frame is None:\n        break\n\n    # {do something with the extracted frame and data here}\n\n    # lets print extracted server data\n    if not (server_data is None):\n        print(server_data)\n\n    # Show output window\n    cv2.imshow(\"Client 5578 Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

  1. Additional data of numpy.ndarray data-type is NOT SUPPORTED at Server's end with its message parameter.

    \u21a9

"},{"location":"gears/netgear/advanced/multi_server/","title":"Multi-Servers Mode","text":""},{"location":"gears/netgear/advanced/multi_server/#multi-servers-mode-for-netgear-api","title":"Multi-Servers Mode for NetGear API","text":"NetGear's Multi-Servers Mode"},{"location":"gears/netgear/advanced/multi_server/#overview","title":"Overview","text":"

In Multi-Servers Mode, NetGear API robustly handles Multiple Servers at once, thereby providing seamless access to frames and unidirectional data transfer across multiple Publishers/Servers in the network at the same time. Each new server connects to a single client can be identified by its unique port address on the network.

The supported patterns for this mode are Publish/Subscribe (zmq.PUB/zmq.SUB) and Request/Reply(zmq.REQ/zmq.REP) and can be easily activated in NetGear API through multiserver_mode attribute of its options dictionary parameter during initialization.

Important Information regarding Multi-Servers Mode

"},{"location":"gears/netgear/advanced/multi_server/#key-features","title":"Key Features","text":"

"},{"location":"gears/netgear/advanced/multi_server/#usage-examples","title":"Usage Examples","text":"

Example Assumptions

"},{"location":"gears/netgear/advanced/multi_server/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

In this example, we will capturing live video-frames on two independent sources (a.k.a Servers), each with a webcam connected to it. Afterwards, these frames will be sent over the network to a single system (a.k.a Client) using this Multi-Servers Mode in NetGear API in real time, and will be displayed as a live montage.

This example is useful for building applications like Real-Time Security System with multiple cameras.

"},{"location":"gears/netgear/advanced/multi_server/#clients-end","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from Multiple Servers) and execute the following python code:

Important Notes

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom imutils import build_montages # (1)\nimport cv2\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple \n# of all unique Server((5566,5567) in our case) and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=(5566, 5567),\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    **options\n)\n\n# Define received frame dictionary\nframe_dict = {}\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # receive data from network\n        data = client.recv()\n\n        # check if data received isn't None\n        if data is None:\n            break\n\n        # extract unique port address and its respective frame\n        unique_address, frame = data\n\n        # {do something with the extracted frame here}\n\n        # get extracted frame's shape\n        (h, w) = frame.shape[:2]\n\n        # update the extracted frame in the received frame dictionary\n        frame_dict[unique_address] = frame\n\n        # build a montage using data dictionary\n        montages = build_montages(frame_dict.values(), (w, h), (2, 1))\n\n        # display the montage(s) on the screen\n        for (i, montage) in enumerate(montages):\n\n            cv2.imshow(\"Montage Footage {}\".format(i), montage)\n\n        # check for 'q' key if pressed\n        key = cv2.waitKey(1) & 0xFF\n        if key == ord(\"q\"):\n            break\n\n    except KeyboardInterrupt:\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n
  1. For building Frames Montage you'll need imutils python library. Install it with pip install imutils command.

"},{"location":"gears/netgear/advanced/multi_server/#server-1s-end","title":"Server-1's End","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and let's called it Server-1. Now execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier and also assign a unique port address (required by Client to identify this system).

You can terminate stream anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import CamGear\n\n# Open suitable video stream (webcam on first index in our case)\nstream = CamGear(source=0).start()\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.x.x\", port=\"5566\", protocol=\"tcp\", pattern=1, **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if not None-type\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/multi_server/#server-2s-end","title":"Server-2's End","text":"

Finally, Open the terminal on another Server System (also with a webcam connected to it at index 0), and let's called it Server-2. Now execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier and also assign a unique port address (required by Client to identify this system).

You can terminate stream anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import CamGear\n\n# Open suitable video stream (webcam on first index in our case)\nstream = CamGear(source=0).start()\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.x.x\", port=\"5567\", protocol=\"tcp\", pattern=1, **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if not None-type\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/multi_server/#bare-minimum-usage-with-opencv","title":"Bare-Minimum Usage with OpenCV","text":"

In this example, we will be re-implementing previous bare-minimum example with OpenCV and NetGear API.

"},{"location":"gears/netgear/advanced/multi_server/#clients-end_1","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from Mutiple Servers) and execute the following python code:

Important Notes

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom imutils import build_montages # (1)\nimport cv2\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple of all \n# unique Server((5566,5567) in our case) and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=(5566, 5567),\n    protocol=\"tcp\",\n    pattern=2,\n    receive_mode=True,\n    **options\n)\n\n# Define received frame dictionary\nframe_dict = {}\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # receive data from network\n        data = client.recv()\n\n        # check if data received isn't None\n        if data is None:\n            break\n\n        # extract unique port address and its respective frame\n        unique_address, frame = data\n\n        # {do something with the extracted frame here}\n\n        # get extracted frame's shape\n        (h, w) = frame.shape[:2]\n\n        # update the extracted frame in the received frame dictionary\n        frame_dict[unique_address] = frame\n\n        # build a montage using data dictionary\n        montages = build_montages(frame_dict.values(), (w, h), (2, 1))\n\n        # display the montage(s) on the screen\n        for (i, montage) in enumerate(montages):\n\n            cv2.imshow(\"Montage Footage {}\".format(i), montage)\n\n        # check for 'q' key if pressed\n        key = cv2.waitKey(1) & 0xFF\n        if key == ord(\"q\"):\n            break\n\n    except KeyboardInterrupt:\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n
  1. For building Frames Montage you'll need imutils python library. Install it with pip install imutils command.

"},{"location":"gears/netgear/advanced/multi_server/#server-1s-end_1","title":"Server-1's End","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and let's called it Server-1. Now execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier and also assign a unique port address (required by Client to identify this system).

You can terminate stream anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# Open suitable video stream (webcam on first index in our case)\nstream = cv2.VideoCapture(0)\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameter\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.x.x\", port=\"5566\", protocol=\"tcp\", pattern=2, **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        (grabbed, frame) = stream.read()\n\n        # check for frame if not grabbed\n        if not grabbed:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.release()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/multi_server/#server-2s-end_1","title":"Server-2's End","text":"

Finally, Open the terminal on another Server System (also with a webcam connected to it at index 0), and let's called it Server-2. Now execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier and also assign a unique port address (required by Client to identify this system).

You can terminate stream anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# Open suitable video stream (webcam on first index in our case)\nstream = cv2.VideoCapture(0)\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.x.x\", port=\"5567\", protocol=\"tcp\", pattern=2, **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        (grabbed, frame) = stream.read()\n\n        # check for frame if not grabbed\n        if not grabbed:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.release()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/multi_server/#using-multi-servers-mode-for-unidirectional-custom-data-transfer","title":"Using Multi-Servers Mode for Unidirectional Custom Data Transfer","text":"

Abstract

With Multi-Servers Mode, you can send additional data of any datatype1 along with frame with frame in real-time, from all connected Server(s) to a single Client unidirectionally.

But numpy.ndarray data-type is NOT supported as data.

In this example, We will be transferring video-frames and data (a Text String, for the sake of simplicity) from two Servers (consisting of a Raspberry Pi with Camera Module & a Laptop with webcam) to a single Client over the network in real-time. The received video-frames at Client's end will displayed as a live montage, whereas the received data will be printed to the terminal.

"},{"location":"gears/netgear/advanced/multi_server/#clients-end_2","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from Mutiple Servers) and execute the following python code:

Important Notes

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom imutils import build_montages # (1)\nimport cv2\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple of all unique Server((5577,5578) in our case) and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=(5577, 5578),\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)  \n# Define received frame dictionary\nframe_dict = {}\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # receive data from network\n        data = client.recv()\n\n        # check if data received isn't None\n        if data is None:\n            break\n\n        # extract unique port address and its respective frame and received data\n        unique_address, extracted_data, frame = data\n\n        # {do something with the extracted frame and data here}\n        # let's display extracted data on our extracted frame\n        cv2.putText(\n            frame,\n            extracted_data,\n            (10, frame.shape[0] - 10),\n            cv2.FONT_HERSHEY_SIMPLEX,\n            0.6,\n            (0, 255, 0),\n            2,\n        )\n\n        # get extracted frame's shape\n        (h, w) = frame.shape[:2]\n\n        # update the extracted frame in the frame dictionary\n        frame_dict[unique_address] = frame\n\n        # build a montage using data dictionary\n        montages = build_montages(frame_dict.values(), (w, h), (2, 1))\n\n        # display the montage(s) on the screen\n        for (i, montage) in enumerate(montages):\n\n            cv2.imshow(\"Montage Footage {}\".format(i), montage)\n\n        # check for 'q' key if pressed\n        key = cv2.waitKey(1) & 0xFF\n        if key == ord(\"q\"):\n            break\n\n    except KeyboardInterrupt:\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n
  1. For building Frames Montage you'll need imutils python library. Install it with pip install imutils command.

"},{"location":"gears/netgear/advanced/multi_server/#server-1s-end_2","title":"Server-1's End","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and let's called it Server-1. Now execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier and also assign a unique port address (required by Client to identify this system).

You can terminate stream anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import VideoGear\nimport cv2\n\n# Open suitable video stream (webcam on first index in our case)\nstream = VideoGear(source=0).start()\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5577\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with frame and data(to be sent) here}\n\n        # let's prepare a text string as data\n        target_data = \"I'm Server-1 at Port: 5577\"\n\n        # send frame and data through server\n        server.send(frame, message=target_data) # (1)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.

"},{"location":"gears/netgear/advanced/multi_server/#server-2s-end_2","title":"Server-2's End","text":"

Finally, Open the terminal on another Server System (this time a Raspberry Pi with Camera Module connected to it), and let's called it Server-2. Now execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier and also assign a unique port address (required by Client to identify this system).

You can terminate stream anytime by pressing Ctrl+C on your keyboard!

Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this backend, otherwise nothing will work.

New Picamera2 backendLegacy Picamera backend
# import libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import PiGear\nfrom libcamera import Transform\nimport cv2\n\n# add various Picamera tweak parameters to dictionary\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.1.xxx\",\n    port=\"5578\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with frame and data(to be sent) here}\n\n        # let's prepare a text string as data\n        text = \"I'm Server-2 at Port: 5578\"\n\n        # send frame and data through server\n        server.send(frame, message=text)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream.\nstream.stop()\n\n# safely close server\nserver.close()\n
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import PiGear\nimport cv2\n\n# add various Picamera tweak parameters to dictionary\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.1.xxx\",\n    port=\"5578\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with frame and data(to be sent) here}\n\n        # let's prepare a text string as data\n        text = \"I'm Server-2 at Port: 5578\"\n\n        # send frame and data through server\n        server.send(frame, message=text)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream.\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/multi_server/#using-multi-servers-mode-with-bidirectional-mode","title":"Using Multi-Servers Mode with Bidirectional Mode","text":"

Abstract

Multi-Servers Mode now also compatible with Bidirectional Mode, which lets you send additional data of any datatype1 along with frame in real-time bidirectionally between a single Client and all connected Server(s).

Important Information

New in v0.2.5

This example was added in v0.2.5.

In this example, We will be transferring video-frames and data (a Text String, for the sake of simplicity) from two Servers (consisting of a Raspberry Pi with Camera Module & a Laptop with webcam) to a single Client, and at same time sending back data (a Text String, for the sake of simplicity) to them over the network all in real-time. The received video-frames at Client's end will displayed as a live montage, whereas the received data will be printed to the terminal.

"},{"location":"gears/netgear/advanced/multi_server/#clients-end_3","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from Mutiple Servers) and execute the following python code:

Important Notes

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom imutils import build_montages # (1)\nimport cv2\n\n# activate both multiserver and bidirectional modes\noptions = {\"multiserver_mode\": True, \"bidirectional_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple of all unique Server((5577,5578) in our case) and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=(5577, 5578),\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)  \n# Define received frame dictionary\nframe_dict = {}\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # prepare data to be sent\n        target_data = \"Hi, I am a Client here.\"\n\n        # receive data from server(s) and also send our data\n        data = client.recv(return_data=target_data)\n\n        # check if data received isn't None\n        if data is None:\n            break\n\n        # extract unique port address and its respective frame and received data\n        unique_address, extracted_data, frame = recv_data\n\n        # {do something with the extracted frame and data here}\n        # let's display extracted data on our extracted frame\n        cv2.putText(\n            frame,\n            extracted_data,\n            (10, frame.shape[0] - 10),\n            cv2.FONT_HERSHEY_SIMPLEX,\n            0.6,\n            (0, 255, 0),\n            2,\n        )\n\n        # get extracted frame's shape\n        (h, w) = frame.shape[:2]\n\n        # update the extracted frame in the frame dictionary\n        frame_dict[unique_address] = frame\n\n        # build a montage using data dictionary\n        montages = build_montages(frame_dict.values(), (w, h), (2, 1))\n\n        # display the montage(s) on the screen\n        for (i, montage) in enumerate(montages):\n\n            cv2.imshow(\"Montage Footage {}\".format(i), montage)\n\n        # check for 'q' key if pressed\n        key = cv2.waitKey(1) & 0xFF\n        if key == ord(\"q\"):\n            break\n\n    except KeyboardInterrupt:\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n
  1. For building Frames Montage you'll need imutils python library. Install it with pip install imutils command.

"},{"location":"gears/netgear/advanced/multi_server/#server-1s-end_3","title":"Server-1's End","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and let's called it Server-1. Now execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier and also assign a unique port address (required by Client to identify this system).

You can terminate stream anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import VideoGear\nimport cv2\n\n# Open suitable video stream (webcam on first index in our case)\nstream = VideoGear(source=0).start()\n\n# activate both multiserver and bidirectional modes\noptions = {\"multiserver_mode\": True, \"bidirectional_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5577\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with frame and data(to be sent) here}\n\n        # let's prepare a text string as data\n        target_data = \"I'm Server-1 at Port: 5577\"\n\n        # send frame & data and also receive data from Client\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # print data just received from Client\n        if not (recv_data is None):\n            print(recv_data)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.

"},{"location":"gears/netgear/advanced/multi_server/#server-2s-end_3","title":"Server-2's End","text":"

Finally, Open the terminal on another Server System (this time a Raspberry Pi with Camera Module connected to it), and let's called it Server-2. Now execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier and also assign a unique port address (required by Client to identify this system).

You can terminate stream anytime by pressing Ctrl+C on your keyboard!

Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this backend, otherwise nothing will work.

New Picamera2 backendLegacy Picamera backend
# import libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import PiGear\nfrom libcamera import Transform\nimport cv2\n\n# add various Picamera2 tweak parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate both multiserver and bidirectional modes\noptions = {\"multiserver_mode\": True, \"bidirectional_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.1.xxx\",\n    port=\"5578\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with frame and data(to be sent) here}\n\n        # let's prepare a text string as data\n        target_data = \"I'm Server-2 at Port: 5578\"\n\n        # send frame & data and also receive data from Client\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # print data just received from Client\n        if not (recv_data is None):\n            print(recv_data)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream.\nstream.stop()\n\n# safely close server\nserver.close()\n
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import PiGear\nimport cv2\n\n# add various Picamera tweak parameters to dictionary\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate both multiserver and bidirectional modes\noptions = {\"multiserver_mode\": True, \"bidirectional_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.1.xxx\",\n    port=\"5578\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with frame and data(to be sent) here}\n\n        # let's prepare a text string as data\n        target_data = \"I'm Server-2 at Port: 5578\"\n\n        # send frame & data and also receive data from Client\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # print data just received from Client\n        if not (recv_data is None):\n            print(recv_data)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream.\nstream.stop()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.

  1. Additional data of numpy.ndarray data-type is NOT SUPPORTED at Server(s) with their message parameter.

    \u21a9\u21a9\u21a9

"},{"location":"gears/netgear/advanced/secure_mode/","title":"Secure Mode","text":""},{"location":"gears/netgear/advanced/secure_mode/#secure-mode-for-netgear-api","title":"Secure Mode for NetGear API","text":""},{"location":"gears/netgear/advanced/secure_mode/#overview","title":"Overview","text":"

Secure Mode provides easy access to powerful, smart & secure ZeroMQ's Security Layers in NetGear API that enables strong encryption on data, and unbreakable authentication between the Server and the Client with the help of custom Certificates/keys and brings cheap, standardized privacy and authentication for distributed systems over the network.

Secure Mode uses a new wire protocol, ZMTP 3.0 that adds a security handshake to all ZeroMQ connections and a new security protocol, CurveZMQ, that implements \"perfect forward security\" between two ZeroMQ peers over a TCP connection.

Secure Mode can be easily activated in NetGear API through secure_mode attribute of its options dictionary parameter, during initialization. Furthermore, for managing this mode, NetGear API provides additional custom_cert_location & overwrite_cert like attribute too.

"},{"location":"gears/netgear/advanced/secure_mode/#supported-zmq-security-layers","title":"Supported ZMQ Security Layers","text":"

Secure mode supports the two most powerful ZMQ security layers:

Important Information regarding Secure Mode

"},{"location":"gears/netgear/advanced/secure_mode/#features","title":"Features","text":"

"},{"location":"gears/netgear/advanced/secure_mode/#exclusive-attributes","title":"Exclusive Attributes","text":"

For implementing Secure Mode, NetGear API currently provide following exclusive attribute for its options dictionary parameter:

"},{"location":"gears/netgear/advanced/secure_mode/#usage-examples","title":"Usage Examples","text":""},{"location":"gears/netgear/advanced/secure_mode/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with Secure Mode in NetGear API:

In Secure Mode, Client's end MUST run before the Server's end to establish a secure connection!

"},{"location":"gears/netgear/advanced/secure_mode/#clients-end","title":"Client's End","text":"

Open your favorite terminal and execute the following python code:

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate StoneHouse security mechanism\noptions = {\"secure_mode\": 1}\n\n# define NetGear Client with `receive_mode = True` and defined parameter\nclient = NetGear(pattern=1, receive_mode=True, logging=True, **options)\n\n# loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n
"},{"location":"gears/netgear/advanced/secure_mode/#servers-end","title":"Server's End","text":"

Then open another terminal on the same system and execute the following python code to send the frames to our client:

You can terminate both sides anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\n\n# open any valid video stream(for e.g `test.mp4` file)\nstream = VideoGear(source=\"test.mp4\").start()\n\n# activate StoneHouse security mechanism\noptions = {\"secure_mode\": 1}\n\n# Define NetGear Server with defined parameters\nserver = NetGear(pattern=1, logging=True, **options)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/secure_mode/#using-secure-mode-with-variable-parameters","title":"Using Secure Mode with Variable Parameters","text":""},{"location":"gears/netgear/advanced/secure_mode/#clients-end_1","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from the Server) and execute the following python code:

In Secure Mode, Client's end MUST run before the Server's end to establish a secure connection!

Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

You need to paste the Public+Secret Keypairs (generated at the Server End) at the $HOME/.vidgear/keys directory of your Client machine for a successful authentication!

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate IronHouse security mechanism\noptions = {\"secure_mode\": 2}\n\n# Define NetGear Client at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=2,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/secure_mode/#servers-end_1","title":"Server's End","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

You also need to copy the Public+Secret Keypairs (generated on running this example code) present in the $HOME/.vidgear/keys directory, and make available at Client's end for a successful authentication.

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\n\n# activate IronHouse security mechanism, and \n# [BEWARE!!!] generating new Keypairs for this example !!!\noptions = {\"secure_mode\": 2, \"overwrite_cert\": True}\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = VideoGear(source=0).start()\n\n# Define NetGear server at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=2,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/ssh_tunnel/","title":"SSH Tunneling Mode","text":""},{"location":"gears/netgear/advanced/ssh_tunnel/#ssh-tunneling-mode-for-netgear-api","title":"SSH Tunneling Mode for NetGear API","text":"NetGear's Bidirectional Mode"},{"location":"gears/netgear/advanced/ssh_tunnel/#overview","title":"Overview","text":"New in v0.2.2

This document was added in v0.2.2.

SSH Tunneling Mode allows you to connect NetGear client and server via secure SSH connection over the untrusted network and access its intranet services across firewalls. This mode works with pyzmq's zmq.ssh module for tunneling ZeroMQ connections over ssh.

This mode implements SSH Remote Port Forwarding which enables accessing Host(client) machine outside the network by exposing port to the public Internet. Thereby, once you have established the tunnel, connections to local machine will actually be connections to remote machine as seen from the server.

Beware \u2620\ufe0f

Cybercriminals or malware could exploit SSH tunnels to hide their unauthorized communications, or to exfiltrate stolen data from the network. More information can be found here \u27b6

All patterns are valid for this mode and it can be easily activated in NetGear API at server end through ssh_tunnel_mode string attribute of its options dictionary parameter during initialization.

Important

Useful Tips

"},{"location":"gears/netgear/advanced/ssh_tunnel/#prerequisites","title":"Prerequisites","text":"

SSH Tunnel Mode requires pexpect or paramiko as an additional dependency which is not part of standard VidGear package. It can be easily installed via pypi as follows:

PramikoPexpect

paramiko is compatible with all platforms.

paramiko support is automatically enabled in ZeroMQ if installed.

# install paramiko\npip install paramiko\n

pexpect is NOT compatible with Windows Machines.

# install pexpect\npip install pexpect\n

"},{"location":"gears/netgear/advanced/ssh_tunnel/#exclusive-attributes","title":"Exclusive Attributes","text":"

All these attributes will work on Server end only whereas Client end will simply discard them.

For implementing SSH Tunneling Mode, NetGear API currently provide following exclusive attribute for its options dictionary parameter:

"},{"location":"gears/netgear/advanced/ssh_tunnel/#usage-example","title":"Usage Example","text":"Assumptions for this Example

In this particular example, we assume that:

"},{"location":"gears/netgear/advanced/ssh_tunnel/#clients-end","title":"Client's End","text":"

Open a terminal on Client System (A Regular PC where you want to display the input frames received from the Server) and execute the following python code:

Requirements for Client's End

To ensure a successful Remote NetGear Connection with Server:

Finding Public IP Address

Only IPv4 IP-addresses are supported

Enabling Dynamic DNS

SSH tunneling requires public IP address to able to access host on public Internet. Thereby, if it's troublesome to remember Public IP address or your IP address change constantly, then you can use dynamic DNS services like https://www.noip.com/

How to TCP Port Forward in your Router

For more information on Forwarding Port in Popular Home Routers. See this document \u27b6

Secsh channel X open FAILED: open failed: Administratively prohibited

Error: This error means that installed OpenSSH is preventing connections to forwarded ports from outside your Client Machine.

Solution: You need to change GatewayPorts no option to GatewayPorts yes in the OpenSSH server configuration file sshd_config to allows anyone to connect to the forwarded ports on Client Machine.

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# Define NetGear Client at given IP address and define parameters \nclient = NetGear(\n    address=\"127.0.0.1\", # don't change this\n    port=\"5454\",\n    pattern=2,\n    receive_mode=True,\n    logging=True,\n)\n\n# loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/ssh_tunnel/#servers-end","title":"Server's End","text":"

Now, Open the terminal on Remote Server System (A Raspberry Pi with a webcam connected to it at index 0), and execute the following python code:

Make sure to replace the Client's Public IP Address and Forwarded TCP port(default is 22) in SSH URL with yours in the following example.

On Server end, NetGear automatically validates if the port is open at specified Client's Public IP Address or not, and if it fails (i.e. port is closed), NetGear will throw AssertionError!

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\n\n# activate SSH tunneling with SSH URL, and\n# [BEWARE!!!] Change SSH URL and SSH password with yours for this example !!!\noptions = {\n    \"ssh_tunnel_mode\": \"test@52.155.1.89\", # defaults to port 22\n    \"ssh_tunnel_pwd\": \"pas$wd\",\n}\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = VideoGear(source=0).start()\n\n# Define NetGear server at given IP address and define parameters\nserver = NetGear(\n    address=\"127.0.0.1\", # don't change this\n    port=\"5454\",\n    pattern=2, \n    logging=True, \n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear_async/overview/","title":"Overview","text":""},{"location":"gears/netgear_async/overview/#netgear_async-api","title":"NetGear_Async API","text":""},{"location":"gears/netgear_async/overview/#overview","title":"Overview","text":"

NetGear_Async can generate the same performance as NetGear API at about one-third the memory consumption, and also provide complete server-client handling with various options to use variable protocols/patterns similar to NetGear, but lacks in term of flexibility as it supports only a few NetGear's Exclusive Modes.

NetGear_Async is built on zmq.asyncio, and powered by a high-performance asyncio event loop called uvloop to achieve unmatchable high-speed and lag-free video streaming over the network with minimal resource constraints. NetGear_Async can transfer thousands of frames in just a few seconds without causing any significant load on your system.

NetGear_Async provides complete server-client handling and options to use variable protocols/patterns similar to NetGear API. Furthermore, NetGear_Async allows us to define our custom Server as source to transform frames easily before sending them across the network(see this doc example).

NetGear_Async now supports additional bidirectional data transmission between receiver(client) and sender(server) while transferring frames. Users can easily build complex applications such as like Real-Time Video Chat in just few lines of code.

In addition to all this, NetGear_Async API also provides internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs, thereby granting it exclusive power for transferring frames incoming from any source to the network.

NetGear_Async as of now supports four ZeroMQ messaging patterns:

Whereas supported protocol are: tcp and ipc.

Helpful Tips

"},{"location":"gears/netgear_async/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through NetGear_Async Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/netgear_async/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/netgear_async/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/netgear_async/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/netgear_async/params/","title":"Parameters","text":""},{"location":"gears/netgear_async/params/#netgear_async-api-parameters","title":"NetGear_Async API Parameters","text":"

NetGear_Async provides a special internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs and their parameters.

"},{"location":"gears/netgear_async/params/#enablepicamera","title":"enablePiCamera","text":"

This parameter provide access to PiGear or CamGear APIs respectively. This means the if enablePiCamera flag is True, the PiGear API will be accessed, and if False, the CamGear API will be accessed.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

NetGear_Async(enablePiCamera=True) # enable access to PiGear API\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/netgear_async/params/#address","title":"address","text":"

This parameter sets the valid network address of the Server/Client. Network addresses unique identifiers across the network.

Data-Type: String

Default Value: Its default value is based on selected primary mode, i.e 'localhost' for Send Mode and '*' for Receive Mode.

Usage:

NetGear_Async(address=\"192.168.0.145\")\n

"},{"location":"gears/netgear_async/params/#port","title":"port","text":"

This parameter sets the valid Network Port of the Server/Client. A network port is a number that identifies one side of a connection between two devices on the network and is used determine to which process or application a message should be delivered.

Data-Type: String

Default Value: Its default value is '5555'

Usage:

NetGear_Async(port=\"5575\")\n

"},{"location":"gears/netgear_async/params/#protocol","title":"protocol","text":"

This parameter sets the valid messaging protocol between Server/Client. A network protocol is a set of established rules that dictates how to format, transmit and receive data so computer network devices - from servers and routers to endpoints - can communicate regardless of the differences in their underlying infrastructures, designs or standards. Supported protocol are: 'tcp' and 'ipc'.

Data-Type: String

Default Value: Its default value is 'tcp'

Usage:

NetGear_Async(protocol=\"ipc\")\n

"},{"location":"gears/netgear_async/params/#pattern","title":"pattern","text":"

This parameter sets the supported messaging pattern(flow of communication) between Server/Client. Messaging patterns are the network-oriented architectural pattern that describes the flow of communication between interconnecting systems. NetGear provides access to ZeroMQ's pre-optimized sockets which enables you to take advantage of these patterns.

Data-Type: Integer

Default Value: Its default value is 0 (i.e zmq.PAIR).

All supported ZMQ patterns for NetGear_Async are:

Usage:

NetGear_Async(pattern=1) # sets zmq.REQ/zmq.REP pattern\n

"},{"location":"gears/netgear_async/params/#receive_mode","title":"receive_mode","text":"

This parameter select the Netgear's Mode of operation. It basically activates Receive Mode(if True) and Send Mode(if False). Furthermore, recv() method will only work when this flag is enabled(i.e. Receive Mode), whereas send() method will only work when this flag is disabled(i.e.Send Mode).

Data-Type: Boolean

Default Value: Its default value is False(i.e. Send Mode is activated by default).

Usage:

NetGear_Async(receive_mode=True) # activates Recieve Mode\n

"},{"location":"gears/netgear_async/params/#timeout","title":"timeout","text":"

In NetGear_Async, the Receiver-end keeps tracks if frames are received from Server-end within this specified timeout value (in seconds), Otherwise TimeoutError will be raised, which helps to close the Receiver-end safely if the Server has lost connection prematurely. This parameter controls that timeout value (i.e. the maximum waiting time (in seconds)) after which Client exit itself with a TimeoutError to save resources. Its minimum value is 0.0 but no max limit.

Data-Type: Float/Integer

Default Value: Its default value is 10.0.

Usage:

NetGear_Async(timeout=5.0) # sets 5secs timeout\n
"},{"location":"gears/netgear_async/params/#options","title":"options","text":"

This parameter provides the flexibility to alter various NetGear_Async API's internal properties and modes.

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

Supported dictionary attributes for NetGear_Async API

"},{"location":"gears/netgear_async/params/#parameters-for-stabilizer-backend","title":"Parameters for Stabilizer Backend","text":"

Enable this backend with stabilize=True in NetGear_Async.

"},{"location":"gears/netgear_async/params/#stabilize","title":"stabilize","text":"

This parameter enable access to Stabilizer Class for stabilizing frames, i.e. can be set to True(to enable) or unset to False(to disable).

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

NetGear_Async(stabilize=True) # enable stablization\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/netgear_async/params/#options_1","title":"options","text":"

This parameter can be used in addition, to pass user-defined parameters supported by Stabilizer Class. These parameters can be formatted as this parameter's attribute.

Supported dictionary attributes for Stabilizer Class are:

"},{"location":"gears/netgear_async/params/#parameters-for-camgear-backend","title":"Parameters for CamGear backend","text":"

Enable this backend with enablePiCamera=False in NetGear_Async. Default is also False.

"},{"location":"gears/netgear_async/params/#source","title":"source","text":"

NetGear_Async API will throw RuntimeError if source provided is invalid.

This parameter defines the source for the input stream.

Data-Type: Based on input.

Default Value: Its default value is 0.

Its valid input can be one of the following:

"},{"location":"gears/netgear_async/params/#stream_mode","title":"stream_mode","text":"

This parameter controls the Stream Mode, .i.e if enabled(stream_mode=True), the CamGear API will interpret the given source input as YouTube URL address.

Due to a FFmpeg bug that causes video to freeze frequently in OpenCV, It is advised to always use GStreamer backend for any livestream videos. Checkout this FAQ for compiling OpenCV with GStreamer support.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

Supported Streaming Websites

The complete list of all supported Streaming Websites URLs can be found here \u27b6

NetGear_Async(source='https://youtu.be/bvetuLwJIkA', stream_mode=True)\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/netgear_async/params/#backend","title":"backend","text":"

This parameter manually selects the backend for OpenCV's VideoCapture class (only if specified).

Data-Type: Integer

Default Value: Its default value is 0

Usage:

All supported backends are listed here \u27b6

Its value can be for e.g. backend = cv2.CAP_DSHOW for selecting Direct Show as backend:

NetGear_Async(source=0, backend = cv2.CAP_DSHOW)\n

"},{"location":"gears/netgear_async/params/#options_2","title":"options","text":"

This parameter provides the ability to alter various Source Tweak Parameters available within OpenCV's VideoCapture API properties.

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

All supported parameters are listed here \u27b6

The desired parameters can be passed to NetGear_Async API by formatting them as this parameter's attributes, as follows:

# formatting parameters as dictionary attributes\noptions = {\"CAP_PROP_FRAME_WIDTH\":320, \"CAP_PROP_FRAME_HEIGHT\":240, \"CAP_PROP_FPS\":60}\n# assigning it\nNetGear_Async(source=0, **options)\n

"},{"location":"gears/netgear_async/params/#parameters-for-pigear-backend","title":"Parameters for PiGear backend","text":"

Enable this backend with enablePiCamera=True in NetGear_Async.

"},{"location":"gears/netgear_async/params/#camera_num","title":"camera_num","text":"

This parameter selects the camera index to be used as the source, allowing you to drive these multiple cameras simultaneously from within a single Python session. Its value can only be zero or greater, otherwise, NetGear_Async API will throw ValueError for any negative value.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

# select Camera Module at index `1`\nNetGear_Async(enablePiCamera=True, camera_num=1)\n

The complete usage example demonstrating the usage of the camera_num parameter is available here \u27b6.

"},{"location":"gears/netgear_async/params/#resolution","title":"resolution","text":"

This parameter controls the resolution - a tuple (i.e. (width,height)) of two values giving the width and height of the output frames.

Make sure both width and height values should be at least 64.

When using the Picamera2 backend, the resolution parameter will be OVERRIDDEN, if the user explicitly defines the output_size property of the sensor configurational parameter.

Data-Type: Tuple

Default Value: Its default value is (640,480).

Usage:

NetGear_Async(enablePiCamera=True, resolution=(1280,720)) # sets 1280x720 resolution\n

"},{"location":"gears/netgear_async/params/#framerate","title":"framerate","text":"

This parameter controls the framerate of the source.

Data-Type: integer/float

Default Value: Its default value is 30.

Usage:

NetGear_Async(enablePiCamera=True, framerate=60) # sets 60fps framerate\n

"},{"location":"gears/netgear_async/params/#options_3","title":"options","text":"

This dictionary parameter in the internal PiGear API backend allows you to control various camera settings for both the picamera2 and legacy picamera backends and some internal API tasks. These settings include:

"},{"location":"gears/netgear_async/params/#a-configurational-camera-parameters","title":"A. Configurational Camera Parameters","text":""},{"location":"gears/netgear_async/params/#b-user-defined-parameters","title":"B. User-defined Parameters","text":""},{"location":"gears/netgear_async/params/#common-parameters","title":"Common Parameters","text":"

These are common parameters that works with every backend in NetGear_Async.

"},{"location":"gears/netgear_async/params/#colorspace","title":"colorspace","text":"

This parameter selects the colorspace of the source stream.

Data-Type: String

Default Value: Its default value is None.

Usage:

All supported colorspace values are given here \u27b6

NetGear_Async(colorspace=\"COLOR_BGR2HSV\")\n

Its complete usage example is given here \u27b6

"},{"location":"gears/netgear_async/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

NetGear_Async(logging=True)\n

"},{"location":"gears/netgear_async/params/#time_delay","title":"time_delay","text":"

This parameter set the time delay (in seconds) before the NetGear_Async API start reading the frames. This delay is only required if the source required some warm-up delay before starting up.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

NetGear_Async(time_delay=1)  # set 1 seconds time delay\n

"},{"location":"gears/netgear_async/usage/","title":"Usage Examples","text":""},{"location":"gears/netgear_async/usage/#netgear_async-api-usage-examples","title":"NetGear_Async API Usage Examples:","text":"

Helpful Tips

After going through following Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/netgear_async/usage/#requirement","title":"Requirement","text":"

NetGear_Async API is the part of asyncio package of VidGear, thereby you need to install VidGear with asyncio support as follows:

pip install vidgear[asyncio]\n

"},{"location":"gears/netgear_async/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with NetGear_Async API:

"},{"location":"gears/netgear_async/usage/#servers-end","title":"Server's End","text":"

Open your favorite terminal and execute the following python code:

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nimport asyncio\n\n# initialize Server with suitable source\nserver = NetGear_Async(source=\"/home/foo/foo1.mp4\").launch()\n\nif __name__ == \"__main__\":\n    # set event loop\n    asyncio.set_event_loop(server.loop)\n    try:\n        # run your main function task until it is complete\n        server.loop.run_until_complete(server.task)\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    finally:\n        # finally close the server\n        server.close()\n
"},{"location":"gears/netgear_async/usage/#clients-end","title":"Client's End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

Client will throw TimeoutError if it fails to connect to the Server in given timeout value!

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nimport cv2, asyncio\n\n# define and launch Client with `receive_mode=True`\nclient = NetGear_Async(receive_mode=True).launch()\n\n# Create a async function where you want to show/manipulate your received frames\nasync def main():\n    # loop over Client's Asynchronous Frame Generator\n    async for frame in client.recv_generator():\n\n        # do something with received frames here\n\n        # Show output window\n        cv2.imshow(\"Output Frame\", frame)\n        key = cv2.waitKey(1) & 0xFF\n\n        # await before continuing\n        await asyncio.sleep(0)\n\nif __name__ == \"__main__\":\n    # Set event loop to client's\n    asyncio.set_event_loop(client.loop)\n    try:\n        # run your main function task until it is complete\n        client.loop.run_until_complete(main())\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n\n    # close all output window\n    cv2.destroyAllWindows()\n    # safely close client\n    client.close()\n

"},{"location":"gears/netgear_async/usage/#using-netgear_async-with-variable-parameters","title":"Using NetGear_Async with Variable Parameters","text":""},{"location":"gears/netgear_async/usage/#clients-end_1","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from the Server) and execute the following python code:

Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

Client will throw TimeoutError if it fails to connect to the Server in given timeout value!

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nimport cv2, asyncio\n\n# define and launch Client with `receive_mode=True`. #change following IP address '192.168.x.xxx' with yours\nclient = NetGear_Async(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=2,\n    receive_mode=True,\n    logging=True,\n).launch()\n\n\n# Create a async function where you want to show/manipulate your received frames\nasync def main():\n    # loop over Client's Asynchronous Frame Generator\n    async for frame in client.recv_generator():\n\n        # do something with received frames here\n\n        # Show output window\n        cv2.imshow(\"Output Frame\", frame)\n        key = cv2.waitKey(1) & 0xFF\n\n        # await before continuing\n        await asyncio.sleep(0)\n\n\nif __name__ == \"__main__\":\n    # Set event loop to client's\n    asyncio.set_event_loop(client.loop)\n    try:\n        # run your main function task until it is complete\n        client.loop.run_until_complete(main())\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n\n    # close all output window\n    cv2.destroyAllWindows()\n    # safely close client\n    client.close()\n
"},{"location":"gears/netgear_async/usage/#servers-end_1","title":"Server's End","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nimport asyncio\n\n# initialize Server with suitable source\nserver = NetGear_Async(\n    source=0,\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=2,\n    logging=True,\n).launch()\n\nif __name__ == \"__main__\":\n    # set event loop\n    asyncio.set_event_loop(server.loop)\n    try:\n        # run your main function task until it is complete\n        server.loop.run_until_complete(server.task)\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    finally:\n        # finally close the server\n        server.close()\n

"},{"location":"gears/netgear_async/usage/#using-netgear_async-with-a-custom-sourceopencv","title":"Using NetGear_Async with a Custom Source(OpenCV)","text":"

NetGear_Async allows you to easily define your own custom Source at Server-end that you want to use to transform your frames before sending them onto the network.

Let's implement a bare-minimum example with a Custom Source using NetGear_Async API and OpenCV:

"},{"location":"gears/netgear_async/usage/#servers-end_2","title":"Server's End","text":"

Open your favorite terminal and execute the following python code:

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import library\nfrom vidgear.gears.asyncio import NetGear_Async\nimport cv2, asyncio\n\n# initialize Server without any source\nserver = NetGear_Async(source=None, logging=True)\n\n# !!! define your own video source here !!!\n# Open any video stream such as live webcam\n# video stream on first index(i.e. 0) device\nstream = cv2.VideoCapture(0)\n\n# Create a async frame generator as custom source\nasync def my_frame_generator():\n\n    # loop over stream until its terminated\n    while True:\n\n        # read frames\n        (grabbed, frame) = stream.read()\n\n        # check if frame empty\n        if not grabbed:\n            break\n\n        # do something with the frame to be sent here\n\n        # yield frame\n        yield frame\n        # sleep for sometime\n        await asyncio.sleep(0)\n\n\nif __name__ == \"__main__\":\n    # set event loop\n    asyncio.set_event_loop(server.loop)\n    # Add your custom source generator to Server configuration\n    server.config[\"generator\"] = my_frame_generator()\n    # Launch the Server\n    server.launch()\n    try:\n        # run your main function task until it is complete\n        server.loop.run_until_complete(server.task)\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    finally:\n        # close stream\n        stream.release()\n        # finally close the server\n        server.close()\n
"},{"location":"gears/netgear_async/usage/#clients-end_2","title":"Client's End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

Client will throw TimeoutError if it fails to connect to the Server in given timeout value!

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nimport cv2, asyncio\n\n# define and launch Client with `receive_mode=True`\nclient = NetGear_Async(receive_mode=True, logging=True).launch()\n\n\n# Create a async function where you want to show/manipulate your received frames\nasync def main():\n    # loop over Client's Asynchronous Frame Generator\n    async for frame in client.recv_generator():\n\n        # {do something with received frames here}\n\n        # Show output window\n        cv2.imshow(\"Output Frame\", frame)\n        key = cv2.waitKey(1) & 0xFF\n\n        # await before continuing\n        await asyncio.sleep(0)\n\n\nif __name__ == \"__main__\":\n    # Set event loop to client's\n    asyncio.set_event_loop(client.loop)\n    try:\n        # run your main function task until it is complete\n        client.loop.run_until_complete(main())\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n\n    # close all output window\n    cv2.destroyAllWindows()\n    # safely close client\n    client.close()\n

"},{"location":"gears/netgear_async/usage/#using-netgear_async-with-other-gears","title":"Using NetGear_Async with Other Gears","text":"

NetGear_Async can be used with any other Gears without any compatibility issues.

Let's implement a bare-minimum example where we are sending Stabilized frames from Server-end and saving them at Client's end with WriteGear as follows:

"},{"location":"gears/netgear_async/usage/#servers-end_3","title":"Server's End","text":"

Open your favorite terminal and execute the following python code:

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nimport asyncio\n\n# initialize Server with suitable source and enable stabilization\nserver = NetGear_Async(\n    source=\"/home/foo/foo1.mp4\", stabilize=True, logging=True\n).launch()\n\nif __name__ == \"__main__\":\n    # set event loop\n    asyncio.set_event_loop(server.loop)\n    try:\n        # run your main function task until it is complete\n        server.loop.run_until_complete(server.task)\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    finally:\n        # finally close the server\n        server.close()\n
"},{"location":"gears/netgear_async/usage/#clients-end_3","title":"Client's End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

Client will throw TimeoutError if it fails to connect to the Server in given timeout value!

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nfrom vidgear.gears import WriteGear\nimport cv2, asyncio\n\n# define and launch Client with `receive_mode=True`\nclient = NetGear_Async(receive_mode=True).launch()\n\n# Define writer with output filename 'Output.mp4'\nwriter = WriteGear(output=\"Output.mp4\", logging=True)\n\n# Create a async function where you want to show/manipulate your received frames\nasync def main():\n    # loop over Client's Asynchronous Frame Generator\n    async for frame in client.recv_generator():\n\n        # {do something with received frames here}\n\n        # write a modified frame to writer\n        writer.write(frame)\n\n        # Show output window\n        cv2.imshow(\"Output Frame\", frame)\n        key = cv2.waitKey(1) & 0xFF\n\n        # await before continuing\n        await asyncio.sleep(0)\n\n\nif __name__ == \"__main__\":\n    # Set event loop to client's\n    asyncio.set_event_loop(client.loop)\n    try:\n        # run your main function task until it is complete\n        client.loop.run_until_complete(main())\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n\n    # close all output window\n    cv2.destroyAllWindows()\n    # safely close client\n    client.close()\n    # safely close writer\n    writer.close()\n

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/","title":"Bidirectional Mode","text":""},{"location":"gears/netgear_async/advanced/bidirectional_mode/#bidirectional-mode-for-netgear_async-api","title":"Bidirectional Mode for NetGear_Async API","text":"NetGear_Async's Bidirectional Mode"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#overview","title":"Overview","text":"New in v0.2.2

This document was added in v0.2.2.

Bidirectional Mode enables seamless support for Bidirectional data transmission between Client and Sender along with video-frames through its synchronous messaging patterns such as zmq.PAIR (ZMQ Pair Pattern) & zmq.REQ/zmq.REP (ZMQ Request/Reply Pattern) in NetGear_Async API.

In Bidirectional Mode, we utilizes the NetGear_Async API's transceive_data method for transmitting data (at Client's end) and receiving data (in Server's end) all while transferring frames in real-time.

This mode can be easily activated in NetGear_Async through bidirectional_mode attribute of its options dictionary parameter during initialization.

Important

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#exclusive-method-and-parameter","title":"Exclusive Method and Parameter","text":"

To send data bidirectionally, NetGear_Async API provides following exclusive method and parameter:

transceive_data only works when Bidirectional Mode is enabled.

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#usage-examples","title":"Usage Examples","text":"

For Bidirectional Mode, NetGear_Async must need User-defined Custom Source at its Server end otherwise it will throw ValueError.

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#bare-minimum-usage-with-opencv","title":"Bare-Minimum Usage with OpenCV","text":"

Following is the bare-minimum code you need to get started with Bidirectional Mode over Custom Source Server built using OpenCV and NetGear_Async API:

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#server-end","title":"Server End","text":"

Open your favorite terminal and execute the following python code:

You can terminate both sides anytime by pressing Ctrl+C on your keyboard!

# import library\nfrom vidgear.gears.asyncio import NetGear_Async\nimport cv2, asyncio\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# initialize Server without any source\nserver = NetGear_Async(source=None, logging=True, **options)\n\n# Create a async frame generator as custom source\nasync def my_frame_generator():\n\n    # !!! define your own video source here !!!\n    # Open any valid video stream(for e.g `foo.mp4` file)\n    stream = cv2.VideoCapture(\"foo.mp4\")\n\n    # loop over stream until its terminated\n    while True:\n        # read frames\n        (grabbed, frame) = stream.read()\n\n        # check for empty frame\n        if not grabbed:\n            break\n\n        # {do something with the frame to be sent here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # receive data from Client\n        recv_data = await server.transceive_data()\n\n        # print data just received from Client\n        if not (recv_data is None):\n            print(recv_data)\n\n        # send our frame & data\n        yield (target_data, frame) # (1)\n\n        # sleep for sometime\n        await asyncio.sleep(0)\n\n    # safely close video stream\n    stream.release()\n\n\nif __name__ == \"__main__\":\n    # set event loop\n    asyncio.set_event_loop(server.loop)\n    # Add your custom source generator to Server configuration\n    server.config[\"generator\"] = my_frame_generator()\n    # Launch the Server\n    server.launch()\n    try:\n        # run your main function task until it is complete\n        server.loop.run_until_complete(server.task)\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    finally:\n        # finally close the server\n        server.close()\n
  1. Everything except numpy.ndarray datatype data is accepted in target_data.
"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#client-end","title":"Client End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nimport cv2, asyncio\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# define and launch Client with `receive_mode=True`\nclient = NetGear_Async(receive_mode=True, logging=True, **options).launch()\n\n\n# Create a async function where you want to show/manipulate your received frames\nasync def main():\n    # loop over Client's Asynchronous Frame Generator\n    async for (data, frame) in client.recv_generator():\n\n        # do something with receive data from server\n        if not (data is None):\n            # let's print it\n            print(data)\n\n        # {do something with received frames here}\n\n        # Show output window(comment these lines if not required)\n        cv2.imshow(\"Output Frame\", frame)\n        cv2.waitKey(1) & 0xFF\n\n        # prepare data to be sent\n        target_data = \"Hi, I am a Client here.\"\n        # send our data to server\n        await client.transceive_data(data=target_data)\n\n        # await before continuing\n        await asyncio.sleep(0)\n\n\nif __name__ == \"__main__\":\n    # Set event loop to client's\n    asyncio.set_event_loop(client.loop)\n    try:\n        # run your main function task until it is complete\n        client.loop.run_until_complete(main())\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n\n    # close all output window\n    cv2.destroyAllWindows()\n\n    # safely close client\n    client.close()\n

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#using-bidirectional-mode-with-variable-parameters","title":"Using Bidirectional Mode with Variable Parameters","text":""},{"location":"gears/netgear_async/advanced/bidirectional_mode/#clients-end","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from the Server) and execute the following python code:

Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nimport cv2, asyncio\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# Define NetGear_Async Client at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear_Async(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# Create a async function where you want to show/manipulate your received frames\nasync def main():\n    # loop over Client's Asynchronous Frame Generator\n    async for (data, frame) in client.recv_generator():\n\n        # do something with receive data from server\n        if not (data is None):\n            # let's print it\n            print(data)\n\n        # {do something with received frames here}\n\n        # Show output window(comment these lines if not required)\n        cv2.imshow(\"Output Frame\", frame)\n        cv2.waitKey(1) & 0xFF\n\n        # prepare data to be sent\n        target_data = \"Hi, I am a Client here.\"\n        # send our data to server\n        await client.transceive_data(data=target_data)\n\n        # await before continuing\n        await asyncio.sleep(0)\n\n\nif __name__ == \"__main__\":\n    # Set event loop to client's\n    asyncio.set_event_loop(client.loop)\n    try:\n        # run your main function task until it is complete\n        client.loop.run_until_complete(main())\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n\n    # close all output window\n    cv2.destroyAllWindows()\n\n    # safely close client\n    client.close()\n

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#server-end_1","title":"Server End","text":"

Now, Open the terminal on another Server System (a Raspberry Pi with Camera Module), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this backend, otherwise nothing will work.

New Picamera2 backendLegacy Picamera backend
# import libs\nfrom vidgear.gears.asyncio import NetGear_Async\nfrom vidgear.gears import VideoGear\nfrom libcamera import Transform\nimport cv2, asyncio\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# initialize Server without any source at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear_Async(\n    source=None,\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# Create a async frame generator as custom source\nasync def my_frame_generator():\n\n    # !!! define your own video source below !!!\n\n    # define various Picamera2 tweak parameters\n    options = {\n        \"queue\": True,\n        \"buffer_count\": 4,\n        \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n        \"transform\": Transform(hflip=1),\n        \"auto_align_output_config\": True,  # auto-align camera configuration\n    }\n\n    # open pi video stream with defined parameters\n    stream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n    # loop over stream until its terminated\n    while True:\n        # read frames\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame to be sent here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # receive data from Client\n        recv_data = await server.transceive_data()\n\n        # print data just received from Client\n        if not (recv_data is None):\n            print(recv_data)\n\n        # send our frame & data\n        yield (target_data, frame) # (1)\n\n        # sleep for sometime\n        await asyncio.sleep(0)\n\n    # safely close video stream\n    stream.stop()\n\n\nif __name__ == \"__main__\":\n    # set event loop\n    asyncio.set_event_loop(server.loop)\n    # Add your custom source generator to Server configuration\n    server.config[\"generator\"] = my_frame_generator()\n    # Launch the Server\n    server.launch()\n    try:\n        # run your main function task until it is complete\n        server.loop.run_until_complete(server.task)\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    finally:\n        # finally close the server\n        server.close()\n
  1. Everything except numpy.ndarray datatype data is accepted in target_data.
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import library\nfrom vidgear.gears.asyncio import NetGear_Async\nfrom vidgear.gears import VideoGear\nimport cv2, asyncio\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# initialize Server without any source at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear_Async(\n    source=None,\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# Create a async frame generator as custom source\nasync def my_frame_generator():\n\n    # !!! define your own video source below !!!\n\n    # define various Picamera tweak parameters\n    options = {\n        \"hflip\": True,\n        \"exposure_mode\": \"auto\",\n        \"iso\": 800,\n        \"exposure_compensation\": 15,\n        \"awb_mode\": \"horizon\",\n        \"sensor_mode\": 0,\n    }\n\n    # open pi video stream with defined parameters\n    stream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n    # loop over stream until its terminated\n    while True:\n        # read frames\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame to be sent here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # receive data from Client\n        recv_data = await server.transceive_data()\n\n        # print data just received from Client\n        if not (recv_data is None):\n            print(recv_data)\n\n        # send our frame & data\n        yield (target_data, frame) # (1)\n\n        # sleep for sometime\n        await asyncio.sleep(0)\n\n    # safely close video stream\n    stream.stop()\n\n\nif __name__ == \"__main__\":\n    # set event loop\n    asyncio.set_event_loop(server.loop)\n    # Add your custom source generator to Server configuration\n    server.config[\"generator\"] = my_frame_generator()\n    # Launch the Server\n    server.launch()\n    try:\n        # run your main function task until it is complete\n        server.loop.run_until_complete(server.task)\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    finally:\n        # finally close the server\n        server.close()\n
  1. Everything except numpy.ndarray datatype data is accepted in target_data.

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#using-bidirectional-mode-for-video-frames-transfer","title":"Using Bidirectional Mode for Video-Frames Transfer","text":"

In this example we are going to implement a bare-minimum example, where we will be sending video-frames (3-Dimensional numpy arrays) of the same Video bidirectionally at the same time, for testing the real-time performance and synchronization between the Server and the Client using this(Bidirectional) Mode.

This feature is great for building applications like Real-Time Video Chat.

We're also using reducer() method for reducing frame-size on-the-go for additional performance.

Remember, Sending large HQ video-frames may required more network bandwidth and packet size which may lead to video latency!

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#server-end_2","title":"Server End","text":"

Open your favorite terminal and execute the following python code:

You can terminate both side anytime by pressing Ctrl+C on your keyboard!

Server end can only send numpy.ndarray datatype as frame but not as data.

# import library\nfrom vidgear.gears.asyncio import NetGear_Async\nfrom vidgear.gears.asyncio.helper import reducer\nimport cv2, asyncio\nimport numpy as np\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# Define NetGear Server without any source and with defined parameters\nserver = NetGear_Async(source=None, pattern=1, logging=True, **options)\n\n# Create a async frame generator as custom source\nasync def my_frame_generator():\n    # !!! define your own video source here !!!\n    # Open any valid video stream(for e.g `foo.mp4` file)\n    stream = cv2.VideoCapture(\"foo.mp4\")\n    # loop over stream until its terminated\n    while True:\n\n        # read frames\n        (grabbed, frame) = stream.read()\n\n        # check for empty frame\n        if not grabbed:\n            break\n\n        # reducer frames size if you want more performance, otherwise comment this line\n        frame = await reducer(frame, percentage=30)  # reduce frame by 30%\n\n        # {do something with the frame to be sent here}\n\n        # send frame & data and also receive data from Client\n        recv_data = await server.transceive_data()\n\n        # receive data from Client\n        if not (recv_data is None):\n            # check data is a numpy frame\n            if isinstance(recv_data, np.ndarray):\n\n                # {do something with received numpy frame here}\n\n                # Let's show it on output window\n                cv2.imshow(\"Received Frame\", recv_data)\n                cv2.waitKey(1) & 0xFF\n            else:\n                # otherwise just print data\n                print(recv_data)\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # send our frame & data to client\n        yield (target_data, frame) # (1)\n\n        # sleep for sometime\n        await asyncio.sleep(0)\n\n    # safely close video stream\n    stream.release()\n\n\nif __name__ == \"__main__\":\n    # set event loop\n    asyncio.set_event_loop(server.loop)\n    # Add your custom source generator to Server configuration\n    server.config[\"generator\"] = my_frame_generator()\n    # Launch the Server\n    server.launch()\n    try:\n        # run your main function task until it is complete\n        server.loop.run_until_complete(server.task)\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    finally:\n        # finally close the server\n        server.close()\n
  1. Everything except numpy.ndarray datatype data is accepted in target_data.

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#client-end_1","title":"Client End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nfrom vidgear.gears.asyncio.helper import reducer\nimport cv2, asyncio\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# define and launch Client with `receive_mode=True`\nclient = NetGear_Async(pattern=1, receive_mode=True, logging=True, **options).launch()\n\n# Create a async function where you want to show/manipulate your received frames\nasync def main():\n    # !!! define your own video source here !!!\n    # again open the same video stream for comparison\n    stream = cv2.VideoCapture(\"foo.mp4\")\n    # loop over Client's Asynchronous Frame Generator\n    async for (server_data, frame) in client.recv_generator():\n\n        # check for server data\n        if not (server_data is None):\n\n            # {do something with the server data here}\n\n            # lets print extracted server data\n            print(server_data)\n\n        # {do something with received frames here}\n\n        # Show output window\n        cv2.imshow(\"Output Frame\", frame)\n        key = cv2.waitKey(1) & 0xFF\n\n        # read frame target data from stream to be sent to server\n        (grabbed, target_data) = stream.read()\n        # check for frame\n        if grabbed:\n            # reducer frames size if you want more performance, otherwise comment this line\n            target_data = await reducer(\n                target_data, percentage=30\n            )  # reduce frame by 30%\n            # send our frame data\n            await client.transceive_data(data=target_data)\n\n        # await before continuing\n        await asyncio.sleep(0)\n\n    # safely close video stream\n    stream.release()\n\n\nif __name__ == \"__main__\":\n    # Set event loop to client's\n    asyncio.set_event_loop(client.loop)\n    try:\n        # run your main function task until it is complete\n        client.loop.run_until_complete(main())\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    # close all output window\n    cv2.destroyAllWindows()\n    # safely close client\n    client.close()\n

  1. Additional data of numpy.ndarray datatype is ONLY SUPPORTED at Client's end with transceive_data method using its data parameter. Whereas Server end can only send numpy.ndarray datatype as frame but not as data.

    \u21a9\u21a9

"},{"location":"gears/pigear/overview/","title":"Overview","text":""},{"location":"gears/pigear/overview/#pigear-api","title":"PiGear API","text":"Raspberry Pi Camera Module"},{"location":"gears/pigear/overview/#overview","title":"Overview","text":"

PiGear is a specialized API similar to the CamGear API but optimized for Raspberry Pi Boards, offering comprehensive support for camera modules (e.g., OmniVision OV5647, Sony IMX219), along with limited compatibility for USB cameras.

PiGear implements a seamless and robust wrapper around the picamera2 python library, simplifying integration with minimal code changes and ensuring a smooth transition for developers already familiar with the Picamera2 API. PiGear leverages the libcamera API under the hood with multi-threading, providing high-performance , enhanced control and functionality for Raspberry Pi camera modules.

PiGear handles common configuration parameters and non-standard settings for various camera types, simplifying the integration process. PiGear currently supports PiCamera2 API parameters such as sensor, controls, transform, and format etc., with internal type and sanity checks for robust performance.

While primarily focused on Raspberry Pi camera modules, PiGear also provides basic functionality for USB webcams only with Picamera2 API, along with the ability to accurately differentiate between USB and Raspberry Pi cameras using metadata.

Backward compatibility with picamera library

PiGear seamlessly switches to the legacy picamera library if the picamera2 library is unavailable, ensuring seamless backward compatibility. For this, PiGear also provides a flexible multi-threaded framework around complete picamera API, allowing developers to effortlessly exploit a wide range of parameters, such as brightness, saturation, sensor_mode, iso, exposure, and more.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

Furthermore, PiGear supports the use of multiple camera modules, including those found on Raspberry Pi Compute Module IO boards and USB cameras (only with Picamera2 API).

Threaded Internal Timer

PiGear ensures proper resource release during the termination of the API, preventing potential issues or resource leaks. PiGear API internally implements a Threaded Internal Timer that silently keeps active track of any frozen-threads or hardware-failures and exits safely if any do occur. This means that if you're running the PiGear API in your script and someone accidentally pulls the Camera-Module cable out, instead of going into a possible kernel panic, the API will exit safely to save resources.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this API, otherwise nothing will work.

Helpful Tips

"},{"location":"gears/pigear/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through PiGear Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/pigear/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/pigear/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/pigear/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/pigear/params/","title":"Parameters","text":""},{"location":"gears/pigear/params/#pigear-api-parameters","title":"PiGear API Parameters","text":""},{"location":"gears/pigear/params/#camera_num","title":"camera_num","text":"

This parameter selects the camera index to be used as the source, allowing you to drive these multiple cameras simultaneously from within a single Python session. Its value can only be zero or greater, otherwise, PiGear API will throw ValueError for any negative value.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

# select Camera Module at index `1`\nPiGear(camera_num=1)\n

The complete usage example demonstrating the usage of the camera_num parameter is available here \u27b6.

"},{"location":"gears/pigear/params/#resolution","title":"resolution","text":"

This parameter controls the resolution - a tuple (i.e. (width,height)) of two values giving the width and height of the output frames.

Make sure both width and height values should be at least 64.

When using the Picamera2 backend, the resolution parameter will be OVERRIDDEN, if the user explicitly defines the output_size property of the sensor configurational parameter in PiGear API.

Data-Type: Tuple

Default Value: Its default value is (640,480).

Usage:

PiGear(resolution=(1280,720)) # sets 1280x720 resolution\n

"},{"location":"gears/pigear/params/#framerate","title":"framerate","text":"

This parameter controls the framerate of the source.

Data-Type: integer/float

Default Value: Its default value is 30.

Usage:

PiGear(framerate=60) # sets 60fps framerate\n

"},{"location":"gears/pigear/params/#colorspace","title":"colorspace","text":"

This parameter controls the colorspace of the output frames.

With the Picamera2 backend, you can also define a custom format (format of output frame pixels) in PiGear API. Checkout this bonus example \u27b6

Data-Type: String

Default Value: Its default value is None (i.e. Default BGR colorspace).

Usage:

All supported colorspace values are described here \u27b6

PiGear(colorspace=\"COLOR_BGR2HSV\")\n

Its complete usage example is given here \u27b6

"},{"location":"gears/pigear/params/#options","title":"options","text":"

This dictionary parameter in the PiGear API allows you to control various camera settings for both the picamera2 and legacy picamera backends and some internal API tasks. These settings include:

"},{"location":"gears/pigear/params/#a-configurational-camera-parameters","title":"A. Configurational Camera Parameters","text":""},{"location":"gears/pigear/params/#b-user-defined-parameters","title":"B. User-defined Parameters","text":"

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

The complete usage example demonstrating the usage of the options parameter is available here \u27b6.

You can format these user-defined and configurational parameters as attributes of this options dictionary parameter as follows:

New Picamera2 backendLegacy Picamera backend
# formulate various Picamera2 API parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"exposure_compensation\": 15,\n    \"sensor\": {\"output_size\": (480, 320)},  # !!! will override `resolution` !!!\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n
# formulate various Picamera API parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n

"},{"location":"gears/pigear/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

PiGear(logging=True)\n

"},{"location":"gears/pigear/params/#time_delay","title":"time_delay","text":"

This parameter set the time delay (in seconds) before the PiGear API start reading the frames. This delay is only required if the source required some warm-up delay before starting up.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

PiGear(time_delay=1)  # set 1 seconds time delay\n

"},{"location":"gears/pigear/usage/","title":"Usage Examples","text":""},{"location":"gears/pigear/usage/#pigear-api-usage-examples","title":"PiGear API Usage Examples:","text":"

PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this API, otherwise nothing will work.

After going through following Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/pigear/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with PiGear API:

Under the hood, PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, PiGear API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

Disabling common libcamera API messages in silent mode.

The picamera2 backend can be a bit verbose with logging messages from the underlying libcamera library, even when logging is disabled (logging=False) in the PiGear API.

Linux Windows (Powershell) MacOS
export LIBCAMERA_LOG_LEVELS=2\n
$Env:LIBCAMERA_LOG_LEVELS=2\n
export LIBCAMERA_LOG_LEVELS=2\n
# import required libraries\nfrom vidgear.gears import PiGear\nimport cv2\n\n# open stream with default parameters\nstream = PiGear().start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/pigear/usage/#using-pigear-with-variable-camera-properties","title":"Using PiGear with Variable Camera Properties","text":"New Picamera2 backendLegacy Picamera backend

PiGear provides a user-friendly interface for the underlying picamera2 library, offering access to almost all of its important configurational parameters. It simplifies configuration for developers with even basic knowledge of Raspberry Pi camera modules, allowing them to easily configure and control the camera functionality with just a few lines of code.

This example doc showcases the capabilities of PiGear and demonstrates how it simplifies camera configuration with Picamera2 API backend.

All supported Picamera2 Library Configurational Parameters [IMPORTANT]

Following are the list of Picamera2 parameters, i.e. if supported, can be applied to the source stream in PiGear API through its options dictionary parameter by formatting them as its attributes.

Few Important points Parameters Datatype Description Supported Supported on USB Cameras Remarks buffer_count int, >=1 number of sets of buffers to allocate for the camera system Read Docs here \u27b6 queue bool whether the system is allowed to queue up a frame ready for a capture request Read Docs here \u27b6 controls dict specify a set of runtime controls that can be regarded as part of the camera configuration Read Docs here \u27b6 sensor dict allow to select a particular mode of operation for the sensor Read Docs here \u27b6 format str Pixel formats Read Docs here \u27b6 and see Bonus example \u27b6 transform Transform1 The 2D plane transform that is applied to all images from all the configured streams. Read Docs here \u27b6 colour_space colour space of the output images Handled by colorspace parameter of PiGear API size A tuple of two values giving the width and height of the output image. (Both numbers should be no less than 64) Handled by resolution parameter of PiGear API display name of the stream that will be displayed in the preview window. Not-Required encode name of the stream that will be used for video recording. Not-Required Limited support for USB Cameras

This example also works with USB Cameras, However:

Enabling verbose logs for backend PiCamera2 Library

The PiGear API allows you to enable more detailed logging from the picamera2 backend library using the enable_verbose_logs user-defined optional parameter attribute. This can be used in conjunction with enabling general logging (logging=True) in the PiGear API for even more granular control over logging output.

PiGear also support changing parameter at runtime. Checkout this bonus example here \u27b6

# import required libraries\nfrom vidgear.gears import PiGear\nfrom libcamera import Transform\nimport cv2\n\n# formulate various Picamera2 API \n# configurational parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"sensor\": {\"output_size\": (480, 320)},  # !!! will override `resolution` !!!\n    \"auto_align_output_size\": True,  # auto-align output size\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

PiGear API switches to the legacy picamerabackend if the picamera2 library is unavailable.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

PiGear also supports almost every parameter available within picamera python library. These parameters can be easily applied to the source stream in PiGear API through its options dictionary parameter by formatting them as its attributes. The complete usage example is as follows:

All supported parameters are listed in PiCamera Docs \u27b6

PiGear also support changing parameter at runtime. Checkout this bonus example here \u27b6

# import required libraries\nfrom vidgear.gears import PiGear\nimport cv2\n\n# formulate various Picamera API \n# configurational parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/pigear/usage/#using-pigear-with-direct-colorspace-manipulation","title":"Using PiGear with Direct Colorspace Manipulation","text":"

PiGear API also supports Direct Colorspace Manipulation, which is ideal for changing source colorspace on the run.

A more detailed information on colorspace manipulation can be found here \u27b6

In following example code, we will start with HSV as source colorspace, and then we will switch to GRAY colorspace when W key is pressed, and then LAB colorspace when E key is pressed, finally default colorspace (i.e. BGR) when S key is pressed. Also, quit when Q key is pressed:

Any incorrect or None-Type value will immediately revert the colorspace to default (i.e. BGR).

# import required libraries\nfrom vidgear.gears import PiGear\nimport cv2\n\n# open pi video stream with defined parameters and change colorspace to `HSV`\nstream = PiGear(\n    resolution=(640, 480),\n    framerate=60,\n    colorspace=\"COLOR_BGR2HSV\",\n    logging=True\n).start()\n\n\n# loop over\nwhile True:\n\n    # read HSV frames\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the HSV frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for key if pressed\n    key = cv2.waitKey(1) & 0xFF\n\n    # check if 'w' key is pressed\n    if key == ord(\"w\"):\n        # directly change colorspace at any instant\n        stream.color_space = cv2.COLOR_BGR2GRAY  # Now colorspace is GRAY\n\n    # check for 'e' key is pressed\n    if key == ord(\"e\"):\n        stream.color_space = cv2.COLOR_BGR2LAB  # Now colorspace is CieLAB\n\n    # check for 's' key is pressed\n    if key == ord(\"s\"):\n        stream.color_space = None  # Now colorspace is default(ie BGR)\n\n    # check for 'q' key is pressed\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/pigear/usage/#using-pigear-with-writegear-api","title":"Using PiGear with WriteGear API","text":"

PiGear can be easily used with WriteGear API directly without any compatibility issues. The suitable example is as follows:

New Picamera2 backendLegacy Picamera backend
# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import WriteGear\nfrom libcamera import Transform\nimport cv2\n\n# formulate various Picamera2 API \n# configurational parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"sensor\": {\"output_size\": (480, 320)},  # will override `resolution`\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# define suitable (Codec,CRF,preset) FFmpeg parameters for writer\noutput_params = {\"-vcodec\": \"libx264\", \"-crf\": 0, \"-preset\": \"fast\"}\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n    # lets convert frame to gray for this example\n    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n\n    # write gray frame to writer\n    writer.write(gray)\n\n    # Show output window\n    cv2.imshow(\"Output Gray Frame\", gray)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n
PiGear API switches to the legacy picamerabackend if the picamera2 library is unavailable.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# formulate various Picamera API \n# configurational parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# define suitable (Codec,CRF,preset) FFmpeg parameters for writer\noutput_params = {\"-vcodec\": \"libx264\", \"-crf\": 0, \"-preset\": \"fast\"}\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n    # lets convert frame to gray for this example\n    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n\n    # write gray frame to writer\n    writer.write(gray)\n\n   # Show output window\n    cv2.imshow(\"Output Gray Frame\", gray)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

  1. A custom libcamera API class. Must be imported as from libcamera import Transform.\u00a0\u21a9

"},{"location":"gears/screengear/overview/","title":"Overview","text":""},{"location":"gears/screengear/overview/#screengear-api","title":"ScreenGear API","text":"ScreenGear API in action"},{"location":"gears/screengear/overview/#overview","title":"Overview","text":"

ScreenGear is designed exclusively for targeting rapid Screencasting Capabilities, which means it can grab frames from your monitor in real-time, either by defining an area on the computer screen or full-screen, at the expense of inconsiderable latency. ScreenGear also seamlessly support frame capturing from multiple monitors as well as supports multiple backends.

ScreenGear API implements a multi-threaded wrapper around dxcam, pyscreenshot & python-mss python library, and also flexibly supports its internal parameter.

Helpful Tips

"},{"location":"gears/screengear/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through ScreenGear Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/screengear/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/screengear/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/screengear/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/screengear/params/","title":"Parameters","text":""},{"location":"gears/screengear/params/#screengear-api-parameters","title":"ScreenGear API Parameters","text":""},{"location":"gears/screengear/params/#monitor","title":"monitor","text":"

This parameter enforces dxcam (if installed) and mss (otherwise) usage, and it is suitable for selecting index of a specific screen/monitor device (from where you want retrieve frames) in multi-monitor setup. For example, its value can be assign to 2, to fetch frames from a secondary monitor screen.

Implication of using monitor parameter

Any value on monitor parameter other than None in ScreenGear API:

Data-Type: Integer, Tuple (only if dxcam backend on Windows)

Default Value: Its default value is None (i.e. disabled by default).

Usage:

With dxcam on Windows With mss backend Using GPU acceleration on Windows

With dxcam library backend, you can also assign which GPU devices ids to use along with monitor device ids as tuple (monitor_idx, gpu_idx), as follows:

# open video stream with defined parameters with \n# monitor at index `1` and GPU at index `0`.\nstream = ScreenGear(monitor=(1,0), logging=True).start()\n

Getting a complete list of monitor devices and GPUs

To get a complete list of monitor devices and outputs(GPUs), you can use dxcam library itself:

>>> import dxcam\n>>> dxcam.device_info()\n'Device[0]:<Device Name:NVIDIA GeForce RTX 3090 Dedicated VRAM:24348Mb VendorId:4318>\\n'\n>>> dxcam.output_info()\n'Device[0] Output[0]: Res:(1920, 1080) Rot:0 Primary:True\\nDevice[0] Output[1]: Res:(1920, 1080) Rot:0 Primary:False\\n'\n

# open video stream with defined parameters \n# with monitor at index `1` selected\nScreenGear(monitor=1)\n

With mss library backend, You can also assign monitor value to -1 to fetch frames from all connected multiple monitor screens with mss backend.

With mss library backend, API will output BGRA colorspace frames instead of default BGR.

# open video stream with defined parameters \n# with monitor at index `1` selected\nScreenGear(monitor=1)\n

"},{"location":"gears/screengear/params/#backend","title":"backend","text":"

This parameter enables pyscreenshot usage and select suitable backend for extracting frames in ScreenGear. The user have the authority of selecting suitable backend which generates best performance as well as the most compatible with their machines. The possible values are: dxcam (Windows only), pil, mss, scrot, maim, imagemagick, pyqt5, pyqt, pyside2, pyside, wx, pygdk3, mac_screencapture, mac_quartz, gnome_dbus, gnome-screenshot, kwin_dbus.

Performance Benchmarking of all backend can be found here \u27b6 and here \u27b6

Remember to install backend library and all of its dependencies you're planning to use with ScreenGear API.

Any value on monitor parameter will disable the backend parameter. You cannot use both parameters at same time.

Backend defaults to dxcam library on Windows (if installed), and pyscreenshot otherwise.

Data-Type: String

Default Value: Its default value is \"\" (i.e. default backend).

Usage:

ScreenGear(backend=\"pil\") # to enforce `pil` as backend for extracting frames.\n

"},{"location":"gears/screengear/params/#colorspace","title":"colorspace","text":"

This parameter selects the colorspace of the source stream.

Data-Type: String

Default Value: Its default value is None.

Usage:

All supported colorspace values are given here \u27b6.

ScreenGear(colorspace=\"COLOR_BGR2HSV\")\n

Its complete usage example is given here \u27b6

"},{"location":"gears/screengear/params/#options","title":"options","text":"

This parameter provides the flexibility to manually set the dimensions of capture screen area.

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, defined below:

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

The desired dimensional coordinates parameters can be passed to ScreenGear API by formatting them as attributes, as follows:

# formatting dimensional parameters as dictionary attributes\noptions = {'top': 40, 'left': 0, 'width': 100, 'height': 100}\n# assigning it\nScreenGear(**options)\n

"},{"location":"gears/screengear/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

ScreenGear(logging=True)\n

"},{"location":"gears/screengear/usage/","title":"Usage Examples","text":""},{"location":"gears/screengear/usage/#screengear-api-usage-examples","title":"ScreenGear API Usage Examples:","text":"

After going through ScreenGear Usage Examples, Checkout more of its advanced configurations here \u27b6

Recommended: Install DXcam library on Windows Machines

On Windows Machines, if installed, ScreenGear API uses dxcam backend machines for higher FPS performance. Thereby, it is highly recommended to install it via pip as follows:

pip install dxcam\n

"},{"location":"gears/screengear/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with ScreenGear API:

# import required libraries\nfrom vidgear.gears import ScreenGear\nimport cv2\n\n# open video stream with default parameters\nstream = ScreenGear().start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/screengear/usage/#using-screengear-with-variable-screen-dimensions","title":"Using ScreenGear with Variable Screen Dimensions","text":"

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 dictionary parameter by formatting them as its attributes.

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, defined below:

The complete usage example is as follows:

# import required libraries\nfrom vidgear.gears import ScreenGear\nimport cv2\n\n# define dimensions of screen w.r.t to given monitor to be captured\noptions = {\"top\": 40, \"left\": 0, \"width\": 100, \"height\": 100}\n\n# open video stream with defined parameters\nstream = ScreenGear(logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/screengear/usage/#using-screengear-with-multiple-screens","title":"Using ScreenGear with Multiple Screens","text":"

ScreenGear API provides us the flexibility to select any connected display for fetching frames, with its monitor parameter:

Implication of using monitor parameter

Any value on monitor parameter other than None in ScreenGear API:

With dxcam on Windows With mss backend Using GPU acceleration on Windows

With dxcam library backend, you can also assign which GPU devices ids to use along with monitor device ids as tuple (monitor_idx, gpu_idx), as follows:

# open video stream with defined parameters with \n# monitor at index `1` and GPU at index `0`.\nstream = ScreenGear(monitor=(1,0), logging=True).start()\n

Getting a complete list of monitor devices and GPUs

To get a complete list of monitor devices and outputs(GPUs), you can use dxcam library itself:

>>> import dxcam\n>>> dxcam.device_info()\n'Device[0]:<Device Name:NVIDIA GeForce RTX 3090 Dedicated VRAM:24348Mb VendorId:4318>\\n'\n>>> dxcam.output_info()\n'Device[0] Output[0]: Res:(1920, 1080) Rot:0 Primary:True\\nDevice[0] Output[1]: Res:(1920, 1080) Rot:0 Primary:False\\n'\n

# import required libraries\nfrom vidgear.gears import ScreenGear\nimport cv2\n\n# open video stream with defined parameters with monitor at index `1` selected\nstream = ScreenGear(monitor=1, logging=True).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

With mss library backend, You can also assign monitor value to -1 to fetch frames from all connected multiple monitor screens with mss backend.

With mss library backend, API will output BGRA colorspace frames instead of default BGR.

# import required libraries\nfrom vidgear.gears import ScreenGear\nimport cv2\n\n# open video stream with defined parameters with monitor at index `1` selected\nstream = ScreenGear(monitor=1, logging=True).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/screengear/usage/#using-screengear-with-variable-backend","title":"Using ScreenGear with Variable Backend","text":"

With ScreenGear API, you can select from many different backends that generates best performance as well as the most compatible with our machine by employing its backend parameter that supports many different backends:

Supported backend values

Its possible values are: dxcam (Windows only), pil, mss, scrot, maim, imagemagick, pyqt5, pyqt, pyside2, pyside, wx, pygdk3, mac_screencapture, mac_quartz, gnome_dbus, gnome-screenshot, kwin_dbus.

Remember to install backend library and all of its dependencies you're planning to use with ScreenGear API. More information on all these backends (except dxcam) can be found here \u27b6

Backend defaults to dxcam library on Windows (if installed), and pyscreenshot otherwise.

Any value on monitor parameter will disable the backend parameter. You cannot use them simultaneously.

# import required libraries\nfrom vidgear.gears import ScreenGear\nimport cv2\n\n# open video stream with defined parameters and `mss` backend \n# for extracting frames.\nstream = ScreenGear(backend=\"mss\", logging=True).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/screengear/usage/#using-screengear-with-direct-colorspace-manipulation","title":"Using ScreenGear with Direct Colorspace Manipulation","text":"

ScreenGear API also supports Direct Colorspace Manipulation, which is ideal for changing source colorspace on the run.

A more detailed information on colorspace manipulation can be found here \u27b6

In following example code, we will start with HSV as source colorspace, and then we will switch to GRAY colorspace when W key is pressed, and then LAB colorspace when E key is pressed, finally default colorspace (i.e. BGR) when S key is pressed. Also, quit when Q key is pressed:

Any incorrect or None-type value, will immediately revert the colorspace to default i.e. BGR.

# import required libraries\nfrom vidgear.gears import ScreenGear\nimport cv2\n\n# Change colorspace to `HSV`\nstream = ScreenGear(colorspace=\"COLOR_BGR2HSV\", logging=True).start()\n\n# loop over\nwhile True:\n\n    # read HSV frames\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the HSV frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for key if pressed\n    key = cv2.waitKey(1) & 0xFF\n\n    # check if 'w' key is pressed\n    if key == ord(\"w\"):\n        # directly change colorspace at any instant\n        stream.color_space = cv2.COLOR_BGR2GRAY  # Now colorspace is GRAY\n\n    # check for 'e' key is pressed\n    if key == ord(\"e\"):\n        stream.color_space = cv2.COLOR_BGR2LAB  # Now colorspace is CieLAB\n\n    # check for 's' key is pressed\n    if key == ord(\"s\"):\n        stream.color_space = None  # Now colorspace is default(ie BGR)\n\n    # check for 'q' key is pressed\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/screengear/usage/#using-screengear-with-writegear-api","title":"Using ScreenGear with WriteGear API","text":"

ScreenGear can be used in conjunction with WriteGear API directly without any compatibility issues. The suitable example is as follows:

# import required libraries\nfrom vidgear.gears import ScreenGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n\n# define dimensions of screen w.r.t to given monitor to be captured\noptions = {\"top\": 40, \"left\": 0, \"width\": 100, \"height\": 100}\n\n# define suitable (Codec,CRF,preset) FFmpeg parameters for writer\noutput_params = {\"-vcodec\": \"libx264\", \"-crf\": 0, \"-preset\": \"fast\"}\n\n# open video stream with defined parameters\nstream = ScreenGear(monitor=1, logging=True, **options).start()\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n    # lets convert frame to gray for this example\n    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n\n    # write gray frame to writer\n    writer.write(gray)\n\n    # Show output window\n    cv2.imshow(\"Output Gray Frame\", gray)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/stabilizer/overview/","title":"Overview","text":""},{"location":"gears/stabilizer/overview/#stabilizer-class","title":"Stabilizer Class","text":"

VidGear's Stabilizer in Action(Video Credits @SIGGRAPH2013)

This video is transcoded with StreamGear API and hosted on GitHub Repository and served with raw.githack.com

"},{"location":"gears/stabilizer/overview/#overview","title":"Overview","text":"

Stabilizer is an auxiliary class that enables Video Stabilization for vidgear with minimalistic latency, and at the expense of little to no additional computational requirements.

The basic idea behind it is to tracks and save the salient feature array for the given number of frames and then uses these anchor point to cancel out all perturbations relative to it for the incoming frames in the queue. This class relies on Fixed-Size Python Queues for error-free & ultra-fast frame handling.

For more detailed information on Stabilizer working, See this blogpost \u27b6

"},{"location":"gears/stabilizer/overview/#features","title":"Features","text":"

Important

"},{"location":"gears/stabilizer/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through Stabilizer Class Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/stabilizer/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/stabilizer/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/stabilizer/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/stabilizer/params/","title":"Parameters","text":""},{"location":"gears/stabilizer/params/#stabilizer-class-parameters","title":"Stabilizer Class Parameters","text":""},{"location":"gears/stabilizer/params/#smoothing_radius","title":"smoothing_radius","text":"

This parameter can be used to alter averaging window size. It basically handles the quality of stabilization at the expense of latency and sudden panning. Larger its value, less will be panning, more will be latency and vice-versa.

Data-Type: Integer

Default Value: Its default value is 25.

Usage:

You can easily pass this parameter as follows:

Stabilizer(smoothing_radius=30)\n

"},{"location":"gears/stabilizer/params/#border_size","title":"border_size","text":"

This parameter enables and set the value for extended border size that compensates for reduction of black borders during stabilization.

Data-Type: Integer

Default Value: Its default value is 0(no borders).

Usage:

You can easily pass this parameter as follows:

Stabilizer(border_size=10)\n

"},{"location":"gears/stabilizer/params/#crop_n_zoom","title":"crop_n_zoom","text":"

This parameter enables cropping and zooming of frames (to original size) to reduce the black borders from being too noticeable (similar to the Stabilized, cropped and Auto-Scaled feature available in Adobe AfterEffects) during stabilization. It simply works in conjunction with the border_size parameter, i.e. when this parameter is enabled, border_size will be used for cropping border instead of extending them.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

You can easily pass this parameter as follows:

Stabilizer(border_size=10, crop_n_zoom=True)\n

"},{"location":"gears/stabilizer/params/#border_type","title":"border_type","text":"

This parameter can be used to change the extended border type. Valid border types are 'black', 'reflect', 'reflect_101', 'replicate' and 'wrap', learn more about it here.

Altering border_type parameter is DISABLED when crop_n_zoom is enabled!

Data-Type: String

Default Value: Its default value is 'black'.

Usage:

You can easily pass this parameter as follows:

Stabilizer(border_type='reflect')\n

"},{"location":"gears/stabilizer/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

Stabilizer(logging=True)\n

"},{"location":"gears/stabilizer/usage/","title":"Usage Examples","text":""},{"location":"gears/stabilizer/usage/#stabilizer-class-usage-examples","title":"Stabilizer Class Usage Examples:","text":"

The stabilizer may not perform well against High-frequency jitter in video. Use at your own risk!

The stabilizer might be slower for High-Quality/Resolution videos-frames.

It is advised to enable logging on the first run for easily identifying any runtime errors.

After going through Stabilizer Class Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/stabilizer/usage/#bare-minimum-usage-with-videocapture-gears","title":"Bare-Minimum Usage with VideoCapture Gears","text":"

Following is the bare-minimum code you need to get started with Stabilizer Class and various VideoCapture Gears:

You can use any VideoCapture Gear instead of CamGear in the similar manner, as shown in this usage example.

# import required libraries\nfrom vidgear.gears.stabilizer import Stabilizer\nfrom vidgear.gears import CamGear\nimport cv2\n\n# To open live video stream on webcam at first index(i.e. 0) device\nstream = CamGear(source=0).start()\n\n# initiate stabilizer object with default parameters\nstab = Stabilizer()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # send current frame to stabilizer for processing\n    stabilized_frame = stab.stabilize(frame)\n\n    # wait for stabilizer which still be initializing\n    if stabilized_frame is None:\n        continue\n\n    # {do something with the stabilized frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Stabilized Frame\", stabilized_frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# clear stabilizer resources\nstab.clean()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/stabilizer/usage/#bare-minimum-usage-with-opencv","title":"Bare-Minimum Usage with OpenCV","text":"

The VidGear's stabilizer class can also work standalone easily with any Computer Vision library such as OpenCV itself. Following is the bare-minimum code you need to get started with Stabilizer Class and OpenCV:

# import required libraries\nfrom vidgear.gears.stabilizer import Stabilizer\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = cv2.VideoCapture(0)\n\n# initiate stabilizer object with default parameters\nstab = Stabilizer()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n        break\n\n    # send current frame to stabilizer for processing\n    stabilized_frame = stab.stabilize(frame)\n\n    # wait for stabilizer which still be initializing\n    if stabilized_frame is None:\n        continue\n\n    # {do something with the stabilized frame here}\n\n    # Show output window\n    cv2.imshow(\"Stabilized Frame\", stabilized_frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# clear stabilizer resources\nstab.clean()\n\n# safely close video stream\nstream.release()\n

"},{"location":"gears/stabilizer/usage/#using-stabilizer-with-variable-parameters","title":"Using Stabilizer with Variable Parameters","text":"

Stabilizer class provide certain parameters which you can use to tweak its internal properties. The complete usage example is as follows:

# import required libraries\nfrom vidgear.gears.stabilizer import Stabilizer\nfrom vidgear.gears import CamGear\nimport cv2\n\n# To open live video stream on webcam at first index(i.e. 0) device\nstream = CamGear(source=0).start()\n\n# initiate stabilizer object with defined parameters\nstab = Stabilizer(smoothing_radius=30, crop_n_zoom=True, border_size=5, logging=True)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # send current frame to stabilizer for processing\n    stabilized_frame = stab.stabilize(frame)\n\n    # wait for stabilizer which still be initializing\n    if stabilized_frame is None:\n        continue\n\n    # {do something with the stabilized frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Stabilized Frame\", stabilized_frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# clear stabilizer resources\nstab.clean()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/stabilizer/usage/#using-stabilizer-with-writegear","title":"Using Stabilizer with WriteGear","text":"

VideoGear's stabilizer can be used in conjunction with WriteGear API directly without any compatibility issues. The complete usage example is as follows:

You can also add live audio input to WriteGear pipeline. See this bonus example \u27b6

# import required libraries\nfrom vidgear.gears.stabilizer import Stabilizer\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# Open suitable video stream\nstream = CamGear(source=\"unstabilized_stream.mp4\").start()\n\n# initiate stabilizer object with default parameters\nstab = Stabilizer()\n\n# Define writer with default parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\")\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if not None-type\n    if frame is None:\n        break\n\n    # send current frame to stabilizer for processing\n    stabilized_frame = stab.stabilize(frame)\n\n    # wait for stabilizer which still be initializing\n    if stabilized_frame is None:\n        continue\n\n    # {do something with the stabilized frame here}\n\n    # write stabilized frame to writer\n    writer.write(stabilized_frame)\n\n    # Show output window\n    cv2.imshow(\"Stabilized Frame\", stabilized_frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# clear stabilizer resources\nstab.clean()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/stabilizer/usage/#using-videogear-with-stabilizer-backend","title":"Using VideoGear with Stabilizer backend","text":"

VideoGear API provides a special internal wrapper around Stabilizer class that enables easy stabilization for various video-streams (real-time or not) with minimum effort and writing way fewer lines of code.

The complete usage example can be found here \u27b6

"},{"location":"gears/streamgear/ffmpeg_install/","title":"FFmpeg Installation","text":""},{"location":"gears/streamgear/ffmpeg_install/#ffmpeg-installation-instructions","title":"FFmpeg Installation Instructions","text":"

StreamGear must requires FFmpeg executables for transcoding Media Chunks. You can following machine-specific instructions for its installation:

StreamGear API will throw RuntimeError, if it fails to detect valid FFmpeg executables on your system.

Enable logging (logging=True) for debugging FFmpeg validation process.

"},{"location":"gears/streamgear/ffmpeg_install/#linux-ffmpeg-installation","title":"Linux FFmpeg Installation","text":"

The StreamGear API supports Auto-Detection and Manual Configuration methods on a Linux machine:

"},{"location":"gears/streamgear/ffmpeg_install/#a-auto-detection","title":"A. Auto-Detection","text":"

This is a recommended approach on Linux Machines

If StreamGear API not receives any input from the user on custom_ffmpeg parameter, then on Linux system, it tries to auto-detects the required FFmpeg installed binaries through validation test that employs subprocess python module.

Installation: You can install easily install official FFmpeg according to your Linux Distro by following this post \u27b6

"},{"location":"gears/streamgear/ffmpeg_install/#b-manual-configuration","title":"B. Manual Configuration","text":"

"},{"location":"gears/streamgear/ffmpeg_install/#windows-ffmpeg-installation","title":"Windows FFmpeg Installation","text":"

The StreamGear API supports Auto-Installation and Manual Configuration methods on Windows systems.

"},{"location":"gears/streamgear/ffmpeg_install/#a-auto-installation","title":"A. Auto-Installation","text":"

This is a recommended approach on Windows Machines

If StreamGear API not receives any input from the user on custom_ffmpeg parameter, then on Windows system StreamGear API auto-generates the required FFmpeg Static Binaries from a dedicated Github Server into the temporary directory (for e.g. C:\\Temp) of your machine.

Warning

"},{"location":"gears/streamgear/ffmpeg_install/#b-manual-configuration_1","title":"B. Manual Configuration","text":"

"},{"location":"gears/streamgear/ffmpeg_install/#macos-ffmpeg-installation","title":"MacOS FFmpeg Installation","text":"

The StreamGear API supports Auto-Detection and Manual Configuration methods on a macOS machine.

"},{"location":"gears/streamgear/ffmpeg_install/#a-auto-detection_1","title":"A. Auto-Detection","text":"

This is a recommended approach on MacOS Machines

If StreamGear API not receives any input from the user on custom_ffmpeg parameter, then on macOS system, it tries to auto-detects the required FFmpeg installed binaries through validation test that employs subprocess python module.

Installation: You can easily install FFmpeg on your macOS machine by following this tutorial \u27b6

"},{"location":"gears/streamgear/ffmpeg_install/#b-manual-configuration_2","title":"B. Manual Configuration","text":"

"},{"location":"gears/streamgear/introduction/","title":"Introduction","text":""},{"location":"gears/streamgear/introduction/#streamgear-api","title":"StreamGear API","text":"StreamGear API's generalized workflow"},{"location":"gears/streamgear/introduction/#overview","title":"Overview","text":"

StreamGear streamlines and simplifies the transcoding workflow to generate Ultra-Low Latency, High-Quality, Dynamic & Adaptive Streaming Formats like MPEG-DASH and Apple HLS with just a few lines of Python code, allowing developers to focus on their application logic rather than dealing with the complexities of transcoding and chunking media files.

StreamGear API provides a standalone, highly extensible, and flexible wrapper around the FFmpeg multimedia framework for generating chunk-encoded media segments from your multimedia content effortlessly.

With StreamGear, you can transcode source video/audio files and real-time video frames into a sequence of multiple smaller chunks/segments of suitable lengths. These segments facilitate streaming at different quality levels (bitrates or spatial resolutions) and allow for seamless switching between quality levels during playback based on available bandwidth. You can serve these segments on a web server, making them easily accessible via standard HTTP GET requests.

SteamGear currently supports both MPEG-DASH (Dynamic Adaptive Streaming over HTTP, ISO/IEC 23009-1) and Apple HLS (HTTP Live Streaming).

Additionally, StreamGear generates a manifest file (such as MPD for DASH) or a master playlist (such as M3U8 for Apple HLS) alongside the segments. These files contain essential segment information, including timing, URLs, and media characteristics like video resolution and adaptive bitrate. They are provided to the client before the streaming session begins.

For streaming with older traditional protocols such as RTMP, RTSP/RTP you could use WriteGear API instead.

New in v0.2.2

Apple HLS support was added in v0.2.2.

Important

Useful Links

"},{"location":"gears/streamgear/introduction/#mode-of-operations","title":"Mode of Operations","text":"

StreamGear primarily operates in following independent modes for transcoding:

Real-time Frames Mode itself is NOT Live-Streaming

To enable live-streaming in Real-time Frames Mode, use the exclusive -livestream attribute of the stream_params dictionary parameter in the StreamGear API. Checkout this usage example \u27b6 for more information.

"},{"location":"gears/streamgear/introduction/#watch-demo","title":"Watch Demo","text":"Watch MPEG-DASH StreamWatch APPLE HLS Stream

Watch StreamGear transcoded MPEG-DASH Stream:

Powered by clappr & shaka-player

This video assets (Manifest and segments) are hosted on GitHub Repository and served with raw.githack.com

Video Credits: \"Tears of Steel\" - Project Mango Teaser

Watch StreamGear transcoded APPLE HLS Stream:

Powered by clappr & HlsjsPlayback

This video assets (Playlist and segments) are hosted on GitHub Repository and served with raw.githack.com

Video Credits: \"Sintel\" - Project Durian Teaser

"},{"location":"gears/streamgear/introduction/#recommended-players","title":"Recommended Players","text":"GUI PlayersCommand-Line PlayersOnline Players

To run Online players locally, you'll need a HTTP server. For creating one yourself, See this well-curated list \u27b6

"},{"location":"gears/streamgear/introduction/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/streamgear/introduction/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/streamgear/introduction/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/streamgear/params/","title":"Parameters","text":""},{"location":"gears/streamgear/params/#streamgear-api-parameters","title":"StreamGear API Parameters","text":""},{"location":"gears/streamgear/params/#output","title":"output","text":"

This parameter sets the valid filename/path for storing the StreamGear assets, including Manifest file (such as MPD in case of DASH) or a Master Playlist (such as M3U8 in case of Apple HLS) and generated sequence of chunks/segments.

StreamGear API will throw ValueError if the provided output is empty or invalid.

Make sure to provide a valid filename with a valid file extension for the selected format value (such as .mpd for MPEG-DASH and .m3u8 for APPLE-HLS), otherwise StreamGear will throw AssertionError.

You can easily delete all previous assets at the output location by using the -clear_prev_assets attribute of the stream_params dictionary parameter.

Data-Type: String

Usage:

Its valid input can be one of the following:

"},{"location":"gears/streamgear/params/#format","title":"format","text":"

This parameter enables the adaptive HTTP streaming format. This parameter currently supported these formats: dash (i.e MPEG-DASH) and hls (i.e Apple HLS).

Make sure to provide a valid filename with a valid file extension in the output parameter for the selected format value (i.e., .mpd for MPEG-DASH and .m3u8 for APPLE-HLS), otherwise StreamGear will throw an AssertionError.

Any improper value assigned to format parameter will result in a ValueError!

Data-Type: String

Default Value: Its default value is dash

Usage:

DASHHLS
# Define streamer with DASH format\nStreamGear(output = \"output_dash.mpd\", format=\"dash\")\n
# Define streamer with HLS format\nStreamGear(output = \"output_hls.m3u8\", format=\"hls\")\n

"},{"location":"gears/streamgear/params/#custom_ffmpeg","title":"custom_ffmpeg","text":"

This parameter assigns the custom path/directory where the custom/downloaded FFmpeg executables are located.

Behavior on Windows Systems

On Windows, if a custom FFmpeg executable's path/directory is not provided through this custom_ffmpeg parameter, the StreamGear API will automatically attempt to download and extract suitable Static FFmpeg binaries at a suitable location on your Windows machine. More information can be found here \u27b6.

Data-Type: String

Default Value: Its default value is None.

Usage:

# Define streamer with custom ffmpeg binary\nStreamGear(output = 'output_foo.mpd', custom_ffmpeg=\"C://foo//bar//ffmpeg.exe\")\n

"},{"location":"gears/streamgear/params/#stream_params","title":"stream_params","text":"

This parameter allows developers to leverage nearly all FFmpeg options, providing effortless and flexible control over its internal settings for transcoding and generating high-quality streams. All supported parameters can be formatted as attributes within this dictionary parameter.

Please read the FFmpeg Documentation carefully before passing any additional values to the stream_params parameter. Incorrect values may cause errors or result in no output.

Data-Type: Dictionary

Default Value: Its default value is {}.

"},{"location":"gears/streamgear/params/#supported-parameters","title":"Supported Parameters","text":""},{"location":"gears/streamgear/params/#a-exclusive-parameters","title":"A. Exclusive Parameters","text":"

StreamGear API provides some exclusive internal parameters to easily generate Streaming Assets and effortlessly tweak its internal properties. These parameters are discussed below:

"},{"location":"gears/streamgear/params/#b-ffmpeg-parameters","title":"B. FFmpeg Parameters","text":"

Almost all FFmpeg parameters can be passed as dictionary attributes in stream_params. For example, to use the libx264 encoder to produce a lossless output video, you can pass the required FFmpeg parameters as dictionary attributes as follows:

Please check the H.264 documentation \u27b6 and FFmpeg Documentation \u27b6 for more information on following parameters.

All FFmpeg parameters are case-sensitive. Double-check each parameter if any errors occur.

In addition to these parameters, almost any FFmpeg parameter (supported by the installed FFmpeg) is also supported. Be sure to read the FFmpeg Documentation carefully first.

# libx264 encoder and its supported parameters\nstream_params = {\"-vcodec\":\"libx264\", \"-crf\": 0, \"-preset\": \"fast\", \"-tune\": \"zerolatency\"} \n

"},{"location":"gears/streamgear/params/#supported-encoders-and-decoders","title":"Supported Encoders and Decoders","text":"

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:

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 for creating HLS/DASH chunks of the primary stream efficiently. However, consider the following points:

# for checking encoder\nffmpeg -encoders           # use `ffmpeg.exe -encoders` on windows\n# for checking decoders\nffmpeg -decoders           # use `ffmpeg.exe -decoders` on windows\n

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

"},{"location":"gears/streamgear/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

StreamGear(logging=True)\n

  1. In Real-time Frames Mode, the Primary Stream's framerate defaults to -input_framerate attribute value, if defined, else it will be 25fps.\u00a0\u21a9

"},{"location":"gears/streamgear/rtfm/overview/","title":"Overview","text":""},{"location":"gears/streamgear/rtfm/overview/#streamgear-api-real-time-frames-mode","title":"StreamGear API: Real-time Frames Mode","text":"Real-time Frames Mode generalized workflow"},{"location":"gears/streamgear/rtfm/overview/#overview","title":"Overview","text":"

When no valid input is received on -video_source attribute of stream_params dictionary parameter, StreamGear API activates this mode where it directly transcodes real-time numpy.ndarray video-frames (as opposed to a entire video file) into a sequence of multiple smaller chunks/segments for adaptive streaming.

This mode works exceptionally well when you desire to flexibility manipulate or transform video-frames in real-time before sending them onto FFmpeg Pipeline for processing. But on the downside, StreamGear DOES NOT automatically maps video-source's audio to generated streams with this mode. You need to manually assign separate audio-source through -audio attribute of stream_params dictionary parameter.

SteamGear supports both MPEG-DASH (Dynamic Adaptive Streaming over HTTP, ISO/IEC 23009-1) and Apple HLS (HTTP Live Streaming) with this mode.

For this mode, StreamGear API provides exclusive stream() method for directly trancoding video-frames into streamable chunks.

New in v0.2.2

Apple HLS support was added in v0.2.2.

Real-time Frames Mode itself is NOT Live-Streaming

To enable live-streaming in Real-time Frames Mode, use the exclusive -livestream attribute of the stream_params dictionary parameter in the StreamGear API. Checkout this usage example \u27b6 for more information.

Please Remember

"},{"location":"gears/streamgear/rtfm/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through StreamGear Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/streamgear/rtfm/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/streamgear/rtfm/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/streamgear/rtfm/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/streamgear/rtfm/usage/","title":"Usage Examples","text":""},{"location":"gears/streamgear/rtfm/usage/#streamgear-api-usage-examples-real-time-frames-mode","title":"StreamGear API Usage Examples: Real-time Frames Mode","text":"

Real-time Frames Mode itself is NOT Live-Streaming

To enable live-streaming in Real-time Frames Mode, use the exclusive -livestream attribute of the stream_params dictionary parameter in the StreamGear API. Checkout following usage example \u27b6 for more information.

Important Information

DEPRECATION NOTICES for v0.3.3 and above

After going through following Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/streamgear/rtfm/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with StreamGear API in Real-time Frames Mode:

We are using CamGear in this Bare-Minimum example, but any VideoCapture Gear will work in the similar manner.

In this mode, StreamGear DOES NOT automatically maps video-source audio to generated streams. You need to manually assign separate audio-source through -audio attribute of stream_params dictionary parameter.

DASHHLS
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# open any valid video stream(for e.g `foo1.mp4` file)\nstream = CamGear(source='foo1.mp4').start() \n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"dash_out.mpd\")\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# open any valid video stream(for e.g `foo1.mp4` file)\nstream = CamGear(source='foo1.mp4').start() \n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\")\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n

After running this bare-minimum example, StreamGear will produce a Manifest file (dash.mpd) with streamable chunks that contains information about a Primary Stream of same resolution and framerate1 as input (without any audio).

"},{"location":"gears/streamgear/rtfm/usage/#bare-minimum-usage-with-controlled-input-framerate","title":"Bare-Minimum Usage with controlled Input-framerate","text":"

In Real-time Frames Mode, StreamGear API provides the exclusive -input_framerate attribute for the stream_params dictionary parameter, which allows you to set the assumed constant framerate for incoming frames.

In this example, we will retrieve the framerate from a webcam video stream and set it as the value for the -input_framerate attribute in StreamGear.

Remember, the input framerate defaults to 25.0 fps if the -input_framerate attribute value is not defined in Real-time Frames mode.

DASHHLS
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = CamGear(source=0).start()\n\n# retrieve framerate from CamGear Stream and pass it as `-input_framerate` value\nstream_params = {\"-input_framerate\":stream.framerate}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = CamGear(source=0).start()\n\n# retrieve framerate from CamGear Stream and pass it as `-input_framerate` value\nstream_params = {\"-input_framerate\":stream.framerate}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n

"},{"location":"gears/streamgear/rtfm/usage/#bare-minimum-usage-with-live-streaming","title":"Bare-Minimum Usage with Live-Streaming","text":"

You can easily activate Low-latency Live-Streaming in Real-time Frames Mode, where chunks will contain information for new frames only and forget previous ones, using the exclusive -livestream attribute of the stream_params dictionary parameter. The complete example is as follows:

In this mode, StreamGear DOES NOT automatically maps video-source audio to generated streams. You need to manually assign separate audio-source through -audio attribute of stream_params dictionary parameter.

DASHHLS

Controlling chunk size in DASH

To control the number of frames kept in Chunks for the DASH stream (controlling latency), you can use the -window_size and -extra_window_size FFmpeg parameters. Lower values for these parameters will result in lower latency.

After every few chunks (equal to the sum of -window_size and -extra_window_size values), all chunks will be overwritten while Live-Streaming. This means that newer chunks in the manifest will contain NO information from older chunks, and the resulting DASH stream will only play the most recent frames, reducing latency.

# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# open any valid video stream(from web-camera attached at index `0`)\nstream = CamGear(source=0).start()\n\n# enable livestreaming and retrieve framerate from CamGear Stream and\n# pass it as `-input_framerate` parameter for controlled framerate\nstream_params = {\"-input_framerate\": stream.framerate, \"-livestream\": True}\n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n

Controlling chunk size in HLS

To control the number of frames kept in Chunks for the HLS stream (controlling latency), you can use the -hls_init_time & -hls_time FFmpeg parameters. Lower values for these parameters will result in lower latency.

After every few chunks (equal to the sum of -hls_init_time & -hls_time values), all chunks will be overwritten while Live-Streaming. This means that newer chunks in the master playlist will contain NO information from older chunks, and the resulting HLS stream will only play the most recent frames, reducing latency.

# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# open any valid video stream(from web-camera attached at index `0`)\nstream = CamGear(source=0).start()\n\n# enable livestreaming and retrieve framerate from CamGear Stream and\n# pass it as `-input_framerate` parameter for controlled framerate\nstream_params = {\"-input_framerate\": stream.framerate, \"-livestream\": True}\n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n

"},{"location":"gears/streamgear/rtfm/usage/#bare-minimum-usage-with-opencv","title":"Bare-Minimum Usage with OpenCV","text":"

You can easily use the StreamGear API directly with any other Video Processing library (for e.g. OpenCV) in Real-time Frames Mode.

The following is a complete StreamGear API usage example with OpenCV:

This is a bare-minimum example with OpenCV, but any other Real-time Frames Mode feature or example will work in a similar manner.

DASHHLS
# import required libraries\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = cv2.VideoCapture(0) \n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"dash_out.mpd\")\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n      break\n\n    # {do something with the frame here}\n    # lets convert frame to gray for this example\n    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n\n    # send frame to streamer\n    streamer.stream(gray)\n\n    # Show output window\n    cv2.imshow(\"Output Gray Frame\", gray)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.release()\n\n# safely close streamer\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = cv2.VideoCapture(0) \n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\")\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n      break\n\n    # {do something with the frame here}\n    # lets convert frame to gray for this example\n    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n\n    # send frame to streamer\n    streamer.stream(gray)\n\n    # Show output window\n    cv2.imshow(\"Output Gray Frame\", gray)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.release()\n\n# safely close streamer\nstreamer.close()\n

"},{"location":"gears/streamgear/rtfm/usage/#usage-with-additional-streams","title":"Usage with Additional Streams","text":"

Similar to Single-Source Mode, in addition to the Primary Stream, you can easily generate any number of additional Secondary Streams with variable bitrate or spatial resolution, using the exclusive -streams attribute of the stream_params dictionary parameter.

To generate Secondary Streams, add each desired resolution and bitrate/framerate as a list of dictionaries to the -streams attribute. StreamGear will handle the rest automatically. The complete example is as follows:

A more detailed information on -streams attribute can be found here \u27b6

In this mode, StreamGear DOES NOT automatically maps video-source audio to generated streams. You need to manually assign separate audio-source through -audio attribute of stream_params dictionary parameter.

Important Information about -streams attribute

Always use the -streams attribute to define additional streams safely. Duplicate or incorrect definitions can break the transcoding pipeline and corrupt the output chunks.

DASHHLS
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = CamGear(source=0).start() \n\n# define various streams\nstream_params = {\n    \"-streams\": [\n        {\"-resolution\": \"1280x720\", \"-framerate\": 30.0},  # Stream1: 1280x720 at 30fps framerate\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream2: 640x360 at 60fps framerate\n        {\"-resolution\": \"320x240\", \"-video_bitrate\": \"500k\"},  # Stream3: 320x240 at 500kbs bitrate\n    ],\n}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"dash_out.mpd\")\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = CamGear(source=0).start() \n\n# define various streams\nstream_params = {\n    \"-streams\": [\n        {\"-resolution\": \"1280x720\", \"-framerate\": 30.0},  # Stream1: 1280x720 at 30fps framerate\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream2: 640x360 at 60fps framerate\n        {\"-resolution\": \"320x240\", \"-video_bitrate\": \"500k\"},  # Stream3: 320x240 at 500kbs bitrate\n    ],\n}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\")\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n

"},{"location":"gears/streamgear/rtfm/usage/#usage-with-file-audio-input","title":"Usage with File Audio-Input","text":"

In Real-time Frames Mode, if you want to add audio to your streams, you need to use the exclusive -audio attribute of the stream_params dictionary parameter.

To add a audio source, provide the path to your audio file as a string to the -audio attribute. The API will automatically validate and map the audio to all generated streams. The complete example is as follows:

Ensure the provided -audio audio source is compatible with the input video source. Incompatibility can cause multiple errors or result in no output at all.

You MUST use -input_framerate attribute to set exact value of input framerate when using external audio in Real-time Frames mode, otherwise audio delay will occur in output streams.

You can also assign a valid audio URL as input instead of a file path. More details can be found here \u27b6

DASHHLS
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# open any valid video stream(for e.g `foo1.mp4` file)\nstream = CamGear(source='foo1.mp4').start() \n\n# add various streams, along with custom audio\nstream_params = {\n    \"-streams\": [\n        {\"-resolution\": \"1920x1080\", \"-video_bitrate\": \"4000k\"},  # Stream1: 1920x1080 at 4000kbs bitrate\n        {\"-resolution\": \"1280x720\", \"-framerate\": 30.0},  # Stream2: 1280x720 at 30fps\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream3: 640x360 at 60fps\n    ],\n    \"-input_framerate\": stream.framerate, # controlled framerate for audio-video sync !!! don't forget this line !!!\n    \"-audio\": \"/home/foo/foo1.aac\" # assign external audio-source\n}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# open any valid video stream(for e.g `foo1.mp4` file)\nstream = CamGear(source='foo1.mp4').start() \n\n# add various streams, along with custom audio\nstream_params = {\n    \"-streams\": [\n        {\"-resolution\": \"1920x1080\", \"-video_bitrate\": \"4000k\"},  # Stream1: 1920x1080 at 4000kbs bitrate\n        {\"-resolution\": \"1280x720\", \"-framerate\": 30.0},  # Stream2: 1280x720 at 30fps\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream3: 640x360 at 60fps\n    ],\n    \"-input_framerate\": stream.framerate, # controlled framerate for audio-video sync !!! don't forget this line !!!\n    \"-audio\": \"/home/foo/foo1.aac\" # assign external audio-source\n}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n

"},{"location":"gears/streamgear/rtfm/usage/#usage-with-device-audio-input","title":"Usage with Device Audio-Input","text":"

In Real-time Frames Mode, you can also use the exclusive -audio attribute of the stream_params dictionary parameter for streaming live audio from an external device.

To stream live audio, format your audio device name followed by a suitable demuxer as a list, and assign it to the -audio attribute. The API will automatically validate and map the audio to all generated streams. The complete example is as follows:

Example Assumptions

Using devices sources with -audio attribute on different OS platforms

To use device sources with the -audio attribute on different OS platforms, follow these instructions:

Windows Linux MacOS

Windows OS users can use the dshow (DirectShow) to list audio input device which is the preferred option for Windows users. You can refer following steps to identify and specify your sound card:

If audio still doesn't work then checkout this troubleshooting guide \u27b6 or reach us out on Gitter \u27b6 Community channel

Linux OS users can use the alsa to list input device to capture live audio input such as from a webcam. You can refer following steps to identify and specify your sound card:

If audio still doesn't work then reach us out on Gitter \u27b6 Community channel

MAC OS users can use the avfoundation to list input devices for grabbing audio from integrated iSight cameras as well as cameras connected via USB or FireWire. You can refer following steps to identify and specify your sound card on MacOS/OSX machines:

If audio still doesn't work then reach us out on Gitter \u27b6 Community channel

It is advised to use this example with live-streaming enabled(True) by using StreamGear API's exclusive -livestream attribute of stream_params dictionary parameter.

Ensure the provided -audio audio source is compatible with the video source device. Incompatibility can cause multiple errors or result in no output at all.

You MUST use -input_framerate attribute to set exact value of input framerate when using external audio in Real-time Frames mode, otherwise audio delay will occur in output streams.

DASHHLS
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# open any valid DEVICE video stream\nstream = CamGear(source=0).start()\n\n# add various streams, along with custom audio\nstream_params = {\n    \"-streams\": [\n        {\n            \"-resolution\": \"640x360\",\n            \"-video_bitrate\": \"4000k\",\n        },  # Stream1: 640x360 at 4000kbs bitrate\n        {\"-resolution\": \"320x240\", \"-framerate\": 30.0},  # Stream2: 320x240 at 30fps\n    ],\n    \"-input_framerate\": stream.framerate,  # controlled framerate for audio-video sync !!! don't forget this line !!!\n    \"-livestream\": True,\n    \"-audio\": [\n        \"-f\",\n        \"dshow\",\n        \"-i\",\n        \"audio=Microphone (USB2.0 Camera)\",\n    ],  # assign appropriate input audio-source device(compatible with video source) and its demuxer\n}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# open any valid DEVICE video stream\nstream = CamGear(source=0).start()\n\n# add various streams, along with custom audio\nstream_params = {\n    \"-streams\": [\n        {\n            \"-resolution\": \"640x360\",\n            \"-video_bitrate\": \"4000k\",\n        },  # Stream1: 640x360 at 4000kbs bitrate\n        {\"-resolution\": \"320x240\", \"-framerate\": 30.0},  # Stream2: 320x240 at 30fps\n    ],\n    \"-input_framerate\": stream.framerate,  # controlled framerate for audio-video sync !!! don't forget this line !!!\n    \"-livestream\": True,\n    \"-audio\": [\n        \"-f\",\n        \"dshow\",\n        \"-i\",\n        \"audio=Microphone (USB2.0 Camera)\",\n    ],  # assign appropriate input audio-source device(compatible with video source) and its demuxer\n}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"hls_out.m3u8\", format=\"hls\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n

"},{"location":"gears/streamgear/rtfm/usage/#usage-with-hardware-video-encoder","title":"Usage with Hardware Video-Encoder","text":"

In Real-time Frames Mode, you can easily change the video encoder according to your requirements by passing the -vcodec FFmpeg parameter as an attribute in the stream_params dictionary parameter. Additionally, you can specify additional properties, features, and optimizations for your system's GPU.

In this example, we will be using h264_vaapi as our Hardware Encoder and specifying the device hardware's location and compatible video filters by formatting them as attributes in the stream_params dictionary parameter.

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.

Checking VAAPI Support for Hardware Encoding

To use VAAPI (Video Acceleration API) as a hardware encoder in this example, follow these steps to ensure your FFmpeg supports VAAPI:

ffmpeg  -hide_banner -encoders | grep vaapi \n\n V..... h264_vaapi           H.264/AVC (VAAPI) (codec h264)\n V..... hevc_vaapi           H.265/HEVC (VAAPI) (codec hevc)\n V..... mjpeg_vaapi          MJPEG (VAAPI) (codec mjpeg)\n V..... mpeg2_vaapi          MPEG-2 (VAAPI) (codec mpeg2video)\n V..... vp8_vaapi            VP8 (VAAPI) (codec vp8)\n

Please read the FFmpeg Documentation carefully before passing any additional values to the stream_params parameter. Incorrect values may cause errors or result in no output.

DASHHLS
# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = VideoGear(source=0).start() \n\n# add various streams with custom Video Encoder and optimizations\nstream_params = {\n    \"-streams\": [\n        {\"-resolution\": \"1920x1080\", \"-video_bitrate\": \"4000k\"},  # Stream1: 1920x1080 at 4000kbs bitrate\n        {\"-resolution\": \"1280x720\", \"-framerate\": 30.0},  # Stream2: 1280x720 at 30fps\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream3: 640x360 at 60fps\n    ],\n    \"-vcodec\": \"h264_vaapi\", # define custom Video encoder\n    \"-vaapi_device\": \"/dev/dri/renderD128\", # define device location\n    \"-vf\": \"format=nv12,hwupload\",  # define video filters\n}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = VideoGear(source=0).start() \n\n# add various streams with custom Video Encoder and optimizations\nstream_params = {\n    \"-streams\": [\n        {\"-resolution\": \"1920x1080\", \"-video_bitrate\": \"4000k\"},  # Stream1: 1920x1080 at 4000kbs bitrate\n        {\"-resolution\": \"1280x720\", \"-framerate\": 30.0},  # Stream2: 1280x720 at 30fps\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream3: 640x360 at 60fps\n    ],\n    \"-vcodec\": \"h264_vaapi\", # define custom Video encoder\n    \"-vaapi_device\": \"/dev/dri/renderD128\", # define device location\n    \"-vf\": \"format=nv12,hwupload\",  # define video pixformat\n}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n

  1. In Real-time Frames Mode, the Primary Stream's framerate defaults to the value of the -input_framerate attribute, if defined. Otherwise, it will be set to 25 fps.\u00a0\u21a9\u21a9\u21a9

"},{"location":"gears/streamgear/ssm/overview/","title":"Overview","text":""},{"location":"gears/streamgear/ssm/overview/#streamgear-api-single-source-mode","title":"StreamGear API: Single-Source Mode","text":"Single-Source Mode generalized workflow"},{"location":"gears/streamgear/ssm/overview/#overview","title":"Overview","text":"

In this mode, StreamGear transcodes entire audio-video file (as opposed to frames-by-frame) into a sequence of multiple smaller chunks/segments for adaptive streaming.

This mode works exceptionally well when you're transcoding long-duration lossless videos(with audio) files for streaming that requires no interruptions. But on the downside, the provided source cannot be flexibly manipulated or transformed before sending onto FFmpeg Pipeline for processing.

SteamGear supports both MPEG-DASH (Dynamic Adaptive Streaming over HTTP, ISO/IEC 23009-1) and Apple HLS (HTTP Live Streaming) with this mode.

For this mode, StreamGear API provides exclusive transcode_source() method to easily process audio-video files into streamable chunks.

This mode can be easily activated by assigning suitable video path as input to -video_source attribute of stream_params dictionary parameter, during StreamGear initialization.

New in v0.2.2

Apple HLS support was added in v0.2.2.

Please Remember

"},{"location":"gears/streamgear/ssm/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through StreamGear Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/streamgear/ssm/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/streamgear/ssm/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/streamgear/ssm/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/streamgear/ssm/usage/","title":"Usage Examples","text":""},{"location":"gears/streamgear/ssm/usage/#streamgear-api-usage-examples-single-source-mode","title":"StreamGear API Usage Examples: Single-Source Mode","text":"

Important Information

DEPRECATION NOTICES for v0.3.3 and above 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:

After going through following Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/streamgear/ssm/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with StreamGear API in Single-Source Mode:

If input video-source (i.e. -video_source) contains any audio stream/channel, then it automatically gets mapped to all generated streams.

DASHHLS
# import required libraries\nfrom vidgear.gears import StreamGear\n\n# activate Single-Source Mode with valid video input\nstream_params = {\"-video_source\": \"foo.mp4\"}\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n# transcode source\nstreamer.transcode_source()\n# close\nstreamer.close()\n

After running this bare-minimum example, StreamGear will produce a Manifest file (dash_out.mpd) with streamable chunks, containing information about a Primary Stream with the same resolution and framerate as the input.

# import required libraries\nfrom vidgear.gears import StreamGear\n\n# activate Single-Source Mode with valid video input\nstream_params = {\"-video_source\": \"foo.mp4\"}\n# describe a suitable master playlist location/name and assign params\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", **stream_params)\n# transcode source\nstreamer.transcode_source()\n# close\nstreamer.close()\n

After running this bare-minimum example, StreamGear will produce a Master Playlist file (hls_out.mpd) with streamable chunks, containing information about a Primary Stream with the same resolution and framerate as the input.

"},{"location":"gears/streamgear/ssm/usage/#usage-with-additional-streams","title":"Usage with Additional Streams","text":"

In addition to the Primary Stream, you can easily generate any number of additional Secondary Streams with variable bitrate or spatial resolutions, using the exclusive -streams attribute of the stream_params dictionary parameter.

To generate Secondary Streams, add each desired resolution and bitrate/framerate as a list of dictionaries to the -streams attribute. StreamGear will handle the rest automatically. The complete example is as follows:

A more detailed information on -streams attribute can be found here \u27b6

If input video-source (i.e. -video_source) contains any audio stream/channel, then it automatically gets mapped to all generated streams without any extra efforts.

Important Information about -streams attribute

Always use the -streams attribute to define additional streams safely. Duplicate or incorrect definitions can break the transcoding pipeline and corrupt the output chunks.

DASHHLS
# import required libraries\nfrom vidgear.gears import StreamGear\n\n# activate Single-Source Mode and also define various streams\nstream_params = {\n    \"-video_source\": \"foo.mp4\",\n    \"-streams\": [\n        {\"-resolution\": \"1920x1080\", \"-video_bitrate\": \"4000k\"},  # Stream1: 1920x1080 at 4000kbs bitrate\n        {\"-resolution\": \"1280x720\", \"-framerate\": 30.0},  # Stream2: 1280x720 at 30fps framerate\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream3: 640x360 at 60fps framerate\n        {\"-resolution\": \"320x240\", \"-video_bitrate\": \"500k\"},  # Stream3: 320x240 at 500kbs bitrate\n    ],\n}\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n# transcode source\nstreamer.transcode_source()\n# close\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import StreamGear\n\n# activate Single-Source Mode and also define various streams\nstream_params = {\n    \"-video_source\": \"foo.mp4\",\n    \"-streams\": [\n        {\"-resolution\": \"1920x1080\", \"-video_bitrate\": \"4000k\"},  # Stream1: 1920x1080 at 4000kbs bitrate\n        {\"-resolution\": \"1280x720\", \"-framerate\": 30.0},  # Stream2: 1280x720 at 30fps framerate\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream3: 640x360 at 60fps framerate\n        {\"-resolution\": \"320x240\", \"-video_bitrate\": \"500k\"},  # Stream3: 320x240 at 500kbs bitrate\n    ],\n}\n# describe a suitable master playlist location/name and assign params\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", **stream_params)\n# transcode source\nstreamer.transcode_source()\n# close\nstreamer.close()\n

"},{"location":"gears/streamgear/ssm/usage/#usage-with-custom-audio-input","title":"Usage with Custom Audio-Input","text":"

In single source mode, by default, if the input video source (i.e., -video_source) contains audio, it gets automatically mapped to all generated streams. However, if you want to add a custom audio source, you can use the exclusive -audio attribute of the stream_params dictionary parameter.

To add a custom audio source, provide the path to your audio file as a string to the -audio attribute. The API will automatically validate and map the audio to all generated streams. The complete example is as follows:

Ensure the provided -audio audio source is compatible with the input video source (-video_source). Incompatibility can cause multiple errors or result in no output at all.

You can also assign a valid audio URL as input instead of a file path. More details can be found here \u27b6

DASHHLS
# import required libraries\nfrom vidgear.gears import StreamGear\n\n# activate Single-Source Mode and various streams, along with custom audio\nstream_params = {\n    \"-video_source\": \"foo.mp4\",\n    \"-streams\": [\n        {\"-resolution\": \"1280x720\", \"-video_bitrate\": \"4000k\"},  # Stream1: 1280x720 at 4000kbs bitrate\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream2: 640x360 at 60fps\n    ],\n    \"-audio\": \"/home/foo/foo1.aac\", # define custom audio-source\n    \"-acodec\": \"copy\", # define copy audio encoder\n}\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n# transcode source\nstreamer.transcode_source()\n# close\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import StreamGear\n\n# activate Single-Source Mode and various streams, along with custom audio\nstream_params = {\n    \"-video_source\": \"foo.mp4\",\n    \"-streams\": [\n        {\"-resolution\": \"1280x720\", \"-video_bitrate\": \"4000k\"},  # Stream1: 1280x720 at 4000kbs bitrate\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream2: 640x360 at 60fps\n    ],\n    \"-audio\": \"/home/foo/foo1.aac\",  # define custom audio-source\n    \"-acodec\": \"copy\", # define copy audio encoder\n}\n# describe a suitable master playlist location/name and assign params\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", **stream_params)\n# transcode source\nstreamer.transcode_source()\n# close\nstreamer.close()\n

"},{"location":"gears/streamgear/ssm/usage/#usage-with-variable-ffmpeg-parameters","title":"Usage with Variable FFmpeg Parameters","text":"

For fine-grained control over the transcoding process, StreamGear provides a highly extensible and flexible wrapper around FFmpeg library and access to almost all of its configurational parameter.

In this example, we'll use the H.265/HEVC video encoder and AAC audio encoder, apply various optimal FFmpeg configurational parameters.

This example assumes that the given input video source (-video_source) contains at least one audio stream.

This example is just conveying the idea on how to use FFmpeg's internal encoders/parameters with StreamGear API. You can use any FFmpeg parameter in the similar manner.

Please read the FFmpeg Documentation carefully before passing any additional values to the stream_params parameter. Incorrect values may cause errors or result in no output.

DASHHLS
# import required libraries\nfrom vidgear.gears import StreamGear\n\n# activate Single-Source Mode and various other parameters\nstream_params = {\n    \"-video_source\": \"foo.mp4\", # define Video-Source\n    \"-vcodec\": \"libx265\", # specify H.265/HEVC video encoder\n    \"-x265-params\": \"lossless=1\", # enables Lossless encoding\n    \"-bpp\": 0.15, # Bits-Per-Pixel(BPP), an Internal StreamGear parameter to ensure good quality of high motion scenes\n    \"-streams\": [\n        {\"-resolution\": \"640x360\", \"-video_bitrate\": \"4000k\"}, # Stream1: 1280x720 at 4000kbs bitrate\n        {\"-resolution\": \"320x240\", \"-framerate\": 60.0},  # Stream2: 640x360 at 60fps\n    ],\n    \"-acodec\": \"aac\", # specify AAC audio encoder\n}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"dash_out.mpd\", logging=True, **stream_params)\n# transcode source\nstreamer.transcode_source()\n# close\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import StreamGear\n\nstream_params = {\n    \"-video_source\": \"foo.mp4\", # define Video-Source\n    \"-vcodec\": \"libx265\", # specify H.265/HEVC video encoder\n    \"-x265-params\": \"lossless=1\", # enables Lossless encoding\n    \"-bpp\": 0.15, # Bits-Per-Pixel(BPP), an Internal StreamGear parameter to ensure good quality of high motion scenes\n    \"-streams\": [\n        {\"-resolution\": \"640x360\", \"-video_bitrate\": \"4000k\"}, # Stream1: 1280x720 at 4000kbs bitrate\n        {\"-resolution\": \"320x240\", \"-framerate\": 60.0},  # Stream2: 640x360 at 60fps\n    ],\n    \"-acodec\": \"aac\", # specify AAC audio encoder\n}\n\n# describe a suitable master playlist file location/name and assign params\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", logging=True, **stream_params)\n# transcode source\nstreamer.transcode_source()\n# close\nstreamer.close()\n

  1. In Real-time Frames Mode, the Primary Stream's framerate defaults to -input_framerate attribute value, if defined, else it will be 25fps.\u00a0\u21a9

"},{"location":"gears/videogear/overview/","title":"Overview","text":""},{"location":"gears/videogear/overview/#videogear-api","title":"VideoGear API","text":"VideoGear API's generalized workflow"},{"location":"gears/videogear/overview/#overview","title":"Overview","text":"

VideoGear API provides a special internal wrapper around VidGear's exclusive Video Stabilizer class.

VideoGear also acts as a Common Video-Capture API that provides internal access for both CamGear and PiGear APIs and their parameters with an exclusive enablePiCamera boolean flag.

VideoGear is ideal when you need to switch to different video sources without changing your code much. Also, it enables easy stabilization for various video-streams (real-time or not) with minimum effort and writing way fewer lines of code.

Helpful Tips

"},{"location":"gears/videogear/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through VideoGear Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/videogear/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/videogear/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/videogear/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/videogear/params/","title":"Parameters","text":""},{"location":"gears/videogear/params/#videogear-api-parameters","title":"VideoGear API Parameters","text":"

VideoGear acts as a Common Video-Capture API that provides internal access for both CamGear and PiGear APIs and their parameters.

"},{"location":"gears/videogear/params/#enablepicamera","title":"enablePiCamera","text":"

This parameter provide direct access to PiGear or CamGear APIs respectively in VideoGear. This means the if enablePiCamera flag is True, the PiGear API will be accessed, and if False, the CamGear API will be accessed.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

VideoGear(enablePiCamera=True) # enable access to PiGear API\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/videogear/params/#parameters-for-stabilizer-backend","title":"Parameters for Stabilizer Backend","text":"

Enable this backend with stabilize=True in VideoGear.

"},{"location":"gears/videogear/params/#stabilize","title":"stabilize","text":"

This parameter enable access to Stabilizer Class for stabilizing frames, i.e. can be set to True(to enable) or unset to False(to disable).

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

VideoGear(stabilize=True) # enable stablization\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/videogear/params/#options","title":"options","text":"

This parameter can be used in addition, to pass user-defined parameters supported by Stabilizer Class. These parameters can be formatted as this parameter's attribute.

Supported dictionary attributes for Stabilizer Class are:

"},{"location":"gears/videogear/params/#parameters-for-camgear-backend","title":"Parameters for CamGear backend","text":"

Enable this backend with enablePiCamera=False in VideoGear. Default is also False.

"},{"location":"gears/videogear/params/#source","title":"source","text":"

VideoGear API will throw RuntimeError if source provided is invalid.

This parameter defines the source for the input stream.

Data-Type: Based on input.

Default Value: Its default value is 0.

Its valid input can be one of the following:

"},{"location":"gears/videogear/params/#stream_mode","title":"stream_mode","text":"

This parameter controls the Stream Mode, .i.e if enabled(stream_mode=True), the VideoGear API will interpret the given source input as YouTube URL address.

Due to a FFmpeg bug that causes video to freeze frequently in OpenCV, It is advised to always use GStreamer backend (backend=cv2.CAP_GSTREAMER) for any livestreams (such as Twitch).

VideoGear automatically enforce GStreamer backend (backend=cv2.CAP_GSTREAMER) for YouTube-livestreams!

VideoGear will exit with RuntimeError for YouTube livestreams, if OpenCV is not compiled with GStreamer(>=v1.0.0) support. Checkout this FAQ for compiling OpenCV with GStreamer support.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

VideoGear(source='https://youtu.be/bvetuLwJIkA', stream_mode=True)\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/videogear/params/#backend","title":"backend","text":"

This parameter manually selects the backend for OpenCV's VideoCapture class (only if specified).

Data-Type: Integer

Default Value: Its default value is 0

Usage:

All supported backends are listed here \u27b6

Its value can be for e.g. backend = cv2.CAP_DSHOW for selecting Direct Show as backend:

VideoGear(source=0, backend = cv2.CAP_DSHOW)\n

"},{"location":"gears/videogear/params/#options_1","title":"options","text":"

This parameter provides the ability to alter various Source Tweak Parameters available within OpenCV's VideoCapture API properties.

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

All supported parameters are listed here \u27b6

The desired parameters can be passed to VideoGear API by formatting them as this parameter's attributes, as follows:

# formatting parameters as dictionary attributes\noptions = {\"CAP_PROP_FRAME_WIDTH\":320, \"CAP_PROP_FRAME_HEIGHT\":240, \"CAP_PROP_FPS\":60}\n# assigning it\nVideoGear(source=0, **options)\n

"},{"location":"gears/videogear/params/#parameters-for-pigear-backend","title":"Parameters for PiGear backend","text":"

Enable this backend with enablePiCamera=True in VideoGear.

"},{"location":"gears/videogear/params/#camera_num","title":"camera_num","text":"

This parameter selects the camera index to be used as the source, allowing you to drive these multiple cameras simultaneously from within a single Python session. Its value can only be zero or greater, otherwise, VideoGear API will throw ValueError for any negative value.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

# select Camera Module at index `1`\nVideoGear(enablePiCamera=True, camera_num=1)\n

The complete usage example demonstrating the usage of the camera_num parameter is available here \u27b6.

"},{"location":"gears/videogear/params/#resolution","title":"resolution","text":"

This parameter controls the resolution - a tuple (i.e. (width,height)) of two values giving the width and height of the output frames.

Make sure both width and height values should be at least 64.

When using the Picamera2 backend, the resolution parameter will be OVERRIDDEN, if the user explicitly defines the output_size property of the sensor configurational parameter.

Data-Type: Tuple

Default Value: Its default value is (640,480).

Usage:

VideoGear(enablePiCamera=True, resolution=(1280,720)) # sets 1280x720 resolution\n

"},{"location":"gears/videogear/params/#framerate","title":"framerate","text":"

This parameter controls the framerate of the source.

Data-Type: integer/float

Default Value: Its default value is 30.

Usage:

VideoGear(enablePiCamera=True, framerate=60) # sets 60fps framerate\n

"},{"location":"gears/videogear/params/#options_2","title":"options","text":"

This dictionary parameter in the internal PiGear API backend allows you to control various camera settings for both the picamera2 and legacy picamera backends and some internal API tasks. These settings include:

"},{"location":"gears/videogear/params/#a-configurational-camera-parameters","title":"A. Configurational Camera Parameters","text":""},{"location":"gears/videogear/params/#b-user-defined-parameters","title":"B. User-defined Parameters","text":"

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

The complete usage example demonstrating the usage of the options parameter is available here \u27b6.

You can format these user-defined and configurational parameters as attributes of this options dictionary parameter as follows:

New Picamera2 backendLegacy Picamera backend
# formulate various Picamera2 API parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"exposure_compensation\": 15,\n    \"sensor\": {\"output_size\": (480, 320)},  # !!! will override `resolution` !!!\n}\n\n# open pi video stream with defined parameters\nstream = VideoGear(enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options).start()\n
# formulate various Picamera API parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = VideoGear(enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options).start()\n

"},{"location":"gears/videogear/params/#common-parameters","title":"Common Parameters","text":"

These are common parameters that works with every backend in VideoGear.

"},{"location":"gears/videogear/params/#colorspace","title":"colorspace","text":"

This parameter selects the colorspace of the source stream.

Data-Type: String

Default Value: Its default value is None.

Usage:

All supported colorspace values are given here \u27b6

VideoGear(colorspace=\"COLOR_BGR2HSV\")\n

Its complete usage example is given here \u27b6

"},{"location":"gears/videogear/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

VideoGear(logging=True)\n

"},{"location":"gears/videogear/params/#time_delay","title":"time_delay","text":"

This parameter set the time delay (in seconds) before the VideoGear API start reading the frames. This delay is only required if the source required some warm-up delay before starting up.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

VideoGear(time_delay=1)  # set 1 seconds time delay\n

"},{"location":"gears/videogear/usage/","title":"Usage Examples","text":""},{"location":"gears/videogear/usage/#videogear-api-usage-examples","title":"VideoGear API Usage Examples:","text":"

After going through following Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/videogear/usage/#bare-minimum-usage-with-camgear-backend","title":"Bare-Minimum Usage with CamGear backend","text":"

VideoGear by default provides direct internal access to CamGear API.

Following is the bare-minimum code you need to access CamGear API with VideoGear:

# import required libraries\nfrom vidgear.gears import VideoGear\nimport cv2\n\n\n# open any valid video stream(for e.g `myvideo.avi` file)\nstream = VideoGear(source=\"myvideo.avi\").start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/videogear/usage/#bare-minimum-usage-with-pigear-backend","title":"Bare-Minimum Usage with PiGear backend","text":"

VideoGear contains a special enablePiCamera flag that when True provides internal access to PiGear API.

Following is the bare-minimum code you need to access PiGear API with VideoGear:

Under the hood, PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, PiGear API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this API, otherwise nothing will work.

# import required libraries\nfrom vidgear.gears import VideoGear\nimport cv2\n\n# enable enablePiCamera boolean flag to access PiGear API backend\nstream = VideoGear(enablePiCamera=True).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/videogear/usage/#using-videogear-with-video-stabilizer-backend","title":"Using VideoGear with Video Stabilizer backend","text":"

VideoGear API provides a special internal wrapper around VidGear's Exclusive Video Stabilizer class and provides easy way of activating stabilization for various video-streams (real-time or not) with its stabilize boolean parameter during initialization.

The usage example is as follows:

For a more detailed information on Video-Stabilizer Class, Read here \u27b6

The stabilizer might be slower for High-Quality/Resolution videos-frames.

# import required libraries\nfrom vidgear.gears import VideoGear\nimport numpy as np\nimport cv2\n\n# open any valid video stream with stabilization enabled(`stabilize = True`)\nstream_stab = VideoGear(source=\"test.mp4\", stabilize=True).start()\n\n# loop over\nwhile True:\n\n    # read stabilized frames\n    frame_stab = stream_stab.read()\n\n    # check for stabilized frame if None-type\n    if frame_stab is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Stabilized Output\", frame_stab)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close streams\nstream_stab.stop()\n

"},{"location":"gears/videogear/usage/#advanced-videogear-usage-with-camgear-backend","title":"Advanced VideoGear usage with CamGear Backend","text":"

VideoGear provides internal access to both CamGear and PiGear APIs, and thereby all additional parameters of PiGear API or CamGear API are also easily accessible within VideoGear API.

The usage example of VideoGear API with Variable Camera Properties is as follows:

This example demonstrates how to use the VideoGear API in a similar manner to the CamGear's example for controlling variable source properties. Any CamGear usage example can be implemented using the VideoGear API in a similar way.

All the supported Source Tweak Parameters can be found here \u27b6

# import required libraries\nfrom vidgear.gears import VideoGear\nimport cv2\n\n\n# define suitable tweak parameters for your stream.\noptions = {\n    \"CAP_PROP_FRAME_WIDTH\": 320, # resolution 320x240\n    \"CAP_PROP_FRAME_HEIGHT\": 240,\n    \"CAP_PROP_FPS\": 60, # framerate 60fps\n}\n\n# To open live video stream on webcam at first index(i.e. 0) \n# device and apply source tweak parameters\nstream = VideoGear(source=0, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/videogear/usage/#advanced-videogear-usage-with-pigear-backend","title":"Advanced VideoGear usage with PiGear Backend","text":"

VideoGear provides internal access to both CamGear and PiGear APIs, and thereby all additional parameters of PiGear API or CamGear API are also easily accessible within VideoGear API.

The usage example of VideoGear API with Variable Camera Properties is as follows:

This example demonstrates how to use the VideoGear API in a similar manner to the PiGear's example for using variable camera properties. Any PiGear usage example can be implemented using the VideoGear API in a similar way.

Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this backend, otherwise nothing will work.

New Picamera2 backendLegacy Picamera backend
# import required libraries\nfrom vidgear.gears import VideoGear\nfrom libcamera import Transform\nimport cv2\n\n# formulate various Picamera2 API \n# configurational parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream with defined parameters\nstream = VideoGear(enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import required libraries\nfrom vidgear.gears import VideoGear\nimport cv2\n\n# formulate various Picamera API \n# configurational parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = VideoGear(enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/videogear/usage/#using-videogear-with-colorspace-manipulation","title":"Using VideoGear with Colorspace Manipulation","text":"

VideoGear API also supports Colorspace Manipulation but NOT Direct like other VideoCapture Gears.

Important: color_space global variable is NOT Supported in VideoGear API

In following example code, we will convert source colorspace to HSV on initialization:

# import required libraries\nfrom vidgear.gears import VideoGear\nimport cv2\n\n# Open any source of your choice, like Webcam first index(i.e. 0) and change its colorspace to `HSV`\nstream = VideoGear(source=0, colorspace=\"COLOR_BGR2HSV\", logging=True).start()\n\n# loop over\nwhile True:\n\n    # read HSV frames\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the HSV frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for key if pressed\n    key = cv2.waitKey(1) & 0xFF\n\n    # check for 'q' key is pressed\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/videogear/usage/#bonus-examples","title":"Bonus Examples","text":"

Checkout more advanced VideoGear examples with unusual configuration here \u27b6

"},{"location":"gears/webgear/advanced/","title":"Advanced Usages","text":""},{"location":"gears/webgear/advanced/#webgear-api-advanced-usage","title":"WebGear API Advanced Usage:","text":"

This is a continuation of the WebGear doc \u27b6. Thereby, It's advised to first get familiarize with this API, and its requirements.

After going through following Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/webgear/advanced/#using-webgear-with-variable-colorspace","title":"Using WebGear with Variable Colorspace","text":"

WebGear by default only supports \"BGR\" colorspace frames as input, but you can use jpeg_compression_colorspace string attribute through its options dictionary parameter to specify incoming frames colorspace.

Let's implement a bare-minimum example using WebGear, where we will be sending GRAY frames to client browser:

New in v0.2.2

This example was added in v0.2.2.

This example works in conjunction with Source ColorSpace manipulation for VideoCapture Gears \u27b6

Supported jpeg_compression_colorspace colorspace values are RGB, BGR, RGBX, BGRX, XBGR, XRGB, GRAY, RGBA, BGRA, ABGR, ARGB, CMYK. More information can be found here \u27b6

# import required libraries\nimport uvicorn\nfrom vidgear.gears.asyncio import WebGear\n\n# various performance tweaks and enable grayscale input\noptions = {\n    \"frame_size_reduction\": 25,\n    \"jpeg_compression_colorspace\": \"GRAY\",  # set grayscale\n    \"jpeg_compression_quality\": 90,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# initialize WebGear app and change its colorspace to grayscale\nweb = WebGear(\n    source=\"foo.mp4\", colorspace=\"COLOR_BGR2GRAY\", logging=True, **options\n)\n\n# run this app on Uvicorn server at address http://0.0.0.0:8000/\nuvicorn.run(web(), host=\"0.0.0.0\", port=8000)\n\n# close app safely\nweb.shutdown()\n

And that's all, Now you can see output at http://localhost:8000/ address on your local machine.

"},{"location":"gears/webgear/advanced/#using-webgear-with-a-custom-sourceopencv","title":"Using WebGear with a Custom Source(OpenCV)","text":"New in v0.2.1

This example was added in v0.2.1.

WebGear allows you to easily define your own custom Source that you want to use to transform your frames before sending them onto the browser.

JPEG Frame-Compression and all of its performance enhancing attributes are disabled with a Custom Source!

Let's implement a bare-minimum example with a Custom Source using WebGear API and OpenCV:

# import necessary libs\nimport uvicorn, asyncio, cv2\nfrom vidgear.gears.asyncio import WebGear\nfrom vidgear.gears.asyncio.helper import reducer\n\n# initialize WebGear app without any source\nweb = WebGear(logging=True)\n\n# create your own custom frame producer\nasync def my_frame_producer():\n\n    # !!! define your own video source here !!!\n    # Open any video stream such as live webcam \n    # video stream on first index(i.e. 0) device\n    stream = cv2.VideoCapture(0)\n    # loop over frames\n    while True:\n        # read frame from provided source\n        (grabbed, frame) = stream.read()\n        # break if NoneType\n        if not grabbed:\n            break\n\n        # do something with your OpenCV frame here\n\n        # reducer frames size if you want more performance otherwise comment this line\n        frame = await reducer(frame, percentage=30, interpolation=cv2.INTER_AREA)  # reduce frame by 30%\n        # handle JPEG encoding\n        encodedImage = cv2.imencode(\".jpg\", frame)[1].tobytes()\n        # yield frame in byte format\n        yield (b\"--frame\\r\\nContent-Type:image/jpeg\\r\\n\\r\\n\" + encodedImage + b\"\\r\\n\")\n        await asyncio.sleep(0)\n    # close stream\n    stream.release()\n\n\n# add your custom frame producer to config\nweb.config[\"generator\"] = my_frame_producer\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

And that's all, Now you can see output at http://localhost:8000/ address.

"},{"location":"gears/webgear/advanced/#using-webgear-with-custom-mounting-points","title":"Using WebGear with Custom Mounting Points","text":"

With our highly extensible WebGear API, you can add your own mounting points, where additional files located, as follows:

# import libs\nimport uvicorn\nfrom starlette.routing import Mount\nfrom starlette.staticfiles import StaticFiles\nfrom vidgear.gears.asyncio import WebGear\n\n# various performance tweaks\noptions = {\n    \"frame_size_reduction\": 40,\n    \"jpeg_compression_quality\": 80,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": False,\n}\n\n# initialize WebGear app\nweb = WebGear(\n    source=\"foo.mp4\", logging=True, **options\n)  # enable source i.e. `test.mp4` and enable `logging` for debugging\n\n# append new route i.e. mount another folder called `test` located at `/home/foo/.vidgear/test` directory\nweb.routes.append(\n    Mount(\"/test\", app=StaticFiles(directory=\"/home/foo/.vidgear/test\"), name=\"test\")\n)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

Then you can use this folder in your HTML page, to host data-files. For example, if we have jQuery script jquery-3.3.1.slim.min.js in this folder and want to integrate it, then, we can do something like this:

<script src=\"{{ url_for('test', path='jquery-3.3.1.slim.min.js') }}\"></script>\n

"},{"location":"gears/webgear/advanced/#using-webgear-with-custom-webpage-routes","title":"Using WebGear with Custom Webpage Routes","text":"

With Webgear's flexible API, you can even add your additional HTML Static webpages without any extra efforts.

Suppose we want to add a simple hello world webpage to our WebGear server. So let's create a bare-minimum hello.html file with HTML code as follows:

<html>\n   <header>\n      <title>This is Hello world page</title>\n   </header>\n   <body>\n      <h1>Hello World</h1>\n      <p>how ya doing?</p>\n   </body>\n</html>\n

Then in our application code, we can integrate this webpage route, as follows:

# import libs\nimport uvicorn, asyncio\nfrom starlette.templating import Jinja2Templates\nfrom starlette.routing import Route\nfrom vidgear.gears.asyncio import WebGear\n\n# Build out Jinja2 template render at `/home/foo/.vidgear/custom_template` path in which our `hello.html` file is located\ntemplate = Jinja2Templates(directory=\"/home/foo/.vidgear/custom_template\")\n\n# render and return our webpage template\nasync def hello_world(request):\n    page = \"hello.html\"\n    context = {\"request\": request}\n    return template.TemplateResponse(page, context)\n\n\n# add various performance tweaks as usual\noptions = {\n    \"frame_size_reduction\": 40,\n    \"jpeg_compression_quality\": 80,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": False,\n}\n\n# initialize WebGear app with a valid source\nweb = WebGear(\n    source=\"/home/foo/foo1.mp4\", logging=True, **options\n)  # enable source i.e. `test.mp4` and enable `logging` for debugging\n\n# append new route to point our rendered webpage\nweb.routes.append(Route(\"/hello\", endpoint=hello_world))\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n
And that's all, Now you can see output at http://localhost:8000/hello address.

"},{"location":"gears/webgear/advanced/#using-webgear-with-middlewares","title":"Using WebGear with MiddleWares","text":"

WebGear natively supports ASGI middleware classes with Starlette for implementing behavior that is applied across your entire ASGI application easily.

New in v0.2.2

This example was added in v0.2.2.

All supported middlewares can be found here \u27b6

For this example, let's use CORSMiddleware for implementing appropriate CORS headers to outgoing responses in our application in order to allow cross-origin requests from browsers, as follows:

The default parameters used by the CORSMiddleware implementation are restrictive by default, so you'll need to explicitly enable particular origins, methods, or headers, in order for browsers to be permitted to use them in a Cross-Domain context.

Starlette provides several arguments for enabling origins, methods, or headers for CORSMiddleware API. More information can be found here \u27b6

# import libs\nimport uvicorn, asyncio\nfrom starlette.middleware import Middleware\nfrom starlette.middleware.cors import CORSMiddleware\nfrom vidgear.gears.asyncio import WebGear\n\n# add various performance tweaks as usual\noptions = {\n    \"frame_size_reduction\": 40,\n    \"jpeg_compression_quality\": 80,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": False,\n}\n\n# initialize WebGear app with a valid source\nweb = WebGear(\n    source=\"/home/foo/foo1.mp4\", logging=True, **options\n)  # enable source i.e. `test.mp4` and enable `logging` for debugging\n\n# define and assign suitable cors middlewares\nweb.middleware = [\n    Middleware(\n        CORSMiddleware,\n        allow_origins=[\"*\"],\n        allow_credentials=True,\n        allow_methods=[\"*\"],\n        allow_headers=[\"*\"],\n    )\n]\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n
And that's all, Now you can see output at http://localhost:8000 address.

"},{"location":"gears/webgear/advanced/#rules-for-altering-webgear-files-and-folders","title":"Rules for Altering WebGear Files and Folders","text":"

WebGear gives us complete freedom of altering data files generated in Auto-Generation Process, But you've to keep the following rules in mind:

"},{"location":"gears/webgear/advanced/#rules-for-altering-data-files","title":"Rules for Altering Data Files","text":""},{"location":"gears/webgear/advanced/#rules-for-altering-data-folders","title":"Rules for Altering Data Folders","text":""},{"location":"gears/webgear/advanced/#bonus-examples","title":"Bonus Examples","text":"

Checkout more advanced WebGear examples with unusual configuration here \u27b6

"},{"location":"gears/webgear/overview/","title":"Overview","text":""},{"location":"gears/webgear/overview/#webgear-api","title":"WebGear API","text":"WebGear API's Video Server running at http://localhost:8000/ address."},{"location":"gears/webgear/overview/#overview","title":"Overview","text":"

WebGear is a powerful ASGI Video-Broadcaster API ideal for transmitting Motion-JPEG-frames from a single source to multiple recipients via the browser.

WebGear API works on Starlette's ASGI application and provides a highly extensible and flexible async wrapper around its complete framework. WebGear can flexibly interact with Starlette's ecosystem of shared middleware, mountable applications, Response classes, Routing tables, Static Files, Templating engine(with Jinja2), etc.

WebGear API uses an intraframe-only compression scheme under the hood where the sequence of video-frames are first encoded as JPEG-DIB (JPEG with Device-Independent Bit compression) and then streamed over HTTP using Starlette's Multipart Streaming Response and a Uvicorn ASGI Server. This method imposes lower processing and memory requirements, but the quality is not the best, since JPEG compression is not very efficient for motion video.

In layman's terms, WebGear acts as a powerful Video Broadcaster that transmits live video-frames to any web-browser in the network. Additionally, WebGear API also provides internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs, thereby granting it exclusive power for transferring frames incoming from any source to the network.

"},{"location":"gears/webgear/overview/#data-files-auto-generation-workflow-for-webgear","title":"Data-Files Auto-Generation WorkFlow for WebGear","text":"Disabling Auto-Generation process in WebGear

Starting with vidgear v0.3.0, you can now completely disable Auto-Generation process in WebGear API using skip_generate_webdata optional boolean attribute. When {skip_generate_webdata:True}, no default data files will be downloaded or validated during initialization.

Only /video route is available when {skip_generate_webdata:True} in WebGear API. All other default routes will be JSONResponses with 404/500 status codes.

Customizing default video endpoint path

Starting with vidgear v0.3.1, you can change default /video video endpoint path to any alphanumeric string value, using custom_video_endpoint optional string attribute. For example:

Only alphanumeric string with no space in between are allowed as custom_video_endpoint value. Any other value will be discarded.

WebGear's Default Theme which expects only default /video video endpoint path, will fail to work, if it is customized to any other value using this custom_video_endpoint attribute.

# custom alphanumeric video endpoint string\noptions = {\"custom_video_endpoint\": \"xyz\"}\n\n# initialize WebGear app\nweb = WebGear(source=\"foo.mp4\", logging=True, **options)\n
Hence, default video endpoint will now be available at /xyz path.

On initializing WebGear API, it automatically checks for three critical data files(i.e index.html, 404.html & 500.html) inside the templates folder of the webgear directory at the default location which gives rise to the following two possible scenario:

"},{"location":"gears/webgear/overview/#default-location","title":"Default Location","text":"

Identifying Default location

You can set logging=True during initialization, for easily identifying the selected default location, which will be something like this on a Linux machine:

WebGear :: DEBUG :: `/home/foo/.vidgear` is the default location for saving WebGear data-files.\n
"},{"location":"gears/webgear/overview/#auto-generation-process","title":"Auto-Generation process","text":"

Info

"},{"location":"gears/webgear/overview/#webgears-default-template","title":"WebGear's Default Template","text":"New in v0.2.1

New Standalone WebGear's Default Theme was added in v0.2.1.

The WebGear API by default uses simple & elegant WebGear's Default Theme which looks like something as follows:

"},{"location":"gears/webgear/overview/#indexhtml","title":"Index.html","text":"

Can be accessed by visiting WebGear app server, running at http://localhost:8000/:

"},{"location":"gears/webgear/overview/#404html","title":"404.html","text":"

Appears when respective URL is not found, for example http://localhost:8000/ok:

"},{"location":"gears/webgear/overview/#500html","title":"500.html","text":"

Appears when an API Error is encountered:

If logging is enabled and an error occurs, then instead of displaying this 500 handler, WebGear will respond with a traceback response.

"},{"location":"gears/webgear/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through WebGear Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/webgear/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/webgear/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/webgear/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/webgear/params/","title":"Parameters","text":""},{"location":"gears/webgear/params/#webgear-api-parameters","title":"WebGear API Parameters","text":"

WebGear provides a special internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs and their parameters.

"},{"location":"gears/webgear/params/#enablepicamera","title":"enablePiCamera","text":"

This parameter provide direct access to PiGear or CamGear APIs respectively in WebGear. This means the if enablePiCamera flag is True, the PiGear API will be accessed, and if False, the CamGear API will be accessed.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

WebGear(enablePiCamera=True) # enable access to PiGear API\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/webgear/params/#options","title":"options","text":"

This parameter can be used to pass user-defined parameter to WebGear API by formatting them as this parameter's attribute.

Data-Type: Dictionary

Default Value: Its default value is {}

"},{"location":"gears/webgear/params/#webgear-specific-attributes","title":"WebGear Specific attributes","text":"

"},{"location":"gears/webgear/params/#parameters-for-stabilizer-backend","title":"Parameters for Stabilizer Backend","text":"

Enable this backend with stabilize=True in WebGear.

"},{"location":"gears/webgear/params/#stabilize","title":"stabilize","text":"

This parameter enable access to Stabilizer Class for stabilizing frames, i.e. can be set to True(to enable) or unset to False(to disable).

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

WebGear(stabilize=True) # enable stablization\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/webgear/params/#options_1","title":"options","text":"

This parameter can be used in addition, to pass user-defined parameters supported by Stabilizer Class. These parameters can be formatted as this parameter's attribute.

Supported dictionary attributes for Stabilizer Class are:

"},{"location":"gears/webgear/params/#parameters-for-camgear-backend","title":"Parameters for CamGear backend","text":"

Enable this backend with enablePiCamera=False in WebGear. Default is also False.

"},{"location":"gears/webgear/params/#source","title":"source","text":"

WebGear API will throw RuntimeError if source provided is invalid.

This parameter defines the source for the input stream.

Data-Type: Based on input.

Default Value: Its default value is 0.

Its valid input can be one of the following:

"},{"location":"gears/webgear/params/#stream_mode","title":"stream_mode","text":"

This parameter controls the Stream Mode, .i.e if enabled(stream_mode=True), the CamGear API will interpret the given source input as YouTube URL address.

Due to a FFmpeg bug that causes video to freeze frequently in OpenCV, It is advised to always use GStreamer backend for any livestream videos. Checkout this FAQ for compiling OpenCV with GStreamer support.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

Supported Streaming Websites

The complete list of all supported Streaming Websites URLs can be found here \u27b6

WebGear(source='https://youtu.be/bvetuLwJIkA', stream_mode=True)\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/webgear/params/#backend","title":"backend","text":"

This parameter manually selects the backend for OpenCV's VideoCapture class (only if specified).

Data-Type: Integer

Default Value: Its default value is 0

Usage:

All supported backends are listed here \u27b6

Its value can be for e.g. backend = cv2.CAP_DSHOW for selecting Direct Show as backend:

WebGear(source=0, backend = cv2.CAP_DSHOW)\n

"},{"location":"gears/webgear/params/#options_2","title":"options","text":"

This parameter provides the ability to alter various Source Tweak Parameters available within OpenCV's VideoCapture API properties.

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

All supported parameters are listed here \u27b6

The desired parameters can be passed to WebGear API by formatting them as this parameter's attributes, as follows:

# formatting parameters as dictionary attributes\noptions = {\"CAP_PROP_FRAME_WIDTH\":320, \"CAP_PROP_FRAME_HEIGHT\":240, \"CAP_PROP_FPS\":60}\n# assigning it\nWebGear(source=0, **options)\n

"},{"location":"gears/webgear/params/#parameters-for-pigear-backend","title":"Parameters for PiGear backend","text":"

Enable this backend with enablePiCamera=True in WebGear.

"},{"location":"gears/webgear/params/#camera_num","title":"camera_num","text":"

This parameter selects the camera index to be used as the source, allowing you to drive these multiple cameras simultaneously from within a single Python session. Its value can only be zero or greater, otherwise, WebGear API will throw ValueError for any negative value.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

# select Camera Module at index `1`\nWebGear(enablePiCamera=True, camera_num=1)\n

The complete usage example demonstrating the usage of the camera_num parameter is available here \u27b6.

"},{"location":"gears/webgear/params/#resolution","title":"resolution","text":"

This parameter controls the resolution - a tuple (i.e. (width,height)) of two values giving the width and height of the output frames.

Make sure both width and height values should be at least 64.

When using the Picamera2 backend, the resolution parameter will be OVERRIDDEN, if the user explicitly defines the output_size property of the sensor configurational parameter.

Data-Type: Tuple

Default Value: Its default value is (640,480).

Usage:

WebGear(enablePiCamera=True, resolution=(1280,720)) # sets 1280x720 resolution\n

"},{"location":"gears/webgear/params/#framerate","title":"framerate","text":"

This parameter controls the framerate of the source.

Data-Type: integer/float

Default Value: Its default value is 30.

Usage:

WebGear(enablePiCamera=True, framerate=60) # sets 60fps framerate\n

"},{"location":"gears/webgear/params/#options_3","title":"options","text":"

This dictionary parameter in the internal PiGear API backend allows you to control various camera settings for both the picamera2 and legacy picamera backends and some internal API tasks. These settings include:

"},{"location":"gears/webgear/params/#a-configurational-camera-parameters","title":"A. Configurational Camera Parameters","text":""},{"location":"gears/webgear/params/#b-user-defined-parameters","title":"B. User-defined Parameters","text":"

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

The complete usage example demonstrating the usage of the options parameter is available here \u27b6.

You can format these user-defined and configurational parameters as attributes of this options dictionary parameter as follows:

New Picamera2 backendLegacy Picamera backend
# formulate various Picamera2 API parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"exposure_compensation\": 15,\n    \"sensor\": {\"output_size\": (480, 320)},  # !!! will override `resolution` !!!\n}\n\n# open pi video stream with defined parameters\nstream = WebGear(enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options).start()\n
# formulate various Picamera API parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = WebGear(enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options).start()\n

"},{"location":"gears/webgear/params/#common-parameters","title":"Common Parameters","text":"

These are common parameters that works with every backend in WebGear.

"},{"location":"gears/webgear/params/#colorspace","title":"colorspace","text":"

This parameter selects the colorspace of the source stream.

Data-Type: String

Default Value: Its default value is None.

Usage:

All supported colorspace values are given here \u27b6

WebGear(colorspace=\"COLOR_BGR2HSV\")\n

Its complete usage example is given here \u27b6

"},{"location":"gears/webgear/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

WebGear(logging=True)\n

"},{"location":"gears/webgear/params/#time_delay","title":"time_delay","text":"

This parameter set the time delay (in seconds) before the WebGear API start reading the frames. This delay is only required if the source required some warm-up delay before starting up.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

WebGear(time_delay=1)  # set 1 seconds time delay\n

"},{"location":"gears/webgear/usage/","title":"Usage Examples","text":""},{"location":"gears/webgear/usage/#webgear-api-usage-examples","title":"WebGear API Usage Examples:","text":""},{"location":"gears/webgear/usage/#requirements","title":"Requirements","text":""},{"location":"gears/webgear/usage/#installation-with-asyncio-support","title":"Installation with Asyncio Support","text":"

WebGear API is the part of asyncio package of VidGear, thereby you need to install VidGear with asyncio support as follows:

pip install vidgear[asyncio]\n
"},{"location":"gears/webgear/usage/#asgi-server","title":"ASGI Server","text":"

You'll also need to install an ASGI Server to run following WebGear usage examples, and by default WebGear ships the state-of-the-art uvicorn Server. But you can also use other ASGI server such as daphne, or hypercorn with it.

"},{"location":"gears/webgear/usage/#performance-enhancements","title":"Performance Enhancements","text":"

WebGear provides certain performance enhancing attributes for its options dictionary parameter to cope with performance-throttling.

Performance Enhancing Attributes

"},{"location":"gears/webgear/usage/#bare-minimum-usage-with-performance-enhancements","title":"Bare-Minimum Usage with Performance Enhancements","text":"

Let's implement our Bare-Minimum usage example with these Performance Enhancing Attributes \u27b6 for speeding up the output.

"},{"location":"gears/webgear/usage/#running-programmatically","title":"Running Programmatically","text":"

You can access and run WebGear VideoStreamer Server programmatically in your python script in just a few lines of code, as follows:

For accessing WebGear on different Client Devices on the network, use \"0.0.0.0\" as host value instead of \"localhost\" on Host Machine. More information can be found here \u27b6

# import required libraries\nimport uvicorn\nfrom vidgear.gears.asyncio import WebGear\n\n# various performance tweaks\noptions = {\n    \"frame_size_reduction\": 40,\n    \"jpeg_compression_quality\": 80,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": False,\n}\n\n# initialize WebGear app\nweb = WebGear(source=\"foo.mp4\", logging=True, **options)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

which can be accessed on any browser on your machine at http://localhost:8000/.

"},{"location":"gears/webgear/usage/#running-from-terminal","title":"Running from Terminal","text":"

You can also access and run WebGear Server directly from the terminal commandline. The following command will run a WebGear VideoStreamer server at http://localhost:8000/:

Make sure your PYTHON_PATH is set to python 3.7+ versions only.

If you're using --options/-op flag, then kindly wrap your dictionary value in single '' quotes.

python3 -m vidgear.gears.asyncio --source test.avi --logging True --options '{\"frame_size_reduction\": 50, \"jpeg_compression_quality\": 80, \"jpeg_compression_fastdct\": True, \"jpeg_compression_fastupsample\": False}'\n

which can also be accessed on any browser on the network at http://localhost:8000/.

Advanced Usage from Terminal

You can run python3 -m vidgear.gears.asyncio -h help command to see all the advanced settings, as follows:

usage: python -m vidgear.gears.asyncio [-h] [-m MODE] [-s SOURCE] [-ep ENABLEPICAMERA] [-S STABILIZE]\n            [-cn CAMERA_NUM] [-yt stream_mode] [-b BACKEND] [-cs COLORSPACE]\n            [-r RESOLUTION] [-f FRAMERATE] [-td TIME_DELAY]\n            [-ip IPADDRESS] [-pt PORT] [-l LOGGING] [-op OPTIONS]\n\nRuns WebGear/WebGear_RTC Video Server through terminal.\n\noptional arguments:\n  -h, --help            show this help message and exit\n  -m {mjpeg,webrtc}, --mode {mjpeg,webrtc}\n                        Whether to use \"MJPEG\" or \"WebRTC\" mode for streaming.\n  -s SOURCE, --source SOURCE\n                        Path to input source for CamGear API.\n  -ep ENABLEPICAMERA, --enablePiCamera ENABLEPICAMERA\n                        Sets the flag to access PiGear(if True) or otherwise\n                        CamGear API respectively.\n  -S STABILIZE, --stabilize STABILIZE\n                        Enables/disables real-time video stabilization.\n  -cn CAMERA_NUM, --camera_num CAMERA_NUM\n                        Sets the camera module index that will be used by\n                        PiGear API.\n  -yt STREAM_MODE, --stream_mode STREAM_MODE\n                        Enables YouTube Mode in CamGear API.\n  -b BACKEND, --backend BACKEND\n                        Sets the backend of the video source in CamGear API.\n  -cs COLORSPACE, --colorspace COLORSPACE\n                        Sets the colorspace of the output video stream.\n  -r RESOLUTION, --resolution RESOLUTION\n                        Sets the resolution (width,height) for camera module\n                        in PiGear API.\n  -f FRAMERATE, --framerate FRAMERATE\n                        Sets the framerate for camera module in PiGear API.\n  -td TIME_DELAY, --time_delay TIME_DELAY\n                        Sets the time delay(in seconds) before start reading\n                        the frames.\n  -ip IPADDRESS, --ipaddress IPADDRESS\n                        Uvicorn binds the socket to this ipaddress.\n  -pt PORT, --port PORT\n                        Uvicorn binds the socket to this port.\n  -l LOGGING, --logging LOGGING\n                        Enables/disables error logging, essential for\n                        debugging.\n  -op OPTIONS, --options OPTIONS\n                        Sets the parameters supported by APIs(whichever being\n                        accessed) to the input videostream, But make sure to\n                        wrap your dict value in single or double quotes.\n

"},{"location":"gears/webgear_rtc/advanced/","title":"Advanced Usages","text":""},{"location":"gears/webgear_rtc/advanced/#webgear_rtc-api-advanced-usage","title":"WebGear_RTC API Advanced Usage:","text":"

This is a continuation of the WebGear_RTC doc \u27b6. Thereby, It's advised to first get familiarize with this API, and its requirements.

After going through following Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/webgear_rtc/advanced/#using-webgear_rtc-as-real-time-broadcaster","title":"Using WebGear_RTC as Real-time Broadcaster","text":"

WebGear_RTC by default only supports one-to-one peer connection with a single consumer or client. But you can use enable_live_broadcast boolean attribute through its options dictionary parameter to easily enable live broadcast/stream to multiple peer consumers/clients at the same time.

Let's implement a bare-minimum example using WebGear_RTC as Real-time Broadcaster:

enable_infinite_frames is enforced by default with this(enable_live_broadcast) attribute.

For accessing WebGear_RTC on different Client Devices on the network, we use \"0.0.0.0\" as host value instead of \"localhost\" on Host Machine. More information can be found here \u27b6

# import required libraries\nimport uvicorn\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# various performance tweaks and enable live broadcasting\noptions = {\n    \"frame_size_reduction\": 25,\n    \"enable_live_broadcast\": True,\n}\n\n# initialize WebGear_RTC app\nweb = WebGear_RTC(source=\"foo.mp4\", logging=True, **options)\n\n# run this app on Uvicorn server at address http://0.0.0.0:8000/\nuvicorn.run(web(), host=\"0.0.0.0\", port=8000)\n\n# close app safely\nweb.shutdown()\n

And that's all, Now you can see output at http://localhost:8000/ address on your local machine.

"},{"location":"gears/webgear_rtc/advanced/#using-webgear_rtc-with-a-custom-sourceopencv","title":"Using WebGear_RTC with a Custom Source(OpenCV)","text":"

WebGear_RTC provides custom_stream attribute with its options parameter that allows you to easily define your own Custom Streaming Class with suitable source that you want to use to transform your frames before sending them onto the browser.

Let's implement a bare-minimum example with a Custom Source using WebGear_RTC API and OpenCV:

New in v0.2.4

This implementation was added in v0.2.4.

Auto-Reconnection or Auto-Refresh works out-of-the-box with this implementation.

Make sure your Custom Streaming Class at-least implements read() and stop() methods as shown in following example, otherwise WebGear_RTC will throw ValueError!

Using Vidgear's VideoCapture APIs instead of OpenCV

You can directly replace Custom Streaming Class(Custom_Stream_Class in following example) with any VideoCapture APIs. These APIs implements read() and stop() methods by-default, so they're also supported out-of-the-box.

See this example \u27b6 for more information.

# import necessary libs\nimport uvicorn, cv2\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# create your own custom streaming class\nclass Custom_Stream_Class:\n    \"\"\"\n    Custom Streaming using OpenCV\n    \"\"\"\n\n    def __init__(self, source=0):\n\n        # !!! define your own video source here !!!\n        self.source = cv2.VideoCapture(source)\n\n        # define running flag\n        self.running = True\n\n    def read(self):\n\n        # don't forget this function!!!\n\n        # check if source was initialized or not\n        if self.source is None:\n            return None\n        # check if we're still running\n        if self.running:\n            # read frame from provided source\n            (grabbed, frame) = self.source.read()\n            # check if frame is available\n            if grabbed:\n\n                # do something with your OpenCV frame here\n\n                # lets convert frame to gray for this example\n                gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n\n                # return our gray frame\n                return gray\n            else:\n                # signal we're not running now\n                self.running = False\n        # return None-type\n        return None\n\n    def stop(self):\n\n        # don't forget this function!!!\n\n        # flag that we're not running\n        self.running = False\n        # close stream\n        if not self.source is None:\n            self.source.release()\n\n# assign your Custom Streaming Class with adequate source (for e.g. foo.mp4) \n# to `custom_stream` attribute in options parameter\noptions = {\"custom_stream\": Custom_Stream_Class(source=\"foo.mp4\")}\n\n# initialize WebGear_RTC app without any source\nweb = WebGear_RTC(logging=True, **options)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

And that's all, Now you can see output at http://localhost:8000/ address.

"},{"location":"gears/webgear_rtc/advanced/#using-webgear_rtc-with-custom-mounting-points","title":"Using WebGear_RTC with Custom Mounting Points","text":"

With our highly extensible WebGear_RTC API, you can add your own mounting points, where additional files located, as follows:

# import libs\nimport uvicorn\nfrom starlette.routing import Mount\nfrom starlette.staticfiles import StaticFiles\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# various performance tweaks\noptions = {\n    \"frame_size_reduction\": 25,\n}\n\n# initialize WebGear_RTC app\nweb = WebGear_RTC(\n    source=\"foo.mp4\", logging=True, **options\n)  # enable source i.e. `test.mp4` and enable `logging` for debugging\n\n# append new route i.e. mount another folder called `test` located at `/home/foo/.vidgear/test` directory\nweb.routes.append(\n    Mount(\"/test\", app=StaticFiles(directory=\"/home/foo/.vidgear/test\"), name=\"test\")\n)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

Then you can use this folder in your HTML page, to host data-files. For example, if we have jQuery script jquery-3.3.1.slim.min.js in this folder and want to integrate it, then, we can do something like this:

<script src=\"{{ url_for('test', path='jquery-3.3.1.slim.min.js') }}\"></script>\n

"},{"location":"gears/webgear_rtc/advanced/#using-webgear_rtc-with-custom-webpage-routes","title":"Using WebGear_RTC with Custom Webpage Routes","text":"

With Webgear_RTC's flexible API, you can even add your additional HTML Static webpages without any extra efforts.

Suppose we want to add a simple hello world webpage to our WebGear_RTC server. So let's create a bare-minimum hello.html file with HTML code as follows:

<html>\n   <header>\n      <title>This is Hello world page</title>\n   </header>\n   <body>\n      <h1>Hello World</h1>\n      <p>how ya doing?</p>\n   </body>\n</html>\n

Then in our application code, we can integrate this webpage route, as follows:

# import libs\nimport uvicorn, asyncio\nfrom starlette.templating import Jinja2Templates\nfrom starlette.routing import Route\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# Build out Jinja2 template render at `/home/foo/.vidgear/custom_template` path in which our `hello.html` file is located\ntemplate = Jinja2Templates(directory=\"/home/foo/.vidgear/custom_template\")\n\n# render and return our webpage template\nasync def hello_world(request):\n    page = \"hello.html\"\n    context = {\"request\": request}\n    return template.TemplateResponse(page, context)\n\n\n# add various performance tweaks as usual\noptions = {\n    \"frame_size_reduction\": 25,\n}\n\n# initialize WebGear_RTC app with a valid source\nweb = WebGear_RTC(\n    source=\"/home/foo/foo1.mp4\", logging=True, **options\n)  # enable source i.e. `test.mp4` and enable `logging` for debugging\n\n# append new route to point our rendered webpage\nweb.routes.append(Route(\"/hello\", endpoint=hello_world))\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n
And that's all, Now you can see output at http://localhost:8000/hello address.

"},{"location":"gears/webgear_rtc/advanced/#using-webgear_rtc-with-middlewares","title":"Using WebGear_RTC with MiddleWares","text":"

WebGear_RTC also natively supports ASGI middleware classes with Starlette for implementing behavior that is applied across your entire ASGI application easily.

New in v0.2.2

This example was added in v0.2.2.

All supported middlewares can be found here \u27b6

For this example, let's use CORSMiddleware for implementing appropriate CORS headers to outgoing responses in our application in order to allow cross-origin requests from browsers, as follows:

The default parameters used by the CORSMiddleware implementation are restrictive by default, so you'll need to explicitly enable particular origins, methods, or headers, in order for browsers to be permitted to use them in a Cross-Domain context.

Starlette provides several arguments for enabling origins, methods, or headers for CORSMiddleware API. More information can be found here \u27b6

# import libs\nimport uvicorn, asyncio\nfrom starlette.middleware import Middleware\nfrom starlette.middleware.cors import CORSMiddleware\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# add various performance tweaks as usual\noptions = {\n    \"frame_size_reduction\": 25,\n}\n\n# initialize WebGear_RTC app with a valid source\nweb = WebGear_RTC(\n    source=\"/home/foo/foo1.mp4\", logging=True, **options\n)  # enable source i.e. `test.mp4` and enable `logging` for debugging\n\n# define and assign suitable cors middlewares\nweb.middleware = [\n    Middleware(\n        CORSMiddleware,\n        allow_origins=[\"*\"],\n        allow_credentials=True,\n        allow_methods=[\"*\"],\n        allow_headers=[\"*\"],\n    )\n]\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

And that's all, Now you can see output at http://localhost:8000 address.

"},{"location":"gears/webgear_rtc/advanced/#rules-for-altering-webgear_rtc-files-and-folders","title":"Rules for Altering WebGear_RTC Files and Folders","text":"

WebGear_RTC gives us complete freedom of altering data files generated in Auto-Generation Process, But you've to keep the following rules in mind:

"},{"location":"gears/webgear_rtc/advanced/#rules-for-altering-data-files","title":"Rules for Altering Data Files","text":""},{"location":"gears/webgear_rtc/advanced/#rules-for-altering-data-folders","title":"Rules for Altering Data Folders","text":""},{"location":"gears/webgear_rtc/advanced/#bonus-examples","title":"Bonus Examples","text":"

Checkout more advanced WebGear_RTC examples with unusual configuration here \u27b6

"},{"location":"gears/webgear_rtc/overview/","title":"Overview","text":""},{"location":"gears/webgear_rtc/overview/#webgear_rtc-api","title":"WebGear_RTC API","text":"WebGear_RTC API's Video Server running at http://localhost:8000/ address."},{"location":"gears/webgear_rtc/overview/#overview","title":"Overview","text":"

WebGear_RTC is similar to WeGear API in many aspects but utilizes WebRTC technology under the hood instead of Motion JPEG, which makes it suitable for building powerful video-streaming solutions for all modern browsers as well as native clients available on all major platforms.

New in v0.2.1

WebGear_RTC API was added in v0.2.1.

WebGear_RTC is implemented with the help of aiortc library which is built on top of asynchronous I/O framework for Web Real-Time Communication (WebRTC) and Object Real-Time Communication (ORTC) and supports many features like SDP generation/parsing, Interactive Connectivity Establishment with half-trickle and mDNS support, DTLS key and certificate generation, DTLS handshake, etc.

WebGear_RTC can handle multiple consumers seamlessly and provides native support for ICE (Interactive Connectivity Establishment) protocol, STUN (Session Traversal Utilities for NAT), and TURN (Traversal Using Relays around NAT) servers that help us to seamlessly establish direct media connection with the remote peers for uninterrupted data flow. It also allows us to define our custom streaming class with suitable source to transform frames easily before sending them across the network(see this doc example).

WebGear_RTC API works in conjunction with Starlette ASGI application and can also flexibly interact with Starlette's ecosystem of shared middleware, mountable applications, Response classes, Routing tables, Static Files, Templating engine(with Jinja2), etc.

Additionally, WebGear_RTC API also provides internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs.

"},{"location":"gears/webgear_rtc/overview/#data-files-auto-generation-workflow-for-webgear_rtc","title":"Data-Files Auto-Generation WorkFlow for WebGear_RTC","text":"

Same as WebGear, WebGear_RTC API automatically checks for three critical data files(i.e index.html, 404.html & 500.html) on initialization inside the templates folder of the webgear_rtc directory at the default location which gives rise to the following two possible scenario:

"},{"location":"gears/webgear_rtc/overview/#default-location","title":"Default Location","text":"

Identifying Default location

You can set logging=True during initialization, for easily identifying the selected default location, which will be something like this on a Linux machine:

WebGear_RTC :: DEBUG :: `/home/foo/.vidgear` is the default location for saving WebGear_RTC data-files.\n
"},{"location":"gears/webgear_rtc/overview/#auto-generation-process","title":"Auto-Generation process","text":"

Info

"},{"location":"gears/webgear_rtc/overview/#webgear_rtcs-default-template","title":"WebGear_RTC's Default Template","text":"

The WebGear_RTC API by default uses simple & elegant WebGear_RTC's Default Theme which looks like something as follows:

"},{"location":"gears/webgear_rtc/overview/#indexhtml","title":"Index.html","text":"

Can be accessed by visiting WebGear_RTC app server, running at http://localhost:8000/:

"},{"location":"gears/webgear_rtc/overview/#404html","title":"404.html","text":"

Appears when respective URL is not found, for example http://localhost:8000/ok:

"},{"location":"gears/webgear_rtc/overview/#500html","title":"500.html","text":"

Appears when an API Error is encountered:

If logging is enabled and an error occurs, then instead of displaying this 500 handler, WebGear_RTC will respond with a traceback response.

"},{"location":"gears/webgear_rtc/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through WebGear_RTC Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/webgear_rtc/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/webgear_rtc/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/webgear_rtc/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/webgear_rtc/params/","title":"Parameters","text":""},{"location":"gears/webgear_rtc/params/#webgear_rtc-api-parameters","title":"WebGear_RTC API Parameters","text":"

WebGear_RTC provides a special internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs and their parameters.

"},{"location":"gears/webgear_rtc/params/#enablepicamera","title":"enablePiCamera","text":"

This parameter provide direct access to PiGear or CamGear APIs respectively in WebGear_RTC. This means the if enablePiCamera flag is True, the PiGear API will be accessed, and if False, the CamGear API will be accessed.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

WebGear_RTC(enablePiCamera=True) # enable access to PiGear API\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/webgear_rtc/params/#options","title":"options","text":"

This parameter can be used to pass user-defined parameter to WebGear_RTC API by formatting them as this parameter's attribute.

Data-Type: Dictionary

Default Value: Its default value is {}

"},{"location":"gears/webgear_rtc/params/#webgear_rtc-specific-attributes","title":"WebGear_RTC Specific attributes","text":"

"},{"location":"gears/webgear_rtc/params/#parameters-for-stabilizer-backend","title":"Parameters for Stabilizer Backend","text":"

Enable this backend with stabilize=True in WebGear_RTC. Default is also False.

"},{"location":"gears/webgear_rtc/params/#stabilize","title":"stabilize","text":"

This parameter enable access to Stabilizer Class for stabilizing frames, i.e. can be set to True(to enable) or unset to False(to disable).

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

WebGear_RTC(stabilize=True) # enable stablization\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/webgear_rtc/params/#options_1","title":"options","text":"

This parameter can be used in addition, to pass user-defined parameters supported by Stabilizer Class. These parameters can be formatted as this parameter's attribute.

Supported dictionary attributes for Stabilizer Class are:

"},{"location":"gears/webgear_rtc/params/#parameters-for-camgear-backend","title":"Parameters for CamGear backend","text":"

Enable this backend with enablePiCamera=False in WebGear_RTC.

"},{"location":"gears/webgear_rtc/params/#source","title":"source","text":"

WebGear_RTC API will throw RuntimeError if source provided is invalid.

This parameter defines the source for the input stream.

Data-Type: Based on input.

Default Value: Its default value is 0.

Its valid input can be one of the following:

"},{"location":"gears/webgear_rtc/params/#stream_mode","title":"stream_mode","text":"

This parameter controls the Stream Mode, .i.e if enabled(stream_mode=True), the CamGear API will interpret the given source input as YouTube URL address.

Due to a FFmpeg bug that causes video to freeze frequently in OpenCV, It is advised to always use GStreamer backend for any livestream videos. Checkout this FAQ for compiling OpenCV with GStreamer support.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

Supported Streaming Websites

The complete list of all supported Streaming Websites URLs can be found here \u27b6

WebGear_RTC(source='https://youtu.be/bvetuLwJIkA', stream_mode=True)\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/webgear_rtc/params/#backend","title":"backend","text":"

This parameter manually selects the backend for OpenCV's VideoCapture class (only if specified).

Data-Type: Integer

Default Value: Its default value is 0

Usage:

All supported backends are listed here \u27b6

Its value can be for e.g. backend = cv2.CAP_DSHOW for selecting Direct Show as backend:

WebGear_RTC(source=0, backend = cv2.CAP_DSHOW)\n

"},{"location":"gears/webgear_rtc/params/#options_2","title":"options","text":"

This parameter provides the ability to alter various Source Tweak Parameters available within OpenCV's VideoCapture API properties.

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

All supported parameters are listed here \u27b6

The desired parameters can be passed to WebGear_RTC API by formatting them as this parameter's attributes, as follows:

# formatting parameters as dictionary attributes\noptions = {\"CAP_PROP_FRAME_WIDTH\":320, \"CAP_PROP_FRAME_HEIGHT\":240, \"CAP_PROP_FPS\":60}\n# assigning it\nWebGear_RTC(source=0, **options)\n

"},{"location":"gears/webgear_rtc/params/#parameters-for-pigear-backend","title":"Parameters for PiGear backend","text":"

Enable this backend with enablePiCamera=True in WebGear_RTC.

"},{"location":"gears/webgear_rtc/params/#camera_num","title":"camera_num","text":"

This parameter selects the camera index to be used as the source, allowing you to drive these multiple cameras simultaneously from within a single Python session. Its value can only be zero or greater, otherwise, WebGear_RTC API will throw ValueError for any negative value.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

# select Camera Module at index `1`\nWebGear_RTC(enablePiCamera=True, camera_num=1)\n

The complete usage example demonstrating the usage of the camera_num parameter is available here \u27b6.

"},{"location":"gears/webgear_rtc/params/#resolution","title":"resolution","text":"

This parameter controls the resolution - a tuple (i.e. (width,height)) of two values giving the width and height of the output frames.

Make sure both width and height values should be at least 64.

When using the Picamera2 backend, the resolution parameter will be OVERRIDDEN, if the user explicitly defines the output_size property of the sensor configurational parameter.

Data-Type: Tuple

Default Value: Its default value is (640,480).

Usage:

WebGear_RTC(enablePiCamera=True, resolution=(1280,720)) # sets 1280x720 resolution\n

"},{"location":"gears/webgear_rtc/params/#framerate","title":"framerate","text":"

This parameter controls the framerate of the source.

Data-Type: integer/float

Default Value: Its default value is 30.

Usage:

WebGear_RTC(enablePiCamera=True, framerate=60) # sets 60fps framerate\n

"},{"location":"gears/webgear_rtc/params/#options_3","title":"options","text":"

This dictionary parameter in the internal PiGear API backend allows you to control various camera settings for both the picamera2 and legacy picamera backends and some internal API tasks. These settings include:

"},{"location":"gears/webgear_rtc/params/#a-configurational-camera-parameters","title":"A. Configurational Camera Parameters","text":""},{"location":"gears/webgear_rtc/params/#b-user-defined-parameters","title":"B. User-defined Parameters","text":"

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

The complete usage example demonstrating the usage of the options parameter is available here \u27b6.

You can format these user-defined and configurational parameters as attributes of this options dictionary parameter as follows:

New Picamera2 backendLegacy Picamera backend
# formulate various Picamera2 API parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"exposure_compensation\": 15,\n    \"sensor\": {\"output_size\": (480, 320)},  # !!! will override `resolution` !!!\n}\n\n# open pi video stream with defined parameters\nstream = WebGear_RTC(enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options).start()\n
# formulate various Picamera API parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = WebGear_RTC(enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options).start()\n

"},{"location":"gears/webgear_rtc/params/#common-parameters","title":"Common Parameters","text":"

These are common parameters that works with every backend in WebGear_RTC.

"},{"location":"gears/webgear_rtc/params/#colorspace","title":"colorspace","text":"

This parameter selects the colorspace of the source stream.

Data-Type: String

Default Value: Its default value is None.

Usage:

All supported colorspace values are given here \u27b6

WebGear_RTC(colorspace=\"COLOR_BGR2HSV\")\n

Its complete usage example is given here \u27b6

"},{"location":"gears/webgear_rtc/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

WebGear_RTC(logging=True)\n

"},{"location":"gears/webgear_rtc/params/#time_delay","title":"time_delay","text":"

This parameter set the time delay (in seconds) before the WebGear_RTC API start reading the frames. This delay is only required if the source required some warm-up delay before starting up.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

WebGear_RTC(time_delay=1)  # set 1 seconds time delay\n

"},{"location":"gears/webgear_rtc/usage/","title":"Usage Examples","text":""},{"location":"gears/webgear_rtc/usage/#webgear_rtc-api-usage-examples","title":"WebGear_RTC API Usage Examples:","text":""},{"location":"gears/webgear_rtc/usage/#requirements","title":"Requirements","text":""},{"location":"gears/webgear_rtc/usage/#installation-with-asyncio-support","title":"Installation with Asyncio Support","text":"

WebGear_RTC API is the part of asyncio package of VidGear, thereby you need to install VidGear with asyncio support as follows:

pip install vidgear[asyncio]\n
"},{"location":"gears/webgear_rtc/usage/#aiortc","title":"Aiortc","text":"

Must Required with WebGear_RTC API. You can easily install it via pip:

Microsoft Visual C++ 14.0 is required.

Installing aiortc on windows requires Microsoft Build Tools for Visual C++ libraries installed. You can easily fix this error by installing any ONE of these choices:

While the error is calling for VC++ 14.0 - but newer versions of Visual C++ libraries works as well.

Afterwards, Select: Workloads \u2192 Desktop development with C++, then for Individual Components, select only:

Finally, proceed installing aiortc via pip.

  pip install aiortc\n
"},{"location":"gears/webgear_rtc/usage/#asgi-server","title":"ASGI Server","text":"

You'll also need to install an ASGI Server to run following WebGear_RTC usage examples, and by default WebGear_RTC ships the state-of-the-art uvicorn Server. But you can also use other ASGI server such as daphne, or hypercorn with it.

"},{"location":"gears/webgear_rtc/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Let's implement a Bare-Minimum usage example:

"},{"location":"gears/webgear_rtc/usage/#running-programmatically","title":"Running Programmatically","text":"

You can access and run WebGear_RTC VideoStreamer Server programmatically in your python script in just a few lines of code, as follows:

For accessing WebGear_RTC on different Client Devices on the network, use \"0.0.0.0\" as host value instead of \"localhost\" on Host Machine. More information can be found here \u27b6

We are using frame_size_reduction attribute for frame size reduction (in percentage) to be streamed with its options dictionary parameter to cope with performance-throttling in this example.

# import required libraries\nimport uvicorn\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# various performance tweaks\noptions = {\n    \"frame_size_reduction\": 25,\n}\n\n# initialize WebGear_RTC app\nweb = WebGear_RTC(source=\"foo.mp4\", logging=True, **options)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

which can be accessed on any browser on your machine at http://localhost:8000/.

"},{"location":"gears/webgear_rtc/usage/#running-from-terminal","title":"Running from Terminal","text":"

You can also access and run WebGear_RTC Server directly from the terminal commandline. The following command will run a WebGear_RTC VideoStreamer server at http://localhost:8000/:

Make sure your PYTHON_PATH is set to python 3.7+ versions only.

If you're using --options/-op flag, then kindly wrap your dictionary value in single '' quotes.

python3 -m vidgear.gears.asyncio --mode webrtc --source test.avi --logging True --options '{\"frame_size_reduction\": 50, \"frame_jpeg_quality\": 80, \"frame_jpeg_optimize\": True, \"frame_jpeg_progressive\": False}'\n

which can also be accessed on any browser on the network at http://localhost:8000/.

Advanced Usage from Terminal

You can run python3 -m vidgear.gears.asyncio -h help command to see all the advanced settings, as follows:

usage: python -m vidgear.gears.asyncio [-h] [-m MODE] [-s SOURCE] [-ep ENABLEPICAMERA] [-S STABILIZE]\n            [-cn CAMERA_NUM] [-yt stream_mode] [-b BACKEND] [-cs COLORSPACE]\n            [-r RESOLUTION] [-f FRAMERATE] [-td TIME_DELAY]\n            [-ip IPADDRESS] [-pt PORT] [-l LOGGING] [-op OPTIONS]\n\nRuns WebGear/WebGear_RTC Video Server through terminal.\n\noptional arguments:\n  -h, --help            show this help message and exit\n  -m {mjpeg,webrtc}, --mode {mjpeg,webrtc}\n                        Whether to use \"MJPEG\" or \"WebRTC\" mode for streaming.\n  -s SOURCE, --source SOURCE\n                        Path to input source for CamGear API.\n  -ep ENABLEPICAMERA, --enablePiCamera ENABLEPICAMERA\n                        Sets the flag to access PiGear(if True) or otherwise\n                        CamGear API respectively.\n  -S STABILIZE, --stabilize STABILIZE\n                        Enables/disables real-time video stabilization.\n  -cn CAMERA_NUM, --camera_num CAMERA_NUM\n                        Sets the camera module index that will be used by\n                        PiGear API.\n  -yt STREAM_MODE, --stream_mode STREAM_MODE\n                        Enables YouTube Mode in CamGear API.\n  -b BACKEND, --backend BACKEND\n                        Sets the backend of the video source in CamGear API.\n  -cs COLORSPACE, --colorspace COLORSPACE\n                        Sets the colorspace of the output video stream.\n  -r RESOLUTION, --resolution RESOLUTION\n                        Sets the resolution (width,height) for camera module\n                        in PiGear API.\n  -f FRAMERATE, --framerate FRAMERATE\n                        Sets the framerate for camera module in PiGear API.\n  -td TIME_DELAY, --time_delay TIME_DELAY\n                        Sets the time delay(in seconds) before start reading\n                        the frames.\n  -ip IPADDRESS, --ipaddress IPADDRESS\n                        Uvicorn binds the socket to this ipaddress.\n  -pt PORT, --port PORT\n                        Uvicorn binds the socket to this port.\n  -l LOGGING, --logging LOGGING\n                        Enables/disables error logging, essential for\n                        debugging.\n  -op OPTIONS, --options OPTIONS\n                        Sets the parameters supported by APIs(whichever being\n                        accessed) to the input videostream, But make sure to\n                        wrap your dict value in single or double quotes.\n

"},{"location":"gears/writegear/introduction/","title":"Introduction","text":""},{"location":"gears/writegear/introduction/#writegear-api","title":"WriteGear API","text":"WriteGear API generalized workflow"},{"location":"gears/writegear/introduction/#overview","title":"Overview","text":"

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 specifications (such asbitrate, codec, framerate, resolution, subtitles, etc.).

WriteGear also supports streaming with traditional protocols such as RTSP/RTP, RTMP. It is powerful enough to perform complex tasks such as Live-Streaming (such as for Twitch, YouTube etc.) and Multiplexing Video-Audio with real-time frames in just few lines of code.

Best of all, WriteGear grants users the complete freedom to play with any FFmpeg parameter with its exclusive Custom Commands function (see this doc) without relying on any third-party API.

In addition to this, WriteGear also provides flexible access to OpenCV's VideoWriter API tools for video-frames encoding without compression.

"},{"location":"gears/writegear/introduction/#modes-of-operation","title":"Modes of Operation","text":"

WriteGear primarily operates in following modes:

Helpful Tips

"},{"location":"gears/writegear/introduction/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/writegear/compression/overview/","title":"Overview","text":""},{"location":"gears/writegear/compression/overview/#writegear-api-compression-mode","title":"WriteGear API: Compression Mode","text":"WriteGear API's Compression Mode generalized workflow"},{"location":"gears/writegear/compression/overview/#overview","title":"Overview","text":"

When compression_mode parameter is enabled (.i.e compression_mode = True), WriteGear API provides a complete, flexible & robust wrapper around FFmpeg to encode lossless & compressed multimedia files.

This mode can process real-time video frames into a lossless compressed format with any suitable setting video/audio properties such as bitrate, codec, framerate, resolution, subtitles, and much more in just a few easy lines of code. It can also perform complex tasks such as Live-Streaming (such as for Twitch), multiplexing video with audio in real-time (see this usage example) while handling all errors robustly.

Important Information

You can speed up the execution time by disabling logging (.i.e logging = False) for production use, and by tweaking FFmpeg parameters in output_params values. Look into FFmpeg docs \u27b6 for such hacks.

"},{"location":"gears/writegear/compression/overview/#custom-ffmpeg-commands-in-writegear-api","title":"Custom FFmpeg Commands in WriteGear API","text":"

WriteGear API now provides the execute_ffmpeg_cmd Function in Compression Mode, that enables the user to pass any custom CLI commands as an input to its internal FFmpeg Pipeline by formating it as a list.

This function opens endless possibilities of exploiting any FFmpeg supported parameter within WriteGear, without relying on a third-party library/API to do the same, and while doing that it robustly handles all errors/warnings quietly.

A complete guide on execute_ffmpeg_cmd Function can be found here \u27b6

"},{"location":"gears/writegear/compression/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through WriteGear Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/writegear/compression/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/writegear/compression/params/","title":"Parameters","text":""},{"location":"gears/writegear/compression/params/#writegear-api-parameters-compression-mode","title":"WriteGear API Parameters: Compression Mode","text":""},{"location":"gears/writegear/compression/params/#output","title":"output","text":"

This parameter sets the valid filename/path/URL for the video output.

Warning

WriteGear API will throw ValueError if output provided is empty or invalid.

Data-Type: String

Usage:

Its valid input can be one of the following:

"},{"location":"gears/writegear/compression/params/#compression_mode","title":"compression_mode","text":"

This parameter selects the WriteGear's Primary Mode of Operation, i.e. if this parameter is enabled (.i.e compression_mode = True) WriteGear will use FFmpeg to encode output video, and if disabled (.i.e compression_mode = False), the OpenCV's VideoWriter API will be used for encoding files/streams.

Data-Type: Boolean

Default Value: Its default value is True.

Usage:

WriteGear(output = 'output.mp4', compression_mode=True)\n

"},{"location":"gears/writegear/compression/params/#custom_ffmpeg","title":"custom_ffmpeg","text":"

This parameter assigns the custom path/directory where the custom FFmpeg executables are located in Compression Mode only.

Compression Mode Behavior on Windows

In Compression Mode, if a custom FFmpeg executable's path | directory is not provided through custom_ffmpeg parameter on Windows machine, then WriteGear API will automatically attempt to download and extract suitable Static FFmpeg binaries at suitable location on your windows machine. More information can be found here \u27b6.

Data-Type: String

Default Value: Its default value is None.

Usage:

# if ffmpeg executables are located at \"/foo/foo1/FFmpeg\"\nWriteGear(output = 'output.mp4', custom_ffmpeg=\"/foo/foo1/FFmpeg\")\n

"},{"location":"gears/writegear/compression/params/#output_params","title":"output_params","text":"

This parameter allows us to exploit almost all FFmpeg supported parameters effortlessly and flexibly for encoding in Compression Mode, by formatting desired FFmpeg Parameters as this parameter's attributes. All supported parameters and encoders for compression mode discussed below:

Kindly read FFmpeg Docs carefully, before passing any values to output_param dictionary parameter. Wrong values may result in undesired Errors or no output at all.

Data-Type: Dictionary

Default Value: Its default value is {}.

"},{"location":"gears/writegear/compression/params/#supported-parameters","title":"Supported Parameters","text":""},{"location":"gears/writegear/compression/params/#supported-encoders","title":"Supported Encoders","text":"

All the encoders that are compiled with FFmpeg in use, are supported by WriteGear API. You can easily check the compiled encoders by running following command in your terminal:

Similarily, supported demuxers and filters depends upons compiled FFmpeg in use.

ffmpeg -encoders           # use `ffmpeg.exe -encoders` on windows\n

"},{"location":"gears/writegear/compression/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

WriteGear(output = 'output.mp4', logging=True)\n

"},{"location":"gears/writegear/compression/usage/","title":"Usage Examples","text":""},{"location":"gears/writegear/compression/usage/#writegear-api-usage-examples-compression-mode","title":"WriteGear API Usage Examples: Compression Mode","text":"

Important Information

After going through WriteGear Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/writegear/compression/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with WriteGear API in Compression Mode:

# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# open any valid video stream(for e.g `myvideo.avi` file)\nstream = CamGear(source=\"myvideo.avi\").start()\n\n# Define writer with default parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\")\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/compression/usage/#using-compression-mode-in-rgb-mode","title":"Using Compression Mode in RGB Mode","text":"

In Compression Mode, WriteGear API contains rgb_mode boolean parameter for RGB Mode, which when enabled (i.e. rgb_mode=True), specifies that incoming frames are of RGB format (instead of default BGR format). This mode makes WriteGear directly compatible with libraries that only supports RGB format.

The complete usage example is as follows:

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = VideoGear(source=0).start()\n\n# Define writer with default parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\")\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # simulating RGB frame for example\n    frame_rgb = frame[:, :, ::-1]\n\n    # writing RGB frame to writer\n    writer.write(frame_rgb, rgb_mode=True)  # activate RGB Mode\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/compression/usage/#using-compression-mode-with-controlled-framerate","title":"Using Compression Mode with controlled FrameRate","text":"

WriteGear API provides -input_framerate attribute for its options dictionary parameter in Compression Mode, which allow us to control/set the constant framerate of the output video.

Advanced Tip for setting constant framerate

If -input_framerate attribute doesn't works for you, then define it in conjunction with another -r FFmpeg parameter as attribute:

# set output constant framerate to (say 60 fps)\noutput_params = {\"-input_framerate\":60, \"-r\":60}\n# assign that to WriteGear\nwriter = WriteGear(output=\"out.mp4\", logging =True, **output_params)\n

But make sure you MUST set value of -r and -input_framerate parameter less than or equal to your input source framerate.

In this code we will retrieve framerate from video stream, and set it as -input_framerate attribute for option parameter in WriteGear API:

# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = CamGear(source=0).start()\n\n# retrieve framerate from CamGear Stream and pass it as `-input_framerate` parameter\noutput_params = {\"-input_framerate\": stream.framerate}\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\", **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if None-type\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/compression/usage/#using-compression-mode-for-live-streaming","title":"Using Compression Mode for live streaming","text":"

In Compression Mode, WriteGear also allows URL strings (as output) for live streaming realtime frames with its output parameter.

In this example, we will stream live camera frames directly to Twitch :

For streaming with traditional protocols such as RTSP/RTP, Checkout this WriteGear's Bonus Examples \u27b6.

YouTube-Live Streaming example code also available in WriteGear's Bonus Examples \u27b6

This example assume you already have a Twitch Account for publishing video.

Make sure to change Twitch Stream Key with yours in following code before running!

# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# Open live webcam video stream on first index(i.e. 0) device\nstream = CamGear(source=0, logging=True).start()\n\n# define required FFmpeg optimizing parameters for your writer\noutput_params = {\n    \"-preset:v\": \"veryfast\",\n    \"-g\": 60,\n    \"-keyint_min\": 60,\n    \"-sc_threshold\": 0,\n    \"-bufsize\": \"2500k\",\n    \"-f\": \"flv\",\n}\n\n# [WARNING] Change your Twitch Stream Key here:\nTWITCH_KEY = \"live_XXXXXXXXXX~XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\"\n\n# Define writer with defined parameters and\nwriter = WriteGear(\n    output=\"rtmp://live.twitch.tv/app/{}\".format(TWITCH_KEY),\n    logging=True,\n    **output_params\n)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/compression/usage/#using-compression-mode-with-hardware-encoders","title":"Using Compression Mode with Hardware encoders","text":"

By default, WriteGear API uses libx264 encoder for encoding output files in Compression Mode. But you can easily change encoder to your suitable supported encoder by passing -vcodec FFmpeg parameter as an attribute with its output_param dictionary parameter. In addition to this, you can also specify the additional properties/features of your system's GPU easily.

User Discretion Advised

This example is just conveying the idea on how to use FFmpeg's hardware encoders with WriteGear API in Compression mode, which MAY/MAY NOT suit your system. Kindly use suitable parameters based your system hardware settings only.

In this example, we will be using h264_vaapi as our hardware encoder and also optionally be specifying our device hardware's location (i.e. '-vaapi_device':'/dev/dri/renderD128') and other features such as '-vf':'format=nv12,hwupload':

Remember to 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:

ffmpeg  -hide_banner -encoders | grep vaapi \n\n V..... h264_vaapi           H.264/AVC (VAAPI) (codec h264)\n V..... hevc_vaapi           H.265/HEVC (VAAPI) (codec hevc)\n V..... mjpeg_vaapi          MJPEG (VAAPI) (codec mjpeg)\n V..... mpeg2_vaapi          MPEG-2 (VAAPI) (codec mpeg2video)\n V..... vp8_vaapi            VP8 (VAAPI) (codec vp8)\n
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# Open live webcam video stream on first index(i.e. 0) device\nstream = CamGear(source=0, logging=True).start()\n\n# define required FFmpeg parameters for your writer\noutput_params = {\n    \"-vcodec\": \"h264_vaapi\",\n    \"-vaapi_device\": \"/dev/dri/renderD128\",\n    \"-vf\": \"format=nv12,hwupload\",\n}\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\", **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/compression/usage/#using-compression-mode-with-opencv","title":"Using Compression Mode with OpenCV","text":"

You can easily use WriterGear API directly with any Video Processing library(For e.g OpenCV itself) in Compression Mode. The complete usage example is as follows:

# import required libraries\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# define suitable (Codec,CRF,preset) FFmpeg parameters for writer\noutput_params = {\"-vcodec\": \"libx264\", \"-crf\": 0, \"-preset\": \"fast\"}\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = cv2.VideoCapture(0)\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n        break\n\n    # {do something with the frame here}\n    # lets convert frame to gray for this example\n    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n\n    # write gray frame to writer\n    writer.write(gray)\n\n    # Show output window\n    cv2.imshow(\"Output Gray Frame\", gray)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.release()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/compression/usage/#using-compression-mode-with-live-audio-input","title":"Using Compression Mode with Live Audio Input","text":"

In Compression Mode, WriteGear API allows us to exploit almost all FFmpeg supported parameters that you can think of in its Compression Mode. Hence, combining audio with live video frames is pretty easy.

In this example code, we will merging the audio from a Audio Device (for e.g. Webcam inbuilt mic) to live frames incoming from the Video Source (for e.g external webcam), and save the output as a compressed video file, all in real time:

Example Assumptions

Identifying and Specifying sound card on different OS platforms Windows Linux MacOS

Windows OS users can use the dshow (DirectShow) to list audio input device which is the preferred option for Windows users. You can refer following steps to identify and specify your sound card:

If audio still doesn't work then checkout this troubleshooting guide \u27b6 or reach us out on Gitter \u27b6 Community channel

Linux OS users can use the alsa to list input device to capture live audio input such as from a webcam. You can refer following steps to identify and specify your sound card:

If audio still doesn't work then reach us out on Gitter \u27b6 Community channel

MAC OS users can use the avfoundation to list input devices for grabbing audio from integrated iSight cameras as well as cameras connected via USB or FireWire. You can refer following steps to identify and specify your sound card on MacOS/OSX machines:

If audio still doesn't work then reach us out on Gitter \u27b6 Community channel

Make sure this -i audio-source it compatible with provided video-source, otherwise you could encounter multiple errors or no output at all.

You MUST use -input_framerate attribute to set exact value of input framerate when using external audio in Real-time Frames mode, otherwise audio delay will occur in output streams.

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = VideoGear(source=0).start()\n\n# change with your webcam soundcard, plus add additional required FFmpeg parameters for your writer\noutput_params = {\n    \"-input_framerate\": stream.framerate,\n    \"-thread_queue_size\": \"512\",\n    \"-ac\": \"2\",\n    \"-ar\": \"48000\",\n    \"-f\": \"alsa\", # !!! warning: always keep this line above \"-i\" parameter !!!\n    \"-i\": \"hw:1\",\n}\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4\nwriter = WriteGear(output=\"Output.mp4\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/compression/advanced/cciw/","title":"Custom FFmpeg Commands","text":""},{"location":"gears/writegear/compression/advanced/cciw/#custom-ffmpeg-commands-in-writegear-api","title":"Custom FFmpeg Commands in WriteGear API","text":"

WriteGear API now provides the execute_ffmpeg_cmd Method in Compression Mode that enables the user to pass any custom FFmpeg CLI (Command Line Interface) commands as input to its internal FFmpeg Pipeline by formating it as a list.

This opens endless possibilities of exploiting every FFmpeg params within WriteGear without relying on a third-party API to do the same and while doing that it robustly handles all errors/warnings quietly.

Important Information

"},{"location":"gears/writegear/compression/advanced/cciw/#features","title":"Features","text":"

"},{"location":"gears/writegear/compression/advanced/cciw/#methods","title":"Methods","text":""},{"location":"gears/writegear/compression/advanced/cciw/#execute_ffmpeg_cmd","title":"execute_ffmpeg_cmd","text":"

This method allows the users to pass the custom FFmpeg terminal commands as a formatted list directly to WriteGear API's FFmpeg pipeline for processing/execution. Its usage is as follows:

# format FFmpeg terminal command `ffmpeg -y -i source_video -acodec copy input_audio.aac` as a list\nffmpeg_command = [\"-y\", \"-i\", source_video, \"-acodec\", \"copy\", \"input_audio.aac\"]\n\n# execute this list using this function\nexecute_ffmpeg_cmd(ffmpeg_command)\n

"},{"location":"gears/writegear/compression/advanced/cciw/#usage-examples","title":"Usage Examples","text":"

Following usage examples is just an idea of what can be done with this powerful function. So just Tinker with various FFmpeg parameters/commands yourself and see it working. Also, if you're unable to run any terminal FFmpeg command, then report an issue.

"},{"location":"gears/writegear/compression/advanced/cciw/#using-writegear-to-separate-audio-from-video","title":"Using WriteGear to separate Audio from Video","text":"

In this example, we will extract and save audio from a URL stream:

# import required libraries\nfrom vidgear.gears import WriteGear\n\n# define a valid url\nurl_to_stream = (\n    \"http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4\"\n)\n\n# Define writer with default parameters\nwriter = WriteGear(output=\"Output.mp4\", logging=True)\n\n# format command to convert stream audio as 'output_audio.aac' as list\nffmpeg_command_to_save_audio = [\n    \"-y\",\n    \"-i\",\n    url_to_stream,\n    \"output_audio.aac\",\n]  # `-y` parameter is to overwrite outputfile if exists\n\n# execute FFmpeg command\nwriter.execute_ffmpeg_cmd(ffmpeg_command_to_save_audio)\n\n# safely close writer\nwriter.close()\n

After running this script, You will get the final 'output_audio.aac' audio file.

"},{"location":"gears/writegear/compression/advanced/cciw/#using-writegear-to-merge-audio-with-video","title":"Using WriteGear to merge Audio with Video","text":"

In this example, we will merge audio with video:

You can also directly add external audio input to video-frames in WriteGear. For more information, See this FAQ example \u27b6

Example Assumptions

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import WriteGear\nimport cv2\nimport time\n\n# Open input video stream\nstream = VideoGear(source=\"input-video.mp4\").start()\n\n# set input audio stream path\ninput_audio = \"input-audio.aac\"\n\n# define your parameters\noutput_params = {\n    \"-input_framerate\": stream.framerate\n}  # output framerate must match source framerate\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\", **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n\n\n# sleep 1 sec as the above video might still be rendering\ntime.sleep(1)\n\n\n# format FFmpeg command to generate `Output_with_audio.mp4` by merging input_audio in above rendered `Output.mp4`\nffmpeg_command = [\n    \"-y\",\n    \"-i\",\n    \"Output.mp4\",\n    \"-i\",\n    input_audio,\n    \"-c:v\",\n    \"copy\",\n    \"-c:a\",\n    \"copy\",\n    \"-map\",\n    \"0:v:0\",\n    \"-map\",\n    \"1:a:0\",\n    \"-shortest\",\n    \"Output_with_audio.mp4\",\n]  # `-y` parameter is to overwrite outputfile if exists\n\n# execute FFmpeg command\nwriter.execute_ffmpeg_cmd(ffmpeg_command)\n

After running this script, You will get the final 'Output_with_audio.mp4' file with both video and audio merged.

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/","title":"FFmpeg Installation","text":""},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#ffmpeg-installation-instructions","title":"FFmpeg Installation Instructions","text":"

WriteGear must requires FFmpeg executables for its Compression capabilities in Compression Mode. You can following machine-specific instructions for its installation:

In case WriteGear API fails to detect valid FFmpeg executables on your system (even if Compression Mode is enabled), it automatically fallbacks to Non-Compression Mode.

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#linux-ffmpeg-installation","title":"Linux FFmpeg Installation","text":"

The WriteGear API supports Auto-Detection and Manual Configuration methods on a Linux machine:

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#a-auto-detection","title":"A. Auto-Detection","text":"

This is a recommended approach on Linux Machines

If WriteGear API not receives any input from the user on custom_ffmpeg parameter, then on Linux system, it tries to auto-detects the required FFmpeg installed binaries through validation test that employs subprocess python module.

Installation: You can install easily install official FFmpeg according to your Linux Distro by following this post \u27b6

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#b-manual-configuration","title":"B. Manual Configuration","text":"

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#windows-ffmpeg-installation","title":"Windows FFmpeg Installation","text":"

The WriteGear API supports Auto-Installation and Manual Configuration methods on Windows systems.

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#a-auto-installation","title":"A. Auto-Installation","text":"

This is a recommended approach on Windows Machines

If WriteGear API not receives any input from the user on custom_ffmpeg parameter, then on Windows system WriteGear API auto-generates the required FFmpeg Static Binaries from a dedicated Github Server into the temporary directory (for e.g. C:\\Temp) of your machine.

Warning

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#b-manual-configuration_1","title":"B. Manual Configuration","text":"

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#macos-ffmpeg-installation","title":"MacOS FFmpeg Installation","text":"

The WriteGear API supports Auto-Detection and Manual Configuration methods on a macOS machine.

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#a-auto-detection_1","title":"A. Auto-Detection","text":"

This is a recommended approach on MacOS Machines

If WriteGear API not receives any input from the user on custom_ffmpeg parameter, then on macOS system, it tries to auto-detects the required FFmpeg installed binaries through validation test that employs subprocess python module.

Installation: You can easily install FFmpeg on your macOS machine by following this tutorial \u27b6

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#b-manual-configuration_2","title":"B. Manual Configuration","text":"

"},{"location":"gears/writegear/non_compression/overview/","title":"Overview","text":""},{"location":"gears/writegear/non_compression/overview/#writegear-api-non-compression-mode","title":"WriteGear API: Non-Compression Mode","text":"WriteGear API's Non-Compression Mode generalized workflow"},{"location":"gears/writegear/non_compression/overview/#overview","title":"Overview","text":"

When compression_mode parameter is disabled (.i.e compression_mode = False), WriteGear API uses basic OpenCV's inbuilt VideoWriter API tools for encoding multimedia files but without compression.

This mode provides flexible access to OpenCV's VideoWriter API,and also supports various parameters available within this API, but lacks the ability to control output quality, compression, and other important features like lossless video compression, audio encoding, etc. which are only available in Compression Mode. Thereby, the resultant output video-file size will be many times larger as compared to Compression Mode.

Important Information

"},{"location":"gears/writegear/non_compression/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through WriteGear Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/writegear/non_compression/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/writegear/non_compression/params/","title":"Parameters","text":""},{"location":"gears/writegear/non_compression/params/#writegear-api-parameters-non-compression-mode","title":"WriteGear API Parameters: Non-Compression Mode","text":""},{"location":"gears/writegear/non_compression/params/#output","title":"output","text":"

This parameter sets the valid output Video filename/path for the output video.

WriteGear API will throw RuntimeError if output provided is empty or invalid.

Data-Type: String

Default Value: Its default value is 0.

Usage:

Make sure to provide valid filename with valid file-extension based on the encoder in use (default is .mp4).

Its valid input can be one of the following:

"},{"location":"gears/writegear/non_compression/params/#compression_mode","title":"compression_mode","text":"

This parameter selects the WriteGear's Primary Mode of Operation, i.e. if this parameter is enabled (.i.e compression_mode = True) WriteGear will use FFmpeg to encode output video, and if disabled (.i.e compression_mode = False), the OpenCV's VideoWriter API will be used for encoding files/streams.

Data-Type: Boolean

Default Value: Its default value is True.

Usage:

WriteGear(output = 'output.mp4', compression_mode=False)\n

"},{"location":"gears/writegear/non_compression/params/#custom_ffmpeg","title":"custom_ffmpeg","text":"

Not supported in Non-Compression Mode!

"},{"location":"gears/writegear/non_compression/params/#output_params","title":"output_params","text":"

This parameter allows us to exploit almost all OpenCV's VideoWriter API supported parameters effortlessly and flexibly for video-encoding in Non-Compression Mode, by formatting desired FFmpeg Parameters as this parameter's attributes. All supported parameters and FOURCC codecs for compression mode discussed below:

Remember, Non-Compression mode lacks the ability to control output quality and other important features like lossless video compression, audio encoding, etc., which are available with WriteGear's Compression Mode only.

Data-Type: Dictionary

Default Value: Its default value is {}.

"},{"location":"gears/writegear/non_compression/params/#supported-attributes","title":"Supported Attributes","text":"

Non-Compression Mode only gives access to a limited number of Parameters through its output_params parameter's attributes, which are as follows:

"},{"location":"gears/writegear/non_compression/params/#a-opencv-parameters","title":"A. OpenCV Parameters","text":"

WriteGear provides access to all available OpenCV's VideoWriter API parameters in Non-Compression Mode.

Parameters Description -fourcc 4-character code of codec used to encode frames -fps controls the framerate of output video(Default value: 25) -backend (optional) In case of multiple backends, this parameter allows us to specify VideoWriter API's backends to use. Its valid values are CAP_FFMPEG or CAP_GSTREAMER(if enabled) -color (optional) If it is not zero(0), the encoder will expect and encode color frames, otherwise it will work with grayscale frames (the flag is currently supported on Windows only)

-height and -width parameter are no longer supported and are automatically derived from the input frames.

"},{"location":"gears/writegear/non_compression/params/#b-exclusive-parameters","title":"B. Exclusive Parameters","text":"

In addition to OpenCV Parameters, WriteGear API also provides few exclusive attribute, which are as follows:

Usage:

To assign desired parameters in Non-Compression Mode, you can format it as dictionary attribute and pass through this(output_params) parameter as follows:

# format parameter as dictionary attribute\noutput_params = {\"-fps\":30} \n# and then, assign it\nWriteGear(output = 'output.mp4', compression_mode=False, **output_params)\n

Its usage example can be found here \u27b6.

"},{"location":"gears/writegear/non_compression/params/#supported-fourcc-codecs","title":"Supported FOURCC Codecs","text":"

FOURCC is a 4-character code of the codec used to encode video in Non-Compression Mode(OpenCV's VideoWriter API) without compression.

List of all supported FOURCC codecs can found here \u27b6

Usage:

To select desired FOURCC codec in Non-Compression Mode, you can format it as dictionary attribute and pass through this(output_params) parameter. For example, using MJPG as codec, we can:

# format codec as dictionary attribute\noutput_params = {\"-fourcc\":\"MJPG\"} \n# and then, assign it\nWriteGear(output = 'output.mp4', compression_mode=False, **output_params)\n

Its usage example can be found here \u27b6.

"},{"location":"gears/writegear/non_compression/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

WriteGear(output = 'output.mp4', compression_mode=False, logging=True)\n

"},{"location":"gears/writegear/non_compression/usage/","title":"Usage Examples","text":""},{"location":"gears/writegear/non_compression/usage/#writegear-api-usage-examples-non-compression-mode","title":"WriteGear API Usage Examples: Non-Compression Mode","text":"

Important Information

After going through WriteGear Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/writegear/non_compression/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with WriteGear API in Non-Compression Mode:

# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# open any valid video stream(for e.g `myvideo.avi` file)\nstream = CamGear(source=\"myvideo.avi\").start()\n\n# Define writer with Non-compression mode and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\", compression_mode=False)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/non_compression/usage/#using-non-compression-mode-with-videocapture-gears","title":"Using Non-Compression Mode with VideoCapture Gears","text":"

In Non-Compression mode, WriteGear API provides flexible control over OpenCV's VideoWriter API parameters through its output_param dictionary parameter by formating them as dictionary attributes. Moreover, WriteGear API can be used in conjunction with any other Gears/APIs effortlessly.

All supported attributes for output_param can be found here \u27b6

The complete usage example is as follows:

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# define suitable tweak parameters for writer\noutput_params = {\"-fourcc\": \"MJPG\", \"-fps\": 30}\n\n# open live video stream on webcam at first index(i.e. 0) device\nstream = VideoGear(source=0, logging=True).start()\n\n# Define writer with defined parameters and suitable output filename \n# for e.g. `Output.mp4`\nwriter = WriteGear(\n    output=\"Output.mp4\", compression_mode=False, logging=True, **output_params\n)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n    # lets convert frame to gray for this example\n    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n\n    # write gray frame to writer\n    writer.write(gray)\n\n    # Show output window\n    cv2.imshow(\"Output Gray Frame\", gray)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/non_compression/usage/#using-non-compression-mode-with-opencv","title":"Using Non-Compression Mode with OpenCV","text":"

You can easily use WriterGear API directly with any Video Processing library(For e.g OpenCV itself) in Non-Compression Mode. The complete usage example is as follows:

# import required libraries\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# define suitable tweak parameters for writer\noutput_params = {\"-fourcc\": \"MJPG\", \"-fps\": 30}\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = cv2.VideoCapture(0)\n\n# Define writer with defined parameters and suitable output filename \n# for e.g. `Output.mp4`\nwriter = WriteGear(\n    output=\"Output.mp4\", compression_mode=False, logging=True, **output_params\n)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n        break\n\n    # {do something with the frame here}\n    # lets convert frame to gray for this example\n    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n\n    # write gray frame to writer\n    writer.write(gray)\n\n    # Show output window\n    cv2.imshow(\"Output Gray Frame\", gray)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.release()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/non_compression/usage/#using-non-compression-mode-with-gstreamer-pipeline","title":"Using Non-Compression Mode with GStreamer Pipeline","text":"

WriteGear API's Non-Compression Mode also supports GStreamer Pipeline as input to its output parameter, when GStreamer Pipeline Mode is enabled. This provides flexible way to write video frames to file or network stream with controlled framerate and bitrate. The complete usage example is as follows:

Requirement for GStreamer Pipelining

GStreamer Pipelining in WriteGear requires your OpenCV to be built with GStreamer support. Checkout this FAQ for compiling OpenCV with GStreamer support.

New in v0.2.5

This example was added in v0.2.5.

In this example we will be constructing GStreamer pipeline to write video-frames into a file(foo.mp4) at 1M video-bitrate.

# import required libraries\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# enable GStreamer Pipeline Mode for writer\noutput_params = {\"-gst_pipeline_mode\": True}\n\n# open live video stream on webcam at first index(i.e. 0) device\nstream = cv2.VideoCapture(0)\n\n# gst pipeline to write to a file `foo.mp4` at 1M video-bitrate\nGSTPipeline = \"appsrc ! videoconvert ! avenc_mpeg4 bitrate=100000 ! mp4mux ! filesink location={}\".format(\n    \"foo.mp4\"\n)\n\n# Define writer with defined parameters and with our Gstreamer pipeline\nwriter = WriteGear(\n    output=GSTPipeline, compression_mode=False, logging=True, **output_params\n)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.release()\n\n# safely close writer\nwriter.close()\n

"},{"location":"help/camgear_ex/","title":"Bonus Examples","text":""},{"location":"help/camgear_ex/#camgear-examples","title":"CamGear Examples","text":""},{"location":"help/camgear_ex/#synchronizing-two-sources-in-camgear","title":"Synchronizing Two Sources in CamGear","text":"

In this example both streams and corresponding frames will be processed synchronously i.e. with no delay:

Using same source with more than one instances of CamGear can lead to Global Interpreter Lock (GIL) that degrades performance even when it is not a bottleneck.

# import required libraries\nfrom vidgear.gears import CamGear\nimport cv2\nimport time\n\n# define and start the stream on first source ( For e.g #0 index device)\nstream1 = CamGear(source=0, logging=True).start() \n\n# define and start the stream on second source ( For e.g #1 index device)\nstream2 = CamGear(source=1, logging=True).start() \n\n# infinite loop\nwhile True:\n\n    frameA = stream1.read()\n    # read frames from stream1\n\n    frameB = stream2.read()\n    # read frames from stream2\n\n    # check if any of two frame is None\n    if frameA is None or frameB is None:\n        #if True break the infinite loop\n        break\n\n    # do something with both frameA and frameB here\n    cv2.imshow(\"Output Frame1\", frameA)\n    cv2.imshow(\"Output Frame2\", frameB)\n    # Show output window of stream1 and stream 2 separately\n\n    key = cv2.waitKey(1) & 0xFF\n    # check for 'q' key-press\n    if key == ord(\"q\"):\n        #if 'q' key-pressed break out\n        break\n\n    if key == ord(\"w\"):\n        #if 'w' key-pressed save both frameA and frameB at same time\n        cv2.imwrite(\"Image-1.jpg\", frameA)\n        cv2.imwrite(\"Image-2.jpg\", frameB)\n        #break   #uncomment this line to break out after taking images\n\ncv2.destroyAllWindows()\n# close output window\n\n# safely close both video streams\nstream1.stop()\nstream2.stop()\n

"},{"location":"help/camgear_ex/#using-variable-yt_dlp-parameters-in-camgear","title":"Using variable yt_dlp parameters in CamGear","text":"

CamGear provides exclusive attributes STREAM_RESOLUTION (for specifying stream resolution) & STREAM_PARAMS (for specifying underlying API(e.g. yt_dlp) parameters) with its options dictionary parameter.

The complete usage example is as follows:

More information on STREAM_RESOLUTION & STREAM_PARAMS attributes can be found here \u27b6

# import required libraries\nfrom vidgear.gears import CamGear\nimport cv2\n\n# specify attributes\noptions = {\"STREAM_RESOLUTION\": \"720p\", \"STREAM_PARAMS\": {\"nocheckcertificate\": True}}\n\n# Add YouTube Video URL as input source (for e.g https://youtu.be/bvetuLwJIkA)\n# and enable Stream Mode (`stream_mode = True`)\nstream = CamGear(\n    source=\"https://youtu.be/bvetuLwJIkA\", stream_mode=True, logging=True, **options\n).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"help/camgear_ex/#using-camgear-for-capturing-rtsprtmp-urls","title":"Using CamGear for capturing RTSP/RTMP URLs","text":"

You can open any network stream (such as RTSP/RTMP) just by providing its URL directly to CamGear's source parameter.

Here's a high-level wrapper code around CamGear API to enable auto-reconnection during capturing:

New in v0.2.2

This example was added in v0.2.2.

Enforcing UDP stream

You can easily enforce UDP for RTSP streams inplace of default TCP, by putting following lines of code on the top of your existing code:

# import required libraries\nimport os\n\n# enforce UDP\nos.environ[\"OPENCV_FFMPEG_CAPTURE_OPTIONS\"] = \"rtsp_transport;udp\"\n

Finally, use backend parameter value as backend=cv2.CAP_FFMPEG in CamGear.

from vidgear.gears import CamGear\nimport cv2\nimport datetime\nimport time\n\n\nclass Reconnecting_CamGear:\n    def __init__(self, cam_address, reset_attempts=50, reset_delay=5):\n        self.cam_address = cam_address\n        self.reset_attempts = reset_attempts\n        self.reset_delay = reset_delay\n        self.source = CamGear(source=self.cam_address).start()\n        self.running = True\n\n    def read(self):\n        if self.source is None:\n            return None\n        if self.running and self.reset_attempts > 0:\n            frame = self.source.read()\n            if frame is None:\n                self.source.stop()\n                self.reset_attempts -= 1\n                print(\n                    \"Re-connection Attempt-{} occured at time:{}\".format(\n                        str(self.reset_attempts),\n                        datetime.datetime.now().strftime(\"%m-%d-%Y %I:%M:%S%p\"),\n                    )\n                )\n                time.sleep(self.reset_delay)\n                self.source = CamGear(source=self.cam_address).start()\n                # return previous frame\n                return self.frame\n            else:\n                self.frame = frame\n                return frame\n        else:\n            return None\n\n    def stop(self):\n        self.running = False\n        self.reset_attempts = 0\n        self.frame = None\n        if not self.source is None:\n            self.source.stop()\n\n\nif __name__ == \"__main__\":\n    # open any valid video stream\n    stream = Reconnecting_CamGear(\n        cam_address=\"rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov\",\n        reset_attempts=20,\n        reset_delay=5,\n    )\n\n    # loop over\n    while True:\n\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if None-type\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # Show output window\n        cv2.imshow(\"Output\", frame)\n\n        # check for 'q' key if pressed\n        key = cv2.waitKey(1) & 0xFF\n        if key == ord(\"q\"):\n            break\n\n    # close output window\n    cv2.destroyAllWindows()\n\n    # safely close video stream\n    stream.stop()\n

"},{"location":"help/camgear_faqs/","title":"FAQs","text":""},{"location":"help/camgear_faqs/#camgear-faqs","title":"CamGear FAQs","text":""},{"location":"help/camgear_faqs/#what-is-camgear-api-and-what-does-it-do","title":"What is CamGear API and what does it do?","text":"

Answer: CamGear supports a diverse range of video streams which can handle/control video stream almost any IP/USB Cameras, multimedia video file format (upto 4k tested), any network stream URL such as http(s), rtp, rtsp, rtmp, mms, etc. In addition to this, it also supports live Gstreamer's RAW pipelines and YouTube video/livestreams URLs. For more info. see CamGear doc \u27b6.

"},{"location":"help/camgear_faqs/#im-only-familiar-with-opencv-how-to-get-started-with-camgear-api","title":"I'm only familiar with OpenCV, how to get started with CamGear API?","text":"

Answer: Answer: First, refer to the Switching from OpenCV guide, then go through CamGear documentation \u27b6. If you still have doubts, ask us on Gitter \u27b6 Community channel.

"},{"location":"help/camgear_faqs/#how-to-change-opencv-source-backend-in-camgear-api","title":"How to change OpenCV source backend in CamGear API?","text":"

Answer: See its Parameters \u27b6. Its, backend(int) parameter sets the backend of the source. Its value can be for e.g. backend = cv2.CAP_DSHOW in case of Direct Show.

"},{"location":"help/camgear_faqs/#how-to-get-framerate-of-the-source-in-camgear-api","title":"How to get framerate of the source in CamGear API?","text":"

Answer: CamGear's framerate global variable can be used to retrieve framerate of the input video stream. See this example \u27b6.

"},{"location":"help/camgear_faqs/#how-to-compile-opencv-with-gstreamer-support","title":"How to compile OpenCV with GStreamer support?","text":"

Answer: For compiling OpenCV with GSstreamer(>=v1.0.0) support:

Linux Windows MacOS

"},{"location":"help/camgear_faqs/#how-to-change-quality-and-parameters-of-youtube-streams-with-camgear","title":"How to change quality and parameters of YouTube Streams with CamGear?","text":"

Answer: CamGear provides exclusive attributes STREAM_RESOLUTION (for specifying stream resolution) & STREAM_PARAMS (for specifying underlying API(e.g. yt_dlp) parameters) with its options dictionary parameter. See this bonus example \u27b6.

"},{"location":"help/camgear_faqs/#how-to-open-rtsp-network-streams-with-camgear","title":"How to open RTSP network streams with CamGear?","text":"

Answer: You can open any local network stream (such as RTSP) just by providing its URL directly to CamGear's source parameter. See this bonus example \u27b6.

"},{"location":"help/camgear_faqs/#how-to-set-camera-settings-with-camgear","title":"How to set Camera Settings with CamGear?","text":"

Answer: See this usage example \u27b6.

"},{"location":"help/camgear_faqs/#can-i-play-4k8k-video-with-camgear-api","title":"Can I play 4K/8k video with CamGear API?","text":"

Answer: Yes, you can if your System Hardware supports it.

"},{"location":"help/camgear_faqs/#how-to-synchronize-between-two-cameras","title":"How to synchronize between two cameras?","text":"

Answer: See this bonus example \u27b6.

"},{"location":"help/camgear_faqs/#can-i-use-gpu-to-decode-the-video-source","title":"Can I use GPU to decode the video source?","text":"

Answer: See this issue comment \u27b6.

"},{"location":"help/camgear_faqs/#why-camgear-is-throwing-warning-that-threaded-queue-mode-is-disabled","title":"Why CamGear is throwing warning that Threaded Queue Mode is disabled?","text":"

Answer: That's a normal behavior. Please read about Threaded Queue Mode \u27b6

"},{"location":"help/general_faqs/","title":"General FAQs","text":""},{"location":"help/general_faqs/#general-faqs","title":"General FAQs","text":""},{"location":"help/general_faqs/#im-new-to-python-programming-or-its-usage-in-opencv-library-how-to-use-vidgear-in-my-projects","title":"\"I'm new to Python Programming or its usage in OpenCV Library\", How to use vidgear in my projects?","text":"

Answer: Before using vidgear, It's recommended to first go through the following dedicated blog sites and learn how OpenCV-Python syntax works (with examples):

Once done, visit Switching from OpenCV \u27b6 to easily replace OpenCV APIs with suitable Gears \u27b6 in your project. All the best!

If you run into any trouble or have any questions, then refer our Help section.

"},{"location":"help/general_faqs/#vidgear-is-using-multi-threading-but-python-is-notorious-for-its-poor-performance-in-multithreading","title":"\"VidGear is using Multi-threading, but Python is notorious for its poor performance in multithreading?\"","text":"

Answer: Refer vidgear's Threaded-Queue-Mode \u27b6

"},{"location":"help/general_faqs/#modulenotfounderror-no-module-named-vidgeargears-vidgear-is-not-a-package","title":"ModuleNotFoundError: No module named 'vidgear.gears'. 'vidgear' is not a package?","text":"

Answer: This error means you either have a file named vidgear.py in your python path or you've named your python script vidgear.py. Replace vidgear name with anything else to fix this error.

"},{"location":"help/general_faqs/#how-to-log-to-a-file-in-vidgear","title":"How to log to a file in VidGear?","text":"

Answer: VidGear provides exclusive VIDGEAR_LOGFILE environment variable to enable logging to a file while logging is enabled (i.e. logging=True) on respective Gear. You just have to set directory pathname (automatically creates vidgear.log file) or a log file pathname itself as value for this environment variable. This can be done on various Operating Systems as follows:

Remember enabling this logging to a file will completely disable any output on the terminal.

Linux Windows (Powershell) MacOS
# path to file\nexport VIDGEAR_LOGFILE=\"$HOME/foo.log\"\n\n# or just directory path \n# !!! Make sure `foo` path already exists !!!\nexport VIDGEAR_LOGFILE=\"$HOME/foo\"\n\n# to remove\nunset VIDGEAR_LOGFILE\n
# path to file\n$Env:VIDGEAR_LOGFILE = \"D:\\foo.log\"\n\n# or just directory path \n# !!! Make sure `foo` path already exists !!!\n$Env:VIDGEAR_LOGFILE = \"D:\\foo\"\n\n# to remove\n$Env:VIDGEAR_LOGFILE = \"\"\n
# path to file\nexport VIDGEAR_LOGFILE=\"$HOME/foo.log\"\n\n# or just directory path \n# !!! Make sure `foo` path already exists !!!\nexport VIDGEAR_LOGFILE=\"$HOME/foo\"\n\n# to remove\nunset VIDGEAR_LOGFILE\n

"},{"location":"help/general_faqs/#can-i-perform-deep-learning-task-with-vidgear","title":"Can I perform Deep Learning task with VidGear?","text":"

Answer: VidGear is a powerful Video Processing library (similar to OpenCV, FFmpeg, etc.) that can read, write, process, send & receive a sequence of video-frames in an optimized manner. But for Deep Learning or Machine Learning tasks, you have to use a third-party library. That being said, all VidGear's APIs can be used with any third-party Library(such as PyTorch, Tensorflow, etc.) that can leverage the overall performance if you're processing video/audio streams/frames in your application with Deep Learning tasks. Also, it eases the workflow since you have to write way fewer lines of code to read/store/process output videos.

"},{"location":"help/general_faqs/#can-i-ask-my-question-directly-without-raising-an-issue","title":"Can I ask my question directly without raising an issue?","text":"

Answer: Yes, please join our Gitter \u27b6 Community channel.

"},{"location":"help/general_faqs/#how-to-contribute-to-vidgear-development","title":"How to contribute to VidGear development?","text":"

Answer: See our Contribution Guidelines \u27b6

"},{"location":"help/general_faqs/#what-oses-are-supported-by-vidgear","title":"What OSes are supported by VidGear?","text":"

Answer: See Supported Systems \u27b6

"},{"location":"help/general_faqs/#what-python-versions-are-supported-by-vidgear","title":"What Python versions are supported by VidGear?","text":"

Answer: See Supported Python legacies \u27b6

"},{"location":"help/general_faqs/#can-i-include-vidgear-in-my-project-commercially-or-not","title":"Can I include VidGear in my project commercially or not?","text":"

Answer: Yes, you can, but strictly under the Terms and Conditions given in VidGear License \u27b6

"},{"location":"help/general_faqs/#i-love-using-vidgear-for-my-projects-how-can-i-support-it","title":"\"I Love using VidGear for my projects\", How can I support it?","text":"

Answer: See Helping VidGear \u27b6

"},{"location":"help/get_help/","title":"Getting Help","text":""},{"location":"help/get_help/#getting-help","title":"Getting Help","text":"Courtesy - Pinterest

Would you like to get help with VidGear?

There are several ways such as:

"},{"location":"help/get_help/#frequently-asked-questions","title":"Frequently Asked Questions","text":"

Got a question related to VidGear Working?

Checkout the Frequently Asked Questions - a curated list of all the questions with adequate answer that we commonly receive for quickly troubleshooting your problems:

"},{"location":"help/get_help/#bonus-examples","title":"Bonus Examples","text":"

How we do this with that API?

Checkout the Bonus Examples - a curated list of all experimental examples with unusual configuration that aren't included in general usage examples:

"},{"location":"help/get_help/#join-our-gitter-community-channel","title":"Join our Gitter Community channel","text":"

Have you come up with some new idea \ud83d\udca1 or looking for the fastest way troubleshoot your problems

Join and chat on our Gitter Community channel:

There you can ask quick questions, swiftly troubleshoot your problems, help others, share ideas & information, etc.

"},{"location":"help/get_help/#this-is-what-you-do-when","title":"This is what you do when...","text":""},{"location":"help/get_help/#reporting-an-issues","title":"Reporting an issues","text":"

Want to report a bug? Suggest a new feature?

Before you do, please read our guidelines \u27b6

"},{"location":"help/get_help/#preparing-a-pull-request","title":"Preparing a Pull Request","text":"

Interested in contributing to VidGear?

Before you do, please read our guidelines \u27b6

"},{"location":"help/netgear_async_ex/","title":"Bonus Examples","text":""},{"location":"help/netgear_async_ex/#netgear_async-examples","title":"NetGear_Async Examples","text":""},{"location":"help/netgear_async_ex/#using-netgear_async-with-webgear","title":"Using NetGear_Async with WebGear","text":"

The complete usage example is as follows:

New in v0.2.2

This example was added in v0.2.2.

"},{"location":"help/netgear_async_ex/#client-webgear-server","title":"Client + WebGear Server","text":"

Open a terminal on Client System where you want to display the input frames (and setup WebGear server) received from the Server and execute the following python code:

After running this code, Make sure to open Browser immediately otherwise NetGear_Async will soon exit with TimeoutError. You can also try setting timeout parameter to a higher value to extend this timeout.

Make sure you use different port value for NetGear_Async and WebGear API.

High CPU utilization may occur on Client's end. User discretion is advised.

Note down the IP-address of this system (required at Server's end) by executing the hostname -I command and also replace it in the following code.\"

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nfrom vidgear.gears.asyncio import WebGear\nfrom vidgear.gears.asyncio.helper import reducer\nimport uvicorn, asyncio, cv2\n\n# Define NetGear_Async Client at given IP address and define parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear_Async(\n    receive_mode=True,\n    pattern=1,\n    logging=True,\n).launch()\n\n# create your own custom frame producer\nasync def my_frame_producer():\n\n    # loop over Client's Asynchronous Frame Generator\n    async for frame in client.recv_generator():\n\n        # {do something with received frames here}\n\n        # reducer frames size if you want more performance otherwise comment this line\n        frame = await reducer(\n            frame, percentage=30, interpolation=cv2.INTER_AREA\n        )  # reduce frame by 30%\n\n        # handle JPEG encoding\n        encodedImage = cv2.imencode(\".jpg\", frame)[1].tobytes()\n        # yield frame in byte format\n        yield (b\"--frame\\r\\nContent-Type:image/jpeg\\r\\n\\r\\n\" + encodedImage + b\"\\r\\n\")\n        await asyncio.sleep(0)\n\n\nif __name__ == \"__main__\":\n    # Set event loop to client's\n    asyncio.set_event_loop(client.loop)\n\n    # initialize WebGear app without any source\n    web = WebGear(logging=True)\n\n    # add your custom frame producer to config with adequate IP address\n    web.config[\"generator\"] = my_frame_producer\n\n    # run this app on Uvicorn server at address http://localhost:8000/\n    uvicorn.run(web(), host=\"localhost\", port=8000)\n\n    # safely close client\n    client.close()\n\n    # close app safely\n    web.shutdown()\n

On successfully running this code, the output stream will be displayed at address http://localhost:8000/ in your Client's Browser.

"},{"location":"help/netgear_async_ex/#server","title":"Server","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

# import library\nfrom vidgear.gears.asyncio import NetGear_Async\nimport cv2, asyncio\n\n# initialize Server without any source\nserver = NetGear_Async(\n    source=None,\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n)\n\n# Create a async frame generator as custom source\nasync def my_frame_generator():\n\n    # !!! define your own video source here !!!\n    # Open any video stream such as live webcam\n    # video stream on first index(i.e. 0) device\n    stream = cv2.VideoCapture(0)\n\n    # loop over stream until its terminated\n    while True:\n\n        # read frames\n        (grabbed, frame) = stream.read()\n\n        # check if frame empty\n        if not grabbed:\n            break\n\n        # do something with the frame to be sent here\n\n        # yield frame\n        yield frame\n        # sleep for sometime\n        await asyncio.sleep(0)\n\n    # close stream\n    stream.release()\n\n\nif __name__ == \"__main__\":\n    # set event loop\n    asyncio.set_event_loop(server.loop)\n    # Add your custom source generator to Server configuration\n    server.config[\"generator\"] = my_frame_generator()\n    # Launch the Server\n    server.launch()\n    try:\n        # run your main function task until it is complete\n        server.loop.run_until_complete(server.task)\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    finally:\n        # finally close the server\n        server.close()\n

"},{"location":"help/netgear_async_faqs/","title":"FAQs","text":""},{"location":"help/netgear_async_faqs/#netgear_async-faqs","title":"NetGear_Async FAQs","text":""},{"location":"help/netgear_async_faqs/#what-is-netgear_async-api-and-what-does-it-do","title":"What is NetGear_Async API and what does it do?","text":"

Answer: NetGear_Async is an asyncio videoframe messaging framework, built on zmq.asyncio, and powered by high-performance asyncio event loop called uvloop to achieve unmatchable high-speed and lag-free video streaming over the network with minimal resource constraints. Basically, this API is able to transfer thousands of frames in just a few seconds without causing any significant load on your system. For more info. see NetGear_Async doc \u27b6

"},{"location":"help/netgear_async_faqs/#how-to-get-started-with-netgear_async-api","title":"How to get started with NetGear_Async API?","text":"

Answer: Answer: Answer: First, refer to the Switching from OpenCV guide, then go through NetGear_Async documentation \u27b6. If you still have doubts, ask us on Gitter \u27b6 Community channel.

See NetGear_Async doc \u27b6. Still in doubt, then ask us on Gitter \u27b6 Community channel.

"},{"location":"help/netgear_async_faqs/#netgear_async-is-throwing-modulenotfounderror-on-importing-why","title":"\"NetGear_Async is throwing ModuleNotFoundError on importing\", Why?","text":"

Answer: This error means, VidGear is installed WITHOUT asyncio package support on your machine. For this support, see Requirements \u27b6.

"},{"location":"help/netgear_async_faqs/#what-is-the-key-difference-between-netgear_async-and-netgear-apis","title":"What is the key difference between NetGear_Async and NetGear APIs?","text":"

Answer:

Key Difference: NetGear_Async is highly memory efficient, but has less features as compared to NetGear API which is marginally faster too.

"},{"location":"help/netgear_async_faqs/#can-i-use-multi-server-bi-directional-like-modes-in-netgear_async","title":"Can I use Multi-Server, Bi-Directional like modes in NetGear_Async?","text":"

Answer: No, NetGear_Async does NOT provide support for any NetGear's Exclusive modes yet.

"},{"location":"help/netgear_async_faqs/#how-to-use-netgear_async-with-custom-server-source-from-opencv","title":"How to use NetGear_Async with custom Server Source from OpenCV?","text":"

Answer: See this usage example \u27b6.

"},{"location":"help/netgear_async_faqs/#why-netgear_async-is-running-slow","title":"Why NetGear_Async is running slow?","text":"

Answer: Checkout tips suggested in this answer \u27b6

"},{"location":"help/netgear_ex/","title":"Bonus Examples","text":""},{"location":"help/netgear_ex/#netgear-examples","title":"NetGear Examples","text":""},{"location":"help/netgear_ex/#using-netgear-with-webgear","title":"Using NetGear with WebGear","text":"

The complete usage example is as follows:

New in v0.2.2

This example was added in v0.2.2.

"},{"location":"help/netgear_ex/#client-webgear-server","title":"Client + WebGear Server","text":"

Open a terminal on Client System where you want to display the input frames (and setup WebGear server) received from the Server and execute the following python code:

After running this code, Make sure to open Browser immediately otherwise NetGear will soon exit with RuntimeError. You can also try setting max_retries and request_timeout like attributes to a higher value to avoid this.

Make sure you use different port value for NetGear and WebGear API.

High CPU utilization may occur on Client's end. User discretion is advised.

Note down the local IP-address of this system (required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

# import necessary libs\nimport uvicorn, asyncio, cv2\nfrom vidgear.gears import NetGear\nfrom vidgear.gears.asyncio import WebGear\nfrom vidgear.gears.asyncio.helper import reducer\n\n# initialize WebGear app without any source\nweb = WebGear(logging=True)\n\n\n# activate jpeg encoding and specify other related parameters\noptions = {\n    \"jpeg_compression\": True,\n    \"jpeg_compression_quality\": 90,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# create your own custom frame producer\nasync def my_frame_producer():\n    # initialize global params\n    # Define NetGear Client at given IP address and define parameters\n    # !!! change following IP address '192.168.x.xxx' with yours !!!\n    client = NetGear(\n        receive_mode=True,\n        address=\"192.168.x.xxx\",\n        port=\"5454\",\n        protocol=\"tcp\",\n        pattern=1,\n        logging=True,\n        **options,\n    )\n\n    # loop over frames\n    while True:\n        # receive frames from network\n        frame = client.recv()\n\n        # if NoneType\n        if frame is None:\n            break\n\n        # do something with your OpenCV frame here\n\n        # reducer frames size if you want more performance otherwise comment this line\n        frame = await reducer(\n            frame, percentage=30, interpolation=cv2.INTER_AREA\n        )  # reduce frame by 30%\n\n        # handle JPEG encoding\n        encodedImage = cv2.imencode(\".jpg\", frame)[1].tobytes()\n        # yield frame in byte format\n        yield (b\"--frame\\r\\nContent-Type:image/jpeg\\r\\n\\r\\n\" + encodedImage + b\"\\r\\n\")\n        await asyncio.sleep(0)\n    # close stream\n    client.close()\n\n\n# add your custom frame producer to config with adequate IP address\nweb.config[\"generator\"] = my_frame_producer\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

On successfully running this code, the output stream will be displayed at address http://localhost:8000/ in your Client's Browser.

"},{"location":"help/netgear_ex/#server","title":"Server","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate jpeg encoding and specify other related parameters\noptions = {\n    \"jpeg_compression\": True,\n    \"jpeg_compression_quality\": 90,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = VideoGear(source=0).start()\n\n# Define NetGear server at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if None-type\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"help/netgear_ex/#using-netgear-with-webgear_rtc","title":"Using NetGear with WebGear_RTC","text":"

The complete usage example is as follows:

New in v0.2.4

This example was added in v0.2.4.

"},{"location":"help/netgear_ex/#client-webgear_rtc-server","title":"Client + WebGear_RTC Server","text":"

Open a terminal on Client System where you want to display the input frames (and setup WebGear_RTC server) received from the Server and execute the following python code:

After running this code, Make sure to open Browser immediately otherwise NetGear will soon exit with RuntimeError. You can also try setting max_retries and request_timeout like attributes to a higher value to avoid this.

Make sure you use different port value for NetGear and WebGear_RTC API.

High CPU utilization may occur on Client's end. User discretion is advised.

Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

For VideoCapture APIs you also need to implement start() in addition to read() and stop() methods in your Custom Streaming Class as shown in following example, otherwise WebGear_RTC will fail to work!

# import necessary libs\nimport uvicorn, cv2\nfrom vidgear.gears import NetGear\nfrom vidgear.gears.helper import reducer\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# create your own custom streaming class\nclass Custom_Stream_Class:\n    \"\"\"\n    Custom Streaming using NetGear Receiver\n    \"\"\"\n\n    def __init__(\n        self,\n        address=None,\n        port=\"5454\",\n        protocol=\"tcp\",\n        pattern=1,\n        logging=True,\n        **options,\n    ):\n        # initialize global params\n        # Define NetGear Client at given IP address and define parameters\n        self.client = NetGear(\n            receive_mode=True,\n            address=address,\n            port=port,\n            protocol=protocol,\n            pattern=pattern,\n            logging=logging,\n            **options\n        )\n        self.running = False\n\n    def start(self):\n\n        # don't forget this function!!!\n        # This function is specific to VideoCapture APIs only\n\n        if not self.source is None:\n            self.source.start()\n\n    def read(self):\n\n        # don't forget this function!!!\n\n        # check if source was initialized or not\n        if self.source is None:\n            return None\n        # check if we're still running\n        if self.running:\n            # receive frames from network\n            frame = self.client.recv()\n            # check if frame is available\n            if not (frame is None):\n\n                # do something with your OpenCV frame here\n\n                # reducer frames size if you want more performance otherwise comment this line\n                frame = reducer(frame, percentage=20)  # reduce frame by 20%\n\n                # return our gray frame\n                return frame\n            else:\n                # signal we're not running now\n                self.running = False\n        # return None-type\n        return None\n\n    def stop(self):\n\n        # don't forget this function!!!\n\n        # flag that we're not running\n        self.running = False\n        # close stream\n        if not (self.client is None):\n            self.client.close()\n            self.client = None\n\n\n# activate jpeg encoding and specify NetGear related parameters\noptions = {\n    \"jpeg_compression\": True,\n    \"jpeg_compression_quality\": 90,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# assign your Custom Streaming Class with adequate NetGear parameters\n# to `custom_stream` attribute in options parameter of WebGear_RTC.\noptions = {\n    \"custom_stream\": Custom_Stream_Class(\n        address=\"192.168.x.xxx\",\n        port=\"5454\",\n        protocol=\"tcp\",\n        pattern=1,\n        logging=True,\n        **options\n    )\n}\n\n# initialize WebGear_RTC app without any source\nweb = WebGear_RTC(logging=True, **options)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

On successfully running this code, the output stream will be displayed at address http://localhost:8000/ in your Client's Browser.

"},{"location":"help/netgear_ex/#server_1","title":"Server","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate jpeg encoding and specify other related parameters\noptions = {\n    \"jpeg_compression\": True,\n    \"jpeg_compression_quality\": 90,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = VideoGear(source=0).start()\n\n# Define NetGear server at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"help/netgear_faqs/","title":"FAQs","text":""},{"location":"help/netgear_faqs/#netgear-faqs","title":"NetGear FAQs","text":""},{"location":"help/netgear_faqs/#what-is-netgear-api-and-what-does-it-do","title":"What is NetGear API and what does it do?","text":"

Answer: NetGear is exclusively designed to transfer video frames & data synchronously (Pair & Request/Reply) as well as asynchronously (Publish/Subscribe) between various interconnecting systems over the network in real-time. For more info. see NetGear doc \u27b6

"},{"location":"help/netgear_faqs/#how-to-get-started-with-netgear-api","title":"How to get started with NetGear API?","text":"

Answer: First, refer to the Switching from OpenCV guide, then go through NetGear documentation \u27b6. If you still have doubts, ask us on Gitter \u27b6 Community channel.

"},{"location":"help/netgear_faqs/#what-exclusive-modes-are-compatible-with-each-other-in-netgear-api","title":"What Exclusive Modes are compatible with each other in NetGear API?","text":"

Here's the compatibility chart for NetGear's Exclusive Modes:

Exclusive Modes Multi-Servers Multi-Clients Secure Bidirectional SSH Tunneling Multi-Servers - No (throws error) Yes Yes No (throws error) Multi-Clients No (throws error) - Yes Yes No (throws error) Secure Yes Yes - Yes Yes Bidirectional Yes Yes Yes - Yes SSH Tunneling No (throws error) No (throws error) Yes Yes -

"},{"location":"help/netgear_faqs/#why-netgear-is-running-slow","title":"Why NetGear is running slow?","text":"

Answer: Here are few tips to troubleshoot performance on your machine:

"},{"location":"help/netgear_faqs/#how-to-find-local-ip-address-on-different-os-platforms","title":"How to find local IP-address on different OS platforms?","text":"

Answer: For finding local IP-address of your machine:

On Linux OSOn Windows OSOn MAC OS

"},{"location":"help/netgear_faqs/#how-to-send-data-along-with-frames-in-multi-servers-and-multi-clients-modes","title":"How to send data along with frames in Multi-Servers and Multi-Clients Modes?","text":"

Answer: See Multi-Servers usage example \u27b6 and Multi-Clients usage example \u27b6

"},{"location":"help/netgear_faqs/#how-to-use-enable-encryption-and-authentication-in-netgear-api","title":"How to use enable Encryption and Authentication in NetGear API?","text":"

Answer: See its Secure Mode doc \u27b6.

"},{"location":"help/netgear_faqs/#how-to-send-custom-data-along-with-frames-bidirectionally-in-netgear-api","title":"How to send custom data along with frames bidirectionally in NetGear API?","text":"

Answer: See its Bidirectional Mode doc \u27b6.

"},{"location":"help/netgear_faqs/#how-to-access-netgear-api-outside-network-or-remotely","title":"How to access NetGear API outside network or remotely?","text":"

Answer: See its SSH Tunneling Mode doc \u27b6.

"},{"location":"help/netgear_faqs/#are-there-any-side-effect-of-sending-data-with-frames","title":"Are there any side-effect of sending data with frames?","text":"

Answer: Yes, it may lead to additional LATENCY depending upon the size/amount of the data being transferred. User discretion is advised.

"},{"location":"help/netgear_faqs/#why-netgear-api-not-working-correctly","title":"Why NetGear API not working correctly?","text":"

Answer: First, carefully go through NetGear doc \u27b6 that contains detailed information. Also, checkout PyZmq Docs \u27b6 for its various settings/parameters. If still it doesn't work for you, then let us know on Gitter \u27b6

"},{"location":"help/netgear_faqs/#how-to-solve-zmqerrorzmqerror-errors","title":"How to solve zmq.error.ZMQError errors?","text":"

Answer: For those used to the idea that a \"server\" provides their address to a client, then you should recheck your preconceptions! Please read the Netgear instructions carefully, and you will note that it is the client device that defines the IP that is provided to the server config. If you get this the wrong way (using the server IP on the client), then you will get a zmq.error.ZMQError error. Make sure it is the client's IP shared across the two systems.

"},{"location":"help/pigear_ex/","title":"Bonus Examples","text":""},{"location":"help/pigear_ex/#pigear-examples","title":"PiGear Examples","text":""},{"location":"help/pigear_ex/#changing-output-pixel-format-in-pigear-api-with-picamera2-backend","title":"Changing Output Pixel Format in PiGear API with Picamera2 Backend","text":"

With the Picamera2 backend, you can also define a custom format (format of output frame pixels) in PiGear API.

Handling output frames with a custom pixel format correctly

While defining custom format as an optional parameter, it is advised to also define the colorspace parameter in the PiGear API. This is required only under TWO conditions:

Otherwise, output frames might NOT be compatible with OpenCV functions, and you need to handle these frames manually!

Picamera2 library has an unconventional naming convention for its pixel formats.

Please note that, Picamera2 takes its pixel format naming from libcamera, which in turn takes them from certain underlying Linux components. The results are not always the most intuitive. For example, OpenCV users will typically want each pixel to be a (B, G, R) triple for which the RGB888 format should be chosen, and not BGR888. Similarly, OpenCV users wanting an alpha channel should select XRGB8888.

For more information, refer Picamera2 docs \u27b6

YUV420/YVU420YUYV

For reducing the size of frames in memory it is advised to use the YUV420 pixels format.

In this example we will be defining custom YUV420 (or YVU420) pixels format of output frame, and converting it back to BGR to be able to display with OpenCV.

You could also instead define colorspace=\"COLOR_YUV420p2RGB\" parameter in PiGear API for converting it back to BGR similarly.

# import required libraries\nfrom vidgear.gears import PiGear\nimport cv2\n\n# formulate `format` Picamera2 API \n# configurational parameters\noptions = {\n    \"format\": \"YUV420\" # or use `YVU420`\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    yuv420_frame = stream.read()\n\n    # check for frame if Nonetype\n    if yuv420_frame is None:\n        break\n\n    # {do something with the `YUV420` frame here}\n\n    # convert `YUV420` to `BGR`\n    bgr = cv2.cvtColor(yuv420_frame, cv2.COLOR_YUV420p2BGR)\n\n    # {do something with the `BGR` frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", bgr)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

YUYV is a one packed 4:2:2 YUV format that is popularly used by USB cameras.

Make sure YUYV pixel format is supported by your USB camera.

In this example we will be defining custom YUYV pixels format of output frame, and converting it back to BGR to be able to display with OpenCV.

You could also instead define colorspace=\"COLOR_YUV2BGR_YUYV\" parameter in PiGear API for converting it back to BGR similarly.

# import required libraries\nfrom vidgear.gears import PiGear\nimport cv2\n\n# formulate `format` Picamera2 API \n# configurational parameters\noptions = {\n    \"format\": \"YUYV\"\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    yuv420_frame = stream.read()\n\n    # check for frame if Nonetype\n    if yuv420_frame is None:\n        break\n\n    # {do something with the `YUV420` frame here}\n\n    # convert `YUV420` to `BGR`\n    bgr = cv2.cvtColor(yuv420_frame, cv2.COLOR_YUV2BGR_YUYV)\n\n    # {do something with the `BGR` frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", bgr)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"help/pigear_ex/#dynamically-adjusting-raspberry-pi-camera-parameters-at-runtime-in-pigear-api","title":"Dynamically Adjusting Raspberry Pi Camera Parameters at Runtime in PiGear API","text":"New Picamera2 backendLegacy Picamera backend

With the picamera2 backend, using stream global parameter in the PiGear API, you can change all camera controls (except output resolution and format) at runtime after the camera has started.

Accessing all available camera controls

A complete list of all the available camera controls can be found in the picamera2 docs \u27b6, and also by inspecting the camera_controls property of the Picamera2 object available with stream global parameter in PiGear API:

# import required libraries\nfrom vidgear.gears import PiGear\n\n# open any pi video stream\nstream = PiGear()\n\n#display all available camera controls\nprint(stream.stream.camera_controls)\n\n# safely close video stream\nstream.stop()\n

This returns a dictionary with the control names as keys, and each value being a tuple of (min, max, default) values for that control. The default value should be interpreted with some caution as in many cases libcamera's default value will be overwritten by the camera tuning as soon as the camera is started.

In this example, we will set the initial Camera Module's brightness value to -0.5 (dark), and will change it to 0.5 (bright) when the Z key is pressed at runtime:

Delay in setting runtime controls

There will be a delay of several frames before the controls take effect. This is because there is perhaps quite a large number of requests for camera frames already in flight, and for some controls (exposure time and analogue gain specifically), the camera may actually take several frames to apply the updates.

Using with construct for Guaranteed Camera Control Updates at Runtime

While directly modifying using set_controls method might seem convenient, it doesn't guarantee that all camera control settings are applied within the same frame at runtime. The with construct provides a structured approach to managing camera control updates in real-time. Here's how to use it:

# import required libraries\nfrom vidgear.gears import PiGear\n\n# formulate initial configurational parameters\noptions = \"controls\": {\"ExposureTime\": 5000, \"AnalogueGain\": 0.5}\n\n# open pi video stream with these parameters\nstream = PiGear(logging=True, **options).start() \n\n# Enter context manager and set runtime controls\n# Within this block, the controls are guaranteed to be applied atomically\nwith stream.stream.controls as controls:  \n    controls.ExposureTime = 10000  # Set new exposure time\n    controls.AnalogueGain = 1.0     # Set new analogue gain\n\n# ...rest of code goes here...\n\n# safely close video stream\nstream.stop()\n
# import required libraries\nfrom vidgear.gears import PiGear\nimport cv2\n\n# formulate initial configurational parameters\n# set brightness to -0.5 (dark)\noptions = {\"controls\": {\"Brightness\": -0.5}}\n\n# open pi video stream with these parameters\nstream = PiGear(logging=True, **options).start() \n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n    # check for 'z' key if pressed\n    if key == ord(\"z\"):\n        # change brightness to 0.5 (bright)\n        stream.stream.set_controls({\"Brightness\": 0.5})\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

You can also use the stream global parameter in PiGear with thepicamera backend to feed any picamera parameters at runtime after the camera has started.

PiGear API switches to the legacy picamerabackend if the picamera2 library is unavailable.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera optional parameter boolean attribute.

In this example we will set initial Camera Module's brightness value 80 (brighter), and will change it 30 (darker) when Z key is pressed at runtime:

# import required libraries\nfrom vidgear.gears import PiGear\nimport cv2\n\n# formulate initial configurational parameters \n# set brightness to `80` (bright)\noptions = {\"brightness\": 80} \n\n# open pi video stream with these parameters\nstream = PiGear(logging=True, **options).start() \n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n    # check for 'z' key if pressed\n    if key == ord(\"z\"):\n        # change brightness to `30` (darker)\n        stream.stream.brightness = 30\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n
"},{"location":"help/pigear_ex/#accessing-multiple-camera-through-its-index-in-pigear-api","title":"Accessing Multiple Camera through its Index in PiGear API","text":"

With the camera_num parameter in the PiGear API, you can easily select the camera index to be used as the source, allowing you to drive these multiple cameras simultaneously from within a single Python session.

The camera_num value can only be zero or greater, otherwise, PiGear API will throw ValueError for any negative value.

New Picamera2 backendLegacy Picamera backend

With the picamera2 backend, you can use the camera_num parameter in PiGear to select the camera index to be used as the source if you have multiple Raspberry Pi camera modules (such as CM4) and/or USB cameras connected simultaneously to your Raspberry Pi.

Accessing metadata about connected cameras.

You can call the global_camera_info() method of the Picamera2 object available with stream global parameter in PiGear API to find out what cameras are attached. This returns a list containing one dictionary for each camera, ordered according the camera number you would pass to the camera_num parameter in PiGear API to open that device. The dictionary contains:

You should always check this list to discover which camera is which as the order can change when the system boots or USB cameras are re-connected as follows:

# import required libraries\nfrom vidgear.gears import PiGear\n\n# open any pi video stream\nstream = PiGear()\n\n#display all available cameras metadata\nprint(stream.stream.global_camera_info())\n\n# safely close video stream\nstream.stop()\n

The PiGear API can accurately differentiate between USB and Raspberry Pi camera modules by utilizing the camera's metadata.

In this example, we will select the USB Camera connected at index 1 on the Raspberry Pi as the primary source for extracting frames in PiGear API:

Limited support for USB Cameras

This example also works with USB Cameras, However:

This example assumes a USB Camera is connected at index 1, and some other camera connected at index 0 on your Raspberry Pi.

# import required libraries\nfrom vidgear.gears import PiGear\nfrom libcamera import Transform\nimport cv2\n\n# formulate various Picamera2 API \n# configurational parameters for USB camera\noptions = {\n    \"sensor\": {\"output_size\": (480, 320)},  # will override `resolution`\n    \"format\": \"RGB888\" # BGR format for this example\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream at index `1` with defined parameters\nstream = PiGear(camera_num=1, resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

With the Picamera backend, you should not change the camera_num parameter unless you are using the Raspberry Pi 3/3+/4 Compute Module IO Boards or third party Arducam Camarray Multiple Camera Solutions, which supports attaching multiple camera modules to the same Raspberry Pi board using appropriate I/O connections.

You can use the camera_num parameter in PiGear with the picamera backend to select the camera index to be used as the source if you have multiple Raspberry Pi camera modules connected.

PiGear API switches to the legacy picamerabackend if the picamera2 library is unavailable.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera optional parameter boolean attribute.

In this example, we will select the Camera Module connected at index 1 on the Raspberry Pi as the primary source for extracting frames in PiGear API:

This example assumes a Camera Module is connected at index 1 on your Raspberry Pi.

# import required libraries\nfrom vidgear.gears import PiGear\nimport cv2\n\n# formulate various Picamera API \n# configurational parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream at index `1` with defined parameters\nstream = PiGear(camera_num=1, resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"help/pigear_faqs/","title":"FAQs","text":""},{"location":"help/pigear_faqs/#pigear-faqs","title":"PiGear FAQs","text":""},{"location":"help/pigear_faqs/#what-is-pigear-api-and-what-does-it-do","title":"What is PiGear API and what does it do?","text":"

Answer: PiGear is a specialized API similar to the CamGear API but optimized for Raspberry Pi Boards, offering comprehensive support for camera modules (e.g., OmniVision OV5647, Sony IMX219), along with limited compatibility for USB cameras. For more info. see PiGear doc \u27b6

"},{"location":"help/pigear_faqs/#im-only-familiar-with-opencv-how-to-get-started-with-pigear-api","title":"I'm only familiar with OpenCV, how to get started with PiGear API?","text":"

Answer: First, refer to the Switching from OpenCV guide, then go through PiGear documentation. If you still have doubts, ask us on Gitter \u27b6 Community channel.

"},{"location":"help/pigear_faqs/#why-my-camera-module-is-not-detected-by-pigear","title":"Why my camera module is not detected by PiGear?","text":"

Answer: Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using PiGear API. Also, recheck/change your Camera Module's ribbon-cable and Camera Module itself, if it damaged or got broken somehow.

"},{"location":"help/pigear_faqs/#how-to-select-camera-index-on-pi-compute-io-board-with-two-cameras-attached","title":"How to select camera index on Pi Compute IO board with two Cameras attached?","text":"

Answer: Refer this bonus example \u27b6

"},{"location":"help/pigear_faqs/#why-pigear-is-throwing-systemerror","title":"Why PiGear is throwing SystemError?","text":"

Answer: This means your Raspberry Pi CSI ribbon-cable is not connected properly to your Camera Module, or damaged, or even both.

"},{"location":"help/pigear_faqs/#how-to-assign-various-configurational-settings-for-camera-module-with-pigear","title":"How to assign various configurational settings for Camera Module with PiGear?","text":"

Answer: See this usage example \u27b6

"},{"location":"help/pigear_faqs/#video-output-is-too-dark-with-pigear-why","title":"\"Video output is too dark with PiGear\", Why?","text":"

Answer: The camera configuration settings might be incorrect. Check this usage example \u27b6 and try tinkering parameters like sensor_mode, shutter_speed, and exposure_mode. Additionally, if your framerate parameter value is too high, try lowering it.

"},{"location":"help/pigear_faqs/#how-to-dynamically-adjust-raspberry-pi-camera-parameters-at-runtime-with-pigear","title":"How to dynamically adjust Raspberry Pi Camera Parameters at runtime with PiGear?","text":"

Answer: See this bonus example \u27b6

"},{"location":"help/pigear_faqs/#is-it-possible-to-change-output-frames-pixel-format-in-pigear-api","title":"Is it possible to change output frames Pixel Format in PiGear API?","text":"

Answer: Yes it is possible with Picamera2 Backend. See this bonus example \u27b6

"},{"location":"help/screengear_ex/","title":"Bonus Examples","text":""},{"location":"help/screengear_ex/#screengear-examples","title":"ScreenGear Examples","text":""},{"location":"help/screengear_ex/#using-screengear-with-netgear-and-writegear","title":"Using ScreenGear with NetGear and WriteGear","text":"

The complete usage example is as follows:

New in v0.2.2

This example was added in v0.2.2.

"},{"location":"help/screengear_ex/#client-writegear","title":"Client + WriteGear","text":"

Open a terminal on Client System (where you want to save the input frames received from the Server) and execute the following python code:

Note down the IP-address of this system(required at Server's end) by executing the command: hostname -I and also replace it in the following code.

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# define various tweak flags\noptions = {\"flag\": 0, \"copy\": True, \"track\": False}\n\n# Define Netgear Client at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# Define writer with default parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\")\n\n# loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n\n# safely close writer\nwriter.close()\n
"},{"location":"help/screengear_ex/#server-screengear","title":"Server + ScreenGear","text":"

Now, Open the terminal on another Server System (with a montior/display attached to it), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import ScreenGear\nfrom vidgear.gears import NetGear\n\n# define dimensions of screen w.r.t to given monitor to be captured\noptions = {\"top\": 40, \"left\": 0, \"width\": 100, \"height\": 100}\n\n# open stream with defined parameters\nstream = ScreenGear(logging=True, **options).start()\n\n# define various netgear tweak flags\noptions = {\"flag\": 0, \"copy\": True, \"track\": False}\n\n# Define Netgear server at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"help/screengear_ex/#using-screengear-with-webgear_rtc","title":"Using ScreenGear with WebGear_RTC","text":"

The complete usage example is as follows:

New in v0.2.4

This example was added in v0.2.4.

Bare-MinimumAdvanced
# import necessary libs\nimport uvicorn, cv2\nfrom vidgear.gears import ScreenGear\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# assign your ScreenGear class with adequate parameters \n# to `custom_stream` attribute in options parameter\noptions = {\"custom_stream\": ScreenGear(logging=True)}\n\n# initialize WebGear_RTC app without any source\nweb = WebGear_RTC(logging=True, **options)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

For VideoCapture APIs you also need to implement start() in addition to read() and stop() methods in your Custom Streaming Class as shown in following example, otherwise WebGear_RTC will fail to work!

# import necessary libs\nimport uvicorn, cv2\nfrom vidgear.gears import ScreenGear\nfrom vidgear.gears.helper import reducer\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# create your own custom streaming class\nclass Custom_Stream_Class:\n    \"\"\"\n    Custom Streaming using ScreenGear\n    \"\"\"\n\n    def __init__(self, backend=\"mss\", logging=False):\n\n        # !!! define your own video source here !!!\n        self.source = ScreenGear(backend=backend, logging=logging)\n\n        # define running flag\n        self.running = True\n\n    def start(self):\n\n        # don't forget this function!!!\n        # This function is specific to VideoCapture APIs only\n\n        if not self.source is None:\n            self.source.start()\n\n    def read(self):\n\n        # don't forget this function!!!\n\n        # check if source was initialized or not\n        if self.source is None:\n            return None\n        # check if we're still running\n        if self.running:\n            # read frame from provided source\n            frame = self.source.read()\n            # check if frame is available\n            if not(frame is None):\n\n                # do something with your OpenCV frame here\n\n                # reducer frames size if you want more performance otherwise comment this line\n                frame = reducer(frame, percentage=20)  # reduce frame by 20%\n\n                # return our gray frame\n                return frame\n            else:\n                # signal we're not running now\n                self.running = False\n        # return None-type\n        return None\n\n    def stop(self):\n\n        # don't forget this function!!!\n\n        # flag that we're not running\n        self.running = False\n        # close stream\n        if not self.source is None:\n            self.source.stop()\n\n\n# assign your Custom Streaming Class with adequate ScreenGear parameters\n# to `custom_stream` attribute in options parameter\noptions = {\"custom_stream\": Custom_Stream_Class(backend=\"pil\", logging=True)}\n\n# initialize WebGear_RTC app without any source\nweb = WebGear_RTC(logging=True, **options)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

"},{"location":"help/screengear_faqs/","title":"FAQs","text":""},{"location":"help/screengear_faqs/#screengear-faqs","title":"ScreenGear FAQs","text":""},{"location":"help/screengear_faqs/#what-is-screengear-api-and-what-does-it-do","title":"What is ScreenGear API and what does it do?","text":"

Answer: ScreenGear is designed exclusively for targeting rapid Screencasting Capabilities, which means it can grab frames from your monitor in real-time, either by defining an area on the computer screen or full-screen, at the expense of inconsiderable latency. ScreenGear also seamlessly support frame capturing from multiple monitors as well as supports multiple backends. For more info. see ScreenGear doc \u27b6

"},{"location":"help/screengear_faqs/#im-only-familiar-with-opencv-how-to-get-started-with-screengear-api","title":"I'm only familiar with OpenCV, how to get started with ScreenGear API?","text":"

Answer: First, refer to the Switching from OpenCV guide, then go through ScreenGear documentation. If you still have doubts, ask us on Gitter \u27b6 Community channel.

"},{"location":"help/screengear_faqs/#screengear-is-slow","title":"ScreenGear is Slow?","text":"

Answer: This maybe due to selected backend for ScreenGear API is not compatible with your machine. See this usage example to change backend \u27b6. Try different backends, and select which works the best for your machine.

"},{"location":"help/screengear_faqs/#how-to-define-area-on-screen-to-record-with-screengear","title":"How to define area on screen to record with ScreenGear?","text":"

Answer: See this usage example \u27b6

"},{"location":"help/screengear_faqs/#how-to-record-video-from-all-connected-screens","title":"How to record video from all connected screens?","text":"

Answer: With mss backend, see ScreenGear's monitor parameter that sets the index of the monitor to grab a frame from. If its value is -1, it will record from all monitors. More information can be found here \u27b6

"},{"location":"help/screengear_faqs/#im-getting-attributeerror-dxcamera-object-has-no-attribute-is_capturing-error","title":"I'm getting \"AttributeError: 'DXCamera' object has no attribute 'is_capturing'\" Error?","text":"

Answer: This is a well-known error in backend dxcam library which occurs when you've multiple GPUs on your Windows machine. To workaround this, you need select Internal GPU in settings as follows:

On Windows 11On Windows 10

In Settings, go to System > Display > Graphics and add your Python.exe as \"Desktop App\", then select \"Power saving\" as follows:

And finally press Save button.

In Settings, go to Graphics Settings and add your Python.exe as \"Desktop App\", then select \"Power saving\" as follows:

And finally press Save button.

"},{"location":"help/stabilizer_ex/","title":"Bonus Examples","text":""},{"location":"help/stabilizer_ex/#stabilizer-class-examples","title":"Stabilizer Class Examples","text":""},{"location":"help/stabilizer_ex/#saving-stabilizer-class-output-with-live-audio-input","title":"Saving Stabilizer Class output with Live Audio Input","text":"

In this example code, we will merging the audio from a Audio Device (for e.g. Webcam inbuilt mic input) with Stabilized frames incoming from the Stabilizer Class (which is also using same Webcam video input through OpenCV), and save the final output as a compressed video file, all in real time:

New in v0.2.2

This example was added in v0.2.2.

Example Assumptions

Identifying and Specifying sound card on different OS platforms Windows Linux MacOS

Windows OS users can use the dshow (DirectShow) to list audio input device which is the preferred option for Windows users. You can refer following steps to identify and specify your sound card:

If audio still doesn't work then checkout this troubleshooting guide \u27b6 or reach us out on Gitter \u27b6 Community channel

Linux OS users can use the alsa to list input device to capture live audio input such as from a webcam. You can refer following steps to identify and specify your sound card:

If audio still doesn't work then reach us out on Gitter \u27b6 Community channel

MAC OS users can use the avfoundation to list input devices for grabbing audio from integrated iSight cameras as well as cameras connected via USB or FireWire. You can refer following steps to identify and specify your sound card on MacOS/OSX machines:

If audio still doesn't work then reach us out on Gitter \u27b6 Community channel

Make sure this -i audio-source it compatible with provided video-source, otherwise you could encounter multiple errors or no output at all.

You MUST use -input_framerate attribute to set exact value of input framerate when using external audio in Real-time Frames mode, otherwise audio delay will occur in output streams.

# import required libraries\nfrom vidgear.gears import WriteGear\nfrom vidgear.gears.stabilizer import Stabilizer\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = cv2.VideoCapture(0)\n\n# initiate stabilizer object with defined parameters\nstab = Stabilizer(smoothing_radius=30, crop_n_zoom=True, border_size=5, logging=True)\n\n# change with your webcam soundcard, plus add additional required FFmpeg parameters for your writer\noutput_params = {\n    \"-input_framerate\": stream.get(cv2.CAP_PROP_FPS),\n    \"-thread_queue_size\": \"512\",\n    \"-ac\": \"2\",\n    \"-ar\": \"48000\",\n    \"-f\": \"alsa\", # (1)\n    \"-i\": \"hw:1\",\n}\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4\nwriter = WriteGear(output=\"Output.mp4\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n        break\n\n    # send current frame to stabilizer for processing\n    stabilized_frame = stab.stabilize(frame)\n\n    # wait for stabilizer which still be initializing\n    if stabilized_frame is None:\n        continue\n\n    # {do something with the stabilized frame here}\n\n    # write stabilized frame to writer\n    writer.write(stabilized_frame)\n\n\n# clear stabilizer resources\nstab.clean()\n\n# safely close video stream\nstream.release()\n\n# safely close writer\nwriter.close()\n
  1. Always keep this line above -i parameter!

"},{"location":"help/stabilizer_ex/#saving-stabilizer-class-output-with-file-audio-input","title":"Saving Stabilizer Class output with File Audio Input","text":"

In this example code, we will be directly merging the audio from a Video-File (to be stabilized) with its processed stabilized frames into a compressed video output in real time:

New in v0.2.4

This example was added in v0.2.4.

Make sure this input video-file (to be stabilized) contains valid audio source, otherwise you could encounter multiple errors or no output at all.

You MUST use -input_framerate attribute to set exact value of input framerate when using external audio in Real-time Frames mode, otherwise audio delay will occur in output streams.

Use -disable_force_termination flag when video duration is too short(<60sec), otherwise WriteGear will not produce any valid output.

# import required libraries\nfrom vidgear.gears import WriteGear\nfrom vidgear.gears.stabilizer import Stabilizer\nimport cv2\n\n# Give suitable video file path to be stabilized\nunstabilized_videofile = \"test.mp4\"\n\n# open stream on given path\nstream = cv2.VideoCapture(unstabilized_videofile)\n\n# initiate stabilizer object with defined parameters\nstab = Stabilizer(smoothing_radius=30, crop_n_zoom=True, border_size=5, logging=True)\n\n# define required FFmpeg optimizing parameters for your writer\noutput_params = {\n    \"-i\": unstabilized_videofile,\n    \"-c:a\": \"aac\",\n    \"-input_framerate\": stream.get(cv2.CAP_PROP_FPS),\n    \"-clones\": [\"-shortest\"],\n    # !!! Uncomment following line if video duration is too short(<60sec). !!!\n    #\"-disable_force_termination\": True,\n}\n\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4\nwriter = WriteGear(output=\"Output.mp4\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n        break\n\n    # send current frame to stabilizer for processing\n    stabilized_frame = stab.stabilize(frame)\n\n    # wait for stabilizer which still be initializing\n    if stabilized_frame is None:\n        continue\n\n    # {do something with the stabilized frame here}\n\n    # write stabilized frame to writer\n    writer.write(stabilized_frame)\n\n\n# clear stabilizer resources\nstab.clean()\n\n# safely close video stream\nstream.release()\n\n# safely close writer\nwriter.close()\n

"},{"location":"help/stabilizer_faqs/","title":"FAQs","text":""},{"location":"help/stabilizer_faqs/#stabilizer-class-faqs","title":"Stabilizer Class FAQs","text":""},{"location":"help/stabilizer_faqs/#what-is-stabilizer-class-and-what-does-it-do","title":"What is Stabilizer Class and what does it do?","text":"

Answer: Stabilizer Class is an auxiliary class that enables Video Stabilization for vidgear with minimalistic latency, and at the expense of little to no additional computational requirements. For more info. see Stabilizer Class doc \u27b6

"},{"location":"help/stabilizer_faqs/#how-much-latency-you-would-typically-expect-with-stabilizer-class","title":"How much latency you would typically expect with Stabilizer Class?","text":"

Answer: The stabilizer will be Slower for High-Quality videos-frames. Try reducing frames size (Use reducer() method) before feeding them for reducing latency. Also, see smoothing_radius parameter of Stabilizer class that handles the quality of stabilization at the expense of latency and sudden panning. The larger its value, the less will be panning, more will be latency, and vice-versa.

"},{"location":"help/stabilizer_faqs/#how-to-remove-black-borders-in-output-video-after-stabilizing-it","title":"How to remove black borders in output video after stabilizing it?","text":"

Answer: See crop_n_zoom parameter of Stabilizer class, that enables the feature, where it crops and zooms frames(to original size) to reduce the black borders from stabilization being too noticeable (similar to the feature available in Adobe AfterEffects). It works in conjunction with the border_size parameter, i.e. when this parameter is enabled border_size will be used for cropping border instead of making them. Its default value is False.

"},{"location":"help/stabilizer_faqs/#can-i-use-stabilizer-directly-with-opencv","title":"Can I use Stabilizer directly with OpenCV?","text":"

Answer: Yes, see this usage example \u27b6.

"},{"location":"help/stabilizer_faqs/#why-stabilization-is-not-working-properly-for-my-video","title":"Why stabilization is not working properly for my video?","text":"

Answer: The Stabilizer may not perform well against High-frequency jitter in video. But,you can check if increasing smoothing_radius parameter value helps but it will add latency too.

"},{"location":"help/streamgear_ex/","title":"Bonus Examples","text":""},{"location":"help/streamgear_ex/#streamgear-examples","title":"StreamGear Examples","text":""},{"location":"help/streamgear_ex/#streamgear-live-streaming-usage-with-pigear","title":"StreamGear Live-Streaming Usage with PiGear","text":"

In this example, we will be Live-Streaming video-frames from Raspberry Pi (with Camera Module connected) using PiGear API and StreamGear API's Real-time Frames Mode:

Use -window_size & -extra_window_size FFmpeg parameters for controlling number of frames to be kept in Chunks. Less these value, less will be latency.

After every few chunks (equal to the sum of -window_size & -extra_window_size values), all chunks will be overwritten in Live-Streaming. Thereby, since newer chunks in manifest/playlist will contain NO information of any older ones, and therefore resultant DASH/HLS stream will play only the most recent frames.

In this mode, StreamGear DOES NOT automatically maps video-source audio to generated streams. You need to manually assign separate audio-source through -audio attribute of stream_params dictionary parameter.

PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using the PiGear API, otherwise nothing will work.

DASHHLS New Picamera2 backendLegacy Picamera backend
# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import StreamGear\nfrom libcamera import Transform\nimport cv2\n\n# formulate various Picamera2 API \n# configurational parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# enable livestreaming and retrieve framerate from CamGear Stream and\n# pass it as `-input_framerate` parameter for controlled framerate\nstream_params = {\"-input_framerate\": stream.framerate, \"-livestream\": True}\n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n
Under the hood, PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, PiGear API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# formulate various Picamera API \n# configurational parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# enable livestreaming and retrieve framerate from CamGear Stream and\n# pass it as `-input_framerate` parameter for controlled framerate\nstream_params = {\"-input_framerate\": stream.framerate, \"-livestream\": True}\n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n
New Picamera2 backendLegacy Picamera backend
# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import StreamGear\nfrom libcamera import Transform\nimport cv2\n\n# formulate various Picamera2 API \n# configurational parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# enable livestreaming and retrieve framerate from CamGear Stream and\n# pass it as `-input_framerate` parameter for controlled framerate\nstream_params = {\"-input_framerate\": stream.framerate, \"-livestream\": True}\n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n
Under the hood, PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, PiGear API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# formulate various Picamera API \n# configurational parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# enable livestreaming and retrieve framerate from CamGear Stream and\n# pass it as `-input_framerate` parameter for controlled framerate\nstream_params = {\"-input_framerate\": stream.framerate, \"-livestream\": True}\n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n

"},{"location":"help/streamgear_faqs/","title":"FAQs","text":""},{"location":"help/streamgear_faqs/#streamgear-faqs","title":"StreamGear FAQs","text":""},{"location":"help/streamgear_faqs/#what-is-streamgear-api-and-what-does-it-do","title":"What is StreamGear API and what does it do?","text":"

Answer: StreamGear automates transcoding workflow for generating Ultra-Low Latency, High-Quality, Dynamic & Adaptive Streaming Formats (such as MPEG-DASH) in just few lines of python code. For more info. see StreamGear doc \u27b6

"},{"location":"help/streamgear_faqs/#how-to-get-started-with-streamgear-api","title":"How to get started with StreamGear API?","text":"

Answer: First, refer to the Switching from OpenCV guide, then go through StreamGear documentation. If you still have doubts, ask us on Gitter \u27b6 Community channel.

"},{"location":"help/streamgear_faqs/#what-is-mpd-file-created-with-streamgear","title":"What is .mpd file created with StreamGear?","text":"

Answer: SteamGear also creates a Manifest file (such as MPD in-case of DASH) besides segments that describe these segment information (timing, URL, media characteristics like video resolution and bit rates) and is provided to the client before the streaming session.

"},{"location":"help/streamgear_faqs/#how-to-play-streaming-assets-created-with-streamgear-api","title":"How to play Streaming Assets created with StreamGear API?","text":"

Answer: You can easily feed Manifest file(.mpd) to DASH Supported Players Input but sure encoded chunks are present along with it. See this list of recommended players \u27b6

"},{"location":"help/streamgear_faqs/#what-adaptive-streaming-formats-are-supported-yet","title":"What Adaptive Streaming Formats are supported yet?","text":"

Answer: SteamGear currently only supports MPEG-DASH (Dynamic Adaptive Streaming over HTTP, ISO/IEC 23009-1) , but other adaptive streaming technologies such as Apple HLS, Microsoft Smooth Streaming, will be added soon.

"},{"location":"help/streamgear_faqs/#is-drm-encryption-supported-in-streamgear-api","title":"Is DRM Encryption supported in StreamGear API?","text":"

Answer: No, DRM Encryption is NOT supported yet.

"},{"location":"help/streamgear_faqs/#how-to-create-additional-streams-in-streamgear-api","title":"How to create additional streams in StreamGear API?","text":"

Answer: See this example \u27b6

"},{"location":"help/streamgear_faqs/#how-to-use-streamgear-api-with-opencv","title":"How to use StreamGear API with OpenCV?","text":"

Answer: See this example \u27b6

"},{"location":"help/streamgear_faqs/#how-to-use-streamgear-api-with-real-time-frames","title":"How to use StreamGear API with real-time frames?","text":"

Answer: See Real-time Frames Mode \u27b6

"},{"location":"help/streamgear_faqs/#how-to-use-hardwaregpu-encoder-for-transcoding-in-streamgear-api","title":"How to use Hardware/GPU encoder for transcoding in StreamGear API?","text":"

Answer: See this example \u27b6

"},{"location":"help/videogear_ex/","title":"Bonus Examples","text":""},{"location":"help/videogear_ex/#videogear-examples","title":"VideoGear Examples","text":""},{"location":"help/videogear_ex/#using-videogear-with-rosrobot-operating-system","title":"Using VideoGear with ROS(Robot Operating System)","text":"

We will be using cv_bridge to convert OpenCV frames to ROS image messages and vice-versa.

In this example, we'll create a node that convert OpenCV frames into ROS image messages, and then publishes them over ROS.

New in v0.2.2

This example was added in v0.2.2.

This example is vidgear implementation of this wiki example.

# import roslib\nimport roslib\n\nroslib.load_manifest(\"my_package\")\n\n# import other required libraries\nimport sys\nimport rospy\nimport cv2\nfrom std_msgs.msg import String\nfrom sensor_msgs.msg import Image\nfrom cv_bridge import CvBridge, CvBridgeError\nfrom vidgear.gears import VideoGear\n\n# custom publisher class\nclass image_publisher:\n    def __init__(self, source=0, logging=False):\n        # create CV bridge\n        self.bridge = CvBridge()\n        # define publisher topic\n        self.image_pub = rospy.Publisher(\"image_topic_pub\", Image)\n        # open stream with given parameters\n        self.stream = VideoGear(source=source, logging=logging).start()\n        # define publisher topic\n        rospy.Subscriber(\"image_topic_sub\", Image, self.callback)\n\n    def callback(self, data):\n\n        # {do something with received ROS node data here}\n\n        # read frames\n        frame = self.stream.read()\n        # check for frame if None-type\n        if not (frame is None):\n\n            # {do something with the frame here}\n\n            # publish our frame\n            try:\n                self.image_pub.publish(self.bridge.cv2_to_imgmsg(frame, \"bgr8\"))\n            except CvBridgeError as e:\n                # catch any errors\n                print(e)\n\n    def close(self):\n        # stop stream\n        self.stream.stop()\n\n\ndef main(args):\n    # !!! define your own video source here !!!\n    # Open any video stream such as live webcam\n    # video stream on first index(i.e. 0) device\n\n    # define publisher\n    ic = image_publisher(source=0, logging=True)\n    # initiate ROS node on publisher\n    rospy.init_node(\"image_publisher\", anonymous=True)\n    try:\n        # run node\n        rospy.spin()\n    except KeyboardInterrupt:\n        print(\"Shutting down\")\n    finally:\n        # close publisher\n        ic.close()\n\n\nif __name__ == \"__main__\":\n    main(sys.argv)\n

"},{"location":"help/videogear_ex/#using-videogear-for-capturing-rtsprtmp-urls","title":"Using VideoGear for capturing RTSP/RTMP URLs","text":"

Here's a high-level wrapper code around VideoGear API to enable auto-reconnection during capturing, plus stabilization is enabled (stabilize=True) in order to stabilize captured frames on-the-go:

New in v0.2.2

This example was added in v0.2.2.

Enforcing UDP stream

You can easily enforce UDP for RTSP streams inplace of default TCP, by putting following lines of code on the top of your existing code:

# import required libraries\nimport os\n\n# enforce UDP\nos.environ[\"OPENCV_FFMPEG_CAPTURE_OPTIONS\"] = \"rtsp_transport;udp\"\n

Finally, use backend parameter value as backend=cv2.CAP_FFMPEG in VideoGear.

from vidgear.gears import VideoGear\nimport cv2\nimport datetime\nimport time\n\n\nclass Reconnecting_VideoGear:\n    def __init__(self, cam_address, stabilize=False, reset_attempts=50, reset_delay=5):\n        self.cam_address = cam_address\n        self.stabilize = stabilize\n        self.reset_attempts = reset_attempts\n        self.reset_delay = reset_delay\n        self.source = VideoGear(\n            source=self.cam_address, stabilize=self.stabilize\n        ).start()\n        self.running = True\n\n    def read(self):\n        if self.source is None:\n            return None\n        if self.running and self.reset_attempts > 0:\n            frame = self.source.read()\n            if frame is None:\n                self.source.stop()\n                self.reset_attempts -= 1\n                print(\n                    \"Re-connection Attempt-{} occured at time:{}\".format(\n                        str(self.reset_attempts),\n                        datetime.datetime.now().strftime(\"%m-%d-%Y %I:%M:%S%p\"),\n                    )\n                )\n                time.sleep(self.reset_delay)\n                self.source = VideoGear(\n                    source=self.cam_address, stabilize=self.stabilize\n                ).start()\n                # return previous frame\n                return self.frame\n            else:\n                self.frame = frame\n                return frame\n        else:\n            return None\n\n    def stop(self):\n        self.running = False\n        self.reset_attempts = 0\n        self.frame = None\n        if not self.source is None:\n            self.source.stop()\n\n\nif __name__ == \"__main__\":\n    # open any valid video stream\n    stream = Reconnecting_VideoGear(\n        cam_address=\"rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov\",\n        reset_attempts=20,\n        reset_delay=5,\n    )\n\n    # loop over\n    while True:\n\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if None-type\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # Show output window\n        cv2.imshow(\"Output\", frame)\n\n        # check for 'q' key if pressed\n        key = cv2.waitKey(1) & 0xFF\n        if key == ord(\"q\"):\n            break\n\n    # close output window\n    cv2.destroyAllWindows()\n\n    # safely close video stream\n    stream.stop()\n

"},{"location":"help/videogear_ex/#using-videogear-for-real-time-stabilization-with-audio-encoding","title":"Using VideoGear for Real-time Stabilization with Audio Encoding","text":"

In this example code, we will be directly merging the audio from a Video-File (to be stabilized) with its processed stabilized frames into a compressed video output in real time:

New in v0.2.4

This example was added in v0.2.4.

Make sure this input video-file (to be stabilized) contains valid audio source, otherwise you could encounter multiple errors or no output at all.

You MUST use -input_framerate attribute to set exact value of input framerate when using external audio in Real-time Frames mode, otherwise audio delay will occur in output streams.

Use -disable_force_termination flag when video duration is too short(<60sec), otherwise WriteGear will not produce any valid output.

# import required libraries\nfrom vidgear.gears import WriteGear\nfrom vidgear.gears import VideoGear\nimport cv2\n\n# Give suitable video file path to be stabilized\nunstabilized_videofile = \"test.mp4\"\n\n# open any valid video path with stabilization enabled(`stabilize = True`)\nstream_stab = VideoGear(source=unstabilized_videofile, stabilize=True, logging=True).start()\n\n# define required FFmpeg optimizing parameters for your writer\noutput_params = {\n    \"-i\": unstabilized_videofile,\n    \"-c:a\": \"aac\",\n    \"-input_framerate\": stream_stab.framerate,\n    \"-clones\": [\"-shortest\"],\n    # !!! Uncomment following line if video duration is too short(<60sec). !!!\n    #\"-disable_force_termination\": True,\n}\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4\nwriter = WriteGear(output=\"Output.mp4\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame_stab = stream_stab.read()\n\n    # check for frame if not grabbed\n    if frame_stab is None:\n        break\n\n    # {do something with the stabilized frame here}\n\n    # write stabilized frame to writer\n    writer.write(frame_stab)\n\n# safely close streams\nstream_stab.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"help/videogear_faqs/","title":"FAQs","text":""},{"location":"help/videogear_faqs/#videogear-faqs","title":"VideoGear FAQs","text":""},{"location":"help/videogear_faqs/#what-is-videogear-api-and-what-does-it-do","title":"What is VideoGear API and what does it do?","text":"

Answer: VideoGear provides a special internal wrapper around VidGear's exclusive Video Stabilizer class. It also act as a Common API, that provided an internal access to both CamGear and PiGear APIs and their parameters, with a special enablePiCamera boolean flag. For more info. see VideoGear doc \u27b6

"},{"location":"help/videogear_faqs/#whats-the-need-of-videogear-api","title":"What's the need of VideoGear API?","text":"

Answer: VideoGear is basically ideal when you need to switch to different video sources without changing your code much. Also, it enables easy stabilization for various video-streams (real-time or not) with minimum efforts and using way fewer lines of code. It also serve as backend for other powerful APIs, such WebGear and NetGear_Async.

"},{"location":"help/videogear_faqs/#which-apis-are-accessible-with-videogear-api","title":"Which APIs are accessible with VideoGear API?","text":"

Answer: VideoGear provided an internal access to both CamGear and PiGear APIs and their parameters, also it contains wrapper around Video Stabilizer class.

"},{"location":"help/videogear_faqs/#can-we-access-writegear-api-or-netgear-api-too-with-videogear","title":"Can we access WriteGear API or NetGear API too with VideoGear?","text":"

Answer: No, only selected VideoCapture APIs (anwsered above) are accessible.

"},{"location":"help/videogear_faqs/#does-using-videogear-instead-of-camgear-api-directly-affects-performance","title":"Does using VideoGear instead of CamGear API directly, affects performance?","text":"

Answer: No, there's no difference, as VideoGear just a high-level wrapper around CamGear API and without any modifications in-between.

"},{"location":"help/webgear_ex/","title":"Bonus Examples","text":""},{"location":"help/webgear_ex/#webgear-examples","title":"WebGear Examples","text":""},{"location":"help/webgear_ex/#using-webgear-with-raspberrypi-camera-module","title":"Using WebGear with RaspberryPi Camera Module","text":"

Because of WebGear API's flexible internal wapper around VideoGear, it can easily access any parameter of CamGear and PiGear videocapture APIs.

Following usage examples are just an idea of what can be done with WebGear API, you can try various VideoGear, CamGear and PiGear parameters directly in WebGear API in the similar manner.

Here's a bare-minimum example of using WebGear API with the Raspberry Pi camera module while tweaking its various properties in few lines of python code:

Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this backend, otherwise nothing will work.

New Picamera2 backendLegacy Picamera backend
# import libs\nimport uvicorn\nfrom libcamera import Transform\nfrom vidgear.gears.asyncio import WebGear\n\n# various WebGear_RTC performance \n# and Picamera2 API tweaks\noptions = {\n    \"frame_size_reduction\": 40,\n    \"jpeg_compression_quality\": 80,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": False,\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# initialize WebGear app\nweb = WebGear(\n    enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options\n)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import libs\nimport uvicorn\nfrom vidgear.gears.asyncio import WebGear\n\n# various webgear performance and Picamera API tweaks\noptions = {\n    \"frame_size_reduction\": 40,\n    \"jpeg_compression_quality\": 80,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": False,\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# initialize WebGear app\nweb = WebGear(\n    enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options\n)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

"},{"location":"help/webgear_ex/#using-webgear-with-real-time-video-stabilization-enabled","title":"Using WebGear with real-time Video Stabilization enabled","text":"

Here's an example of using WebGear API with real-time Video Stabilization enabled:

# import libs\nimport uvicorn\nfrom vidgear.gears.asyncio import WebGear\n\n# various webgear performance tweaks\noptions = {\n    \"frame_size_reduction\": 40,\n    \"jpeg_compression_quality\": 80,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": False,\n}\n\n# initialize WebGear app  with a raw source and enable video stabilization(`stabilize=True`)\nweb = WebGear(source=\"foo.mp4\", stabilize=True, logging=True, **options)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

"},{"location":"help/webgear_ex/#display-two-sources-simultaneously-in-webgear","title":"Display Two Sources Simultaneously in WebGear","text":"

In this example, we'll be displaying two video feeds side-by-side simultaneously on browser using WebGear API by defining two separate frame generators:

New in v0.2.2

This example was added in v0.2.2.

Step-1 (Trigger Auto-Generation Process): Firstly, run this bare-minimum code to trigger the Auto-generation process, this will create .vidgear directory at current location (directory where you'll run this code):

# import required libraries\nimport uvicorn\nfrom vidgear.gears.asyncio import WebGear\n\n# provide current directory to save data files\noptions = {\"custom_data_location\": \"./\"}\n\n# initialize WebGear app\nweb = WebGear(source=0, logging=True, **options)\n\n# close app safely\nweb.shutdown()\n

Step-2 (Replace HTML file): Now, go inside .vidgear webgear templates directory at current location of your machine, and there replace content of index.html file with following:

{% extends \"base.html\" %}\n{% block content %}\n  <h1 class=\"glow\">WebGear Video Feed</h1>\n   <div class=\"rows\">\n     <img src=\"/video\" alt=\"Feed\"/>\n     <img src=\"/video2\" alt=\"Feed\"/>\n   </div>\n{% endblock %}\n

Step-3 (Build your own Frame Producers): Now, create a python script code with OpenCV source, as follows:

# import necessary libs\nimport uvicorn, asyncio, cv2\nfrom vidgear.gears.asyncio import WebGear\nfrom vidgear.gears.asyncio.helper import reducer\nfrom starlette.responses import StreamingResponse\nfrom starlette.routing import Route\n\n# provide current directory to load data files\noptions = {\"custom_data_location\": \"./\"}\n\n# initialize WebGear app without any source\nweb = WebGear(logging=True, **options)\n\n# create your own custom frame producer\nasync def my_frame_producer1():\n\n   # !!! define your first video source here !!!\n   # Open any video stream such as \"foo1.mp4\"\n   stream = cv2.VideoCapture(\"foo1.mp4\")\n   # loop over frames\n   while True:\n       # read frame from provided source\n       (grabbed, frame) = stream.read()\n       # break if NoneType\n       if not grabbed:\n           break\n\n       # do something with your OpenCV frame here\n\n       # reducer frames size if you want more performance otherwise comment this line\n       frame = await reducer(frame, percentage=30)  # reduce frame by 30%\n       # handle JPEG encoding\n       encodedImage = cv2.imencode(\".jpg\", frame)[1].tobytes()\n       # yield frame in byte format\n       yield (b\"--frame\\r\\nContent-Type:video/jpeg2000\\r\\n\\r\\n\" + encodedImage + b\"\\r\\n\")\n       await asyncio.sleep(0.00001)\n   # close stream\n   stream.release()\n\n\n# create your own custom frame producer\nasync def my_frame_producer2():\n\n   # !!! define your second video source here !!!\n   # Open any video stream such as \"foo2.mp4\"\n   stream = cv2.VideoCapture(\"foo2.mp4\")\n   # loop over frames\n   while True:\n       # read frame from provided source\n       (grabbed, frame) = stream.read()\n       # break if NoneType\n       if not grabbed:\n           break\n\n       # do something with your OpenCV frame here\n\n       # reducer frames size if you want more performance otherwise comment this line\n       frame = await reducer(frame, percentage=30)  # reduce frame by 30%\n       # handle JPEG encoding\n       encodedImage = cv2.imencode(\".jpg\", frame)[1].tobytes()\n       # yield frame in byte format\n       yield (b\"--frame\\r\\nContent-Type:video/jpeg2000\\r\\n\\r\\n\" + encodedImage + b\"\\r\\n\")\n       await asyncio.sleep(0.00001)\n   # close stream\n   stream.release()\n\n\nasync def custom_video_response(scope):\n   \"\"\"\n   Return a async video streaming response for `my_frame_producer2` generator\n   \"\"\"\n   assert scope[\"type\"] in [\"http\", \"https\"]\n   await asyncio.sleep(0.00001)\n   return StreamingResponse(\n       my_frame_producer2(),\n       media_type=\"multipart/x-mixed-replace; boundary=frame\",\n   )\n\n\n# add your custom frame producer to config\nweb.config[\"generator\"] = my_frame_producer1\n\n# append new route i.e. new custom route with custom response\nweb.routes.append(\n    Route(\"/video2\", endpoint=custom_video_response)\n    )\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

On successfully running this code, the output stream will be displayed at address http://localhost:8000/ in Browser.

"},{"location":"help/webgear_faqs/","title":"FAQs","text":""},{"location":"help/webgear_faqs/#webgear-faqs","title":"WebGear FAQs","text":""},{"location":"help/webgear_faqs/#what-is-webgear-api-and-what-does-it-do","title":"What is WebGear API and what does it do?","text":"

Answer: WebGear is a powerful ASGI Video-Broadcaster API ideal for transmitting Motion-JPEG-frames from a single source to multiple recipients via the browser. For more info. see WebGear doc \u27b6

"},{"location":"help/webgear_faqs/#how-to-get-started-with-webgear-api","title":"How to get started with WebGear API?","text":"

Answer: First, refer to the Switching from OpenCV guide, then go through WebGear documentation. If you still have doubts, ask us on Gitter \u27b6 Community channel.

"},{"location":"help/webgear_faqs/#webgear-is-throwing-modulenotfounderror-on-importing-why","title":"\"WebGear is throwing ModuleNotFoundError on importing\", Why?","text":"

Answer: This error means, VidGear is installed WITHOUT asyncio package support on your machine. For this support, see Requirements \u27b6.

"},{"location":"help/webgear_faqs/#can-webgear-always-need-active-internet-connection","title":"Can WebGear always need Active Internet Connection?","text":"

Answer: No, it just need internet only once during its Auto-Generation Process \u27b6 to download default data-files and it takes few seconds. You can also download files manually from Github Server, otherwise you can also add your own custom files. For more information see Data-Files Auto-Generation WorkFlow \u27b6

"},{"location":"help/webgear_faqs/#is-it-possible-to-stream-on-a-different-device-on-the-network-with-webgear","title":"Is it possible to stream on a different device on the network with WebGear?","text":"

If you set \"0.0.0.0\" as host value instead of \"localhost\" on Host Machine, then you must still use http://localhost:8000/ to access stream on that same host machine browser.

For accessing WebGear on different Client Devices on the network, use \"0.0.0.0\" as host value instead of \"localhost\" on Host Machine. Then type the IP-address of source machine followed by the defined port value in your desired Client Device's browser (for e.g. http://192.27.0.101:8000) to access the stream.

"},{"location":"help/webgear_faqs/#can-i-manually-place-default-files-for-webgear","title":"Can I manually place default files for WebGear?","text":"

Answer: Yes, you can either download default files from Github Server, and manually place at default location, OR, you can yourself create the require three critical files (i.e index.html, 404.html & 500.html) inside templates folder at the default location, thereby you don't need any internet connection at all. For more information see Data-Files Auto-Generation WorkFlow \u27b6

"},{"location":"help/webgear_faqs/#how-to-send-opencv-frames-directly-to-webgear-server","title":"How to send OpenCV frames directly to Webgear Server?","text":"

Answer: See this usage example \u27b6.

"},{"location":"help/webgear_faqs/#how-can-i-add-my-custom-webpage-to-webgear","title":"How can I add my custom WebPage to WebGear?","text":"

Answer: See this usage example \u27b6.

"},{"location":"help/webgear_faqs/#how-can-to-add-cors-headers-to-webgear","title":"How can to add CORS headers to WebGear?","text":"

Answer: See this usage example \u27b6.

"},{"location":"help/webgear_faqs/#can-i-change-the-default-location","title":"Can I change the default location?","text":"

Answer: Yes, you can use WebGear's custom_data_location attribute of option parameter in WebGear API, to change default location to somewhere else.

"},{"location":"help/webgear_faqs/#can-i-deleterename-the-webgear-default-data","title":"Can I delete/rename the WebGear default data?","text":"

Answer: Yes, but you've to follow these rules \u27b6

"},{"location":"help/webgear_faqs/#what-web-browser-are-supported-by-webgear-api","title":"What Web browser are supported by WebGear API?","text":"

Answer: All modern browser with Javascript support are supported by WebGear. If not, then discuss with us on Gitter \u27b6 Community channel.

"},{"location":"help/webgear_rtc_ex/","title":"Bonus Examples","text":""},{"location":"help/webgear_rtc_ex/#webgear_rtc_rtc-examples","title":"WebGear_RTC_RTC Examples","text":""},{"location":"help/webgear_rtc_ex/#using-webgear_rtc-with-raspberrypi-camera-module","title":"Using WebGear_RTC with RaspberryPi Camera Module","text":"

Because of WebGear_RTC API's flexible internal wapper around VideoGear, it can easily access any parameter of CamGear and PiGear videocapture APIs.

Following usage examples are just an idea of what can be done with WebGear_RTC API, you can try various VideoGear, CamGear and PiGear parameters directly in WebGear_RTC API in the similar manner.

Here's a bare-minimum example of using WebGear_RTC API with the Raspberry Pi camera module while tweaking its various properties in just one-liner:

Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this backend, otherwise nothing will work.

New Picamera2 backendLegacy Picamera backend
# import libs\nimport uvicorn\nfrom libcamera import Transform\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# various WebGear_RTC performance \n# and Picamera2 API tweaks\noptions = {\n    \"frame_size_reduction\": 25,\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# initialize WebGear app\nweb = WebGear_RTC(\n    enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options\n)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import libs\nimport uvicorn\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# various WebGear_RTC performance and Picamera API tweaks\noptions = {\n    \"frame_size_reduction\": 25,\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# initialize WebGear app\nweb = WebGear_RTC(\n    enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options\n)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

"},{"location":"help/webgear_rtc_ex/#using-webgear_rtc-with-real-time-video-stabilization-enabled","title":"Using WebGear_RTC with real-time Video Stabilization enabled","text":"

Here's an example of using WebGear_RTC API with real-time Video Stabilization enabled:

# import libs\nimport uvicorn\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# various webgear_rtc performance tweaks\noptions = {\n    \"frame_size_reduction\": 25,\n}\n\n# initialize WebGear_RTC app  with a raw source and enable video stabilization(`stabilize=True`)\nweb = WebGear_RTC(source=\"foo.mp4\", stabilize=True, logging=True, **options)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

"},{"location":"help/webgear_rtc_ex/#display-two-sources-simultaneously-in-webgear_rtc","title":"Display Two Sources Simultaneously in WebGear_RTC","text":"

In this example, we'll be displaying two video feeds side-by-side simultaneously on browser using WebGear_RTC API by simply concatenating frames in real-time:

New in v0.2.4

This example was added in v0.2.4.

# import necessary libs\nimport uvicorn, cv2\nimport numpy as np\nfrom vidgear.gears.helper import reducer\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# initialize WebGear_RTC app without any source\nweb = WebGear_RTC(logging=True)\n\n# frame concatenator\ndef get_conc_frame(frame1, frame2):\n    h1, w1 = frame1.shape[:2]\n    h2, w2 = frame2.shape[:2]\n\n    # create empty matrix\n    vis = np.zeros((max(h1, h2), w1 + w2, 3), np.uint8)\n\n    # combine 2 frames\n    vis[:h1, :w1, :3] = frame1\n    vis[:h2, w1 : w1 + w2, :3] = frame2\n\n    return vis\n\n\n# create your own custom streaming class\nclass Custom_Stream_Class:\n    \"\"\"\n    Custom Streaming using two OpenCV sources\n    \"\"\"\n\n    def __init__(self, source1=None, source2=None):\n\n        # !!! define your own video source here !!!\n        # check is source are provided\n        if source1 is None or source2 is None:\n            raise ValueError(\"Provide both source\")\n\n        # initialize global params\n        # define both source here\n        self.stream1 = cv2.VideoCapture(source1)\n        self.stream2 = cv2.VideoCapture(source2)\n\n        # define running flag\n        self.running = True\n\n    def read(self):\n\n        # don't forget this function!!!\n\n        # check if sources were initialized or not\n        if self.stream1 is None or self.stream2 is None:\n            return None\n\n        # check if we're still running\n        if self.running:\n            # read video frame\n            (grabbed1, frame1) = self.stream1.read()\n            (grabbed2, frame2) = self.stream2.read()\n\n            # if NoneType\n            if not grabbed1 or not grabbed2:\n\n                # do something with your OpenCV frame here\n\n                # concatenate frame\n                frame = get_conc_frame(frame1, frame2)\n\n                # reducer frames size if you want more performance otherwise comment this line\n                # frame = await reducer(frame, percentage=30)  # reduce frame by 30%\n\n                # return our gray frame\n                return frame\n            else:\n                # signal we're not running now\n                self.running = False\n        # return None-type\n        return None\n\n    def stop(self):\n\n        # don't forget this function!!!\n\n        # flag that we're not running\n        self.running = False\n        # close stream\n        if not (self.stream1 is None):\n            self.stream1.release()\n            self.stream1 = None\n\n        if not (self.stream2 is None):\n            self.stream2.release()\n            self.stream2 = None\n\n\n# assign your Custom Streaming Class with adequate two sources\n# to `custom_stream` attribute in options parameter\noptions = {\n    \"custom_stream\": Custom_Stream_Class(\n        source1=\"foo1.mp4\", source2=\"foo2.mp4\"\n    )\n}\n\n# initialize WebGear_RTC app without any source\nweb = WebGear_RTC(logging=True, **options)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

On successfully running this code, the output stream will be displayed at address http://localhost:8000/ in Browser.

"},{"location":"help/webgear_rtc_faqs/","title":"FAQs","text":""},{"location":"help/webgear_rtc_faqs/#webgear_rtc-faqs","title":"WebGear_RTC FAQs","text":""},{"location":"help/webgear_rtc_faqs/#what-is-webgear_rtc-api-and-what-does-it-do","title":"What is WebGear_RTC API and what does it do?","text":"

Answer: WebGear_RTC utilizes WebRTC technology under the hood, which makes it suitable for building powerful video-streaming solutions for all modern browsers as well as native clients available on all major platforms. For more info. see WebGear_RTC doc \u27b6

"},{"location":"help/webgear_rtc_faqs/#how-to-get-started-with-webgear_rtc-api","title":"How to get started with WebGear_RTC API?","text":"

Answer: First, refer to the Switching from OpenCV guide, then go through WebGear_RTC documentation. If you still have doubts, ask us on Gitter \u27b6 Community channel.

"},{"location":"help/webgear_rtc_faqs/#how-webgear_rtc-is-different-to-webgear-api-which-should-i-choose","title":"How WebGear_RTC is different to WebGear API, which should I choose?","text":"

Answer: WebGear_RTC is similar to WeGear API in many aspects but utilizes WebRTC technology under the hood instead of Motion JPEG. You can choose any API according to your application, but the quality would be better on WebGear API, on-the-other-hand latency would be better on WebGear_RTC API. Also, WebRTC protocol accepts a wide range of devices, whereas WebGear is limited only to modern browsers.

"},{"location":"help/webgear_rtc_faqs/#webgear_rtc-is-throwing-modulenotfounderror-on-importing-why","title":"\"WebGear_RTC is throwing ModuleNotFoundError on importing\", Why?","text":"

Answer: This error means, VidGear is installed WITHOUT asyncio package support on your machine. For this support, see Requirements \u27b6.

"},{"location":"help/webgear_rtc_faqs/#can-webgear_rtc-always-need-active-internet-connection","title":"Can WebGear_RTC always need Active Internet Connection?","text":"

Answer: No, it just need internet only once during its Auto-Generation Process \u27b6 to download default data-files and it takes few seconds. You can also download files manually from Github Server, otherwise you can also add your own custom files. For more information see Data-Files Auto-Generation WorkFlow \u27b6

"},{"location":"help/webgear_rtc_faqs/#is-it-possible-to-stream-on-a-different-device-on-the-network-with-webgear_rtc","title":"Is it possible to stream on a different device on the network with WebGear_RTC?","text":"

If you set \"0.0.0.0\" as host value instead of \"localhost\" on Host Machine, then you must still use http://localhost:8000/ to access stream on your host machine browser.

For accessing WebGear_RTC on different Client Devices on the network, use \"0.0.0.0\" as host value instead of \"localhost\" on Host Machine. Then type the IP-address of source machine followed by the defined port value in your desired Client Device's browser (for e.g. http://192.27.0.101:8000) to access the stream.

"},{"location":"help/webgear_rtc_faqs/#can-i-manually-place-default-files-for-webgear_rtc","title":"Can I manually place default files for WebGear_RTC?","text":"

Answer: Yes, you can either download default files from Github Server, and manually place at default location, OR, you can yourself create the require three critical files (i.e index.html, 404.html & 500.html) inside templates folder at the default location, thereby you don't need any internet connection at all. For more information see Data-Files Auto-Generation WorkFlow \u27b6

"},{"location":"help/webgear_rtc_faqs/#how-to-stream-webgear_rtc-server-output-to-multiple-clients","title":"How to stream Webgear_RTC Server output to multiple clients?","text":"

Answer: See this usage example \u27b6.

"},{"location":"help/webgear_rtc_faqs/#how-to-send-opencv-frames-directly-to-webgear_rtc-server","title":"How to send OpenCV frames directly to Webgear_RTC Server?","text":"

Answer: See this usage example \u27b6.

"},{"location":"help/webgear_rtc_faqs/#how-can-i-add-my-custom-webpage-to-webgear_rtc","title":"How can I add my custom WebPage to WebGear_RTC?","text":"

Answer: See this usage example \u27b6.

"},{"location":"help/webgear_rtc_faqs/#how-can-to-add-cors-headers-to-webgear_rtc","title":"How can to add CORS headers to WebGear_RTC?","text":"

Answer: See this usage example \u27b6.

"},{"location":"help/webgear_rtc_faqs/#can-i-change-the-default-location","title":"Can I change the default location?","text":"

Answer: Yes, you can use WebGear_RTC's custom_data_location attribute of option parameter in WebGear_RTC API, to change default location to somewhere else.

"},{"location":"help/webgear_rtc_faqs/#can-i-deleterename-the-webgear_rtc-default-data","title":"Can I delete/rename the WebGear_RTC default data?","text":"

Answer: Yes, but you've to follow these rules \u27b6

"},{"location":"help/writegear_ex/","title":"Bonus Examples","text":""},{"location":"help/writegear_ex/#writegear-examples","title":"WriteGear Examples","text":""},{"location":"help/writegear_ex/#using-writegears-compression-mode-for-rtsprtp-live-streaming","title":"Using WriteGear's Compression Mode for RTSP/RTP Live-Streaming","text":"

In Compression Mode, you can use WriteGear for livestreaming with traditional protocols such as RTSP/RTP. The example to achieve that is as follows:

New in v0.2.6

This example was added in v0.2.6.

Creating your own RTSP Server locally

If you want to create your RTSP Server locally, then checkout MediaMTX (formerly rtsp-simple-server) - ready-to-use and zero-dependency real-time media server and media proxy that allows to publish, read, proxy, record and playback video and audio streams.

This example assume you already have a RTSP Server running at specified RTSP address with format rtsp://[RTSP_ADDRESS]:[RTSP_PORT]/[RTSP_PATH] for publishing video frames.

Make sure to change RTSP address rtsp://localhost:8554/mystream with yours in following code before running!

# import required libraries\nimport cv2\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\n\n# open any valid video stream(for e.g `foo.mp4` file)\nstream = CamGear(source=\"foo.mp4\").start()\n\n# define required FFmpeg parameters for your writer\noutput_params = {\"-f\": \"rtsp\", \"-rtsp_transport\": \"tcp\"}\n\n# Define writer with defined parameters and RTSP address\n# [WARNING] Change your RTSP address `rtsp://localhost:8554/mystream` with yours!\nwriter = WriteGear(\n    output=\"rtsp://localhost:8554/mystream\", logging=True, **output_params\n)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"help/writegear_ex/#using-writegears-compression-mode-for-youtube-live-streaming","title":"Using WriteGear's Compression Mode for YouTube-Live Streaming","text":"

In Compression Mode, you can also use WriteGear for Youtube-Livestreaming. The example is as follows:

New in v0.2.1

This example was added in v0.2.1.

This example assume you already have a YouTube Account with Live-Streaming enabled for publishing video.

Make sure to change YouTube-Live Stream Key with yours in following code before running!

Without AudioWith Audio
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# define and open video source\nstream = CamGear(source=\"/home/foo/foo.mp4\", logging=True).start()\n\n# define required FFmpeg parameters for your writer\noutput_params = {\n    \"-clones\": [\"-f\", \"lavfi\", \"-i\", \"anullsrc\"],\n    \"-vcodec\": \"libx264\",\n    \"-preset\": \"medium\",\n    \"-b:v\": \"4500k\",\n    \"-bufsize\": \"512k\",\n    \"-pix_fmt\": \"yuv420p\",\n    \"-f\": \"flv\",\n}\n\n# [WARNING] Change your YouTube-Live Stream Key here:\nYOUTUBE_STREAM_KEY = \"xxxx-xxxx-xxxx-xxxx-xxxx\"\n\n# Define writer with defined parameters\nwriter = WriteGear(\n    output=\"rtmp://a.rtmp.youtube.com/live2/{}\".format(YOUTUBE_STREAM_KEY),\n    logging=True,\n    **output_params\n)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

This code assume given input video source contains valid audio stream.

# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# define video source(with audio) here\nVIDEO_SOURCE = \"/home/foo/foo.mp4\"\n\n# Open stream\nstream = CamGear(source=VIDEO_SOURCE, logging=True).start()\n\n# define required FFmpeg parameters for your writer\n# [NOTE]: Added VIDEO_SOURCE as audio-source\noutput_params = {\n    \"-i\": VIDEO_SOURCE,\n    \"-acodec\": \"aac\",\n    \"-ar\": 44100,\n    \"-b:a\": 712000,\n    \"-vcodec\": \"libx264\",\n    \"-preset\": \"medium\",\n    \"-b:v\": \"4500k\",\n    \"-bufsize\": \"512k\",\n    \"-pix_fmt\": \"yuv420p\",\n    \"-f\": \"flv\",\n}\n\n# [WARNING] Change your YouTube-Live Stream Key here:\nYOUTUBE_STREAM_KEY = \"xxxx-xxxx-xxxx-xxxx-xxxx\"\n\n# Define writer with defined parameters\nwriter = WriteGear(\n    output=\"rtmp://a.rtmp.youtube.com/live2/{}\".format(YOUTUBE_STREAM_KEY),\n    logging=True,\n    **output_params\n)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"help/writegear_ex/#using-writegears-compression-mode-with-v4l2loopback-virtual-cameras","title":"Using WriteGear's Compression Mode with v4l2loopback Virtual Cameras","text":"

With WriteGear's Compression Mode, you can directly feed video-frames to v4l2loopback generated Virtual Camera devices on Linux Machines. The complete usage example is as follows:

New in v0.3.0

This example was added in v0.3.0.

Example Assumptions Creating your own Virtual Camera device with v4l2loopback module.

To install and create a v4l2loopback virtual camera device on Linux Mint OS/Ubuntu (may slightly differ for other distros), run following two terminal commands:

$ sudo apt-get install v4l2loopback-dkms v4l2loopback-utils linux-modules-extra-$(uname -r)\n\n$ sudo modprobe v4l2loopback devices=1 video_nr=0 exclusive_caps=1 card_label='VCamera'\n

For further information on parameters used, checkout v4l2loopback docs

Finally, You can check the loopback device you just created by listing contents of /sys/devices/virtual/video4linux directory with terminal command:

$ sudo ls -1 /sys/devices/virtual/video4linux\n\nvideo0 \n

Now you can use /dev/video0 Virtual Camera device path in WriteGear API.

v4l2: open /dev/videoX: Permission denied

If you got this error, then you must add your username to the video group by running following commands:

$ sudo adduser $(whoami) video\n$ sudo usermod -a -G video $(whoami)\n
Afterwards, restart your computer to finialize these changes.

Note: If the problem still persists, then try to run your python script as superuser with sudo command.

Default libx264 encoder is incompatible with v4l2loopback module.

Kindly use other encoders such as libxvid, mpeg4 etc.

# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# open any valid video stream(for e.g `foo.mp4` file)\nstream = CamGear(source=\"foo.mp4\").start()\n\n# define required FFmpeg parameters for your writer\n# also retrieve framerate from CamGear Stream and pass it as `-input_framerate` parameter\noutput_params = {\n    \"-input_framerate\": stream.framerate,\n    \"-vcodec\": \"libxvid\",\n    \"-f\": \"v4l2\",\n    \"-pix_fmt\": \"yuv420p\",\n}\n\n# Define writer with \"/dev/video0\" as source and user-defined parameters \nwriter = WriteGear(output=\"/dev/video0\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if None-type\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

The data sent to the v4l2loopback device /dev/video0 in this example with WriteGear API, can then be read by any v4l2-capable application (such as OpenCV, VLC, ffplay etc.)

"},{"location":"help/writegear_ex/#using-writegears-compression-mode-for-creating-mp4-segments","title":"Using WriteGear's Compression Mode for creating MP4 segments","text":"

In Compression Mode, you can also use WriteGear for creating MP4 segments from almost any video source. The example is as follows:

New in v0.2.1

This example was added in v0.2.1.

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# Open any video source `foo.mp4`\nstream = VideoGear(\n    source=\"foo.mp4\", logging=True\n).start()\n\n# define required FFmpeg optimizing parameters for your writer\noutput_params = {\n    \"-c:v\": \"libx264\",\n    \"-crf\": 22,\n    \"-map\": 0,\n    \"-segment_time\": 9,\n    \"-g\": 9,\n    \"-sc_threshold\": 0,\n    \"-force_key_frames\": \"expr:gte(t,n_forced*9)\",\n    \"-clones\": [\"-f\", \"segment\"],\n}\n\n# Define writer with defined parameters\nwriter = WriteGear(output=\"output%03d.mp4\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"help/writegear_ex/#using-writegears-compression-mode-to-add-external-audio-file-input-to-video-frames","title":"Using WriteGear's Compression Mode to add external audio file input to video frames","text":"

You can also use WriteGear for merging external audio with live video-source:

New in v0.2.1

This example was added in v0.2.1.

Make sure this -i audio-source it compatible with provided video-source, otherwise you could encounter multiple errors or no output at all.

# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# open any valid video stream(for e.g `foo_video.mp4` file)\nstream = CamGear(source=\"foo_video.mp4\").start()\n\n# add various parameters, along with custom audio\nstream_params = {\n    \"-input_framerate\": stream.framerate,  # controlled framerate for audio-video sync !!! don't forget this line !!!\n    \"-i\": \"foo_audio.aac\",  # assigns input audio-source: \"foo_audio.aac\"\n}\n\n# Define writer with defined parameters\nwriter = WriteGear(output=\"Output.mp4\", logging=True, **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"help/writegear_ex/#using-writegears-compression-mode-for-generating-timely-accurate-video","title":"Using WriteGear's Compression Mode for generating Timely Accurate Video","text":"

If you need timely accurate video with exactly same speed as real-time input, then you need to use FFmpeg directly through its execute_ffmpeg_cmd method:

New in v0.2.4

This example was added in v0.2.4.

In this example we are capturing video from desktop screen in a Timely Accurate manner.

Windows Linux MacOS
# import required libraries\nfrom vidgear.gears import WriteGear\n\n# Define writer with defined parameters and with some dummy name\nwriter = WriteGear(output=\"Output.mp4\", logging=True)\n\n# format FFmpeg command to generate time accurate video\nffmpeg_command = [\n    \"-y\",\n    \"-f\",\n    \"gdigrab\",\n    \"-framerate\",\n    \"30\",\n    \"-i\",\n    \"desktop\",\n    \"Output.mkv\",\n]  # `-y` parameter is to overwrite outputfile if exists\n\n# execute FFmpeg command\nwriter.execute_ffmpeg_cmd(ffmpeg_command)\n\n# safely close writer\nwriter.close()\n
# import required libraries\nfrom vidgear.gears import WriteGear\n\n# Define writer with defined parameters and with some dummy name\nwriter = WriteGear(output=\"Output.mp4\", logging=True)\n\n# format FFmpeg command to generate time accurate video\nffmpeg_command = [\n    \"-y\",\n    \"-f\",\n    \"x11grab\",\n    \"-framerate\",\n    \"30\",\n    \"-i\",\n    \"default\",\n    \"Output.mkv\",\n]  # `-y` parameter is to overwrite outputfile if exists\n\n# execute FFmpeg command\nwriter.execute_ffmpeg_cmd(ffmpeg_command)\n\n# safely close writer\nwriter.close()\n
# import required libraries\nfrom vidgear.gears import WriteGear\n\n# Define writer with defined parameters and with some dummy name\nwriter = WriteGear(output=\"Output.mp4\", logging=True)\n\n# format FFmpeg command to generate time accurate video\nffmpeg_command = [\n    \"-y\",\n    \"-f\",\n    \"avfoundation\",\n    \"-framerate\",\n    \"30\",\n    \"-i\",\n    \"default\",\n    \"Output.mkv\",\n]  # `-y` parameter is to overwrite outputfile if exists\n\n# execute FFmpeg command\nwriter.execute_ffmpeg_cmd(ffmpeg_command)\n\n# safely close writer\nwriter.close()\n

"},{"location":"help/writegear_ex/#using-writegear-with-rosrobot-operating-system","title":"Using WriteGear with ROS(Robot Operating System)","text":"

We will be using cv_bridge to convert OpenCV frames to ROS image messages and vice-versa.

In this example, we'll create a node that listens to a ROS image message topic, converts the received images messages into OpenCV frames, draws a circle on it, and then process these frames into a lossless compressed file format in real-time.

New in v0.2.2

This example was added in v0.2.2.

This example is vidgear implementation of this wiki example.

# import roslib\nimport roslib\n\nroslib.load_manifest(\"my_package\")\n\n# import other required libraries\nimport sys\nimport rospy\nimport cv2\nfrom std_msgs.msg import String\nfrom sensor_msgs.msg import Image\nfrom cv_bridge import CvBridge, CvBridgeError\nfrom vidgear.gears import WriteGear\n\n# custom publisher class\nclass image_subscriber:\n    def __init__(self, output=\"Output.mp4\"):\n        # create CV bridge\n        self.bridge = CvBridge()\n        # define publisher topic\n        self.image_pub = rospy.Subscriber(\"image_topic_sub\", Image, self.callback)\n        # Define writer with default parameters\n        self.writer = WriteGear(output=output)\n\n    def callback(self, data):\n        # convert received data to frame\n        try:\n            cv_image = self.bridge.imgmsg_to_cv2(data, \"bgr8\")\n        except CvBridgeError as e:\n            print(e)\n\n        # check if frame is valid\n        if cv_image:\n\n            # {do something with the frame here}\n            # let's add a circle\n            (rows, cols, channels) = cv_image.shape\n            if cols > 60 and rows > 60:\n                cv2.circle(cv_image, (50, 50), 10, 255)\n\n            # write frame to writer\n            self.writer.write(cv_image)\n\n        def close(self):\n            # safely close video stream\n            self.writer.close()\n\n\ndef main(args):\n    # define publisher with suitable output filename\n    # such as `Output.mp4` for saving output\n    ic = image_subscriber(output=\"Output.mp4\")\n    # initiate ROS node on publisher\n    rospy.init_node(\"image_subscriber\", anonymous=True)\n    try:\n        # run node\n        rospy.spin()\n    except KeyboardInterrupt:\n        print(\"Shutting down\")\n    finally:\n        # close publisher\n        ic.close()\n\n\nif __name__ == \"__main__\":\n    main(sys.argv)\n

"},{"location":"help/writegear_faqs/","title":"FAQs","text":""},{"location":"help/writegear_faqs/#writegear-faqs","title":"WriteGear FAQs","text":""},{"location":"help/writegear_faqs/#what-is-writegear-api-and-what-does-it-do","title":"What is WriteGear API and what does it do?","text":"

Answer: WriteGear handles various powerful Writer Tools that provide us the freedom to do almost anything imagine with multimedia files. For more info. see WriteGear doc \u27b6

"},{"location":"help/writegear_faqs/#im-only-familiar-with-opencv-how-to-get-started-with-writegear-api","title":"I'm only familiar with OpenCV, how to get started with WriteGear API?","text":"

Answer: Answer: First, refer to the Switching from OpenCV guide, then go through WriteGear documentation. If you still have doubts, ask us on Gitter \u27b6 Community channel.

"},{"location":"help/writegear_faqs/#why-writegear-is-throwing-valueerror","title":"Why WriteGear is throwing ValueError?","text":"

Answer: WriteGear will exit with ValueError if you feed frames of different dimensions or channels.

"},{"location":"help/writegear_faqs/#how-to-install-and-configure-ffmpeg-correctly-for-writegear-on-my-machine","title":"How to install and configure FFmpeg correctly for WriteGear on my machine?","text":"

Answer: Follow these Installation Instructions \u27b6 for its installation.

"},{"location":"help/writegear_faqs/#can-i-use-writegear-directly-with-opencv","title":"Can I use WriteGear directly with OpenCV?","text":"

Answer: Yes,

"},{"location":"help/writegear_faqs/#what-ffmpegs-encoders-and-parameters-are-supported-by-writegear-in-compression-mode","title":"What FFmpeg's encoders and parameters are supported by WriteGear in compression mode?","text":"

Answer: See Supported Parameters \u27b6 and Supported encoders \u27b6

"},{"location":"help/writegear_faqs/#what-opencvs-fourcc-and-parameters-are-supported-by-writegear-in-non-compression-mode","title":"What OpenCV's FOURCC and parameters are supported by WriteGear in non-compression mode?","text":"

Answer: See Supported Parameters \u27b6 and Supported FOURCC \u27b6.

"},{"location":"help/writegear_faqs/#why-this-fourcc-is-not-working-for-me","title":"Why this FOURCC is not working for me?","text":"

Answer: Remember not all the FOURCC and Video extensions are compatible and supported by OpenCV VideoWriter Class. You\u2019ll need to try different combinations of FourCC and file extensions. Furthermore, OpenCV does not return any helpful error messages regarding this problem, so it\u2019s pretty much based on trial and error.

"},{"location":"help/writegear_faqs/#can-i-pass-my-custom-ffmpeg-commands-directly-in-writegear-api","title":"Can I pass my custom FFmpeg commands directly in WriteGear API?","text":"

Answer: Yes, See Custom FFmpeg Commands in WriteGear API \u27b6.

"},{"location":"help/writegear_faqs/#how-to-use-specific-hardware-encoder-in-writegear","title":"How to use specific Hardware Encoder in WriteGear?","text":"

Answer: See this usage example \u27b6

"},{"location":"help/writegear_faqs/#how-to-add-live-audio-to-writegear","title":"How to add live audio to WriteGear?","text":"

Answer: See this doc \u27b6

"},{"location":"help/writegear_faqs/#how-to-separate-and-merge-audio-fromto-video","title":"How to separate and merge audio from/to video?","text":"

Answer: See these usage examples \u27b6

"},{"location":"help/writegear_faqs/#can-i-live-stream-to-twitch-with-writegear-api","title":"Can I live stream to Twitch with WriteGear API?","text":"

Answer: Yes, See this usage example \u27b6

"},{"location":"help/writegear_faqs/#is-youtube-live-streaming-possible-with-writegear","title":"Is YouTube-Live Streaming possible with WriteGear?","text":"

Answer: Yes, See this bonus example \u27b6.

"},{"location":"help/writegear_faqs/#how-to-live-streaming-using-rtsprtp-protocol-with-writegear","title":"How to Live-Streaming using RTSP/RTP protocol with WriteGear?","text":"

Answer: See this bonus example \u27b6.

"},{"location":"help/writegear_faqs/#how-to-create-mp4-segments-from-a-video-stream-with-writegear","title":"How to create MP4 segments from a video stream with WriteGear?","text":"

Answer: See this bonus example \u27b6.

"},{"location":"help/writegear_faqs/#how-add-external-audio-file-input-to-video-frames","title":"How add external audio file input to video frames?","text":"

Answer: See this bonus example \u27b6.

"},{"location":"help/writegear_faqs/#why-this-ffmpeg-parameter-is-not-working-for-me-in-compression-mode","title":"Why this FFmpeg parameter is not working for me in compression mode?","text":"

Answer: If some FFmpeg parameter doesn't work for you, then tell us on Gitter \u27b6, and if that doesn't help, then finally report an issue \u27b6

"},{"location":"help/writegear_faqs/#why-writegear-is-switching-to-non-compression-mode-even-if-it-is-not-enable","title":"Why WriteGear is switching to Non-compression Mode, even if it is not enable?","text":"

Answer: In case WriteGear API fails to detect valid FFmpeg executables on your system (even if Compression Mode is enabled), it will automatically fallback to Non-Compression Mode. Follow Installation Instructions \u27b6 for FFmpeg installation.

"},{"location":"installation/pip_install/","title":"Install using pip","text":""},{"location":"installation/pip_install/#install-using-pip","title":"Install using pip","text":"

Best option for easily getting stable VidGear installed.

"},{"location":"installation/pip_install/#prerequisites","title":"Prerequisites","text":"

When installing VidGear with pip, you need to manually install following prerequisites:

Upgrade your pip

It strongly advised to upgrade to latest pip before installing vidgear to avoid any undesired installation error(s).

There are two mechanisms to upgrade pip:

pipensurepip

You can use existing pip to upgrade itself:

Install pip if not present Linux / MacOS Windows
python get-pip.py\n
py get-pip.py\n

More details about this script can be found in pypa/get-pip\u2019s README.

Linux / MacOS Windows
python -m pip install pip --upgrade\n
py -m pip install pip --upgrade\n

Python also comes with an ensurepip module1, which can easily upgrade/install pip in any Python environment.

Linux / MacOS Windows
python -m ensurepip --upgrade\n
py -m ensurepip --upgrade\n

"},{"location":"installation/pip_install/#critical-prerequisites","title":"Critical Prerequisites","text":""},{"location":"installation/pip_install/#opencv","title":"OpenCV","text":"

Must require OpenCV(3.0+) python binaries installed for all core functions. You easily install it directly via pip:

OpenCV installation from source

You can also follow online tutorials for building & installing OpenCV on Windows, Linux, MacOS and Raspberry Pi machines manually from its source.

Make sure not to install both pip and source version together. Otherwise installation will fail to work!

Other OpenCV binaries

OpenCV maintainers also provide additional binaries via pip that contains both main modules and contrib/extra modules opencv-contrib-python, and for server (headless) environments like opencv-python-headless and opencv-contrib-python-headless. You can also install any one of them in similar manner. More information can be found here.

pip install opencv-python       \n
"},{"location":"installation/pip_install/#api-specific-prerequisites","title":"API Specific Prerequisites","text":""},{"location":"installation/pip_install/#ffmpeg","title":"FFmpeg","text":"

Require only for the video compression and encoding compatibility within StreamGear API and WriteGear API's Compression Mode.

FFmpeg Installation

"},{"location":"installation/pip_install/#picamera2","title":"Picamera2","text":"

Required only if you're using Raspberry Pi Camera Modules (or USB webcams) with the PiGear API. Here's how to install Picamera2 python library:

Using Legacy picamera library with PiGear (v0.3.3 and above)

PiGear API (version 0.3.3 onwards) prioritizes the newer Picamera2 library under the hood for Raspberry Pi camera modules. However, if your operating system doesn't support Picamera2, you can still use the legacy picamera library. Here's how to easily install it using pip:

pip install picamera\n

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

Picamera2 is only supported on Raspberry Pi OS Bullseye (or later) images, both 32 and 64-bit.

Picamera2 is NOT supported on:

Installation using apt (Recommended)Installation using pip As of September 2022, Picamera2 is pre-installed on images downloaded from Raspberry Pi. So you don't have to install it manually.

If Picamera2 is not already installed, then your image is presumably older and you should start with system upgrade:

sudo apt update && upgrade\n

If you have installed Picamera2 previously using pip, then you should also uninstall this (pip3 uninstall picamera2).

Thereafter, you can install Picamera2 with all the GUI (Qt and OpenGL) dependencies using:

sudo apt install -y python3-picamera2\n

Or, If you DON'T want the GUI dependencies, use:

sudo apt install -y python3-picamera2 --no-install-recommends\n

This is NOT the recommended way to install Picamera2.

However, if you wish to install Picamera2 with all the GUI (Qt and OpenGL) dependencies with pip, use:

sudo apt install -y python3-libcamera python3-kms++\nsudo apt install -y python3-pyqt5 python3-prctl \nsudo apt install -y libatlas-base-dev ffmpeg python3-pip\npip3 install numpy --upgrade\npip3 install picamera2[gui]\n

Or, If you DON'T want the GUI dependencies, use:

sudo apt install -y python3-libcamera python3-kms++\nsudo apt install -y python3-prctl libatlas-base-dev\nsudo apt install -y ffmpeg libopenjp2-7 python3-pip\npip3 install numpy --upgrade\npip3 install picamera2\n
"},{"location":"installation/pip_install/#uvloop","title":"Uvloop","text":"

Required only if you're using the NetGear_Async API on UNIX machines for maximum performance. You can easily install it via pip:

uvloop is NOT yet supported on Windows Machines.

pip install uvloop\n
"},{"location":"installation/pip_install/#dxcam","title":"DXcam","text":"

Required only if you're using the ScreenGear API on Windows machines for better FPS performance. You can easily install it via pip:

FYI, DXcam is ONLY supported on Windows Machines.

pip install dxcam\n
"},{"location":"installation/pip_install/#installation","title":"Installation","text":"Installation command with pip has been changed in v0.2.4

The legacy pip install vidgear command now installs critical bare-minimum dependencies only. Therefore in order to automatically install all the API specific dependencies as previous versions, use pip install vidgear[core] command instead.

v0.2.4 and newerOlder
# Install latest stable release with all Core dependencies\npip install -U vidgear[core]\n

[core] keyword isn't available in versions older than v0.2.4

# Install older stable release with all Core dependencies\npip install vidgear<0.2.4\n

Similarly in your python project files like setup.py or requirements.txt or setup.cfg, use vidgear dependency as vidgear[core]>=0.2.4 instead.

This change does not affects pip install vidgear[asyncio] command.

Installation is as simple as:

Installing vidgear with only selective dependencies

Starting with version v0.2.2, you can now run any VidGear API by installing only just specific dependencies required by the API in use(except for some Core dependencies).

This is useful when you want to manually review, select and install minimal API-specific dependencies on bare-minimum vidgear from scratch on your system:

Windows Installation

If you are using Windows, some of the commands given below, may not work out-of-the-box.

A quick solution may be to preface every Python command with python -m like this:

# Install latest stable release with all Core dependencies\npython -m pip install -U vidgear[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npython -m pip install -U vidgear[asyncio]\n

And, If you don't have the privileges to the directory you're installing package. Then use --user flag, that makes pip install packages in your home directory instead:

# Install latest stable release with all Core dependencies\npython -m pip install --upgrade --user vidgear[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npython -m pip install --upgrade --user vidgear[asyncio]\n

Or, If you're using py as alias for installed python, then:

# Install latest stable release with all Core dependencies\npy -m pip install --upgrade --user vidgear[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npy -m pip install --upgrade --user vidgear[asyncio]\n
# Install latest stable release with all Core dependencies\npip install -U vidgear[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npip install -U vidgear[asyncio]\n

And if you prefer to install VidGear directly from the repository:

# Install latest stable release with all Core dependencies\npip install git+git://github.com/abhiTronix/vidgear@master#egg=vidgear[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npip install git+git://github.com/abhiTronix/vidgear@master#egg=vidgear[asyncio]\n

Or you can also download its wheel (.whl) package from our repository's releases section, and thereby can be installed as follows:

# Install latest stable release with all Core dependencies\npip install vidgear-0.3.3-py3-none-any.whl[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npip install vidgear-0.3.3-py3-none-any.whl[asyncio]\n

  1. The ensurepip module is missing/disabled on Ubuntu. Use pip method only.\u00a0\u21a9

"},{"location":"installation/source_install/","title":"Install from source","text":""},{"location":"installation/source_install/#install-from-source","title":"Install from source","text":"

Best option for trying latest patches(maybe experimental), forking for Pull Requests, or automatically installing all prerequisites(with a few exceptions).

"},{"location":"installation/source_install/#prerequisites","title":"Prerequisites","text":"

When installing VidGear from source, following are some API specific prerequisites you may need to install manually:

What about rest of the prerequisites?

Any other python prerequisites (Critical/API specific) will be automatically installed based on your OS/System specifications.

Upgrade your pip

It strongly advised to upgrade to latest pip before installing vidgear to avoid any undesired installation error(s).

There are two mechanisms to upgrade pip:

pipensurepip

You can use existing pip to upgrade itself:

Install pip if not present Linux/MacOSWindows
python get-pip.py\n
py get-pip.py\n

More details about this script can be found in pypa/get-pip\u2019s README.

Linux/MacOSWindows
python -m pip install pip --upgrade\n
py -m pip install pip --upgrade\n

Python also comes with an ensurepip module1, which can easily upgrade/install pip in any Python environment.

Linux/MacOSWindows
python -m ensurepip --upgrade\n
py -m ensurepip --upgrade\n
"},{"location":"installation/source_install/#api-specific-prerequisites","title":"API Specific Prerequisites","text":""},{"location":"installation/source_install/#ffmpeg","title":"FFmpeg","text":"

Require only for the video compression and encoding compatibility within StreamGear API and WriteGear API's Compression Mode.

FFmpeg Installation

"},{"location":"installation/source_install/#picamera2","title":"Picamera2","text":"

Required only if you're using Raspberry Pi Camera Modules (or USB webcams) with the PiGear API. Here's how to install Picamera2 python library:

Using Legacy picamera library with PiGear (v0.3.3 and above)

PiGear API (version 0.3.3 onwards) prioritizes the newer Picamera2 library under the hood for Raspberry Pi camera modules. However, if your operating system doesn't support Picamera2, you can still use the legacy picamera library. Here's how to easily install it using pip:

pip install picamera\n

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

Picamera2 is only supported on Raspberry Pi OS Bullseye (or later) images, both 32 and 64-bit.

Picamera2 is NOT supported on:

Installation using apt (Recommended)Installation using pip As of September 2022, Picamera2 is pre-installed on images downloaded from Raspberry Pi. So you don't have to install it manually.

If Picamera2 is not already installed, then your image is presumably older and you should start with system upgrade:

sudo apt update && upgrade\n

If you have installed Picamera2 previously using pip, then you should also uninstall this (pip3 uninstall picamera2).

Thereafter, you can install Picamera2 with all the GUI (Qt and OpenGL) dependencies using:

sudo apt install -y python3-picamera2\n

Or, If you DON'T want the GUI dependencies, use:

sudo apt install -y python3-picamera2 --no-install-recommends\n

This is NOT the recommended way to install Picamera2.

However, if you wish to install Picamera2 with all the GUI (Qt and OpenGL) dependencies with pip, use:

sudo apt install -y python3-libcamera python3-kms++\nsudo apt install -y python3-pyqt5 python3-prctl \nsudo apt install -y libatlas-base-dev ffmpeg python3-pip\npip3 install numpy --upgrade\npip3 install picamera2[gui]\n

Or, If you DON'T want the GUI dependencies, use:

sudo apt install -y python3-libcamera python3-kms++\nsudo apt install -y python3-prctl libatlas-base-dev\nsudo apt install -y ffmpeg libopenjp2-7 python3-pip\npip3 install numpy --upgrade\npip3 install picamera2\n
"},{"location":"installation/source_install/#installation","title":"Installation","text":"

If you want to checkout the latest beta testing branch , you can do so with the following commands:

This can be useful if you want to provide feedback for a new feature or bug fix in the testing branch.

DO NOT clone or install any other branch other than testing unless advised, as it is not tested with CI environments and possibly very unstable or unusable.

Installing vidgear with only selective dependencies

Starting with version v0.2.2, you can now run any VidGear API by installing only just specific dependencies required by the API in use(except for some Core dependencies).

This is useful when you want to manually review, select and install minimal API-specific dependencies on bare-minimum vidgear from scratch on your system:

Windows Installation

If you are using Windows, some of the commands given below, may not work out-of-the-box.

A quick solution may be to preface every Python command with python -m like this:

# Install latest stable release with all Core dependencies\npython -m pip install -U .[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npython -m pip install -U .[asyncio]\n

And, If you don't have the privileges to the directory you're installing package. Then use --user flag, that makes pip install packages in your home directory instead:

# Install latest stable release with all Core dependencies\npython -m pip install --upgrade --user .[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npython -m pip install --upgrade --user .[asyncio]\n

Or, If you're using py as alias for installed python, then:

# Install latest stable release with all Core dependencies\npy -m pip install --upgrade --user .[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npy -m pip install --upgrade --user .[asyncio]\n
# clone the repository and get inside\ngit clone https://github.com/abhiTronix/vidgear.git && cd vidgear\n\n# checkout the latest testing branch\ngit checkout testing\n\n# Install latest stable release with all Core dependencies\npip install -U .[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npip install -U .[asyncio]\n

  1. The ensurepip module is missing/disabled on Ubuntu. Use pip method only.\u00a0\u21a9

"}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Overview","text":""},{"location":"#introduction","title":"Introduction","text":"

VidGear is a cross-platform High-Performance Video-Processing Framework for building complex real-time media applications in python

VidGear provides an easy-to-use, highly extensible, Multi-Threaded + Asyncio API Framework on top of many state-of-the-art specialized libraries like OpenCV, FFmpeg, ZeroMQ, picamera2, starlette, yt_dlp, pyscreenshot, dxcam, aiortc and python-mss at its backend, and enable us to flexibly exploit their internal parameters and methods, while silently delivering robust error-handling and real-time performance \u26a1\ufe0f.

\"Write Less and Accomplish More\" \u2014 VidGear's Motto

VidGear focuses on simplicity, and thereby lets programmers and software developers to easily integrate and perform Complex Video Processing Tasks without going through hefty documentation and in just a few lines of code.

"},{"location":"#getting-started","title":"Getting Started","text":"

In case you're run into any problems, consult the Help section.

If you're just getting started with OpenCV-Python programming, then refer this FAQ \u27b6

"},{"location":"#gears","title":"Gears","text":"

VidGear is built with multiple Gears each with some unique functionality.

Each Gear is designed exclusively to handle/control/process different data-specific & device-specific video streams, network streams, and media encoders/decoders.

These Gears can be classified as follows:

"},{"location":"#videocapture-gears","title":"VideoCapture Gears","text":""},{"location":"#videowriter-gears","title":"VideoWriter Gears","text":""},{"location":"#streaming-gears","title":"Streaming Gears","text":""},{"location":"#network-gears","title":"Network Gears","text":"

"},{"location":"#contributions","title":"Contributions","text":"

Contributions are welcome, and greatly appreciated!

Please see our Contribution Guidelines for more details.

"},{"location":"#community-channel","title":"Community Channel","text":"

If you've come up with some new idea, or looking for the fastest way troubleshoot your problems. Please checkout our Gitter community channel \u27b6

"},{"location":"#become-a-stargazer","title":"Become a Stargazer","text":"

You can be a Stargazer by starring us on Github, it helps us a lot and you're making it easier for others to find & trust this library. Thanks!

"},{"location":"#donations","title":"Donations","text":"

VidGear is free and open source and will always remain so.

It is something I am doing with my own free time. But so much more needs to be done, and I need your help to do this. For just the price of a cup of coffee, you can make a difference

"},{"location":"#citation","title":"Citation","text":"

Here is a Bibtex entry you can use to cite this project in a publication:

@software{vidgear,\n  author       = {Abhishek Thakur and\n                  Zoe Papakipos and\n                  Christian Clauss and\n                  Christian Hollinger and\n                  Ian Max Andolina and\n                  Vincent Boivin and\n                  Kyle Ahn and\n                  freol35241 and\n                  Benjamin Lowe and\n                  Micka\u00ebl Schoentgen and\n                  Renaud Bouckenooghe and\n                  Ibtsam Ahmad},\n  title        = {abhiTronix/vidgear: VidGear Stable v0.3.2},\n  month        = sep,\n  year         = 2023,\n  publisher    = {Zenodo},\n  version      = {vidgear-0.3.2},\n  doi          = {10.5281/zenodo.8332548},\n  url          = {https://doi.org/10.5281/zenodo.8332548}\n}\n

"},{"location":"changelog/","title":"Release Notes","text":""},{"location":"changelog/#release-notes","title":"Release Notes","text":""},{"location":"changelog/#v033-2024-06-22","title":"v0.3.3 (2024-06-22)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v032-2023-09-10","title":"v0.3.2 (2023-09-10)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v031-2023-07-22","title":"v0.3.1 (2023-07-22)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests "},{"location":"changelog/#v030-2023-01-26","title":"v0.3.0 (2023-01-26)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v026-2022-07-05","title":"v0.2.6 (2022-07-05)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v025-2021-02-11","title":"v0.2.5 (2021-02-11)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests "},{"location":"changelog/#v024-2021-12-05","title":"v0.2.4 (2021-12-05)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v023-2021-10-27","title":"v0.2.3 (2021-10-27)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v022-2021-09-02","title":"v0.2.2 (2021-09-02)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v021-2021-04-25","title":"v0.2.1 (2021-04-25)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v020-2021-01-01","title":"v0.2.0 (2021-01-01)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests "},{"location":"changelog/#v019-2020-08-31","title":"v0.1.9 (2020-08-31)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests "},{"location":"changelog/#v018-2020-06-12","title":"v0.1.8 (2020-06-12)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests "},{"location":"changelog/#v017-2020-04-29","title":"v0.1.7 (2020-04-29)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v016-2020-01-01","title":"v0.1.6 (2020-01-01)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests New Contributors "},{"location":"changelog/#v015-2019-07-24","title":"v0.1.5 (2019-07-24)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests "},{"location":"changelog/#v014-2019-05-11","title":"v0.1.4 (2019-05-11)","text":"New Features Updates/Improvements Breaking Updates/Changes Bug-fixes Pull Requests "},{"location":"changelog/#v013-2019-04-07","title":"v0.1.3 (2019-04-07)","text":"Bug-fixes Pull Requests "},{"location":"changelog/#v012-2019-03-27","title":"v0.1.2 (2019-03-27)","text":"New Features Updates/Improvements Bug-fixes Pull Requests "},{"location":"changelog/#v011-2019-03-24","title":"v0.1.1 (2019-03-24)","text":"New Features Bug-fixes "},{"location":"changelog/#v010-2019-03-17","title":"v0.1.0 (2019-03-17)","text":"New Features "},{"location":"contribution/","title":"Contribution Overview","text":""},{"location":"contribution/#contribution-overview","title":"Contribution Overview","text":"

Contributions are welcome, We'd love your contribution to VidGear in order to fix bugs or to implement new features!

Contribution Opportunities

If you're looking for something to work on, check for the PR WELCOMED labeled issues on our GitHub Repository.

"},{"location":"contribution/#submission-guidelines","title":"Submission Guidelines","text":""},{"location":"contribution/#submission-contexts","title":"Submission Contexts","text":""},{"location":"contribution/#got-a-question-or-problem","title":"Got a question or problem?","text":"

For quick questions, please refrain from opening an issue, instead read our FAQ & Troubleshooting section or you can reach us on Gitter community channel.

"},{"location":"contribution/#found-a-typo","title":"Found a typo?","text":"

There's no need to contribute for some typos. Just reach us on Gitter \u27b6 community channel, We will correct them in (less than) no time.

"},{"location":"contribution/#found-a-bug","title":"Found a bug?","text":"

If you encountered a bug, you can help us by submitting an issue in our GitHub repository. Even better, you can submit a Pull Request(PR) with a fix, but make sure to read the guidelines \u27b6.

"},{"location":"contribution/#request-for-a-featureimprovement","title":"Request for a feature/improvement?","text":"Subscribe to Github Repository

You can subscribe our GitHub Repository to receive notifications through email for new pull requests, commits and issues that are created in VidGear. Learn more about it here \u27b6

You can request our GitHub Repository for a new feature/improvement based on the type of request:

Please submit an issue with a proposal template for your request to explain how it benefits everyone in the community.

"},{"location":"gears/","title":"Introduction","text":""},{"location":"gears/#introduction","title":"Introduction","text":"Gears: generalized workflow"},{"location":"gears/#gears-what-are-these","title":"Gears , What are these?","text":"

VidGear is built on Standalone APIs - also known as Gears , each with some unique functionality. Each Gears is designed exclusively to handle/control/process different data-specific & device-specific video streams, network streams, and media encoders/decoders.

Gears allows users to work with an inherently optimized, easy-to-use, extensible, and exposed API Framework on top of many state-of-the-art libraries, while silently delivering robust error handling and unmatched real-time performance.

"},{"location":"gears/#gears-classification","title":"Gears Classification","text":"

These Gears can be classified as follows:

"},{"location":"gears/#a-videocapture-gears","title":"A. VideoCapture Gears","text":"

Basic Function: Retrieves numpy.ndarray frames from various sources.

"},{"location":"gears/#b-videowriter-gears","title":"B. VideoWriter Gears","text":"

Basic Function: Writes numpy.ndarray frames to a video file or network stream.

"},{"location":"gears/#c-streaming-gears","title":"C. Streaming Gears","text":"

Basic Function: Transcodes/Broadcasts files and numpy.ndarray frames for streaming.

You can also use WriteGear for streaming with traditional protocols such as RTMP, RTSP/RTP.

"},{"location":"gears/#d-network-gears","title":"D. Network Gears","text":"

Basic Function: Sends/Receives data and numpy.ndarray frames over connected networks.

"},{"location":"help/","title":"Helping VidGear","text":""},{"location":"help/#helping-vidgear","title":"Helping VidGear","text":"

Liked VidGear? Would you like to help VidGear, other users, and the author?

There are many simple ways to help us:

"},{"location":"help/#star-vidgear-on-github","title":"Star VidGear on GitHub","text":"

You can star VidGear on GitHub:

It helps us a lot by making it easier for others to find & trust this library. Thanks!

"},{"location":"help/#help-others-with-issues-on-github","title":"Help others with issues on GitHub","text":"

You can see through any opened or pinned existing issues on our GitHub repository, and try helping others, wherever possible:

"},{"location":"help/#watch-the-github-repository","title":"Watch the GitHub repository","text":"

You can watch \ud83d\udc40 VidGear Activities on GitHub:

When you watch a repository, you will be notified of all conversations for that repository, including when someone creates a new issue, or pushes a new pull request.

You can try helping solving those issues, or give valuable feedback/review on new Pull Requests.

"},{"location":"help/#helping-author","title":"Helping Author","text":"

Donations help keep VidGear's development alive and motivate me (as author).

It is something I am doing with my own free time. But so much more needs to be done, and I need your help to do this. For just the price of a cup of coffee, you can make a difference

Thanks a million!

"},{"location":"help/#connect-with-author","title":"Connect with Author","text":"

You can connect with me, the author \ud83d\udc4b:

"},{"location":"installation/","title":"Installation Overview","text":""},{"location":"installation/#installation-overview","title":"Installation Overview","text":""},{"location":"installation/#supported-systems","title":"Supported Systems","text":"

VidGear is well-tested and supported on the following systems(but not limited to), with python 3.8+ and pip installed:

"},{"location":"installation/#supported-python-legacies","title":"Supported Python legacies","text":"

Depreciation Notice

Python-3.7 legacies support has been dropped from Vidgear.

Python 3.8+ are only supported legacies for installing Vidgear v0.3.1 and above.

"},{"location":"installation/#installation-methods","title":"Installation methods","text":""},{"location":"license/","title":"License","text":""},{"location":"license/#license","title":"License","text":"

This library is released under the Apache 2.0 License.

"},{"location":"license/#copyright-notice","title":"Copyright Notice","text":"
Copyright (c) 2019 Abhishek Thakur(@abhiTronix) <abhi.una12@gmail.com>\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n
"},{"location":"switch_from_cv/","title":"Switching from OpenCV","text":""},{"location":"switch_from_cv/#switching-from-opencv-library","title":"Switching from OpenCV Library","text":"

Switching OpenCV with VidGear APIs is fairly painless process, and will just require changing a few lines in your python script.

This document is intended to software developers who want to migrate their python code from OpenCV Library to VidGear APIs.

Prior knowledge of Python or OpenCV won't be covered in this guide. Proficiency with OpenCV-Python (Python API for OpenCV) is a must in order understand this document.

If you're just getting started with OpenCV-Python programming, then refer this FAQ \u27b6

"},{"location":"switch_from_cv/#why-vidgear-is-better-than-opencv","title":"Why VidGear is better than OpenCV?","text":"

Learn more about OpenCV here \u27b6

VidGear employs OpenCV at its backend and enhances its existing capabilities even further by introducing many new state-of-the-art functionalities such as:

Vidgear offers all this at once while maintaining the same standard OpenCV-Python (Python API for OpenCV) coding syntax for all of its APIs, thereby making it even easier to implement complex real-time OpenCV applications in python code without changing things much.

"},{"location":"switch_from_cv/#switching-the-videocapture-apis","title":"Switching the VideoCapture APIs","text":"

Let's compare a bare-minimum python code for extracting frames out of any Webcam/USB-camera (connected at index 0), between OpenCV's VideoCapture Class and VidGear's CamGear VideoCapture API side-by-side:

CamGear API share the same syntax as other VideoCapture APIs, thereby you can easily switch to any of those APIs in a similar manner.

OpenCV VideoCapture ClassVidGear's CamGear API
# import required libraries\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = cv2.VideoCapture(0) \n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n      break\n\n\n    # {do something with the frame here}\n\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.release()\n
# import required libraries\nfrom vidgear.gears import CamGear\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = CamGear(source=0).start() \n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

and both syntax almost looks the same, easy, isn't it?

"},{"location":"switch_from_cv/#differences","title":"Differences","text":"

Let's breakdown a few noteworthy difference in both syntaxes:

Task OpenCV VideoCapture Class VidGear's CamGear API Initiating stream = cv2.VideoCapture(0) stream = CamGear(source=0).start() Reading frames (grabbed, frame) = stream.read() frame = stream.read() Checking empty frame if not grabbed: if frame is None: Terminating stream.release() stream.stop()

Now checkout other VideoCapture Gears \u27b6

"},{"location":"switch_from_cv/#switching-the-videowriter-api","title":"Switching the VideoWriter API","text":"

Let's extend previous bare-minimum python code and save those extracted frames to disk as a valid file, with OpenCV's VideoWriter Class and VidGear's WriteGear (with FFmpeg backend), compared side-to-side:

WriteGear API also provides backend for OpenCV's VideoWriter Class. More information here \u27b6

OpenCV VideoWriter ClassVidGear's WriteGear API
# import required libraries\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = cv2.VideoCapture(0) \n\n# Define the codec and create VideoWriter object with suitable output \n# filename for e.g. `Output.avi`\nfourcc = cv2.VideoWriter_fourcc(*'XVID') \nwriter = cv2.VideoWriter('output.avi', fourcc, 20.0, (640, 480)) \n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n      break\n\n\n    # {do something with the frame here}\n\n\n    # write frame to writer\n    writer.write(frame)\n\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.release()\n\n# safely close writer\nwriter.release() \n
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = CamGear(source=0).start() \n\n# Define WriteGear Object with suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output = 'Output.mp4') \n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if None-type\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

Noticed WriteGear's coding syntax looks similar but less complex?

"},{"location":"switch_from_cv/#differences_1","title":"Differences","text":"

Let's breakdown a few noteworthy difference in both syntaxes:

Task OpenCV VideoWriter Class VidGear's WriteGear API Initiating writer = cv2.VideoWriter('output.avi', cv2.VideoWriter_fourcc(*'XVID'), 20.0, (640, 480)) writer = WriteGear(output='Output.mp4') Writing frames writer.write(frame) writer.write(frame) Terminating writer.release() writer.close()

Now checkout more about WriteGear API here \u27b6

"},{"location":"bonus/TQM/","title":"Threaded Queue Mode","text":""},{"location":"bonus/TQM/#threaded-queue-mode","title":"Threaded Queue Mode","text":""},{"location":"bonus/TQM/#overview","title":"Overview","text":"Threaded-Queue-Mode: generalized timing diagram

Threaded Queue Mode is designed exclusively for VidGear's Videocapture Gears (namely CamGear, VideoGear) and few Network Gears (such as NetGear(Client's end)) for achieving high-performance, asynchronous, error-free video-frames handling.

Threaded-Queue-Mode is enabled by default, but can be disabled, only if extremely necessary.

Threaded-Queue-Mode is NOT required and thereby automatically disabled for Live feed such as Camera Devices/Modules, since .

"},{"location":"bonus/TQM/#what-does-threaded-queue-mode-exactly-do","title":"What does Threaded-Queue-Mode exactly do?","text":"

Threaded-Queue-Mode helps VidGear do the Threaded Video-Processing tasks in highly optimized, well-organized, and most competent way possible:

"},{"location":"bonus/TQM/#a-enables-multi-threading","title":"A. Enables Multi-Threading","text":"

In case you don't already know, OpenCV's' read() is a Blocking I/O function for reading and decoding the next video-frame, and consumes much of the I/O bound memory depending upon our video source properties & system hardware. This essentially means, the corresponding thread that reads data from it, is continuously blocked from retrieving the next frame. As a result, our python program appears slow and sluggish even without any type of computationally expensive image processing operations. This problem is far more severe on low memory SBCs like Raspberry Pis.

In Threaded-Queue-Mode, VidGear creates several Python Threads within one process to offload the frame-decoding task to a different thread. Thereby, VidGear is able to execute different Video I/O-bounded operations at the same time by overlapping there waiting times. Moreover, threads are managed by operating system itself and is capable of distributing them between available CPU cores efficiently. In this way, Threaded-Queue-Mode keeps on processing frames faster in the background without affecting by sluggishness in our main python program thread.

"},{"location":"bonus/TQM/#b-utilizes-fixed-size-queues","title":"B. Utilizes Fixed-Size Queues","text":"

Although Multi-threading is fast, easy, and efficient, it can lead to some serious undesired effects like frame-skipping, Global Interpreter Lock, race conditions, etc. This is because there is no isolation whatsoever in python threads, and in case there is any crash it will cause the whole process to crash. That's not all, the memory of the process is shared by different threads and that may result in random process crashes due to unwanted race conditions.

These problems are avoided in Threaded-Queue-Mode by utilizing Thread-Safe, Memory-Efficient, and Fixed-Size Queues (with approximately same O(1) performance in both directions), that isolates the frame-decoding thread from other parallel threads and provide synchronized access to incoming frames without any obstruction.

"},{"location":"bonus/TQM/#c-accelerates-frame-processing","title":"C. Accelerates Frame Processing","text":"

With queues, VidGear always maintains a fixed-length frames buffer in the memory and blocks the thread temporarily if the queue is full to avoid possible frame drops or otherwise pops out the frames synchronously without any obstructions. This significantly accelerates frame processing rate (and therefore our overall video processing pipeline) comes from dramatically reducing latency \u2014 since we don\u2019t have to wait for the read() method to finish reading and decoding a frame; instead, there is always a pre-decoded frame ready for us to process.

"},{"location":"bonus/TQM/#what-are-the-advantages-of-threaded-queue-mode","title":"What are the advantages of Threaded-Queue-Mode?","text":"

"},{"location":"bonus/TQM/#manually-disabling-threaded-queue-mode","title":"Manually disabling Threaded-Queue-Mode","text":"

To manually disable Threaded-Queue-Mode, VidGear provides THREADED_QUEUE_MODE boolean attribute for options dictionary parameter in respective VideoCapture APIs:

Important Warnings

Disabling Threaded-Queue-Mode may lead to Random Intermittent Bugs that can be quite difficult to discover. More insight can be found here \u27b6

THREADED_QUEUE_MODE (boolean): This attribute can be used to override Threaded-Queue-Mode mode to manually disable it:

options = {'THREADED_QUEUE_MODE': False} # to disable Threaded Queue Mode. \n

and you can pass it to options dictionary parameter of the respective API.

"},{"location":"bonus/colorspace_manipulation/","title":"Colorspace Manipulation","text":""},{"location":"bonus/colorspace_manipulation/#colorspace-manipulation-for-videocapture-gears","title":"Colorspace Manipulation for VideoCapture Gears","text":""},{"location":"bonus/colorspace_manipulation/#source-colorspace-manipulation","title":"Source ColorSpace manipulation","text":"

All VidGear's Videocapture Gears (namely CamGear, ScreenGear, VideoGear) and some Streaming Gears (namely WebGear, WebGear_RTC) and Network Gears (Client's end) - provides exclusive internal support for Source Color Space manipulation.

There are two ways to alter source colorspace:

"},{"location":"bonus/colorspace_manipulation/#using-colorspace-parameter","title":"Using colorspace parameter","text":"

Primarily, the safest way is by colorspace (string) parameter of the respective VideoCapture API, that can be used to easily alter the colorspace of the input source, during initialization. But on the downside, colorspace parameter value CANNOT be changed/altered at runtime.

All possible values for this parameter are discussed below \u27b6

"},{"location":"bonus/colorspace_manipulation/#using-color_space-global-variable","title":"Using color_space global variable","text":"

Alternatively, a more direct approach is by using color_space (integer) global variable the respective VideoCapture API, can be used for directly changing the source colorspace at runtime. It can be used in conjunction with colorspace parameter easily.

Supported Colorspace Conversions

Any conversion from default Source colorspace (i.e. BGR in case of OpenCV), to any other colorspace and vice-versa (use None to revert), is supported.

Important Information

Tip

It is advised to enable logging(logging = True) on the first run for easily identifying any runtime errors.

"},{"location":"bonus/colorspace_manipulation/#supported-colorspace-parameter-values","title":"Supported colorspace parameter values","text":"

All supported string values for colorspace parameter are as follows:

You can check all OpenCV Colorspace Conversion Codes here \u27b6.

Supported Conversion Values Description COLOR_BGR2BGRA BGR to BGRA COLOR_BGR2RGBA BGR to RGBA COLOR_BGR2RGB BGR to RGB backward conversions to RGB/BGR COLOR_BGR2GRAY BGR to GRAY COLOR_BGR2BGR565 BGR to BGR565 COLOR_BGR2BGR555 BGR to BGR555 COLOR_BGR2XYZ BGR to CIE XYZ COLOR_BGR2YCrCb BGR to luma-chroma (aka YCC) COLOR_BGR2HSV BGR to HSV (hue saturation value) COLOR_BGR2Lab BGR to CIE Lab COLOR_BGR2Luv BGR to CIE Luv COLOR_BGR2HLS BGR to HLS (hue lightness saturation) COLOR_BGR2HSV_FULL BGR to HSV_FULL COLOR_BGR2HLS_FULL BGR to HLS_FULL COLOR_BGR2YUV BGR to YUV COLOR_BGR2YUV_I420 BGR to YUV 4:2:0 family COLOR_BGR2YUV_IYUV BGR to IYUV COLOR_BGR2YUV_YV12 BGR to YUV_YV12 None Back to default colorspace (i.e. BGR)

"},{"location":"bonus/colorspace_manipulation/#usage-examples","title":"Usage examples","text":""},{"location":"bonus/colorspace_manipulation/#using-camgear-with-direct-colorspace-manipulation","title":"Using CamGear with Direct Colorspace Manipulation","text":"

The complete usage example can be found here \u27b6

"},{"location":"bonus/colorspace_manipulation/#using-pigear-with-direct-colorspace-manipulation","title":"Using PiGear with Direct Colorspace Manipulation","text":"

The complete usage example can be found here \u27b6

"},{"location":"bonus/colorspace_manipulation/#using-videogear-with-colorspace-manipulation","title":"Using VideoGear with Colorspace Manipulation","text":"

The complete usage example can be found here \u27b6

"},{"location":"bonus/colorspace_manipulation/#using-screengear-with-direct-colorspace-manipulation","title":"Using ScreenGear with Direct Colorspace Manipulation","text":"

The complete usage example can be found here \u27b6

"},{"location":"bonus/reference/camgear/","title":"CamGear API References","text":"

CamGear API usage examples can be found here \u27b6

CamGear API parameters are explained here \u27b6

CamGear supports a diverse range of video streams which can handle/control video stream almost any IP/USB Cameras, multimedia video file format (upto 4k tested), any network stream URL such as http(s), rtp, rtsp, rtmp, mms, etc. It also supports Gstreamer's RAW pipelines.

CamGear API provides a flexible, high-level multi-threaded wrapper around OpenCV's VideoCapture API with direct access to almost all of its available parameters. It relies on Threaded Queue mode for threaded, error-free and synchronized frame handling.

CamGear internally implements yt_dlp backend class for seamlessly pipelining live video-frames and metadata from various streaming services like YouTube, Dailymotion, Twitch, and many more \u27b6

Source code in vidgear/gears/camgear.py
class CamGear:\n    \"\"\"\n    CamGear supports a diverse range of video streams which can handle/control video stream almost any IP/USB Cameras, multimedia video file format (upto 4k tested),\n    any network stream URL such as http(s), rtp, rtsp, rtmp, mms, etc. It also supports Gstreamer's RAW pipelines.\n\n    CamGear API provides a flexible, high-level multi-threaded wrapper around OpenCV's VideoCapture API with direct access to almost all of its available parameters.\n    It relies on Threaded Queue mode for threaded, error-free and synchronized frame handling.\n\n    CamGear internally implements `yt_dlp` backend class for seamlessly pipelining live video-frames and metadata from various streaming services like YouTube, Dailymotion,\n    Twitch, and [many more \u27b6](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md#supported-sites)\n    \"\"\"\n\n    def __init__(\n        self,\n        source=0,\n        stream_mode=False,\n        backend=0,\n        colorspace=None,\n        logging=False,\n        time_delay=0,\n        **options\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the CamGear class.\n\n        Parameters:\n            source (based on input): defines the source for the input stream.\n            stream_mode (bool): controls the exclusive **Stream Mode** for handling streaming URLs.\n            backend (int): selects the backend for OpenCV's VideoCapture class.\n            colorspace (str): selects the colorspace of the input stream.\n            logging (bool): enables/disables logging.\n            time_delay (int): time delay (in sec) before start reading the frames.\n            options (dict): provides ability to alter Source Tweak Parameters.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # initialize global\n        self.ytv_metadata = {}\n\n        # check if Stream-Mode is ON (True)\n        if stream_mode:\n            # TODO: check GStreamer backend support\n            # gst_support = check_gstreamer_support(logging=self.__logging)\n            # handle special Stream Mode parameters\n            stream_resolution = get_supported_resolution(\n                options.pop(\"STREAM_RESOLUTION\", \"best\"), logging=self.__logging\n            )\n            # handle Stream-Mode\n            if not (yt_dlp is None):\n                # extract user-defined params\n                yt_stream_params = options.pop(\"STREAM_PARAMS\", {})\n                if isinstance(yt_stream_params, dict):\n                    yt_stream_params = {\n                        str(k).strip(): v for k, v in yt_stream_params.items()\n                    }\n                else:\n                    yt_stream_params = {}\n                try:\n                    # Validate source for Yt_dlp backend\n                    logger.info(\n                        \"Verifying Streaming URL using yt-dlp backend. Please wait...\"\n                    )\n                    # initialize YT_backend\n                    ytbackend = YT_backend(\n                        source_url=source, logging=self.__logging, **yt_stream_params\n                    )\n                    if ytbackend:\n                        # save video metadata\n                        self.ytv_metadata = ytbackend.meta_data\n                        # handle live-streams\n                        # Throw warning for livestreams\n                        ytbackend.is_livestream and logger.warning(\n                            \"Livestream URL detected. It is strongly recommended to use the GStreamer backend (`backend=cv2.CAP_GSTREAMER`) with these URLs.\"\n                        )\n                        # check whether stream-resolution was specified and available\n                        if not (stream_resolution in ytbackend.streams.keys()):\n                            logger.warning(\n                                \"Specified stream-resolution `{}` is not available. Reverting to `best`!\".format(\n                                    stream_resolution\n                                )\n                            )\n                            # revert to best\n                            stream_resolution = \"best\"\n                        else:\n                            self.__logging and logger.debug(\n                                \"Using `{}` resolution for streaming.\".format(\n                                    stream_resolution\n                                )\n                            )\n                        # extract stream URL as source using stream-resolution\n                        source = ytbackend.streams[stream_resolution]\n                        # log progress\n                        self.__logging and logger.debug(\n                            \"YouTube source ID: `{}`, Title: `{}`, Quality: `{}`\".format(\n                                self.ytv_metadata[\"id\"],\n                                self.ytv_metadata[\"title\"],\n                                stream_resolution,\n                            )\n                        )\n                except Exception as e:\n                    # raise error if something went wrong\n                    raise ValueError(\n                        \"[CamGear:ERROR] :: Stream Mode is enabled but Input URL is invalid!\"\n                    )\n            else:\n                # raise import errors\n                import_dependency_safe(\"yt_dlp\")\n\n        # assigns special parameter to global variable and clear\n        # Threaded Queue Mode\n        self.__threaded_queue_mode = options.pop(\"THREADED_QUEUE_MODE\", True)\n        if not isinstance(self.__threaded_queue_mode, bool):\n            # reset improper values\n            self.__threaded_queue_mode = True\n        # Thread Timeout\n        self.__thread_timeout = options.pop(\"THREAD_TIMEOUT\", None)\n        if self.__thread_timeout and isinstance(self.__thread_timeout, (int, float)):\n            # set values\n            self.__thread_timeout = float(self.__thread_timeout)\n        else:\n            # defaults to 5mins timeout\n            self.__thread_timeout = None\n\n        self.__queue = None\n        # initialize queue for video files only\n        if self.__threaded_queue_mode and isinstance(source, str):\n            # define queue and assign it to global var\n            self.__queue = queue.Queue(maxsize=96)  # max bufferlen 96 to check overflow\n            # log it\n            self.__logging and logger.debug(\n                \"Enabling Threaded Queue Mode for the current video source!\"\n            )\n        else:\n            # otherwise disable it\n            self.__threaded_queue_mode = False\n            # log it\n            self.__logging and logger.warning(\n                \"Threaded Queue Mode is disabled for the current video source!\"\n            )\n\n        self.__thread_timeout and logger.info(\n            \"Setting Video-Thread Timeout to {}s.\".format(self.__thread_timeout)\n        )\n\n        # stream variable initialization\n        self.stream = None\n\n        if backend and isinstance(backend, int):\n            # add backend if specified and initialize the camera stream\n            if check_CV_version() == 3:\n                # Different OpenCV 3.4.x statement\n                self.stream = cv2.VideoCapture(source + backend)\n            else:\n                # Two parameters are available since OpenCV 4+ (master branch)\n                self.stream = cv2.VideoCapture(source, backend)\n            logger.info(\"Setting backend `{}` for this source.\".format(backend))\n        else:\n            # initialize the camera stream\n            self.stream = cv2.VideoCapture(source)\n\n        # initializing colorspace variable\n        self.color_space = None\n\n        # apply attributes to source if specified\n        options = {str(k).strip(): v for k, v in options.items()}\n        for key, value in options.items():\n            property = capPropId(key)\n            not (property is None) and self.stream.set(property, value)\n\n        # handle colorspace value\n        if not (colorspace is None):\n            self.color_space = capPropId(colorspace.strip())\n            self.__logging and not (self.color_space is None) and logger.debug(\n                \"Enabling `{}` colorspace for this video stream!\".format(\n                    colorspace.strip()\n                )\n            )\n\n        # initialize and assign frame-rate variable\n        self.framerate = 0.0\n        _fps = self.stream.get(cv2.CAP_PROP_FPS)\n        if _fps > 1.0:\n            self.framerate = _fps\n\n        # applying time delay to warm-up webcam only if specified\n        time_delay and isinstance(time_delay, (int, float)) and time.sleep(time_delay)\n\n        # frame variable initialization\n        (grabbed, self.frame) = self.stream.read()\n\n        # check if valid stream\n        if grabbed:\n            # render colorspace if defined\n            if not (self.color_space is None):\n                self.frame = cv2.cvtColor(self.frame, self.color_space)\n\n            # initialize and append to queue\n            self.__threaded_queue_mode and self.__queue.put(self.frame)\n        else:\n            raise RuntimeError(\n                \"[CamGear:ERROR] :: Source is invalid, CamGear failed to initialize stream on this source!\"\n            )\n\n        # thread initialization\n        self.__thread = None\n\n        # initialize termination flag event\n        self.__terminate = Event()\n\n        # initialize stream read flag event\n        self.__stream_read = Event()\n\n    def start(self):\n        \"\"\"\n        Launches the internal *Threaded Frames Extractor* daemon.\n\n        **Returns:** A reference to the CamGear class object.\n        \"\"\"\n\n        self.__thread = Thread(target=self.__update, name=\"CamGear\", args=())\n        self.__thread.daemon = True\n        self.__thread.start()\n        return self\n\n    def __update(self):\n        \"\"\"\n        A **Threaded Frames Extractor**, that keep iterating frames from OpenCV's VideoCapture API to a internal monitored queue,\n        until the thread is terminated, or frames runs out.\n        \"\"\"\n\n        # keep iterating infinitely\n        # until the thread is terminated\n        # or frames runs out\n        # if the thread indicator variable is set, stop the thread\n        while not self.__terminate.is_set():\n            # stream not read yet\n            self.__stream_read.clear()\n\n            # otherwise, read the next frame from the stream\n            (grabbed, frame) = self.stream.read()\n\n            # stream read completed\n            self.__stream_read.set()\n\n            # check for valid frame if received\n            if not grabbed:\n                # no frames received, then safely exit\n                if self.__threaded_queue_mode:\n                    if self.__queue.empty():\n                        break\n                    else:\n                        continue\n                else:\n                    break\n\n            # apply colorspace to frames if valid\n            if not (self.color_space is None):\n                # apply colorspace to frames\n                color_frame = None\n                try:\n                    color_frame = cv2.cvtColor(frame, self.color_space)\n                except Exception as e:\n                    # Catch if any error occurred\n                    color_frame = None\n                    self.color_space = None\n                    self.__logging and logger.exception(str(e))\n                    logger.warning(\"Assigned colorspace value is invalid. Discarding!\")\n                self.frame = color_frame if not (color_frame is None) else frame\n            else:\n                self.frame = frame\n\n            # append to queue\n            self.__threaded_queue_mode and self.__queue.put(self.frame)\n\n        # signal queue we're done\n        self.__threaded_queue_mode and self.__queue.put(None)\n        self.__threaded_queue_mode = False\n\n        # indicate immediate termination\n        self.__terminate.set()\n        self.__stream_read.set()\n\n        # release resources\n        self.stream.release()\n\n    def read(self):\n        \"\"\"\n        Extracts frames synchronously from monitored queue, while maintaining a fixed-length frame buffer in the memory,\n        and blocks the thread if the queue is full.\n\n        **Returns:** A n-dimensional numpy array.\n        \"\"\"\n        while self.__threaded_queue_mode and not self.__terminate.is_set():\n            return self.__queue.get(timeout=self.__thread_timeout)\n        # return current frame\n        # only after stream is read\n        return (\n            self.frame\n            if not self.__terminate.is_set()  # check if already terminated\n            and self.__stream_read.wait(timeout=self.__thread_timeout)  # wait for it\n            else None\n        )\n\n    def stop(self):\n        \"\"\"\n        Safely terminates the thread, and release the multi-threaded resources.\n        \"\"\"\n        self.__logging and logger.debug(\"Terminating processes.\")\n        # terminate Threaded queue mode separately\n        self.__threaded_queue_mode = False\n\n        # indicate that the thread\n        # should be terminated immediately\n        self.__stream_read.set()\n        self.__terminate.set()\n\n        # wait until stream resources are released (producer thread might be still grabbing frame)\n        if self.__thread is not None:\n            if not (self.__queue is None):\n                while not self.__queue.empty():\n                    try:\n                        self.__queue.get_nowait()\n                    except queue.Empty:\n                        continue\n                    self.__queue.task_done()\n            self.__thread.join()\n

"},{"location":"bonus/reference/camgear/#vidgear.gears.camgear.CamGear.__init__","title":"__init__(self, source=0, stream_mode=False, backend=0, colorspace=None, logging=False, time_delay=0, **options) special","text":"

This constructor method initializes the object state and attributes of the CamGear class.

Parameters:

Name Type Description Default source based on input

defines the source for the input stream.

0 stream_mode bool

controls the exclusive Stream Mode for handling streaming URLs.

False backend int

selects the backend for OpenCV's VideoCapture class.

0 colorspace str

selects the colorspace of the input stream.

None logging bool

enables/disables logging.

False time_delay int

time delay (in sec) before start reading the frames.

0 options dict

provides ability to alter Source Tweak Parameters.

{} Source code in vidgear/gears/camgear.py
def __init__(\n    self,\n    source=0,\n    stream_mode=False,\n    backend=0,\n    colorspace=None,\n    logging=False,\n    time_delay=0,\n    **options\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the CamGear class.\n\n    Parameters:\n        source (based on input): defines the source for the input stream.\n        stream_mode (bool): controls the exclusive **Stream Mode** for handling streaming URLs.\n        backend (int): selects the backend for OpenCV's VideoCapture class.\n        colorspace (str): selects the colorspace of the input stream.\n        logging (bool): enables/disables logging.\n        time_delay (int): time delay (in sec) before start reading the frames.\n        options (dict): provides ability to alter Source Tweak Parameters.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # initialize global\n    self.ytv_metadata = {}\n\n    # check if Stream-Mode is ON (True)\n    if stream_mode:\n        # TODO: check GStreamer backend support\n        # gst_support = check_gstreamer_support(logging=self.__logging)\n        # handle special Stream Mode parameters\n        stream_resolution = get_supported_resolution(\n            options.pop(\"STREAM_RESOLUTION\", \"best\"), logging=self.__logging\n        )\n        # handle Stream-Mode\n        if not (yt_dlp is None):\n            # extract user-defined params\n            yt_stream_params = options.pop(\"STREAM_PARAMS\", {})\n            if isinstance(yt_stream_params, dict):\n                yt_stream_params = {\n                    str(k).strip(): v for k, v in yt_stream_params.items()\n                }\n            else:\n                yt_stream_params = {}\n            try:\n                # Validate source for Yt_dlp backend\n                logger.info(\n                    \"Verifying Streaming URL using yt-dlp backend. Please wait...\"\n                )\n                # initialize YT_backend\n                ytbackend = YT_backend(\n                    source_url=source, logging=self.__logging, **yt_stream_params\n                )\n                if ytbackend:\n                    # save video metadata\n                    self.ytv_metadata = ytbackend.meta_data\n                    # handle live-streams\n                    # Throw warning for livestreams\n                    ytbackend.is_livestream and logger.warning(\n                        \"Livestream URL detected. It is strongly recommended to use the GStreamer backend (`backend=cv2.CAP_GSTREAMER`) with these URLs.\"\n                    )\n                    # check whether stream-resolution was specified and available\n                    if not (stream_resolution in ytbackend.streams.keys()):\n                        logger.warning(\n                            \"Specified stream-resolution `{}` is not available. Reverting to `best`!\".format(\n                                stream_resolution\n                            )\n                        )\n                        # revert to best\n                        stream_resolution = \"best\"\n                    else:\n                        self.__logging and logger.debug(\n                            \"Using `{}` resolution for streaming.\".format(\n                                stream_resolution\n                            )\n                        )\n                    # extract stream URL as source using stream-resolution\n                    source = ytbackend.streams[stream_resolution]\n                    # log progress\n                    self.__logging and logger.debug(\n                        \"YouTube source ID: `{}`, Title: `{}`, Quality: `{}`\".format(\n                            self.ytv_metadata[\"id\"],\n                            self.ytv_metadata[\"title\"],\n                            stream_resolution,\n                        )\n                    )\n            except Exception as e:\n                # raise error if something went wrong\n                raise ValueError(\n                    \"[CamGear:ERROR] :: Stream Mode is enabled but Input URL is invalid!\"\n                )\n        else:\n            # raise import errors\n            import_dependency_safe(\"yt_dlp\")\n\n    # assigns special parameter to global variable and clear\n    # Threaded Queue Mode\n    self.__threaded_queue_mode = options.pop(\"THREADED_QUEUE_MODE\", True)\n    if not isinstance(self.__threaded_queue_mode, bool):\n        # reset improper values\n        self.__threaded_queue_mode = True\n    # Thread Timeout\n    self.__thread_timeout = options.pop(\"THREAD_TIMEOUT\", None)\n    if self.__thread_timeout and isinstance(self.__thread_timeout, (int, float)):\n        # set values\n        self.__thread_timeout = float(self.__thread_timeout)\n    else:\n        # defaults to 5mins timeout\n        self.__thread_timeout = None\n\n    self.__queue = None\n    # initialize queue for video files only\n    if self.__threaded_queue_mode and isinstance(source, str):\n        # define queue and assign it to global var\n        self.__queue = queue.Queue(maxsize=96)  # max bufferlen 96 to check overflow\n        # log it\n        self.__logging and logger.debug(\n            \"Enabling Threaded Queue Mode for the current video source!\"\n        )\n    else:\n        # otherwise disable it\n        self.__threaded_queue_mode = False\n        # log it\n        self.__logging and logger.warning(\n            \"Threaded Queue Mode is disabled for the current video source!\"\n        )\n\n    self.__thread_timeout and logger.info(\n        \"Setting Video-Thread Timeout to {}s.\".format(self.__thread_timeout)\n    )\n\n    # stream variable initialization\n    self.stream = None\n\n    if backend and isinstance(backend, int):\n        # add backend if specified and initialize the camera stream\n        if check_CV_version() == 3:\n            # Different OpenCV 3.4.x statement\n            self.stream = cv2.VideoCapture(source + backend)\n        else:\n            # Two parameters are available since OpenCV 4+ (master branch)\n            self.stream = cv2.VideoCapture(source, backend)\n        logger.info(\"Setting backend `{}` for this source.\".format(backend))\n    else:\n        # initialize the camera stream\n        self.stream = cv2.VideoCapture(source)\n\n    # initializing colorspace variable\n    self.color_space = None\n\n    # apply attributes to source if specified\n    options = {str(k).strip(): v for k, v in options.items()}\n    for key, value in options.items():\n        property = capPropId(key)\n        not (property is None) and self.stream.set(property, value)\n\n    # handle colorspace value\n    if not (colorspace is None):\n        self.color_space = capPropId(colorspace.strip())\n        self.__logging and not (self.color_space is None) and logger.debug(\n            \"Enabling `{}` colorspace for this video stream!\".format(\n                colorspace.strip()\n            )\n        )\n\n    # initialize and assign frame-rate variable\n    self.framerate = 0.0\n    _fps = self.stream.get(cv2.CAP_PROP_FPS)\n    if _fps > 1.0:\n        self.framerate = _fps\n\n    # applying time delay to warm-up webcam only if specified\n    time_delay and isinstance(time_delay, (int, float)) and time.sleep(time_delay)\n\n    # frame variable initialization\n    (grabbed, self.frame) = self.stream.read()\n\n    # check if valid stream\n    if grabbed:\n        # render colorspace if defined\n        if not (self.color_space is None):\n            self.frame = cv2.cvtColor(self.frame, self.color_space)\n\n        # initialize and append to queue\n        self.__threaded_queue_mode and self.__queue.put(self.frame)\n    else:\n        raise RuntimeError(\n            \"[CamGear:ERROR] :: Source is invalid, CamGear failed to initialize stream on this source!\"\n        )\n\n    # thread initialization\n    self.__thread = None\n\n    # initialize termination flag event\n    self.__terminate = Event()\n\n    # initialize stream read flag event\n    self.__stream_read = Event()\n
"},{"location":"bonus/reference/camgear/#vidgear.gears.camgear.CamGear.read","title":"read(self)","text":"

Extracts frames synchronously from monitored queue, while maintaining a fixed-length frame buffer in the memory, and blocks the thread if the queue is full.

Returns: A n-dimensional numpy array.

Source code in vidgear/gears/camgear.py
def read(self):\n    \"\"\"\n    Extracts frames synchronously from monitored queue, while maintaining a fixed-length frame buffer in the memory,\n    and blocks the thread if the queue is full.\n\n    **Returns:** A n-dimensional numpy array.\n    \"\"\"\n    while self.__threaded_queue_mode and not self.__terminate.is_set():\n        return self.__queue.get(timeout=self.__thread_timeout)\n    # return current frame\n    # only after stream is read\n    return (\n        self.frame\n        if not self.__terminate.is_set()  # check if already terminated\n        and self.__stream_read.wait(timeout=self.__thread_timeout)  # wait for it\n        else None\n    )\n
"},{"location":"bonus/reference/camgear/#vidgear.gears.camgear.CamGear.start","title":"start(self)","text":"

Launches the internal Threaded Frames Extractor daemon.

Returns: A reference to the CamGear class object.

Source code in vidgear/gears/camgear.py
def start(self):\n    \"\"\"\n    Launches the internal *Threaded Frames Extractor* daemon.\n\n    **Returns:** A reference to the CamGear class object.\n    \"\"\"\n\n    self.__thread = Thread(target=self.__update, name=\"CamGear\", args=())\n    self.__thread.daemon = True\n    self.__thread.start()\n    return self\n
"},{"location":"bonus/reference/camgear/#vidgear.gears.camgear.CamGear.stop","title":"stop(self)","text":"

Safely terminates the thread, and release the multi-threaded resources.

Source code in vidgear/gears/camgear.py
def stop(self):\n    \"\"\"\n    Safely terminates the thread, and release the multi-threaded resources.\n    \"\"\"\n    self.__logging and logger.debug(\"Terminating processes.\")\n    # terminate Threaded queue mode separately\n    self.__threaded_queue_mode = False\n\n    # indicate that the thread\n    # should be terminated immediately\n    self.__stream_read.set()\n    self.__terminate.set()\n\n    # wait until stream resources are released (producer thread might be still grabbing frame)\n    if self.__thread is not None:\n        if not (self.__queue is None):\n            while not self.__queue.empty():\n                try:\n                    self.__queue.get_nowait()\n                except queue.Empty:\n                    continue\n                self.__queue.task_done()\n        self.__thread.join()\n
"},{"location":"bonus/reference/helper/","title":"Helper Methods","text":""},{"location":"bonus/reference/helper/#vidgear.gears.helper.logger_handler--logger_handler","title":"logger_handler","text":"

Returns the logger handler

Returns: A logger handler

Source code in vidgear/gears/helper.py
def logger_handler():\n    \"\"\"\n    ## logger_handler\n\n    Returns the logger handler\n\n    **Returns:** A logger handler\n    \"\"\"\n    # logging formatter\n    formatter = ColoredFormatter(\n        \"{green}{asctime}{reset} :: {bold_purple}{name:^13}{reset} :: {log_color}{levelname:^8}{reset} :: {bold_white}{message}\",\n        datefmt=\"%H:%M:%S\",\n        reset=True,\n        log_colors={\n            \"INFO\": \"bold_cyan\",\n            \"DEBUG\": \"bold_yellow\",\n            \"WARNING\": \"bold_red,fg_thin_yellow\",\n            \"ERROR\": \"bold_red\",\n            \"CRITICAL\": \"bold_red,bg_white\",\n        },\n        style=\"{\",\n    )\n    # check if VIDGEAR_LOGFILE defined\n    file_mode = os.environ.get(\"VIDGEAR_LOGFILE\", False)\n    # define handler\n    handler = log.StreamHandler()\n    if file_mode and isinstance(file_mode, str):\n        file_path = os.path.abspath(file_mode)\n        if (os.name == \"nt\" or os.access in os.supports_effective_ids) and os.access(\n            os.path.dirname(file_path), os.W_OK\n        ):\n            file_path = (\n                os.path.join(file_path, \"vidgear.log\")\n                if os.path.isdir(file_path)\n                else file_path\n            )\n            handler = log.FileHandler(file_path, mode=\"a\")\n            formatter = log.Formatter(\n                \"{asctime} :: {name} :: {levelname} :: {message}\",\n                datefmt=\"%H:%M:%S\",\n                style=\"{\",\n            )\n\n    handler.setFormatter(formatter)\n    return handler\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.check_CV_version--check_cv_version","title":"check_CV_version","text":"

Returns: OpenCV's version first bit

Source code in vidgear/gears/helper.py
def check_CV_version():\n    \"\"\"\n    ## check_CV_version\n\n    **Returns:** OpenCV's version first bit\n    \"\"\"\n    if parse_version(cv2.__version__) >= parse_version(\"4\"):\n        return 4\n    else:\n        return 3\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.check_gstreamer_support--check_gstreamer_support","title":"check_gstreamer_support","text":"

Checks whether OpenCV is compiled with Gstreamer(>=1.0.0) support.

Parameters:

Name Type Description Default logging bool

enables logging for its operations

False

Returns: A Boolean value

Source code in vidgear/gears/helper.py
def check_gstreamer_support(logging=False):\n    \"\"\"\n    ## check_gstreamer_support\n\n    Checks whether OpenCV is compiled with Gstreamer(`>=1.0.0`) support.\n\n    Parameters:\n        logging (bool): enables logging for its operations\n\n    **Returns:** A Boolean value\n    \"\"\"\n    raw = cv2.getBuildInformation()\n    gst = [\n        x.strip()\n        for x in raw.split(\"\\n\")\n        if x and re.search(r\"GStreamer[,-:]+\\s*(?:YES|NO)\", x)\n    ]\n    if gst and \"YES\" in gst[0]:\n        version = re.search(r\"(\\d+\\.)?(\\d+\\.)?(\\*|\\d+)\", gst[0])\n        logging and logger.debug(\"Found GStreamer version:{}\".format(version[0]))\n        return version[0] >= \"1.0.0\"\n    else:\n        logger.warning(\"GStreamer not found!\")\n        return False\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.get_supported_resolution--get_supported_resolution","title":"get_supported_resolution","text":"

Parameters:

Name Type Description Default value string

value to be validated

required logging bool

enables logging for its operations

False

Returns: Valid stream resolution

Source code in vidgear/gears/helper.py
def get_supported_resolution(value, logging=False):\n    \"\"\"\n    ## get_supported_resolution\n\n    Parameters:\n        value (string): value to be validated\n        logging (bool): enables logging for its operations\n\n    **Returns:** Valid stream resolution\n    \"\"\"\n    # default to best\n    stream_resolution = \"best\"\n    supported_stream_qualities = [\n        \"144p\",\n        \"240p\",\n        \"360p\",\n        \"480p\",\n        \"720p\",\n        \"1080p\",\n        \"1440p\",\n        \"2160p\",\n        \"4320p\",\n        \"worst\",\n        \"best\",\n    ]\n    if isinstance(value, str):\n        if value.strip().lower() in supported_stream_qualities:\n            stream_resolution = value.strip().lower()\n            logging and logger.debug(\n                \"Selecting `{}` resolution for streams.\".format(stream_resolution)\n            )\n        else:\n            logger.warning(\n                \"Specified stream-resolution `{}` is not supported. Reverting to `best`!\".format(\n                    value\n                )\n            )\n    else:\n        logger.warning(\n            \"Specified stream-resolution `{}` is Invalid. Reverting to `best`!\".format(\n                value\n            )\n        )\n    return stream_resolution\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.dimensions_to_resolutions--dimensions_to_resolutions","title":"dimensions_to_resolutions","text":"

Parameters:

Name Type Description Default value list

list of dimensions (e.g. 640x360)

required

Returns: list of resolutions (e.g. 360p)

Source code in vidgear/gears/helper.py
def dimensions_to_resolutions(value):\n    \"\"\"\n    ## dimensions_to_resolutions\n\n    Parameters:\n        value (list): list of dimensions (e.g. `640x360`)\n\n    **Returns:** list of resolutions (e.g. `360p`)\n    \"\"\"\n    supported_resolutions = {\n        \"256x144\": \"144p\",\n        \"426x240\": \"240p\",\n        \"640x360\": \"360p\",\n        \"854x480\": \"480p\",\n        \"1280x720\": \"720p\",\n        \"1920x1080\": \"1080p\",\n        \"2560x1440\": \"1440p\",\n        \"3840x2160\": \"2160p\",\n        \"7680x4320\": \"4320p\",\n    }\n    return (\n        list(map(supported_resolutions.get, value, value))\n        if isinstance(value, list)\n        else []\n    )\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.mkdir_safe--mkdir_safe","title":"mkdir_safe","text":"

Safely creates directory at given path.

Parameters:

Name Type Description Default dir_path string

path to the directory

required logging bool

enables logging for its operations

False Source code in vidgear/gears/helper.py
def mkdir_safe(dir_path, logging=False):\n    \"\"\"\n    ## mkdir_safe\n\n    Safely creates directory at given path.\n\n    Parameters:\n        dir_path (string): path to the directory\n        logging (bool): enables logging for its operations\n\n    \"\"\"\n    try:\n        os.makedirs(dir_path)\n        logging and logger.debug(\"Created directory at `{}`\".format(dir_path))\n    except (OSError, IOError) as e:\n        if e.errno != errno.EACCES and e.errno != errno.EEXIST:\n            raise\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.delete_ext_safe--delete_ext_safe","title":"delete_ext_safe","text":"

Safely deletes files with given extensions at given path.

Parameters:

Name Type Description Default dir_path string

path to the directory

required extensions list

list of extensions to be deleted

[] logging bool

enables logging for its operations

False Source code in vidgear/gears/helper.py
def delete_ext_safe(dir_path, extensions=[], logging=False):\n    \"\"\"\n    ## delete_ext_safe\n\n    Safely deletes files with given extensions at given path.\n\n    Parameters:\n        dir_path (string): path to the directory\n        extensions (list): list of extensions to be deleted\n        logging (bool): enables logging for its operations\n\n    \"\"\"\n    if not extensions or not os.path.exists(dir_path):\n        logger.warning(\"Invalid input provided for deleting!\")\n        return\n\n    logger.critical(\"Clearing Assets at `{}`!\".format(dir_path))\n\n    for ext in extensions:\n        if len(ext) == 2:\n            files_ext = [\n                os.path.join(dir_path, f)\n                for f in os.listdir(dir_path)\n                if f.startswith(ext[0]) and f.endswith(ext[1])\n            ]\n        else:\n            files_ext = [\n                os.path.join(dir_path, f)\n                for f in os.listdir(dir_path)\n                if f.endswith(ext)\n            ]\n        for file in files_ext:\n            delete_file_safe(file)\n            logging and logger.debug(\"Deleted file: `{}`\".format(file))\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.capPropId--cappropid","title":"capPropId","text":"

Retrieves the OpenCV property's Integer(Actual) value from string.

Parameters:

Name Type Description Default property string

inputs OpenCV property as string.

required logging bool

enables logging for its operations

True

Returns: Resultant integer value.

Source code in vidgear/gears/helper.py
def capPropId(property, logging=True):\n    \"\"\"\n    ## capPropId\n\n    Retrieves the OpenCV property's Integer(Actual) value from string.\n\n    Parameters:\n        property (string): inputs OpenCV property as string.\n        logging (bool): enables logging for its operations\n\n    **Returns:** Resultant integer value.\n    \"\"\"\n    integer_value = 0\n    try:\n        integer_value = getattr(cv2, property)\n    except Exception as e:\n        logging and logger.exception(str(e))\n        logger.critical(\"`{}` is not a valid OpenCV property!\".format(property))\n        return None\n    return integer_value\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.reducer--reducer","title":"reducer","text":"

Reduces frame size by given percentage

Parameters:

Name Type Description Default frame numpy.ndarray

inputs numpy array(frame).

None percentage int/float

inputs size-reduction percentage.

0 interpolation int

Change resize interpolation.

4

Returns: A reduced numpy ndarray array.

Source code in vidgear/gears/helper.py
def reducer(frame=None, percentage=0, interpolation=cv2.INTER_LANCZOS4):\n    \"\"\"\n    ## reducer\n\n    Reduces frame size by given percentage\n\n    Parameters:\n        frame (numpy.ndarray): inputs numpy array(frame).\n        percentage (int/float): inputs size-reduction percentage.\n        interpolation (int): Change resize interpolation.\n\n    **Returns:**  A reduced numpy ndarray array.\n    \"\"\"\n    # check if frame is valid\n    if frame is None:\n        raise ValueError(\"[Helper:ERROR] :: Input frame cannot be NoneType!\")\n\n    # check if valid reduction percentage is given\n    if not (percentage > 0 and percentage < 90):\n        raise ValueError(\n            \"[Helper:ERROR] :: Given frame-size reduction percentage is invalid, Kindly refer docs.\"\n        )\n\n    if not (isinstance(interpolation, int)):\n        raise ValueError(\n            \"[Helper:ERROR] :: Given interpolation is invalid, Kindly refer docs.\"\n        )\n\n    # grab the frame size\n    (height, width) = frame.shape[:2]\n\n    # calculate the ratio of the width from percentage\n    reduction = ((100 - percentage) / 100) * width\n    ratio = reduction / float(width)\n    # construct the dimensions\n    dimensions = (int(reduction), int(height * ratio))\n\n    # return the resized frame\n    return cv2.resize(frame, dimensions, interpolation=interpolation)\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.create_blank_frame--create_blank_frame","title":"create_blank_frame","text":"

Create blank frames of given frame size with text

Parameters:

Name Type Description Default frame numpy.ndarray

inputs numpy array(frame).

None text str

Text to be written on frame.

''

Returns: A reduced numpy ndarray array.

Source code in vidgear/gears/helper.py
def create_blank_frame(frame=None, text=\"\", logging=False):\n    \"\"\"\n    ## create_blank_frame\n\n    Create blank frames of given frame size with text\n\n    Parameters:\n        frame (numpy.ndarray): inputs numpy array(frame).\n        text (str): Text to be written on frame.\n    **Returns:**  A reduced numpy ndarray array.\n    \"\"\"\n    # check if frame is valid\n    if frame is None or not (isinstance(frame, np.ndarray)):\n        raise ValueError(\"[Helper:ERROR] :: Input frame is invalid!\")\n    # grab the frame size\n    (height, width) = frame.shape[:2]\n    # create blank frame\n    blank_frame = np.zeros(frame.shape, frame.dtype)\n    # setup text\n    if text and isinstance(text, str):\n        logging and logger.debug(\"Adding text: {}\".format(text))\n        # setup font\n        font = cv2.FONT_HERSHEY_SCRIPT_COMPLEX\n        # get boundary of this text\n        fontScale = min(height, width) / (25 / 0.25)\n        textsize = cv2.getTextSize(text, font, fontScale, 5)[0]\n        # get coords based on boundary\n        textX = (width - textsize[0]) // 2\n        textY = (height + textsize[1]) // 2\n        # put text\n        cv2.putText(\n            blank_frame, text, (textX, textY), font, fontScale, (125, 125, 125), 6\n        )\n\n    # return frame\n    return blank_frame\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.dict2Args--dict2args","title":"dict2Args","text":"

Converts dictionary attributes to list(args)

Parameters:

Name Type Description Default param_dict dict

Parameters dictionary

required

Returns: Arguments list

Source code in vidgear/gears/helper.py
def dict2Args(param_dict):\n    \"\"\"\n    ## dict2Args\n\n    Converts dictionary attributes to list(args)\n\n    Parameters:\n        param_dict (dict): Parameters dictionary\n\n    **Returns:** Arguments list\n    \"\"\"\n    args = []\n    for key in param_dict.keys():\n        if key in [\"-clones\"] or key.startswith(\"-core\"):\n            if isinstance(param_dict[key], list):\n                args.extend(param_dict[key])\n            else:\n                logger.warning(\n                    \"{} with invalid datatype:`{}`, Skipped!\".format(\n                        \"Core parameter\" if key.startswith(\"-core\") else \"Clone\",\n                        param_dict[key],\n                    )\n                )\n        else:\n            args.append(key)\n            args.append(str(param_dict[key]))\n    return args\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.get_valid_ffmpeg_path--get_valid_ffmpeg_path","title":"get_valid_ffmpeg_path","text":"

Validate the given FFmpeg path/binaries, and returns a valid FFmpeg executable path.

Parameters:

Name Type Description Default custom_ffmpeg string

path to custom FFmpeg executables

'' is_windows boolean

is running on Windows OS?

False ffmpeg_download_path string

FFmpeg static binaries download location (Windows only)

'' logging bool

enables logging for its operations

False

Returns: A valid FFmpeg executable path string.

Source code in vidgear/gears/helper.py
def get_valid_ffmpeg_path(\n    custom_ffmpeg=\"\", is_windows=False, ffmpeg_download_path=\"\", logging=False\n):\n    \"\"\"\n    ## get_valid_ffmpeg_path\n\n    Validate the given FFmpeg path/binaries, and returns a valid FFmpeg executable path.\n\n    Parameters:\n        custom_ffmpeg (string): path to custom FFmpeg executables\n        is_windows (boolean): is running on Windows OS?\n        ffmpeg_download_path (string): FFmpeg static binaries download location _(Windows only)_\n        logging (bool): enables logging for its operations\n\n    **Returns:** A valid FFmpeg executable path string.\n    \"\"\"\n    final_path = \"\"\n    if is_windows:\n        # checks if current os is windows\n        if custom_ffmpeg:\n            # if custom FFmpeg path is given assign to local variable\n            final_path += custom_ffmpeg\n        else:\n            # otherwise auto-download them\n            try:\n                if not (ffmpeg_download_path):\n                    # otherwise save to Temp Directory\n                    import tempfile\n\n                    ffmpeg_download_path = tempfile.gettempdir()\n\n                logging and logger.debug(\n                    \"FFmpeg Windows Download Path: {}\".format(ffmpeg_download_path)\n                )\n\n                # download Binaries\n                os_bit = (\n                    (\"win64\" if platform.machine().endswith(\"64\") else \"win32\")\n                    if is_windows\n                    else \"\"\n                )\n                _path = download_ffmpeg_binaries(\n                    path=ffmpeg_download_path, os_windows=is_windows, os_bit=os_bit\n                )\n                # assign to local variable\n                final_path += _path\n\n            except Exception as e:\n                # log if any error occurred\n                logger.exception(str(e))\n                logger.error(\n                    \"Error in downloading FFmpeg binaries, Check your network and Try again!\"\n                )\n                return False\n\n        if os.path.isfile(final_path):\n            # check if valid FFmpeg file exist\n            pass\n        elif os.path.isfile(os.path.join(final_path, \"ffmpeg.exe\")):\n            # check if FFmpeg directory exists, if does, then check for valid file\n            final_path = os.path.join(final_path, \"ffmpeg.exe\")\n        else:\n            # else return False\n            logging and logger.debug(\n                \"No valid FFmpeg executables found at Custom FFmpeg path!\"\n            )\n            return False\n    else:\n        # otherwise perform test for Unix\n        if custom_ffmpeg:\n            # if custom FFmpeg path is given assign to local variable\n            if os.path.isfile(custom_ffmpeg):\n                # check if valid FFmpeg file exist\n                final_path += custom_ffmpeg\n            elif os.path.isfile(os.path.join(custom_ffmpeg, \"ffmpeg\")):\n                # check if FFmpeg directory exists, if does, then check for valid file\n                final_path = os.path.join(custom_ffmpeg, \"ffmpeg\")\n            else:\n                # else return False\n                logging and logger.debug(\n                    \"No valid FFmpeg executables found at Custom FFmpeg path!\"\n                )\n                return False\n        else:\n            # otherwise assign ffmpeg binaries from system\n            final_path += \"ffmpeg\"\n\n    logging and logger.debug(\"Final FFmpeg Path: {}\".format(final_path))\n\n    # Final Auto-Validation for FFmeg Binaries. returns final path if test is passed\n    return final_path if validate_ffmpeg(final_path, logging=logging) else False\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.download_ffmpeg_binaries--download_ffmpeg_binaries","title":"download_ffmpeg_binaries","text":"

Generates FFmpeg Static Binaries for windows(if not available)

Parameters:

Name Type Description Default path string

path for downloading custom FFmpeg executables

required os_windows boolean

is running on Windows OS?

False os_bit string

32-bit or 64-bit OS?

''

Returns: A valid FFmpeg executable path string.

Source code in vidgear/gears/helper.py
def download_ffmpeg_binaries(path, os_windows=False, os_bit=\"\"):\n    \"\"\"\n    ## download_ffmpeg_binaries\n\n    Generates FFmpeg Static Binaries for windows(if not available)\n\n    Parameters:\n        path (string): path for downloading custom FFmpeg executables\n        os_windows (boolean): is running on Windows OS?\n        os_bit (string): 32-bit or 64-bit OS?\n\n    **Returns:** A valid FFmpeg executable path string.\n    \"\"\"\n    final_path = \"\"\n    if os_windows and os_bit:\n        # initialize with available FFmpeg Static Binaries GitHub Server\n        file_url = \"https://github.com/abhiTronix/FFmpeg-Builds/releases/latest/download/ffmpeg-static-{}-gpl.zip\".format(\n            os_bit\n        )\n\n        file_name = os.path.join(\n            os.path.abspath(path), \"ffmpeg-static-{}-gpl.zip\".format(os_bit)\n        )\n        file_path = os.path.join(\n            os.path.abspath(path),\n            \"ffmpeg-static-{}-gpl/bin/ffmpeg.exe\".format(os_bit),\n        )\n        base_path, _ = os.path.split(file_name)  # extract file base path\n        # check if file already exists\n        if os.path.isfile(file_path):\n            final_path += file_path  # skip download if does\n        else:\n            # import libs\n            import zipfile\n\n            # check if given path has write access\n            assert os.access(path, os.W_OK), (\n                \"[Helper:ERROR] :: Permission Denied, Cannot write binaries to directory = \"\n                + path\n            )\n            # remove leftovers if exists\n            os.path.isfile(file_name) and delete_file_safe(file_name)\n            # download and write file to the given path\n            with open(file_name, \"wb\") as f:\n                logger.debug(\n                    \"No Custom FFmpeg path provided. Auto-Installing FFmpeg static binaries from GitHub Mirror now. Please wait...\"\n                )\n                # create session\n                with requests.Session() as http:\n                    # setup retry strategy\n                    retries = Retry(\n                        total=3,\n                        backoff_factor=1,\n                        status_forcelist=[429, 500, 502, 503, 504],\n                    )\n                    # Mount it for https usage\n                    adapter = TimeoutHTTPAdapter(timeout=2.0, max_retries=retries)\n                    http.mount(\"https://\", adapter)\n                    response = http.get(file_url, stream=True)\n                    response.raise_for_status()\n                    total_length = (\n                        response.headers.get(\"content-length\")\n                        if \"content-length\" in response.headers\n                        else len(response.content)\n                    )\n                    assert not (\n                        total_length is None\n                    ), \"[Helper:ERROR] :: Failed to retrieve files, check your Internet connectivity!\"\n                    bar = tqdm(total=int(total_length), unit=\"B\", unit_scale=True)\n                    for data in response.iter_content(chunk_size=4096):\n                        f.write(data)\n                        len(data) > 0 and bar.update(len(data))\n                    bar.close()\n            logger.debug(\"Extracting executables.\")\n            with zipfile.ZipFile(file_name, \"r\") as zip_ref:\n                zip_fname, _ = os.path.split(zip_ref.infolist()[0].filename)\n                zip_ref.extractall(base_path)\n            # perform cleaning\n            delete_file_safe(file_name)\n            logger.debug(\"FFmpeg binaries for Windows configured successfully!\")\n            final_path += file_path\n    # return final path\n    return final_path\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.validate_ffmpeg--validate_ffmpeg","title":"validate_ffmpeg","text":"

Validate FFmeg Binaries. returns True if tests are passed.

Parameters:

Name Type Description Default path string

absolute path of FFmpeg binaries

required logging bool

enables logging for its operations

False

Returns: A boolean value, confirming whether tests passed, or not?.

Source code in vidgear/gears/helper.py
def validate_ffmpeg(path, logging=False):\n    \"\"\"\n    ## validate_ffmpeg\n\n    Validate FFmeg Binaries. returns `True` if tests are passed.\n\n    Parameters:\n        path (string): absolute path of FFmpeg binaries\n        logging (bool): enables logging for its operations\n\n    **Returns:** A boolean value, confirming whether tests passed, or not?.\n    \"\"\"\n    try:\n        # get the FFmpeg version\n        version = check_output([path, \"-version\"])\n        firstline = version.split(b\"\\n\")[0]\n        version = firstline.split(b\" \")[2].strip()\n        # log if test are passed\n        logging and logger.info(\"FFmpeg validity Test Passed!\")\n        logging and logger.debug(\n            \"Found valid FFmpeg Version: `{}` installed on this system\".format(version)\n        )\n    except Exception as e:\n        # log if test are failed\n        logging and logger.exception(str(e))\n        logger.error(\"FFmpeg validity Test Failed!\")\n        return False\n    return True\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.check_output--check_output","title":"check_output","text":"

Returns stdin output from subprocess module

Source code in vidgear/gears/helper.py
def check_output(*args, **kwargs):\n    \"\"\"\n    ## check_output\n\n    Returns stdin output from subprocess module\n    \"\"\"\n    # import libs\n    import subprocess as sp\n\n    # workaround for python bug: https://bugs.python.org/issue37380\n    if platform.system() == \"Windows\":\n        # see comment https://bugs.python.org/msg370334\n        sp._cleanup = lambda: None\n\n    # handle additional params\n    retrieve_stderr = kwargs.pop(\"force_retrieve_stderr\", False)\n\n    # execute command in subprocess\n    process = sp.Popen(\n        stdout=sp.PIPE,\n        stderr=sp.DEVNULL if not (retrieve_stderr) else sp.PIPE,\n        *args,\n        **kwargs,\n    )\n    output, stderr = process.communicate()\n    retcode = process.poll()\n\n    # handle return code\n    if retcode and not (retrieve_stderr):\n        cmd = kwargs.get(\"args\")\n        if cmd is None:\n            cmd = args[0]\n        error = sp.CalledProcessError(retcode, cmd)\n        error.output = output\n        raise error\n\n    return output if not (retrieve_stderr) else stderr\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.generate_auth_certificates--generate_auth_certificates","title":"generate_auth_certificates","text":"

Auto-Generates, and Auto-validates CURVE ZMQ key-pairs for NetGear API's Secure Mode.

Parameters:

Name Type Description Default path string

path for generating CURVE key-pairs

required overwrite boolean

overwrite existing key-pairs or not?

False logging bool

enables logging for its operations

False

Returns: A valid CURVE key-pairs path as string.

Source code in vidgear/gears/helper.py
def generate_auth_certificates(path, overwrite=False, logging=False):\n    \"\"\"\n    ## generate_auth_certificates\n\n    Auto-Generates, and Auto-validates CURVE ZMQ key-pairs for NetGear API's Secure Mode.\n\n    Parameters:\n        path (string): path for generating CURVE key-pairs\n        overwrite (boolean): overwrite existing key-pairs or not?\n        logging (bool): enables logging for its operations\n\n    **Returns:** A valid CURVE key-pairs path as string.\n    \"\"\"\n    # import necessary lib\n    import zmq.auth\n\n    # check if path corresponds to vidgear only\n    if os.path.basename(path) != \".vidgear\":\n        path = os.path.join(path, \".vidgear\")\n\n    # generate keys dir\n    keys_dir = os.path.join(path, \"keys\")\n    mkdir_safe(keys_dir, logging=logging)\n\n    # generate separate public and private key dirs\n    public_keys_dir = os.path.join(keys_dir, \"public_keys\")\n    secret_keys_dir = os.path.join(keys_dir, \"private_keys\")\n\n    # check if overwriting is allowed\n    if overwrite:\n        # delete previous certificates\n        for dirs in [public_keys_dir, secret_keys_dir]:\n            if os.path.exists(dirs):\n                shutil.rmtree(dirs)\n            mkdir_safe(dirs, logging=logging)\n\n        # generate new keys\n        server_public_file, server_secret_file = zmq.auth.create_certificates(\n            keys_dir, \"server\"\n        )\n        client_public_file, client_secret_file = zmq.auth.create_certificates(\n            keys_dir, \"client\"\n        )\n\n        # move keys to their appropriate directory respectively\n        for key_file in os.listdir(keys_dir):\n            if key_file.endswith(\".key\"):\n                shutil.move(os.path.join(keys_dir, key_file), public_keys_dir)\n            elif key_file.endswith(\".key_secret\"):\n                shutil.move(os.path.join(keys_dir, key_file), secret_keys_dir)\n            else:\n                # clean redundant keys if present\n                redundant_key = os.path.join(keys_dir, key_file)\n                if os.path.isfile(redundant_key):\n                    delete_file_safe(redundant_key)\n    else:\n        # otherwise validate available keys\n        status_public_keys = validate_auth_keys(public_keys_dir, \".key\")\n        status_private_keys = validate_auth_keys(secret_keys_dir, \".key_secret\")\n\n        # check if all valid keys are found\n        if status_private_keys and status_public_keys:\n            return (keys_dir, secret_keys_dir, public_keys_dir)\n\n        # check if valid public keys are found\n        if not (status_public_keys):\n            mkdir_safe(public_keys_dir, logging=logging)\n\n        # check if valid private keys are found\n        if not (status_private_keys):\n            mkdir_safe(secret_keys_dir, logging=logging)\n\n        # generate new keys\n        server_public_file, server_secret_file = zmq.auth.create_certificates(\n            keys_dir, \"server\"\n        )\n        client_public_file, client_secret_file = zmq.auth.create_certificates(\n            keys_dir, \"client\"\n        )\n\n        # move keys to their appropriate directory respectively\n        for key_file in os.listdir(keys_dir):\n            if key_file.endswith(\".key\") and not (status_public_keys):\n                shutil.move(\n                    os.path.join(keys_dir, key_file), os.path.join(public_keys_dir, \".\")\n                )\n            elif key_file.endswith(\".key_secret\") and not (status_private_keys):\n                shutil.move(\n                    os.path.join(keys_dir, key_file), os.path.join(secret_keys_dir, \".\")\n                )\n            else:\n                # clean redundant keys if present\n                redundant_key = os.path.join(keys_dir, key_file)\n                if os.path.isfile(redundant_key):\n                    delete_file_safe(redundant_key)\n\n    # validate newly generated keys\n    status_public_keys = validate_auth_keys(public_keys_dir, \".key\")\n    status_private_keys = validate_auth_keys(secret_keys_dir, \".key_secret\")\n\n    # raise error is validation test fails\n    if not (status_private_keys) or not (status_public_keys):\n        raise RuntimeError(\n            \"[Helper:ERROR] :: Unable to generate valid ZMQ authentication certificates at `{}`!\".format(\n                keys_dir\n            )\n        )\n\n    # finally return valid key paths\n    return (keys_dir, secret_keys_dir, public_keys_dir)\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.validate_audio--validate_audio","title":"validate_audio","text":"

Validates audio by retrieving audio-bitrate from file.

Parameters:

Name Type Description Default path string

absolute path of FFmpeg binaries

required source string/list

source to be validated.

None

Returns: A string value, confirming whether audio is present, or not?.

Source code in vidgear/gears/helper.py
def validate_audio(path, source=None):\n    \"\"\"\n    ## validate_audio\n\n    Validates audio by retrieving audio-bitrate from file.\n\n    Parameters:\n        path (string): absolute path of FFmpeg binaries\n        source (string/list): source to be validated.\n\n    **Returns:** A string value, confirming whether audio is present, or not?.\n    \"\"\"\n    if source is None or not (source):\n        logger.warning(\"Audio input source is empty!\")\n        return \"\"\n\n    # create ffmpeg command\n    cmd = [path, \"-hide_banner\"] + (\n        source if isinstance(source, list) else [\"-i\", source]\n    )\n    # extract metadata\n    metadata = check_output(cmd, force_retrieve_stderr=True)\n    # extract bitrate\n    audio_bitrate_meta = [\n        line.strip()\n        for line in metadata.decode(\"utf-8\").split(\"\\n\")\n        if \"Audio:\" in line\n    ]\n    audio_bitrate = (\n        re.findall(r\"([0-9]+)\\s(kb|mb|gb)\\/s\", audio_bitrate_meta[0])[-1]\n        if audio_bitrate_meta\n        else \"\"\n    )\n    # extract samplerate\n    audio_samplerate_metadata = [\n        line.strip()\n        for line in metadata.decode(\"utf-8\").split(\"\\n\")\n        if all(x in line for x in [\"Audio:\", \"Hz\"])\n    ]\n    audio_samplerate = (\n        re.findall(r\"[0-9]+\\sHz\", audio_samplerate_metadata[0])[0]\n        if audio_samplerate_metadata\n        else \"\"\n    )\n    # format into actual readable bitrate value\n    if audio_bitrate:\n        # return bitrate directly\n        return \"{}{}\".format(int(audio_bitrate[0].strip()), audio_bitrate[1].strip()[0])\n    elif audio_samplerate:\n        # convert samplerate to bitrate first\n        sample_rate_value = int(audio_samplerate.split(\" \")[0])\n        channels_value = 1 if \"mono\" in audio_samplerate_metadata[0] else 2\n        bit_depth_value = re.findall(\n            r\"(u|s|f)([0-9]+)(le|be)\", audio_samplerate_metadata[0]\n        )[0][1]\n        return (\n            (\n                str(\n                    get_audio_bitrate(\n                        sample_rate_value, channels_value, int(bit_depth_value)\n                    )\n                )\n                + \"k\"\n            )\n            if bit_depth_value\n            else \"\"\n        )\n    else:\n        return \"\"\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.extract_time--extract_time","title":"extract_time","text":"

Extract time from give string value.

Parameters:

Name Type Description Default value string

string value.

required

Returns: Time (in seconds) as integer.

Source code in vidgear/gears/helper.py
def extract_time(value):\n    \"\"\"\n    ## extract_time\n\n    Extract time from give string value.\n\n    Parameters:\n        value (string): string value.\n\n    **Returns:** Time _(in seconds)_ as integer.\n    \"\"\"\n    if not (value):\n        logger.warning(\"Value is empty!\")\n        return 0\n    else:\n        stripped_data = value.strip()\n        t_duration = re.findall(r\"\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{2})?\", stripped_data)\n        return (\n            sum(\n                float(x) * 60**i\n                for i, x in enumerate(reversed(t_duration[0].split(\":\")))\n            )\n            if t_duration\n            else 0\n        )\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.validate_video--validate_video","title":"validate_video","text":"

Validates video by retrieving resolution/size and framerate from file.

Parameters:

Name Type Description Default path string

absolute path of FFmpeg binaries

required video_path string

absolute path to Video.

None

Returns: A dictionary of retieved Video resolution (as tuple(width, height)) and framerate (as float).

Source code in vidgear/gears/helper.py
def validate_video(path, video_path=None, logging=False):\n    \"\"\"\n    ## validate_video\n\n    Validates video by retrieving resolution/size and framerate from file.\n\n    Parameters:\n        path (string): absolute path of FFmpeg binaries\n        video_path (string): absolute path to Video.\n\n    **Returns:** A dictionary of retieved Video resolution _(as tuple(width, height))_ and framerate _(as float)_.\n    \"\"\"\n    if video_path is None or not (video_path):\n        logger.warning(\"Video path is empty!\")\n        return None\n\n    # extract metadata\n    metadata = check_output(\n        [path, \"-hide_banner\", \"-i\", video_path], force_retrieve_stderr=True\n    )\n    # clean and search\n    stripped_data = [x.decode(\"utf-8\").strip() for x in metadata.split(b\"\\n\")]\n    logging and logger.debug(stripped_data)\n    result = {}\n    for data in stripped_data:\n        output_a = re.findall(r\"([1-9]\\d+)x([1-9]\\d+)\", data)\n        output_b = re.findall(r\"\\d+(?:\\.\\d+)?\\sfps\", data)\n        if len(result) == 2:\n            break\n        if output_b and not \"framerate\" in result:\n            result[\"framerate\"] = re.findall(r\"[\\d\\.\\d]+\", output_b[0])[0]\n        if output_a and not \"resolution\" in result:\n            result[\"resolution\"] = output_a[-1]\n\n    # return values\n    return result if (len(result) == 2) else None\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.is_valid_url--is_valid_url","title":"is_valid_url","text":"

Checks URL validity by testing its scheme against FFmpeg's supported protocols

Parameters:

Name Type Description Default path string

absolute path of FFmpeg binaries

required url string

URL to be validated

None logging bool

enables logging for its operations

False

Returns: A boolean value, confirming whether tests passed, or not?.

Source code in vidgear/gears/helper.py
def is_valid_url(path, url=None, logging=False):\n    \"\"\"\n    ## is_valid_url\n\n    Checks URL validity by testing its scheme against\n    FFmpeg's supported protocols\n\n    Parameters:\n        path (string): absolute path of FFmpeg binaries\n        url (string): URL to be validated\n        logging (bool): enables logging for its operations\n\n    **Returns:** A boolean value, confirming whether tests passed, or not?.\n    \"\"\"\n    if url is None or not (url):\n        logger.warning(\"URL is empty!\")\n        return False\n    # extract URL scheme\n    extracted_scheme_url = url.split(\"://\", 1)[0]\n    # extract all FFmpeg supported protocols\n    protocols = check_output([path, \"-hide_banner\", \"-protocols\"])\n    splitted = [x.decode(\"utf-8\").strip() for x in protocols.split(b\"\\n\")]\n    supported_protocols = splitted[splitted.index(\"Output:\") + 1 : len(splitted) - 1]\n    # RTSP is a demuxer somehow\n    # support both RTSP and RTSPS(over SSL)\n    supported_protocols += (\n        [\"rtsp\", \"rtsps\"] if \"rtsp\" in get_supported_demuxers(path) else []\n    )\n    # Test and return result whether scheme is supported\n    if extracted_scheme_url and extracted_scheme_url in supported_protocols:\n        logging and logger.debug(\n            \"URL scheme `{}` is supported by FFmpeg.\".format(extracted_scheme_url)\n        )\n        return True\n    else:\n        logger.warning(\n            \"URL scheme `{}` isn't supported by FFmpeg!\".format(extracted_scheme_url)\n        )\n        return False\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.import_dependency_safe--import_dependency_safe","title":"import_dependency_safe","text":"

Imports specified dependency safely. By default(error = raise), if a dependency is missing, an ImportError with a meaningful message will be raised. Otherwise if error = log a warning will be logged and on error = silent everything will be quit. But If a dependency is present, but older than specified, an error is raised if specified.

Parameters:

Name Type Description Default name string

name of dependency to be imported.

required error string

raise or Log or silence ImportError. Possible values are \"raise\", \"log\" and silent. Default is \"raise\".

'raise' pkg_name string

(Optional) package name of dependency(if different pip name). Otherwise name will be used.

None min_version string

(Optional) required minimum version of the dependency to be imported.

None custom_message string

(Optional) custom Import error message to be raised or logged.

None

Returns: The imported module, when found and the version is correct(if specified). Otherwise None.

Source code in vidgear/gears/helper.py
def import_dependency_safe(\n    name,\n    error=\"raise\",\n    pkg_name=None,\n    min_version=None,\n    custom_message=None,\n):\n    \"\"\"\n    ## import_dependency_safe\n\n    Imports specified dependency safely. By default(`error = raise`), if a dependency is missing,\n    an ImportError with a meaningful message will be raised. Otherwise if `error = log` a warning\n    will be logged and on `error = silent` everything will be quit. But If a dependency is present,\n    but older than specified, an error is raised if specified.\n\n    Parameters:\n        name (string): name of dependency to be imported.\n        error (string): raise or Log or silence ImportError. Possible values are `\"raise\"`, `\"log\"` and `silent`. Default is `\"raise\"`.\n        pkg_name (string): (Optional) package name of dependency(if different `pip` name). Otherwise `name` will be used.\n        min_version (string): (Optional) required minimum version of the dependency to be imported.\n        custom_message (string): (Optional) custom Import error message to be raised or logged.\n\n    **Returns:** The imported module, when found and the version is correct(if specified). Otherwise `None`.\n    \"\"\"\n    # check specified parameters\n    sub_class = \"\"\n    if not name or not isinstance(name, str):\n        return None\n    else:\n        # extract name in case of relative import\n        name = name.strip()\n        if name.startswith(\"from\"):\n            name = name.split(\" \")\n            name, sub_class = (name[1].strip(), name[-1].strip())\n\n    assert error in [\n        \"raise\",\n        \"log\",\n        \"silent\",\n    ], \"[Vidgear:ERROR] :: Invalid value at `error` parameter.\"\n\n    # specify package name of dependency(if defined). Otherwise use name\n    install_name = pkg_name if not (pkg_name is None) else name\n\n    # create message\n    msg = (\n        custom_message\n        if not (custom_message is None)\n        else \"Failed to find required dependency '{}'. Install it with  `pip install {}` command.\".format(\n            name, install_name\n        )\n    )\n    # try importing dependency\n    try:\n        module = importlib.import_module(name)\n        module = getattr(module, sub_class) if sub_class else module\n    except Exception as e:\n        if error == \"raise\":\n            if isinstance(e, ModuleNotFoundError):\n                # raise message\n                raise ModuleNotFoundError(msg) from None\n            else:\n                # raise error+message\n                raise ImportError(msg) from e\n        elif error == \"log\":\n            logger.error(msg, exc_info=sys.exc_info())\n            return None\n        else:\n            return None\n\n    # check if minimum required version\n    if not (min_version) is None:\n        # Handle submodules\n        parent_module = name.split(\".\")[0]\n        if parent_module != name:\n            # grab parent module\n            module_to_get = sys.modules[parent_module]\n        else:\n            module_to_get = module\n        # extract version\n        version = get_module_version(module_to_get)\n        # verify\n        if parse_version(version) < parse_version(min_version):\n            # create message\n            msg = \"\"\"Unsupported version '{}' found. Vidgear requires '{}' dependency installed with version '{}' or greater. \n            Update it with  `pip install -U {}` command.\"\"\".format(\n                parent_module, min_version, version, install_name\n            )\n            # handle errors.\n            if error == \"silent\":\n                return None\n            else:\n                # raise\n                raise ImportError(msg)\n\n    return module\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.get_video_bitrate--get_video_bitrate","title":"get_video_bitrate","text":"

Calculate optimum Bitrate from resolution, framerate, bits-per-pixels values

Parameters:

Name Type Description Default width int

video-width

required height int

video-height

required fps float

video-framerate

required bpp float

bit-per-pixels value

required

Returns: Video bitrate (in Kbps) as integer.

Source code in vidgear/gears/helper.py
def get_video_bitrate(width, height, fps, bpp):\n    \"\"\"\n    ## get_video_bitrate\n\n    Calculate optimum Bitrate from resolution, framerate, bits-per-pixels values\n\n    Parameters:\n        width (int): video-width\n        height (int): video-height\n        fps (float): video-framerate\n        bpp (float): bit-per-pixels value\n\n    **Returns:** Video bitrate _(in Kbps)_ as integer.\n    \"\"\"\n    return round((width * height * bpp * fps) / 1000)\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.check_WriteAccess--check_writeaccess","title":"check_WriteAccess","text":"

Checks whether given path directory has Write-Access.

Parameters:

Name Type Description Default path string

absolute path of directory

required is_windows boolean

is running on Windows OS?

False logging bool

enables logging for its operations

False

Returns: A boolean value, confirming whether Write-Access available, or not?.

Source code in vidgear/gears/helper.py
def check_WriteAccess(path, is_windows=False, logging=False):\n    \"\"\"\n    ## check_WriteAccess\n\n    Checks whether given path directory has Write-Access.\n\n    Parameters:\n        path (string): absolute path of directory\n        is_windows (boolean): is running on Windows OS?\n        logging (bool): enables logging for its operations\n\n    **Returns:** A boolean value, confirming whether Write-Access available, or not?.\n    \"\"\"\n    # check if path exists\n    dirpath = Path(path)\n    try:\n        if not (dirpath.exists() and dirpath.is_dir()):\n            logger.warning(\n                \"Specified directory `{}` doesn't exists or valid.\".format(path)\n            )\n            return False\n        else:\n            path = dirpath.resolve()\n    except:\n        return False\n    # check filepath on *nix systems\n    if not is_windows:\n        uid = os.geteuid()\n        gid = os.getegid()\n        s = os.stat(path)\n        mode = s[stat.ST_MODE]\n        return (\n            ((s[stat.ST_UID] == uid) and (mode & stat.S_IWUSR))\n            or ((s[stat.ST_GID] == gid) and (mode & stat.S_IWGRP))\n            or (mode & stat.S_IWOTH)\n        )\n    # otherwise, check filepath on windows\n    else:\n        write_accessible = False\n        temp_fname = os.path.join(path, \"temp.tmp\")\n        try:\n            fd = os.open(temp_fname, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)\n            os.close(fd)\n            write_accessible = True\n        except Exception as e:\n            if isinstance(e, PermissionError):\n                logger.error(\n                    \"You don't have adequate access rights to use `{}` directory!\".format(\n                        path\n                    )\n                )\n            logging and logger.exception(str(e))\n        finally:\n            delete_file_safe(temp_fname)\n        return write_accessible\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.check_open_port--check_open_port","title":"check_open_port","text":"

Checks whether specified port open at given IP address.

Parameters:

Name Type Description Default address string

given IP address.

required port int

check if port is open at given address.

22

Returns: A boolean value, confirming whether given port is open at given IP address.

Source code in vidgear/gears/helper.py
def check_open_port(address, port=22):\n    \"\"\"\n    ## check_open_port\n\n    Checks whether specified port open at given IP address.\n\n    Parameters:\n        address (string): given IP address.\n        port (int): check if port is open at given address.\n\n    **Returns:** A boolean value, confirming whether given port is open at given IP address.\n    \"\"\"\n    if not address:\n        return False\n    with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:\n        if sock.connect_ex((address, port)) == 0:\n            return True\n        else:\n            return False\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.delete_file_safe--delete_ext_safe","title":"delete_ext_safe","text":"

Safely deletes files at given path.

Parameters:

Name Type Description Default file_path string

path to the file

required Source code in vidgear/gears/helper.py
def delete_file_safe(file_path):\n    \"\"\"\n    ## delete_ext_safe\n\n    Safely deletes files at given path.\n\n    Parameters:\n        file_path (string): path to the file\n    \"\"\"\n    try:\n        dfile = Path(file_path)\n        dfile.unlink(missing_ok=True)\n    except Exception as e:\n        logger.exception(str(e))\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.get_supported_demuxers--get_supported_demuxers","title":"get_supported_demuxers","text":"

Find and returns FFmpeg's supported demuxers

Parameters:

Name Type Description Default path string

absolute path of FFmpeg binaries

required

Returns: List of supported demuxers.

Source code in vidgear/gears/helper.py
def get_supported_demuxers(path):\n    \"\"\"\n    ## get_supported_demuxers\n\n    Find and returns FFmpeg's supported demuxers\n\n    Parameters:\n        path (string): absolute path of FFmpeg binaries\n\n    **Returns:** List of supported demuxers.\n    \"\"\"\n    demuxers = check_output([path, \"-hide_banner\", \"-demuxers\"])\n    splitted = [x.decode(\"utf-8\").strip() for x in demuxers.split(b\"\\n\")]\n    split_index = [idx for idx, s in enumerate(splitted) if \"--\" in s][0]\n    supported_demuxers = splitted[split_index + 1 : len(splitted) - 1]\n    # compile regex\n    finder = re.compile(r\"\\s\\s[a-z0-9_,-]+\\s+\")\n    # find all outputs\n    outputs = finder.findall(\"\\n\".join(supported_demuxers))\n    # return output findings\n    return [o.strip() for o in outputs]\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.get_supported_vencoders--get_supported_vencoders","title":"get_supported_vencoders","text":"

Find and returns FFmpeg's supported video encoders

Parameters:

Name Type Description Default path string

absolute path of FFmpeg binaries

required

Returns: List of supported encoders.

Source code in vidgear/gears/helper.py
def get_supported_vencoders(path):\n    \"\"\"\n    ## get_supported_vencoders\n\n    Find and returns FFmpeg's supported video encoders\n\n    Parameters:\n        path (string): absolute path of FFmpeg binaries\n\n    **Returns:** List of supported encoders.\n    \"\"\"\n    encoders = check_output([path, \"-hide_banner\", \"-encoders\"])\n    splitted = encoders.split(b\"\\n\")\n    # extract video encoders\n    supported_vencoders = [\n        x.decode(\"utf-8\").strip()\n        for x in splitted[2 : len(splitted) - 1]\n        if x.decode(\"utf-8\").strip().startswith(\"V\")\n    ]\n    # compile regex\n    finder = re.compile(r\"[A-Z]*[\\.]+[A-Z]*\\s[a-z0-9_-]*\")\n    # find all outputs\n    outputs = finder.findall(\"\\n\".join(supported_vencoders))\n    # return output findings\n    return [[s for s in o.split(\" \")][-1] for o in outputs]\n
"},{"location":"bonus/reference/helper/#vidgear.gears.helper.validate_auth_keys--validate_auth_keys","title":"validate_auth_keys","text":"

Validates, and also maintains generated ZMQ CURVE Key-pairs.

Parameters:

Name Type Description Default path string

path of generated CURVE key-pairs

required extension string

type of key-pair to be validated

required

Returns: A boolean value, confirming whether tests passed, or not?.

Source code in vidgear/gears/helper.py
def validate_auth_keys(path, extension):\n    \"\"\"\n    ## validate_auth_keys\n\n    Validates, and also maintains generated ZMQ CURVE Key-pairs.\n\n    Parameters:\n        path (string): path of generated CURVE key-pairs\n        extension (string): type of key-pair to be validated\n\n    **Returns:** A boolean value, confirming whether tests passed, or not?.\n    \"\"\"\n    # check for valid path\n    if not (os.path.exists(path)):\n        return False\n\n    # check if directory empty\n    if not (os.listdir(path)):\n        return False\n\n    keys_buffer = []  # stores auth-keys\n\n    # loop over auth-keys\n    for key_file in os.listdir(path):\n        key = os.path.splitext(key_file)\n        # check if valid key is generated\n        if key and (key[0] in [\"server\", \"client\"]) and (key[1] == extension):\n            keys_buffer.append(key_file)  # store it\n\n    # remove invalid keys if found\n    len(keys_buffer) == 1 and delete_file_safe(os.path.join(path, keys_buffer[0]))\n\n    # return results\n    return True if (len(keys_buffer) == 2) else False\n
"},{"location":"bonus/reference/helper_async/","title":"Helper Methods","text":""},{"location":"bonus/reference/helper_async/#vidgear.gears.asyncio.helper.reducer--reducer","title":"reducer","text":"

Asynchronous method that reduces frame size by given percentage.

Parameters:

Name Type Description Default frame numpy.ndarray

inputs numpy array(frame).

None percentage int/float

inputs size-reduction percentage.

0 interpolation int

Change resize interpolation.

4

Returns: A reduced numpy ndarray array.

Source code in vidgear/gears/asyncio/helper.py
async def reducer(frame=None, percentage=0, interpolation=cv2.INTER_LANCZOS4):\n    \"\"\"\n    ## reducer\n\n    Asynchronous method that reduces frame size by given percentage.\n\n    Parameters:\n        frame (numpy.ndarray): inputs numpy array(frame).\n        percentage (int/float): inputs size-reduction percentage.\n        interpolation (int): Change resize interpolation.\n\n    **Returns:**  A reduced numpy ndarray array.\n    \"\"\"\n    # check if frame is valid\n    if frame is None:\n        raise ValueError(\"[Helper:ERROR] :: Input frame cannot be NoneType!\")\n\n    # check if valid reduction percentage is given\n    if not (percentage > 0 and percentage < 90):\n        raise ValueError(\n            \"[Helper:ERROR] :: Given frame-size reduction percentage is invalid, Kindly refer docs.\"\n        )\n\n    if not (isinstance(interpolation, int)):\n        raise ValueError(\n            \"[Helper:ERROR] :: Given interpolation is invalid, Kindly refer docs.\"\n        )\n\n    # grab the frame size\n    (height, width) = frame.shape[:2]\n\n    # calculate the ratio of the width from percentage\n    reduction = ((100 - percentage) / 100) * width\n    ratio = reduction / float(width)\n    # construct the dimensions\n    dimensions = (int(reduction), int(height * ratio))\n\n    # return the resized frame\n    return cv2.resize(frame, dimensions, interpolation=interpolation)\n
"},{"location":"bonus/reference/helper_async/#vidgear.gears.asyncio.helper.create_blank_frame--create_blank_frame","title":"create_blank_frame","text":"

Create blank frames of given frame size with text

Parameters:

Name Type Description Default frame numpy.ndarray

inputs numpy array(frame).

None text str

Text to be written on frame.

''

Returns: A reduced numpy ndarray array.

Source code in vidgear/gears/asyncio/helper.py
def create_blank_frame(frame=None, text=\"\", logging=False):\n    \"\"\"\n    ## create_blank_frame\n\n    Create blank frames of given frame size with text\n\n    Parameters:\n        frame (numpy.ndarray): inputs numpy array(frame).\n        text (str): Text to be written on frame.\n    **Returns:**  A reduced numpy ndarray array.\n    \"\"\"\n    # check if frame is valid\n    if frame is None or not (isinstance(frame, np.ndarray)):\n        raise ValueError(\"[Helper:ERROR] :: Input frame is invalid!\")\n    # grab the frame size\n    (height, width) = frame.shape[:2]\n    # create blank frame\n    blank_frame = np.zeros(frame.shape, frame.dtype)\n    # setup text\n    if text and isinstance(text, str):\n        if logging:\n            logger.debug(\"Adding text: {}\".format(text))\n        # setup font\n        font = cv2.FONT_HERSHEY_SCRIPT_COMPLEX\n        # get boundary of this text\n        fontScale = min(height, width) / (25 / 0.25)\n        textsize = cv2.getTextSize(text, font, fontScale, 5)[0]\n        # get coords based on boundary\n        textX = (width - textsize[0]) // 2\n        textY = (height + textsize[1]) // 2\n        # put text\n        cv2.putText(\n            blank_frame, text, (textX, textY), font, fontScale, (125, 125, 125), 6\n        )\n\n    # return frame\n    return blank_frame\n
"},{"location":"bonus/reference/helper_async/#vidgear.gears.asyncio.helper.generate_webdata--generate_webdata","title":"generate_webdata","text":"

Auto-Generates, and Auto-validates default data for WebGear and WebGear_RTC APIs.

Parameters:

Name Type Description Default path string

path for generating data

required c_name string

class name that is generating files

'webgear' overwrite_default boolean

overwrite existing data or not?

False logging bool

enables logging for its operations

False

Returns: A valid data path as string.

Source code in vidgear/gears/asyncio/helper.py
def generate_webdata(path, c_name=\"webgear\", overwrite_default=False, logging=False):\n    \"\"\"\n    ## generate_webdata\n\n    Auto-Generates, and Auto-validates default data for WebGear and WebGear_RTC APIs.\n\n    Parameters:\n        path (string): path for generating data\n        c_name (string): class name that is generating files\n        overwrite_default (boolean): overwrite existing data or not?\n        logging (bool): enables logging for its operations\n\n    **Returns:** A valid data path as string.\n    \"\"\"\n    # check if path corresponds to vidgear only\n    if os.path.basename(path) != \".vidgear\":\n        path = os.path.join(path, \".vidgear\")\n\n    # generate parent directory\n    path = os.path.join(path, c_name)\n    mkdir_safe(path, logging=logging)\n\n    # self-generate dirs\n    template_dir = os.path.join(path, \"templates\")  # generates HTML templates dir\n    static_dir = os.path.join(path, \"static\")  # generates static dir\n    # generate js & css static and favicon img subdirs\n    js_static_dir = os.path.join(static_dir, \"js\")\n    css_static_dir = os.path.join(static_dir, \"css\")\n    favicon_dir = os.path.join(static_dir, \"img\")\n\n    mkdir_safe(static_dir, logging=logging)\n    mkdir_safe(template_dir, logging=logging)\n    mkdir_safe(js_static_dir, logging=logging)\n    mkdir_safe(css_static_dir, logging=logging)\n    mkdir_safe(favicon_dir, logging=logging)\n\n    # check if overwriting is enabled\n    if overwrite_default or not validate_webdata(\n        template_dir, [\"index.html\", \"404.html\", \"500.html\"]\n    ):\n        logger.critical(\n            \"Overwriting existing {} data-files with default data-files from the server!\".format(\n                c_name.capitalize()\n            )\n            if overwrite_default\n            else \"Failed to detect critical {} data-files: index.html, 404.html & 500.html!\".format(\n                c_name.capitalize()\n            )\n        )\n        # download default files\n        logging and logger.info(\n            \"Downloading default data-files from the Gitlab Server: {}\".format(\n                \"https://gitlab.com/abhiTronix/vidgear-vitals\"\n            )\n        )\n        download_webdata(\n            template_dir,\n            c_name=c_name,\n            files=[\"index.html\", \"404.html\", \"500.html\", \"base.html\"],\n            logging=logging,\n        )\n        download_webdata(\n            css_static_dir, c_name=c_name, files=[\"custom.css\"], logging=logging\n        )\n        download_webdata(\n            js_static_dir,\n            c_name=c_name,\n            files=[\"custom.js\"],\n            logging=logging,\n        )\n        download_webdata(\n            favicon_dir, c_name=c_name, files=[\"favicon-32x32.png\"], logging=logging\n        )\n    else:\n        # validate important data-files\n        if logging:\n            logger.debug(\"Found valid WebGear data-files successfully.\")\n\n    return path\n
"},{"location":"bonus/reference/helper_async/#vidgear.gears.asyncio.helper.download_webdata--download_webdata","title":"download_webdata","text":"

Downloads given list of files for WebGear and WebGear_RTC APIs(if not available) from GitHub/Gitlab Servers, and also Validates them.

Parameters:

Name Type Description Default path string

path for downloading data

required c_name string

class name that is generating files

'webgear' files list

list of files to be downloaded

[] logging bool

enables logging for its operations

False

Returns: A valid path as string.

Source code in vidgear/gears/asyncio/helper.py
def download_webdata(path, c_name=\"webgear\", files=[], logging=False):\n    \"\"\"\n    ## download_webdata\n\n    Downloads given list of files for WebGear and WebGear_RTC APIs(if not available) from GitHub/Gitlab Servers,\n    and also Validates them.\n\n    Parameters:\n        path (string): path for downloading data\n        c_name (string): class name that is generating files\n        files (list): list of files to be downloaded\n        logging (bool): enables logging for its operations\n\n    **Returns:** A valid path as string.\n    \"\"\"\n    basename = os.path.basename(path)\n    if logging:\n        logger.debug(\"Downloading {} data-files at `{}`\".format(basename, path))\n\n    # list all registered urls\n    reg_urls = [\n        \"https://gitlab.com/abhiTronix/vidgear-vitals/-/raw/main\",\n        \"https://raw.githubusercontent.com/abhiTronix/vidgear-vitals/main\",\n    ]\n\n    # create session\n    with requests.Session() as http:\n        for url in reg_urls:\n            try:\n                for file in files:\n                    # get filename\n                    file_name = os.path.join(path, file)\n                    # get URL\n                    file_url = \"{}/{}{}/{}/{}\".format(\n                        url,\n                        c_name,\n                        \"/static\" if basename != \"templates\" else \"\",\n                        basename,\n                        file,\n                    )\n                    # download and write file to the given path\n                    logging and logger.debug(\n                        \"Downloading {} data-file: {}.\".format(basename, file)\n                    )\n\n                    with open(file_name, \"wb\") as f:\n                        # setup retry strategy\n                        retries = Retry(\n                            total=3,\n                            backoff_factor=1,\n                            status_forcelist=[429, 500, 502, 503, 504],\n                        )\n                        # Mount it for https usage\n                        adapter = TimeoutHTTPAdapter(timeout=2.0, max_retries=retries)\n                        http.mount(\"https://\", adapter)\n                        response = http.get(file_url, stream=True)\n                        response.raise_for_status()\n                        total_length = (\n                            response.headers.get(\"content-length\")\n                            if \"content-length\" in response.headers\n                            else len(response.content)\n                        )\n                        assert not (\n                            total_length is None\n                        ), \"[Helper:ERROR] :: Failed to retrieve files, check your Internet connectivity!\"\n                        bar = tqdm(total=int(total_length), unit=\"B\", unit_scale=True)\n                        for data in response.iter_content(chunk_size=256):\n                            f.write(data)\n                            if len(data) > 0:\n                                bar.update(len(data))\n                        bar.close()\n            except AssertionError as e:\n                # raise if connection error\n                raise e\n            except Exception as e:\n                # log error\n                logger.exception(str(e))\n                # log event if necessary\n                url != reg_urls[1] and logger.error(\n                    \"Download failed for Gitlab Server! Retrying from GitHub Server: {}\".format(\n                        url, \"https://github.com/abhiTronix/vidgear-vitals\"\n                    )\n                )\n            else:\n                # break otherwise\n                break\n\n    if logging:\n        logger.debug(\"Verifying downloaded data:\")\n    if validate_webdata(path, files=files, logging=logging):\n        if logging:\n            logger.info(\"Successful!\")\n        return path\n    else:\n        raise RuntimeError(\n            \"[Helper:ERROR] :: Failed to download required {} data-files at: {}, Check your Internet connectivity!\".format(\n                basename, path\n            )\n        )\n
"},{"location":"bonus/reference/helper_async/#vidgear.gears.asyncio.helper.validate_webdata--validate_auth_keys","title":"validate_auth_keys","text":"

Validates, and also maintains downloaded list of files.

Parameters:

Name Type Description Default path string

path of downloaded files

required files list

list of files to be validated

[] logging bool

enables logging for its operations

False

Returns: A boolean value, confirming whether tests passed, or not?.

Source code in vidgear/gears/asyncio/helper.py
def validate_webdata(path, files=[], logging=False):\n    \"\"\"\n    ## validate_auth_keys\n\n    Validates, and also maintains downloaded list of files.\n\n    Parameters:\n        path (string): path of downloaded files\n        files (list): list of files to be validated\n        logging (bool): enables logging for its operations\n\n    **Returns:** A  boolean value, confirming whether tests passed, or not?.\n    \"\"\"\n    # check if valid path or directory empty\n    if not (os.path.exists(path)) or not (os.listdir(path)):\n        return False\n\n    files_buffer = []\n    # loop over files\n    for file in os.listdir(path):\n        if file in files:\n            files_buffer.append(file)  # store them\n\n    # return results\n    if len(files_buffer) < len(files):\n        if logging:\n            logger.warning(\n                \"`{}` file(s) missing from data-files!\".format(\n                    \" ,\".join(list(set(files_buffer) ^ set(files)))\n                )\n            )\n        return False\n    else:\n        return True\n
"},{"location":"bonus/reference/netgear/","title":"NetGear API References","text":"

NetGear API usage examples can be found here \u27b6

NetGear API parameters are explained here \u27b6

NetGear is exclusively designed to transfer video frames synchronously and asynchronously between interconnecting systems over the network in real-time.

NetGear implements a high-level wrapper around PyZmQ python library that contains python bindings for ZeroMQ - a high-performance asynchronous distributed messaging library that provides a message queue, but unlike message-oriented middleware, its system can run without a dedicated message broker.

NetGear also supports real-time Frame Compression capabilities for optimizing performance while sending the frames directly over the network, by encoding the frame before sending it and decoding it on the client's end automatically in real-time.

Info

NetGear API now internally implements robust Lazy Pirate pattern (auto-reconnection) for its synchronous messaging patterns (i.e. zmq.PAIR & zmq.REQ/zmq.REP) at both Server and Client ends, where its API instead of doing a blocking receive, will:

NetGear as of now seamlessly supports three ZeroMQ messaging patterns:

whereas the supported protocol are: tcp and ipc.

Modes of Operation Source code in vidgear/gears/netgear.py
class NetGear:\n    \"\"\"\n    NetGear is exclusively designed to transfer video frames synchronously and asynchronously between interconnecting systems over the network in real-time.\n\n    NetGear implements a high-level wrapper around PyZmQ python library that contains python bindings for ZeroMQ - a high-performance asynchronous distributed messaging library\n    that provides a message queue, but unlike message-oriented middleware, its system can run without a dedicated message broker.\n\n    NetGear also supports real-time Frame Compression capabilities for optimizing performance while sending the frames directly over the network, by encoding the frame before sending\n    it and decoding it on the client's end automatically in real-time.\n\n    !!! info\n        NetGear API now internally implements robust *Lazy Pirate pattern* (auto-reconnection) for its synchronous messaging patterns _(i.e. `zmq.PAIR` & `zmq.REQ/zmq.REP`)_\n        at both Server and Client ends, where its API instead of doing a blocking receive, will:\n\n        * Poll the socket and receive from it only when it's sure a reply has arrived.\n        * Attempt to reconnect, if no reply has arrived within a timeout period.\n        * Abandon the connection if there is still no reply after several requests.\n\n    NetGear as of now seamlessly supports three ZeroMQ messaging patterns:\n\n    - `zmq.PAIR` _(ZMQ Pair Pattern)_\n    - `zmq.REQ/zmq.REP` _(ZMQ Request/Reply Pattern)_\n    - `zmq.PUB/zmq.SUB` _(ZMQ Publish/Subscribe Pattern)_\n\n    _whereas the supported protocol are: `tcp` and `ipc`_.\n\n    ??? tip \"Modes of Operation\"\n\n        * **Primary Modes**\n\n            NetGear API primarily has two modes of operations:\n\n            * **Send Mode:** _which employs `send()` function to send video frames over the network in real-time._\n\n            * **Receive Mode:** _which employs `recv()` function to receive frames, sent over the network with *Send Mode* in real-time. The mode sends back confirmation when the\n            frame is received successfully in few patterns._\n\n        * **Exclusive Modes**\n\n            In addition to these primary modes, NetGear API offers applications-specific Exclusive Modes:\n\n            * **Multi-Servers Mode:** _In this exclusive mode, NetGear API robustly **handles multiple servers at once**, thereby providing seamless access to frames and unidirectional\n            data transfer from multiple Servers/Publishers across the network in real-time._\n\n            * **Multi-Clients Mode:** _In this exclusive mode, NetGear API robustly **handles multiple clients at once**, thereby providing seamless access to frames and unidirectional\n            data transfer to multiple Client/Consumers across the network in real-time._\n\n            * **Bidirectional Mode:** _This exclusive mode **provides seamless support for bidirectional data transmission between between Server and Client along with video frames**._\n\n            * **Secure Mode:** _In this exclusive mode, NetGear API **provides easy access to powerful, smart & secure ZeroMQ's Security Layers** that enables strong encryption on\n            data, and unbreakable authentication between the Server and Client with the help of custom certificates/keys that brings cheap, standardized privacy and authentication\n            for distributed systems over the network._\n    \"\"\"\n\n    def __init__(\n        self,\n        address=None,\n        port=None,\n        protocol=None,\n        pattern=0,\n        receive_mode=False,\n        logging=False,\n        **options\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the NetGear class.\n\n        Parameters:\n            address (str): sets the valid network address of the Server/Client.\n            port (str): sets the valid Network Port of the Server/Client.\n            protocol (str): sets the valid messaging protocol between Server/Client.\n            pattern (int): sets the supported messaging pattern(flow of communication) between Server/Client\n            receive_mode (bool): select the Netgear's Mode of operation.\n            logging (bool): enables/disables logging.\n            options (dict): provides the flexibility to alter various NetGear internal properties.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # raise error(s) for critical Class imports\n        import_dependency_safe(\n            \"zmq\" if zmq is None else \"\", min_version=\"4.0\", pkg_name=\"pyzmq\"\n        )\n        import_dependency_safe(\n            \"simplejpeg\" if simplejpeg is None else \"\", error=\"log\", min_version=\"1.6.1\"\n        )\n\n        # define valid messaging patterns => `0`: zmq.PAIR, `1`:(zmq.REQ,zmq.REP), and `1`:(zmq.SUB,zmq.PUB)\n        valid_messaging_patterns = {\n            0: (zmq.PAIR, zmq.PAIR),\n            1: (zmq.REQ, zmq.REP),\n            2: (zmq.PUB, zmq.SUB),\n        }\n\n        # Handle messaging pattern\n        msg_pattern = None\n        # check whether user-defined messaging pattern is valid\n        if isinstance(pattern, int) and pattern in valid_messaging_patterns.keys():\n            # assign value\n            msg_pattern = valid_messaging_patterns[pattern]\n        else:\n            # otherwise default to 0:`zmq.PAIR`\n            pattern = 0\n            msg_pattern = valid_messaging_patterns[pattern]\n            self.__logging and logger.warning(\n                \"Wrong pattern value, Defaulting to `zmq.PAIR`! Kindly refer Docs for more Information.\"\n            )\n        # assign pattern to global parameter for further use\n        self.__pattern = pattern\n\n        # Handle messaging protocol\n        if protocol is None or not (protocol in [\"tcp\", \"ipc\"]):\n            # else default to `tcp` protocol\n            protocol = \"tcp\"\n            # log it\n            self.__logging and logger.warning(\n                \"Protocol is not supported or not provided. Defaulting to `tcp` protocol!\"\n            )\n\n        # Handle connection params\n\n        self.__msg_flag = 0  # handles connection flags\n        self.__msg_copy = False  # handles whether to copy data\n        self.__msg_track = False  # handles whether to track packets\n\n        # Handle NetGear's internal exclusive modes and params\n\n        # define Secure Mode\n        self.__z_auth = None\n\n        # define SSH Tunneling Mode\n        self.__ssh_tunnel_mode = None  # handles ssh_tunneling mode state\n        self.__ssh_tunnel_pwd = None\n        self.__ssh_tunnel_keyfile = None\n        self.__paramiko_present = False if paramiko is None else True\n\n        # define Multi-Server mode\n        self.__multiserver_mode = False  # handles multi-server mode state\n\n        # define Multi-Client mode\n        self.__multiclient_mode = False  # handles multi-client mode state\n\n        # define Bidirectional mode\n        self.__bi_mode = False  # handles Bidirectional mode state\n\n        # define Secure mode\n        valid_security_mech = {0: \"Grasslands\", 1: \"StoneHouse\", 2: \"IronHouse\"}\n        self.__secure_mode = 0  # handles ZMQ security layer status\n        auth_cert_dir = \"\"  # handles valid ZMQ certificates dir\n        self.__auth_publickeys_dir = \"\"  # handles valid ZMQ public certificates dir\n        self.__auth_secretkeys_dir = \"\"  # handles valid ZMQ private certificates dir\n        overwrite_cert = False  # checks if certificates overwriting allowed\n        custom_cert_location = \"\"  # handles custom ZMQ certificates path\n\n        # define frame-compression handler\n        self.__jpeg_compression = (\n            True if not (simplejpeg is None) else False\n        )  # enabled by default for all connections if simplejpeg is installed\n        self.__jpeg_compression_quality = 90  # 90% quality\n        self.__jpeg_compression_fastdct = True  # fastest DCT on by default\n        self.__jpeg_compression_fastupsample = False  # fastupsample off by default\n        self.__jpeg_compression_colorspace = \"BGR\"  # use BGR colorspace by default\n\n        # defines frame compression on return data\n        self.__ex_compression_params = None\n\n        # define receiver return data handler\n        self.__return_data = None\n\n        # generate 8-digit random system id\n        self.__id = \"\".join(\n            secrets.choice(string.ascii_uppercase + string.digits) for i in range(8)\n        )\n\n        # define termination flag\n        self.__terminate = False\n\n        # additional settings for reliability\n        if pattern < 2:\n            # define zmq poller for reliable transmission\n            self.__poll = zmq.Poller()\n            # define max retries\n            self.__max_retries = 3\n            # request timeout\n            self.__request_timeout = 4000  # 4 secs\n        else:\n            # subscriber timeout\n            self.__subscriber_timeout = None\n\n        # Handle user-defined options dictionary values\n        # reformat dictionary\n        options = {str(k).strip(): v for k, v in options.items()}\n\n        # loop over dictionary key & values and assign to global variables if valid\n        for key, value in options.items():\n            # handle multi-server mode\n            if key == \"multiserver_mode\" and isinstance(value, bool):\n                # check if valid pattern assigned\n                if pattern > 0:\n                    # activate Multi-server mode\n                    self.__multiserver_mode = value\n                else:\n                    # otherwise disable it and raise error\n                    self.__multiserver_mode = False\n                    logger.critical(\"Multi-Server Mode is disabled!\")\n                    raise ValueError(\n                        \"[NetGear:ERROR] :: `{}` pattern is not valid when Multi-Server Mode is enabled. Kindly refer Docs for more Information.\".format(\n                            pattern\n                        )\n                    )\n\n            # handle multi-client mode\n            elif key == \"multiclient_mode\" and isinstance(value, bool):\n                # check if valid pattern assigned\n                if pattern > 0:\n                    # activate Multi-client mode\n                    self.__multiclient_mode = value\n                else:\n                    # otherwise disable it and raise error\n                    self.__multiclient_mode = False\n                    logger.critical(\"Multi-Client Mode is disabled!\")\n                    raise ValueError(\n                        \"[NetGear:ERROR] :: `{}` pattern is not valid when Multi-Client Mode is enabled. Kindly refer Docs for more Information.\".format(\n                            pattern\n                        )\n                    )\n\n            # handle bidirectional mode\n            elif key == \"bidirectional_mode\" and isinstance(value, bool):\n                # check if pattern is valid\n                if pattern < 2:\n                    # activate Bidirectional mode if specified\n                    self.__bi_mode = value\n                else:\n                    # otherwise disable it and raise error\n                    self.__bi_mode = False\n                    logger.warning(\"Bidirectional data transmission is disabled!\")\n                    raise ValueError(\n                        \"[NetGear:ERROR] :: `{}` pattern is not valid when Bidirectional Mode is enabled. Kindly refer Docs for more Information!\".format(\n                            pattern\n                        )\n                    )\n\n            # handle secure mode\n            elif (\n                key == \"secure_mode\"\n                and isinstance(value, int)\n                and (value in valid_security_mech)\n            ):\n                self.__secure_mode = value\n\n            elif key == \"custom_cert_location\" and isinstance(value, str):\n                # verify custom auth certificates path for secure mode\n                custom_cert_location = os.path.abspath(value)\n                assert os.path.isdir(\n                    custom_cert_location\n                ), \"[NetGear:ERROR] :: `custom_cert_location` value must be the path to a valid directory!\"\n                assert check_WriteAccess(\n                    custom_cert_location,\n                    is_windows=True if os.name == \"nt\" else False,\n                    logging=self.__logging,\n                ), \"[NetGear:ERROR] :: Permission Denied!, cannot write ZMQ authentication certificates to '{}' directory!\".format(\n                    value\n                )\n            elif key == \"overwrite_cert\" and isinstance(value, bool):\n                # enable/disable auth certificate overwriting in secure mode\n                overwrite_cert = value\n\n            # handle ssh-tunneling mode\n            elif key == \"ssh_tunnel_mode\" and isinstance(value, str):\n                # enable SSH Tunneling Mode\n                self.__ssh_tunnel_mode = value.strip()\n            elif key == \"ssh_tunnel_pwd\" and isinstance(value, str):\n                # add valid SSH Tunneling password\n                self.__ssh_tunnel_pwd = value\n            elif key == \"ssh_tunnel_keyfile\" and isinstance(value, str):\n                # add valid SSH Tunneling key-file\n                self.__ssh_tunnel_keyfile = value if os.path.isfile(value) else None\n                if self.__ssh_tunnel_keyfile is None:\n                    logger.warning(\n                        \"Discarded invalid or non-existential SSH Tunnel Key-file at {}!\".format(\n                            value\n                        )\n                    )\n\n            # handle jpeg compression\n            elif (\n                key == \"jpeg_compression\"\n                and not (simplejpeg is None)\n                and isinstance(value, (bool, str))\n            ):\n                if isinstance(value, str) and value.strip().upper() in [\n                    \"RGB\",\n                    \"BGR\",\n                    \"RGBX\",\n                    \"BGRX\",\n                    \"XBGR\",\n                    \"XRGB\",\n                    \"GRAY\",\n                    \"RGBA\",\n                    \"BGRA\",\n                    \"ABGR\",\n                    \"ARGB\",\n                    \"CMYK\",\n                ]:\n                    # set encoding colorspace\n                    self.__jpeg_compression_colorspace = value.strip().upper()\n                    # enable frame-compression encoding value\n                    self.__jpeg_compression = True\n                else:\n                    # enable frame-compression encoding value\n                    self.__jpeg_compression = value\n            elif key == \"jpeg_compression_quality\" and isinstance(value, (int, float)):\n                # set valid jpeg quality\n                if value >= 10 and value <= 100:\n                    self.__jpeg_compression_quality = int(value)\n                else:\n                    logger.warning(\"Skipped invalid `jpeg_compression_quality` value!\")\n            elif key == \"jpeg_compression_fastdct\" and isinstance(value, bool):\n                # enable jpeg fastdct\n                self.__jpeg_compression_fastdct = value\n            elif key == \"jpeg_compression_fastupsample\" and isinstance(value, bool):\n                # enable jpeg  fastupsample\n                self.__jpeg_compression_fastupsample = value\n\n            # assign maximum retries in synchronous patterns\n            elif key == \"max_retries\" and isinstance(value, int) and pattern < 2:\n                if value >= 0:\n                    self.__max_retries = value\n                else:\n                    logger.warning(\"Invalid `max_retries` value skipped!\")\n\n            # assign request timeout in synchronous patterns\n            elif key == \"request_timeout\" and isinstance(value, int) and pattern < 2:\n                if value >= 4:\n                    self.__request_timeout = value * 1000  # covert to milliseconds\n                else:\n                    logger.warning(\"Invalid `request_timeout` value skipped!\")\n\n            # assign subscriber timeout\n            elif (\n                key == \"subscriber_timeout\" and isinstance(value, int) and pattern == 2\n            ):\n                if value > 0:\n                    self.__subscriber_timeout = value * 1000  # covert to milliseconds\n                else:\n                    logger.warning(\"Invalid `request_timeout` value skipped!\")\n\n            # handle ZMQ flags\n            elif key == \"flag\" and isinstance(value, int):\n                self.__msg_flag = value\n                self.__msg_flag and logger.warning(\n                    \"The flag optional value is set to `1` (NOBLOCK) for this run. This might cause NetGear to not terminate gracefully.\"\n                )\n            elif key == \"copy\" and isinstance(value, bool):\n                self.__msg_copy = value\n            elif key == \"track\" and isinstance(value, bool):\n                self.__msg_track = value\n                self.__msg_copy and self.__msg_track and logger.info(\n                    \"The `track` optional value will be ignored for this run because `copy=True` is also defined.\"\n                )\n            else:\n                pass\n\n        # Handle ssh tunneling if enabled\n        if not (self.__ssh_tunnel_mode is None):\n            # SSH Tunnel Mode only available for server mode\n            if receive_mode:\n                logger.error(\"SSH Tunneling cannot be enabled for Client-end!\")\n            else:\n                # check if SSH tunneling possible\n                ssh_address = self.__ssh_tunnel_mode\n                ssh_address, ssh_port = (\n                    ssh_address.split(\":\")\n                    if \":\" in ssh_address\n                    else [ssh_address, \"22\"]\n                )  # default to port 22\n                if \"47\" in ssh_port:\n                    self.__ssh_tunnel_mode = self.__ssh_tunnel_mode.replace(\n                        \":47\", \"\"\n                    )  # port-47 is reserved for testing\n                else:\n                    # extract ip for validation\n                    ssh_user, ssh_ip = (\n                        ssh_address.split(\"@\")\n                        if \"@\" in ssh_address\n                        else [\"\", ssh_address]\n                    )\n                    # validate ip specified port\n                    assert check_open_port(\n                        ssh_ip, port=int(ssh_port)\n                    ), \"[NetGear:ERROR] :: Host `{}` is not available for SSH Tunneling at port-{}!\".format(\n                        ssh_address, ssh_port\n                    )\n\n        # Handle multiple exclusive modes if enabled\n        if self.__multiclient_mode and self.__multiserver_mode:\n            raise ValueError(\n                \"[NetGear:ERROR] :: Multi-Client and Multi-Server Mode cannot be enabled simultaneously!\"\n            )\n        elif self.__multiserver_mode or self.__multiclient_mode:\n            # check if Bidirectional Mode also enabled\n            if self.__bi_mode:\n                # log it\n                self.__logging and logger.debug(\n                    \"Bidirectional Data Transmission is also enabled for this connection!\"\n                )\n            # check if SSH Tunneling Mode also enabled\n            if self.__ssh_tunnel_mode:\n                # raise error\n                raise ValueError(\n                    \"[NetGear:ERROR] :: SSH Tunneling and {} Mode cannot be enabled simultaneously. Kindly refer docs!\".format(\n                        \"Multi-Server\" if self.__multiserver_mode else \"Multi-Client\"\n                    )\n                )\n        elif self.__bi_mode:\n            # log Bidirectional mode activation\n            self.__logging and logger.debug(\n                \"Bidirectional Data Transmission is enabled for this connection!\"\n            )\n        elif self.__ssh_tunnel_mode:\n            # log Bidirectional mode activation\n            self.__logging and logger.debug(\n                \"SSH Tunneling is enabled for host:`{}` with `{}` back-end.\".format(\n                    self.__ssh_tunnel_mode,\n                    \"paramiko\" if self.__paramiko_present else \"pexpect\",\n                )\n            )\n\n        # On Windows, NetGear requires the ``WindowsSelectorEventLoop`` but Python 3.8 and above,\n        # defaults to an ``ProactorEventLoop`` loop that is not compatible with it. Thereby,\n        # we had to set it manually.\n        platform.system() == \"Windows\" and asyncio.set_event_loop_policy(\n            asyncio.WindowsSelectorEventLoopPolicy()\n        )\n\n        # define ZMQ messaging context instance\n        self.__msg_context = zmq.Context.instance()\n\n        # initialize and assign receive mode to global variable\n        self.__receive_mode = receive_mode\n\n        # Handle Secure mode\n        if self.__secure_mode > 0:\n            # activate and log if overwriting is enabled\n            if receive_mode:\n                overwrite_cert = False\n                overwrite_cert and logger.warning(\n                    \"Overwriting ZMQ Authentication certificates is disabled for Client's end!\"\n                )\n            else:\n                overwrite_cert and self.__logging and logger.info(\n                    \"Overwriting ZMQ Authentication certificates over previous ones!\"\n                )\n\n            # Validate certificate generation paths\n            # Start threaded authenticator for this context\n            try:\n                # check if custom certificates path is specified\n                if custom_cert_location:\n                    (\n                        auth_cert_dir,\n                        self.__auth_secretkeys_dir,\n                        self.__auth_publickeys_dir,\n                    ) = generate_auth_certificates(\n                        custom_cert_location, overwrite=overwrite_cert, logging=logging\n                    )\n                else:\n                    # otherwise auto-generate suitable path\n                    (\n                        auth_cert_dir,\n                        self.__auth_secretkeys_dir,\n                        self.__auth_publickeys_dir,\n                    ) = generate_auth_certificates(\n                        os.path.join(expanduser(\"~\"), \".vidgear\"),\n                        overwrite=overwrite_cert,\n                        logging=logging,\n                    )\n                # log it\n                self.__logging and logger.debug(\n                    \"`{}` is the default location for storing ZMQ authentication certificates/keys.\".format(\n                        auth_cert_dir\n                    )\n                )\n\n                # start an authenticator for this context\n                self.__z_auth = ThreadAuthenticator(self.__msg_context)\n                self.__z_auth.start()\n                self.__z_auth.allow(str(address))  # allow current address\n\n                # check if `IronHouse` is activated\n                if self.__secure_mode == 2:\n                    # tell authenticator to use the certificate from given valid dir\n                    self.__z_auth.configure_curve(\n                        domain=\"*\", location=self.__auth_publickeys_dir\n                    )\n                else:\n                    # otherwise tell the authenticator how to handle the CURVE requests, if `StoneHouse` is activated\n                    self.__z_auth.configure_curve(\n                        domain=\"*\", location=auth.CURVE_ALLOW_ANY\n                    )\n            except zmq.ZMQError as e:\n                if \"Address in use\" in str(e):\n                    logger.info(\"ZMQ Authenticator already running.\")\n                else:\n                    # catch if any error occurred and disable Secure mode\n                    logger.exception(str(e))\n                    self.__secure_mode = 0\n                    logger.error(\n                        \"ZMQ Security Mechanism is disabled for this connection due to errors!\"\n                    )\n\n        # check whether `receive_mode` is enabled\n        if self.__receive_mode:\n            # define connection address\n            address = \"*\" if address is None else address\n\n            # check if multiserver_mode is enabled\n            if self.__multiserver_mode:\n                # check if unique server port address list/tuple is assigned or not in multiserver_mode\n                if port is None or not isinstance(port, (tuple, list)):\n                    # raise error if not\n                    raise ValueError(\n                        \"[NetGear:ERROR] :: Incorrect port value! Kindly provide a list/tuple of Server ports while Multi-Server mode is enabled. For more information refer VidGear docs.\"\n                    )\n                else:\n                    # otherwise log it\n                    logger.debug(\n                        \"Enabling Multi-Server Mode at PORTS: {}!\".format(port)\n                    )\n                # create port address buffer for keeping track of connected client's port(s)\n                self.__port_buffer = []\n            # check if multiclient_mode is enabled\n            elif self.__multiclient_mode:\n                # check if unique server port address is assigned or not in multiclient_mode\n                if port is None:\n                    # raise error if not\n                    raise ValueError(\n                        \"[NetGear:ERROR] :: Kindly provide a unique & valid port value at Client-end. For more information refer VidGear docs.\"\n                    )\n                else:\n                    # otherwise log it\n                    logger.debug(\n                        \"Enabling Multi-Client Mode at PORT: {} on this device!\".format(\n                            port\n                        )\n                    )\n                # assign value to global variable\n                self.__port = port\n            else:\n                # otherwise assign local port address if None\n                port = \"5555\" if port is None else port\n\n            try:\n                # define thread-safe messaging socket\n                self.__msg_socket = self.__msg_context.socket(msg_pattern[1])\n\n                # define pub-sub flag\n                self.__pattern == 2 and self.__msg_socket.set_hwm(1)\n\n                # enable specified secure mode for the socket\n                if self.__secure_mode > 0:\n                    # load server key\n                    server_secret_file = os.path.join(\n                        self.__auth_secretkeys_dir, \"server.key_secret\"\n                    )\n                    server_public, server_secret = auth.load_certificate(\n                        server_secret_file\n                    )\n                    # load  all CURVE keys\n                    self.__msg_socket.curve_secretkey = server_secret\n                    self.__msg_socket.curve_publickey = server_public\n                    # enable CURVE connection for this socket\n                    self.__msg_socket.curve_server = True\n\n                # define exclusive socket options for `patterns=2`\n                if self.__pattern == 2:\n                    self.__msg_socket.setsockopt_string(zmq.SUBSCRIBE, \"\")\n                    self.__subscriber_timeout and self.__msg_socket.setsockopt(\n                        zmq.RCVTIMEO, self.__subscriber_timeout\n                    )\n                    self.__subscriber_timeout and self.__msg_socket.setsockopt(\n                        zmq.LINGER, 0\n                    )\n\n                # if multiserver_mode is enabled, then assign port addresses to zmq socket\n                if self.__multiserver_mode:\n                    # bind socket to given server protocol, address and ports\n                    for pt in port:\n                        self.__msg_socket.bind(\n                            protocol + \"://\" + str(address) + \":\" + str(pt)\n                        )\n                else:\n                    # bind socket to given protocol, address and port normally\n                    self.__msg_socket.bind(\n                        protocol + \"://\" + str(address) + \":\" + str(port)\n                    )\n\n                # additional settings\n                if pattern < 2:\n                    if self.__multiserver_mode:\n                        self.__connection_address = []\n                        for pt in port:\n                            self.__connection_address.append(\n                                protocol + \"://\" + str(address) + \":\" + str(pt)\n                            )\n                    else:\n                        self.__connection_address = (\n                            protocol + \"://\" + str(address) + \":\" + str(port)\n                        )\n                    self.__msg_pattern = msg_pattern[1]\n                    self.__poll.register(self.__msg_socket, zmq.POLLIN)\n                    self.__logging and logger.debug(\n                        \"Reliable transmission is enabled for this pattern with max-retries: {} and timeout: {} secs.\".format(\n                            self.__max_retries, self.__request_timeout / 1000\n                        )\n                    )\n                else:\n                    self.__logging and self.__subscriber_timeout and logger.debug(\n                        \"Timeout: {} secs is enabled for this system.\".format(\n                            self.__subscriber_timeout / 1000\n                        )\n                    )\n\n            except Exception as e:\n                # otherwise log and raise error\n                logger.exception(str(e))\n                # Handle Secure Mode\n                self.__secure_mode and logger.critical(\n                    \"Failed to activate Secure Mode: `{}` for this connection!\".format(\n                        valid_security_mech[self.__secure_mode]\n                    )\n                )\n                # raise errors for exclusive modes\n                if self.__multiserver_mode or self.__multiclient_mode:\n                    raise RuntimeError(\n                        \"[NetGear:ERROR] :: Receive Mode failed to activate {} Mode at address: {} with pattern: {}! Kindly recheck all parameters.\".format(\n                            (\n                                \"Multi-Server\"\n                                if self.__multiserver_mode\n                                else \"Multi-Client\"\n                            ),\n                            (protocol + \"://\" + str(address) + \":\" + str(port)),\n                            pattern,\n                        )\n                    )\n                else:\n                    self.__bi_mode and logger.critical(\n                        \"Failed to activate Bidirectional Mode for this connection!\"\n                    )\n                    raise RuntimeError(\n                        \"[NetGear:ERROR] :: Receive Mode failed to bind address: {} and pattern: {}! Kindly recheck all parameters.\".format(\n                            (protocol + \"://\" + str(address) + \":\" + str(port)), pattern\n                        )\n                    )\n\n            # Handle threaded queue mode\n            self.__logging and logger.debug(\n                \"Threaded Queue Mode is enabled by default for this connection.\"\n            )\n\n            # define deque and assign it to global var\n            self.__queue = deque(maxlen=96)  # max len 96 to check overflow\n\n            # initialize and start threaded recv_handler\n            self.__thread = Thread(target=self.__recv_handler, name=\"NetGear\", args=())\n            self.__thread.daemon = True\n            self.__thread.start()\n\n            if self.__logging:\n                # finally log progress\n                logger.debug(\n                    \"Successfully Binded to address: {} with pattern: {}.\".format(\n                        (protocol + \"://\" + str(address) + \":\" + str(port)), pattern\n                    )\n                )\n                self.__jpeg_compression and logger.debug(\n                    \"JPEG Frame-Compression is activated for this connection with Colorspace:`{}`, Quality:`{}`%, Fastdct:`{}`, and Fastupsample:`{}`.\".format(\n                        self.__jpeg_compression_colorspace,\n                        self.__jpeg_compression_quality,\n                        (\"enabled\" if self.__jpeg_compression_fastdct else \"disabled\"),\n                        (\n                            \"enabled\"\n                            if self.__jpeg_compression_fastupsample\n                            else \"disabled\"\n                        ),\n                    )\n                )\n                self.__secure_mode and logger.debug(\n                    \"Successfully enabled ZMQ Security Mechanism: `{}` for this connection.\".format(\n                        valid_security_mech[self.__secure_mode]\n                    )\n                )\n                logger.debug(\"Multi-threaded Receive Mode is successfully enabled.\")\n                logger.debug(\"Unique System ID is {}.\".format(self.__id))\n                logger.debug(\"Receive Mode is now activated.\")\n\n        else:\n            # otherwise default to `Send Mode`\n            # define connection address\n            address = \"localhost\" if address is None else address\n\n            # check if multiserver_mode is enabled\n            if self.__multiserver_mode:\n                # check if unique server port address is assigned or not in multiserver_mode\n                if port is None:\n                    # raise error if not\n                    raise ValueError(\n                        \"[NetGear:ERROR] :: Kindly provide a unique & valid port value at Server-end. For more information refer VidGear docs.\"\n                    )\n                else:\n                    # otherwise log it\n                    logger.debug(\n                        \"Enabling Multi-Server Mode at PORT: {} on this device!\".format(\n                            port\n                        )\n                    )\n                # assign value to global variable\n                self.__port = port\n            # check if multiclient_mode is enabled\n            elif self.__multiclient_mode:\n                # check if unique client port address list/tuple is assigned or not in multiclient_mode\n                if port is None or not isinstance(port, (tuple, list)):\n                    # raise error if not\n                    raise ValueError(\n                        \"[NetGear:ERROR] :: Incorrect port value! Kindly provide a list/tuple of Client ports while Multi-Client mode is enabled. For more information refer VidGear docs.\"\n                    )\n                else:\n                    # otherwise log it\n                    logger.debug(\n                        \"Enabling Multi-Client Mode at PORTS: {}!\".format(port)\n                    )\n                # create port address buffer for keeping track of connected client ports\n                self.__port_buffer = []\n            else:\n                # otherwise assign local port address if None\n                port = \"5555\" if port is None else port\n\n            try:\n                # define thread-safe messaging socket\n                self.__msg_socket = self.__msg_context.socket(msg_pattern[0])\n\n                # if req/rep pattern, define additional flags\n                if self.__pattern == 1:\n                    self.__msg_socket.REQ_RELAXED = True\n                    self.__msg_socket.REQ_CORRELATE = True\n\n                # if pub/sub pattern, define additional optimizer\n                if self.__pattern == 2:\n                    self.__msg_socket.set_hwm(1)\n\n                # enable specified secure mode for the socket\n                if self.__secure_mode > 0:\n                    # load client key\n                    client_secret_file = os.path.join(\n                        self.__auth_secretkeys_dir, \"client.key_secret\"\n                    )\n                    client_public, client_secret = auth.load_certificate(\n                        client_secret_file\n                    )\n                    # load  all CURVE keys\n                    self.__msg_socket.curve_secretkey = client_secret\n                    self.__msg_socket.curve_publickey = client_public\n                    # load server key\n                    server_public_file = os.path.join(\n                        self.__auth_publickeys_dir, \"server.key\"\n                    )\n                    server_public, _ = auth.load_certificate(server_public_file)\n                    # inject public key to make a CURVE connection.\n                    self.__msg_socket.curve_serverkey = server_public\n\n                # check if multi-client_mode is enabled\n                if self.__multiclient_mode:\n                    # bind socket to given server protocol, address and ports\n                    for pt in port:\n                        self.__msg_socket.connect(\n                            protocol + \"://\" + str(address) + \":\" + str(pt)\n                        )\n                else:\n                    # handle SSH tunneling if enabled\n                    if self.__ssh_tunnel_mode:\n                        # establish tunnel connection\n                        ssh.tunnel_connection(\n                            self.__msg_socket,\n                            protocol + \"://\" + str(address) + \":\" + str(port),\n                            self.__ssh_tunnel_mode,\n                            keyfile=self.__ssh_tunnel_keyfile,\n                            password=self.__ssh_tunnel_pwd,\n                            paramiko=self.__paramiko_present,\n                        )\n                    else:\n                        # connect socket to given protocol, address and port\n                        self.__msg_socket.connect(\n                            protocol + \"://\" + str(address) + \":\" + str(port)\n                        )\n\n                # additional settings\n                if pattern < 2:\n                    if self.__multiclient_mode:\n                        self.__connection_address = []\n                        for pt in port:\n                            self.__connection_address.append(\n                                protocol + \"://\" + str(address) + \":\" + str(pt)\n                            )\n                    else:\n                        self.__connection_address = (\n                            protocol + \"://\" + str(address) + \":\" + str(port)\n                        )\n                    self.__msg_pattern = msg_pattern[0]\n                    self.__poll.register(self.__msg_socket, zmq.POLLIN)\n\n                    self.__logging and logger.debug(\n                        \"Reliable transmission is enabled for this pattern with max-retries: {} and timeout: {} secs.\".format(\n                            self.__max_retries, self.__request_timeout / 1000\n                        )\n                    )\n\n            except Exception as e:\n                # otherwise log and raise error\n                logger.exception(str(e))\n                # Handle Secure Mode\n                self.__secure_mode and logger.critical(\n                    \"Failed to activate Secure Mode: `{}` for this connection!\".format(\n                        valid_security_mech[self.__secure_mode]\n                    )\n                )\n                # raise errors for exclusive modes\n                if self.__multiserver_mode or self.__multiclient_mode:\n                    raise RuntimeError(\n                        \"[NetGear:ERROR] :: Send Mode failed to activate {} Mode at address: {} with pattern: {}! Kindly recheck all parameters.\".format(\n                            (\n                                \"Multi-Server\"\n                                if self.__multiserver_mode\n                                else \"Multi-Client\"\n                            ),\n                            (protocol + \"://\" + str(address) + \":\" + str(port)),\n                            pattern,\n                        )\n                    )\n                else:\n                    self.__bi_mode and logger.critical(\n                        \"Failed to activate Bidirectional Mode for this connection!\"\n                    )\n                    self.__ssh_tunnel_mode and logger.critical(\n                        \"Failed to initiate SSH Tunneling Mode for this server with `{}` back-end!\".format(\n                            \"paramiko\" if self.__paramiko_present else \"pexpect\"\n                        )\n                    )\n                    raise RuntimeError(\n                        \"[NetGear:ERROR] :: Send Mode failed to connect address: {} and pattern: {}! Kindly recheck all parameters.\".format(\n                            (protocol + \"://\" + str(address) + \":\" + str(port)), pattern\n                        )\n                    )\n\n            if self.__logging:\n                # finally log progress\n                logger.debug(\n                    \"Successfully connected to address: {} with pattern: {}.\".format(\n                        (protocol + \"://\" + str(address) + \":\" + str(port)), pattern\n                    )\n                )\n                self.__jpeg_compression and logger.debug(\n                    \"JPEG Frame-Compression is activated for this connection with Colorspace:`{}`, Quality:`{}`%, Fastdct:`{}`, and Fastupsample:`{}`.\".format(\n                        self.__jpeg_compression_colorspace,\n                        self.__jpeg_compression_quality,\n                        (\"enabled\" if self.__jpeg_compression_fastdct else \"disabled\"),\n                        (\n                            \"enabled\"\n                            if self.__jpeg_compression_fastupsample\n                            else \"disabled\"\n                        ),\n                    )\n                )\n                self.__secure_mode and logger.debug(\n                    \"Enabled ZMQ Security Mechanism: `{}` for this connection.\".format(\n                        valid_security_mech[self.__secure_mode]\n                    )\n                )\n                logger.debug(\"Unique System ID is {}.\".format(self.__id))\n                logger.debug(\n                    \"Send Mode is successfully activated and ready to send data.\"\n                )\n\n    def __recv_handler(self):\n        \"\"\"\n        A threaded receiver handler, that keep iterating data from ZMQ socket to a internally monitored deque,\n        until the thread is terminated, or socket disconnects.\n        \"\"\"\n        # initialize variables\n        frame = None\n        msg_json = None\n\n        # keep looping infinitely until the thread is terminated\n        while not self.__terminate:\n            # check queue buffer for overflow\n            if len(self.__queue) >= 96:\n                # stop iterating if overflowing occurs\n                time.sleep(0.000001)\n                continue\n\n            if self.__pattern < 2:\n                socks = dict(self.__poll.poll(self.__request_timeout * 3))\n                if socks.get(self.__msg_socket) == zmq.POLLIN:\n                    msg_json = self.__msg_socket.recv_json(\n                        flags=self.__msg_flag | zmq.DONTWAIT\n                    )\n                else:\n                    logger.critical(\"No response from Server(s), Reconnecting again...\")\n                    self.__msg_socket.close(linger=0)\n                    self.__poll.unregister(self.__msg_socket)\n                    self.__max_retries -= 1\n\n                    if not (self.__max_retries):\n                        if self.__multiserver_mode:\n                            logger.error(\"All Servers seems to be offline, Abandoning!\")\n                        else:\n                            logger.error(\"Server seems to be offline, Abandoning!\")\n                        self.__terminate = True\n                        continue\n\n                    # Create new connection\n                    try:\n                        self.__msg_socket = self.__msg_context.socket(\n                            self.__msg_pattern\n                        )\n                        if isinstance(self.__connection_address, list):\n                            for _connection in self.__connection_address:\n                                self.__msg_socket.bind(_connection)\n                        else:\n                            self.__msg_socket.bind(self.__connection_address)\n                    except Exception as e:\n                        logger.exception(str(e))\n                        self.__terminate = True\n                        raise RuntimeError(\"API failed to restart the Client-end!\")\n                    self.__poll.register(self.__msg_socket, zmq.POLLIN)\n\n                    continue\n            else:\n                try:\n                    msg_json = self.__msg_socket.recv_json(flags=self.__msg_flag)\n                except zmq.ZMQError as e:\n                    if e.errno == zmq.EAGAIN:\n                        logger.critical(\"Connection Timeout. Exiting!\")\n                        self.__terminate = True\n                        self.__queue.append(None)\n                        break\n\n            # check if terminate_flag` received\n            if msg_json and msg_json[\"terminate_flag\"]:\n                # if multiserver_mode is enabled\n                if self.__multiserver_mode:\n                    # check and remove from which ports signal is received\n                    if msg_json[\"port\"] in self.__port_buffer:\n                        # if pattern is 1, then send back server the info about termination\n                        if self.__pattern == 1:\n                            self.__msg_socket.send_string(\n                                \"Termination signal successfully received at client!\"\n                            )\n                        self.__port_buffer.remove(msg_json[\"port\"])\n                        self.__logging and logger.warning(\n                            \"Termination signal received from Server at port: {}!\".format(\n                                msg_json[\"port\"]\n                            )\n                        )\n                    # if termination signal received from all servers then exit client.\n                    if not self.__port_buffer:\n                        logger.critical(\n                            \"Termination signal received from all Servers!!!\"\n                        )\n                        self.__terminate = True  # termination\n                else:\n                    # if pattern is 1, then send back server the info about termination\n                    if self.__pattern == 1:\n                        self.__msg_socket.send_string(\n                            \"Termination signal successfully received at Client's end!\"\n                        )\n                    # termination\n                    self.__terminate = True\n                    # notify client\n                    self.__logging and logger.critical(\n                        \"Termination signal received from server!\"\n                    )\n                continue\n\n            try:\n                msg_data = self.__msg_socket.recv(\n                    flags=self.__msg_flag | zmq.DONTWAIT,\n                    copy=self.__msg_copy,\n                    track=self.__msg_track,\n                )\n            except zmq.ZMQError as e:\n                logger.critical(\"Socket Session Expired. Exiting!\")\n                self.__terminate = True\n                self.__queue.append(None)\n                break\n\n            # handle data transfer in synchronous modes.\n            if self.__pattern < 2:\n                if self.__bi_mode or self.__multiclient_mode:\n                    # check if we are returning `ndarray` frames\n                    if not (self.__return_data is None) and isinstance(\n                        self.__return_data, np.ndarray\n                    ):\n                        # handle return data for compression\n                        return_data = np.copy(self.__return_data)\n\n                        # check whether exit_flag is False\n                        if not (return_data.flags[\"C_CONTIGUOUS\"]):\n                            # check whether the incoming frame is contiguous\n                            return_data = np.ascontiguousarray(\n                                return_data, dtype=return_data.dtype\n                            )\n\n                        # handle jpeg-compression encoding\n                        if self.__jpeg_compression:\n                            if self.__jpeg_compression_colorspace == \"GRAY\":\n                                if return_data.ndim == 2:\n                                    # patch for https://gitlab.com/jfolz/simplejpeg/-/issues/11\n                                    return_data = return_data[:, :, np.newaxis]\n                                return_data = simplejpeg.encode_jpeg(\n                                    return_data,\n                                    quality=self.__jpeg_compression_quality,\n                                    colorspace=self.__jpeg_compression_colorspace,\n                                    fastdct=self.__jpeg_compression_fastdct,\n                                )\n                            else:\n                                return_data = simplejpeg.encode_jpeg(\n                                    return_data,\n                                    quality=self.__jpeg_compression_quality,\n                                    colorspace=self.__jpeg_compression_colorspace,\n                                    colorsubsampling=\"422\",\n                                    fastdct=self.__jpeg_compression_fastdct,\n                                )\n\n                        return_dict = (\n                            dict(port=self.__port)\n                            if self.__multiclient_mode\n                            else dict()\n                        )\n\n                        return_dict.update(\n                            dict(\n                                return_type=(type(self.__return_data).__name__),\n                                compression=(\n                                    {\n                                        \"dct\": self.__jpeg_compression_fastdct,\n                                        \"ups\": self.__jpeg_compression_fastupsample,\n                                        \"colorspace\": self.__jpeg_compression_colorspace,\n                                    }\n                                    if self.__jpeg_compression\n                                    else False\n                                ),\n                                array_dtype=(\n                                    str(self.__return_data.dtype)\n                                    if not (self.__jpeg_compression)\n                                    else \"\"\n                                ),\n                                array_shape=(\n                                    self.__return_data.shape\n                                    if not (self.__jpeg_compression)\n                                    else \"\"\n                                ),\n                                data=None,\n                            )\n                        )\n\n                        # send the json dict\n                        self.__msg_socket.send_json(\n                            return_dict, self.__msg_flag | zmq.SNDMORE\n                        )\n                        # send the array with correct flags\n                        self.__msg_socket.send(\n                            return_data,\n                            flags=self.__msg_flag,\n                            copy=self.__msg_copy,\n                            track=self.__msg_track,\n                        )\n                    else:\n                        return_dict = (\n                            dict(port=self.__port)\n                            if self.__multiclient_mode\n                            else dict()\n                        )\n                        return_dict.update(\n                            dict(\n                                return_type=(type(self.__return_data).__name__),\n                                data=self.__return_data,\n                            )\n                        )\n                        self.__msg_socket.send_json(return_dict, self.__msg_flag)\n                else:\n                    # send confirmation message to server\n                    self.__msg_socket.send_string(\n                        \"Data received on device: {} !\".format(self.__id)\n                    )\n            else:\n                # else raise warning\n                if self.__return_data:\n                    logger.warning(\"`return_data` is disabled for this pattern!\")\n\n            # check if encoding was enabled\n            if msg_json[\"compression\"]:\n                # decode JPEG frame\n                frame = simplejpeg.decode_jpeg(\n                    msg_data,\n                    colorspace=msg_json[\"compression\"][\"colorspace\"],\n                    fastdct=self.__jpeg_compression_fastdct\n                    or msg_json[\"compression\"][\"dct\"],\n                    fastupsample=self.__jpeg_compression_fastupsample\n                    or msg_json[\"compression\"][\"ups\"],\n                )\n                # check if valid frame returned\n                if frame is None:\n                    self.__terminate = True\n                    # otherwise raise error and exit\n                    raise RuntimeError(\n                        \"[NetGear:ERROR] :: Received compressed JPEG frame decoding failed\"\n                    )\n                if msg_json[\"compression\"][\"colorspace\"] == \"GRAY\" and frame.ndim == 3:\n                    # patch for https://gitlab.com/jfolz/simplejpeg/-/issues/11\n                    frame = np.squeeze(frame, axis=2)\n            else:\n                # recover and reshape frame from buffer\n                frame_buffer = np.frombuffer(msg_data, dtype=msg_json[\"dtype\"])\n                frame = frame_buffer.reshape(msg_json[\"shape\"])\n\n            # check if multiserver_mode\n            if self.__multiserver_mode:\n                # save the unique port addresses\n                if not msg_json[\"port\"] in self.__port_buffer:\n                    self.__port_buffer.append(msg_json[\"port\"])\n                # extract if any message from server and display it\n                if msg_json[\"message\"]:\n                    self.__queue.append((msg_json[\"port\"], msg_json[\"message\"], frame))\n                else:\n                    # append recovered unique port and frame to queue\n                    self.__queue.append((msg_json[\"port\"], frame))\n            # extract if any message from server if Bidirectional Mode is enabled\n            elif self.__bi_mode:\n                if msg_json[\"message\"]:\n                    # append grouped frame and data to queue\n                    self.__queue.append((msg_json[\"message\"], frame))\n                else:\n                    self.__queue.append((None, frame))\n            else:\n                # otherwise append recovered frame to queue\n                self.__queue.append(frame)\n\n    def recv(self, return_data=None):\n        \"\"\"\n        A Receiver end method, that extracts received frames synchronously from monitored deque, while maintaining a\n        fixed-length frame buffer in the memory, and blocks the thread if the deque is full.\n\n        Parameters:\n            return_data (any): inputs return data _(of any datatype)_, for sending back to Server.\n\n        **Returns:** A n-dimensional numpy array.\n        \"\"\"\n        # check whether `receive mode` is activated\n        if not (self.__receive_mode):\n            # raise value error and exit\n            self.__terminate = True\n            raise ValueError(\n                \"[NetGear:ERROR] :: `recv()` function cannot be used while receive_mode is disabled. Kindly refer vidgear docs!\"\n            )\n\n        # handle Bidirectional return data\n        if (self.__bi_mode or self.__multiclient_mode) and not (return_data is None):\n            self.__return_data = return_data\n\n        # check whether or not termination flag is enabled\n        while not self.__terminate:\n            try:\n                # check if queue is empty\n                if len(self.__queue) > 0:\n                    return self.__queue.popleft()\n                else:\n                    time.sleep(0.00001)\n                    continue\n            except KeyboardInterrupt:\n                self.__terminate = True\n                break\n        # otherwise return NoneType\n        return None\n\n    def send(self, frame, message=None):\n        \"\"\"\n        A Server end method, that sends the data and frames over the network to Client(s).\n\n        Parameters:\n            frame (numpy.ndarray): inputs numpy array(frame).\n            message (any): input for sending additional data _(of any datatype except `numpy.ndarray`)_ to Client(s).\n\n        **Returns:** Data _(of any datatype)_ in selected exclusive modes, otherwise None-type.\n\n        \"\"\"\n        # check whether `receive_mode` is disabled\n        if self.__receive_mode:\n            # raise value error and exit\n            self.__terminate = True\n            raise ValueError(\n                \"[NetGear:ERROR] :: `send()` function cannot be used while receive_mode is enabled. Kindly refer vidgear docs!\"\n            )\n\n        if not (message is None) and isinstance(message, np.ndarray):\n            logger.warning(\n                \"Skipped unsupported `message` of datatype: {}!\".format(\n                    type(message).__name__\n                )\n            )\n            message = None\n\n        # define exit_flag and assign value\n        exit_flag = True if (frame is None or self.__terminate) else False\n\n        # check whether exit_flag is False\n        if not (exit_flag) and not (frame.flags[\"C_CONTIGUOUS\"]):\n            # check whether the incoming frame is contiguous\n            frame = np.ascontiguousarray(frame, dtype=frame.dtype)\n\n        # handle JPEG compression encoding\n        if self.__jpeg_compression:\n            if self.__jpeg_compression_colorspace == \"GRAY\":\n                if frame.ndim == 2:\n                    # patch for https://gitlab.com/jfolz/simplejpeg/-/issues/11\n                    frame = np.expand_dims(frame, axis=2)\n                frame = simplejpeg.encode_jpeg(\n                    frame,\n                    quality=self.__jpeg_compression_quality,\n                    colorspace=self.__jpeg_compression_colorspace,\n                    fastdct=self.__jpeg_compression_fastdct,\n                )\n            else:\n                frame = simplejpeg.encode_jpeg(\n                    frame,\n                    quality=self.__jpeg_compression_quality,\n                    colorspace=self.__jpeg_compression_colorspace,\n                    colorsubsampling=\"422\",\n                    fastdct=self.__jpeg_compression_fastdct,\n                )\n\n        # check if multiserver_mode is activated and assign values with unique port\n        msg_dict = dict(port=self.__port) if self.__multiserver_mode else dict()\n\n        # prepare the exclusive json dict\n        msg_dict.update(\n            dict(\n                terminate_flag=exit_flag,\n                compression=(\n                    {\n                        \"dct\": self.__jpeg_compression_fastdct,\n                        \"ups\": self.__jpeg_compression_fastupsample,\n                        \"colorspace\": self.__jpeg_compression_colorspace,\n                    }\n                    if self.__jpeg_compression\n                    else False\n                ),\n                message=message,\n                pattern=str(self.__pattern),\n                dtype=str(frame.dtype) if not (self.__jpeg_compression) else \"\",\n                shape=frame.shape if not (self.__jpeg_compression) else \"\",\n            )\n        )\n\n        # send the json dict\n        self.__msg_socket.send_json(msg_dict, self.__msg_flag | zmq.SNDMORE)\n        # send the frame array with correct flags\n        self.__msg_socket.send(\n            frame, flags=self.__msg_flag, copy=self.__msg_copy, track=self.__msg_track\n        )\n\n        # check if synchronous patterns, then wait for confirmation\n        if self.__pattern < 2:\n            # check if Bidirectional data transmission is enabled\n            if self.__bi_mode or self.__multiclient_mode:\n                # handles return data\n                recvd_data = None\n\n                socks = dict(self.__poll.poll(self.__request_timeout))\n                if socks.get(self.__msg_socket) == zmq.POLLIN:\n                    # handle return data\n                    recv_json = self.__msg_socket.recv_json(flags=self.__msg_flag)\n                else:\n                    logger.critical(\"No response from Client, Reconnecting again...\")\n                    # Socket is confused. Close and remove it.\n                    self.__msg_socket.setsockopt(zmq.LINGER, 0)\n                    self.__msg_socket.close()\n                    self.__poll.unregister(self.__msg_socket)\n                    self.__max_retries -= 1\n\n                    if not (self.__max_retries):\n                        if self.__multiclient_mode:\n                            logger.error(\n                                \"All Clients failed to respond on multiple attempts.\"\n                            )\n                        else:\n                            logger.error(\n                                \"Client failed to respond on multiple attempts.\"\n                            )\n                        self.__terminate = True\n                        raise RuntimeError(\n                            \"[NetGear:ERROR] :: Client(s) seems to be offline, Abandoning.\"\n                        )\n\n                    # Create new connection\n                    self.__msg_socket = self.__msg_context.socket(self.__msg_pattern)\n                    if isinstance(self.__connection_address, list):\n                        for _connection in self.__connection_address:\n                            self.__msg_socket.connect(_connection)\n                    else:\n                        # handle SSH tunneling if enabled\n                        if self.__ssh_tunnel_mode:\n                            # establish tunnel connection\n                            ssh.tunnel_connection(\n                                self.__msg_socket,\n                                self.__connection_address,\n                                self.__ssh_tunnel_mode,\n                                keyfile=self.__ssh_tunnel_keyfile,\n                                password=self.__ssh_tunnel_pwd,\n                                paramiko=self.__paramiko_present,\n                            )\n                        else:\n                            # connect normally\n                            self.__msg_socket.connect(self.__connection_address)\n                    self.__poll.register(self.__msg_socket, zmq.POLLIN)\n                    # return None for mean-time\n                    return None\n\n                # save the unique port addresses\n                if (\n                    self.__multiclient_mode\n                    and not recv_json[\"port\"] in self.__port_buffer\n                ):\n                    self.__port_buffer.append(recv_json[\"port\"])\n\n                if recv_json[\"return_type\"] == \"ndarray\":\n                    recv_array = self.__msg_socket.recv(\n                        flags=self.__msg_flag,\n                        copy=self.__msg_copy,\n                        track=self.__msg_track,\n                    )\n                    # check if encoding was enabled\n                    if recv_json[\"compression\"]:\n                        # decode JPEG frame\n                        recvd_data = simplejpeg.decode_jpeg(\n                            recv_array,\n                            colorspace=recv_json[\"compression\"][\"colorspace\"],\n                            fastdct=self.__jpeg_compression_fastdct\n                            or recv_json[\"compression\"][\"dct\"],\n                            fastupsample=self.__jpeg_compression_fastupsample\n                            or recv_json[\"compression\"][\"ups\"],\n                        )\n                        # check if valid frame returned\n                        if recvd_data is None:\n                            self.__terminate = True\n                            # otherwise raise error and exit\n                            raise RuntimeError(\n                                \"[NetGear:ERROR] :: Received compressed frame `{}` decoding failed with flag: {}.\".format(\n                                    recv_json[\"compression\"],\n                                    self.__ex_compression_params,\n                                )\n                            )\n\n                        if (\n                            recv_json[\"compression\"][\"colorspace\"] == \"GRAY\"\n                            and recvd_data.ndim == 3\n                        ):\n                            # patch for https://gitlab.com/jfolz/simplejpeg/-/issues/11\n                            recvd_data = np.squeeze(recvd_data, axis=2)\n                    else:\n                        recvd_data = np.frombuffer(\n                            recv_array, dtype=recv_json[\"array_dtype\"]\n                        ).reshape(recv_json[\"array_shape\"])\n                else:\n                    recvd_data = recv_json[\"data\"]\n\n                return (\n                    (recv_json[\"port\"], recvd_data)\n                    if self.__multiclient_mode\n                    else recvd_data\n                )\n            else:\n                # otherwise log normally\n                socks = dict(self.__poll.poll(self.__request_timeout))\n                if socks.get(self.__msg_socket) == zmq.POLLIN:\n                    recv_confirmation = self.__msg_socket.recv()\n                else:\n                    logger.critical(\"No response from Client, Reconnecting again...\")\n                    # Socket is confused. Close and remove it.\n                    self.__msg_socket.setsockopt(zmq.LINGER, 0)\n                    self.__msg_socket.close()\n                    self.__poll.unregister(self.__msg_socket)\n                    self.__max_retries -= 1\n\n                    if not (self.__max_retries):\n                        logger.error(\"Client failed to respond on repeated attempts.\")\n                        self.__terminate = True\n                        raise RuntimeError(\n                            \"[NetGear:ERROR] :: Client seems to be offline, Abandoning!\"\n                        )\n\n                    # Create new connection\n                    self.__msg_socket = self.__msg_context.socket(self.__msg_pattern)\n                    # handle SSH tunneling if enabled\n                    if self.__ssh_tunnel_mode:\n                        # establish tunnel connection\n                        ssh.tunnel_connection(\n                            self.__msg_socket,\n                            self.__connection_address,\n                            self.__ssh_tunnel_mode,\n                            keyfile=self.__ssh_tunnel_keyfile,\n                            password=self.__ssh_tunnel_pwd,\n                            paramiko=self.__paramiko_present,\n                        )\n                    else:\n                        # connect normally\n                        self.__msg_socket.connect(self.__connection_address)\n                    self.__poll.register(self.__msg_socket, zmq.POLLIN)\n                    return None\n\n                # log confirmation\n                self.__logging and logger.debug(recv_confirmation)\n\n    def close(self, kill=False):\n        \"\"\"\n        Safely terminates the threads, and NetGear resources.\n\n        Parameters:\n            kill (bool): Kills ZMQ context instead of graceful exiting in receive mode.\n        \"\"\"\n        # log it\n        self.__logging and logger.debug(\n            \"Terminating various {} Processes.\".format(\n                \"Receive Mode\" if self.__receive_mode else \"Send Mode\"\n            )\n        )\n        #  whether `receive_mode` is enabled or not\n        if self.__receive_mode:\n            # check whether queue mode is empty\n            if not (self.__queue is None) and self.__queue:\n                self.__queue.clear()\n            # call immediate termination\n            self.__terminate = True\n            # properly close the socket\n            self.__logging and logger.debug(\"Terminating. Please wait...\")\n            # Handle Secure Mode Thread\n            if self.__z_auth:\n                self.__logging and logger.debug(\"Terminating Authenticator Thread.\")\n                self.__z_auth.stop()\n                while self.__z_auth.is_alive():\n                    pass\n            # wait until stream resources are released\n            # (producer thread might be still grabbing frame)\n            if self.__thread is not None:\n                self.__logging and logger.debug(\"Terminating Main Thread.\")\n                # properly handle thread exit\n                if self.__thread.is_alive() and kill:\n                    # force close if still alive\n                    logger.warning(\"Thread still running...Killing it forcefully!\")\n                    self.__msg_context.destroy()\n                    self.__thread.join()\n                else:\n                    self.__msg_socket.close(linger=0)\n                    self.__thread.join()\n                self.__thread = None\n            self.__logging and logger.debug(\"Terminated Successfully!\")\n        else:\n            # indicate that process should be terminated\n            self.__terminate = True\n            # log if kill enabled\n            kill and logger.warning(\n                \"`kill` parmeter is only available in the receive mode.\"\n            )\n            # Handle Secure Mode Thread\n            if self.__z_auth:\n                self.__logging and logger.debug(\"Terminating Authenticator Thread.\")\n                self.__z_auth.stop()\n                while self.__z_auth.is_alive():\n                    pass\n            # check if all attempts of reconnecting failed, then skip to closure\n            if (self.__pattern < 2 and not self.__max_retries) or (\n                self.__multiclient_mode and not self.__port_buffer\n            ):\n                try:\n                    # properly close the socket\n                    self.__msg_socket.setsockopt(zmq.LINGER, 0)\n                    self.__msg_socket.close()\n                except ZMQError:\n                    pass\n                finally:\n                    # exit\n                    return\n\n            if self.__multiserver_mode:\n                # check if multiserver_mode\n                # send termination flag to client with its unique port\n                term_dict = dict(terminate_flag=True, port=self.__port)\n            else:\n                # otherwise send termination flag to client\n                term_dict = dict(terminate_flag=True)\n\n            try:\n                if self.__multiclient_mode:\n                    for _ in self.__port_buffer:\n                        self.__msg_socket.send_json(term_dict)\n                else:\n                    self.__msg_socket.send_json(term_dict)\n\n                # check for confirmation if available within 1/5 timeout\n                if self.__pattern < 2:\n                    self.__logging and logger.debug(\"Terminating. Please wait...\")\n                    if self.__msg_socket.poll(self.__request_timeout // 5, zmq.POLLIN):\n                        self.__msg_socket.recv()\n            except Exception as e:\n                if not isinstance(e, ZMQError):\n                    logger.exception(str(e))\n            finally:\n                # properly close the socket\n                self.__msg_socket.setsockopt(zmq.LINGER, 0)\n                self.__msg_socket.close()\n                self.__logging and logger.debug(\"Terminated Successfully!\")\n

"},{"location":"bonus/reference/netgear/#vidgear.gears.netgear.NetGear.__init__","title":"__init__(self, address=None, port=None, protocol=None, pattern=0, receive_mode=False, logging=False, **options) special","text":"

This constructor method initializes the object state and attributes of the NetGear class.

Parameters:

Name Type Description Default address str

sets the valid network address of the Server/Client.

None port str

sets the valid Network Port of the Server/Client.

None protocol str

sets the valid messaging protocol between Server/Client.

None pattern int

sets the supported messaging pattern(flow of communication) between Server/Client

0 receive_mode bool

select the Netgear's Mode of operation.

False logging bool

enables/disables logging.

False options dict

provides the flexibility to alter various NetGear internal properties.

{} Source code in vidgear/gears/netgear.py
def __init__(\n    self,\n    address=None,\n    port=None,\n    protocol=None,\n    pattern=0,\n    receive_mode=False,\n    logging=False,\n    **options\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the NetGear class.\n\n    Parameters:\n        address (str): sets the valid network address of the Server/Client.\n        port (str): sets the valid Network Port of the Server/Client.\n        protocol (str): sets the valid messaging protocol between Server/Client.\n        pattern (int): sets the supported messaging pattern(flow of communication) between Server/Client\n        receive_mode (bool): select the Netgear's Mode of operation.\n        logging (bool): enables/disables logging.\n        options (dict): provides the flexibility to alter various NetGear internal properties.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # raise error(s) for critical Class imports\n    import_dependency_safe(\n        \"zmq\" if zmq is None else \"\", min_version=\"4.0\", pkg_name=\"pyzmq\"\n    )\n    import_dependency_safe(\n        \"simplejpeg\" if simplejpeg is None else \"\", error=\"log\", min_version=\"1.6.1\"\n    )\n\n    # define valid messaging patterns => `0`: zmq.PAIR, `1`:(zmq.REQ,zmq.REP), and `1`:(zmq.SUB,zmq.PUB)\n    valid_messaging_patterns = {\n        0: (zmq.PAIR, zmq.PAIR),\n        1: (zmq.REQ, zmq.REP),\n        2: (zmq.PUB, zmq.SUB),\n    }\n\n    # Handle messaging pattern\n    msg_pattern = None\n    # check whether user-defined messaging pattern is valid\n    if isinstance(pattern, int) and pattern in valid_messaging_patterns.keys():\n        # assign value\n        msg_pattern = valid_messaging_patterns[pattern]\n    else:\n        # otherwise default to 0:`zmq.PAIR`\n        pattern = 0\n        msg_pattern = valid_messaging_patterns[pattern]\n        self.__logging and logger.warning(\n            \"Wrong pattern value, Defaulting to `zmq.PAIR`! Kindly refer Docs for more Information.\"\n        )\n    # assign pattern to global parameter for further use\n    self.__pattern = pattern\n\n    # Handle messaging protocol\n    if protocol is None or not (protocol in [\"tcp\", \"ipc\"]):\n        # else default to `tcp` protocol\n        protocol = \"tcp\"\n        # log it\n        self.__logging and logger.warning(\n            \"Protocol is not supported or not provided. Defaulting to `tcp` protocol!\"\n        )\n\n    # Handle connection params\n\n    self.__msg_flag = 0  # handles connection flags\n    self.__msg_copy = False  # handles whether to copy data\n    self.__msg_track = False  # handles whether to track packets\n\n    # Handle NetGear's internal exclusive modes and params\n\n    # define Secure Mode\n    self.__z_auth = None\n\n    # define SSH Tunneling Mode\n    self.__ssh_tunnel_mode = None  # handles ssh_tunneling mode state\n    self.__ssh_tunnel_pwd = None\n    self.__ssh_tunnel_keyfile = None\n    self.__paramiko_present = False if paramiko is None else True\n\n    # define Multi-Server mode\n    self.__multiserver_mode = False  # handles multi-server mode state\n\n    # define Multi-Client mode\n    self.__multiclient_mode = False  # handles multi-client mode state\n\n    # define Bidirectional mode\n    self.__bi_mode = False  # handles Bidirectional mode state\n\n    # define Secure mode\n    valid_security_mech = {0: \"Grasslands\", 1: \"StoneHouse\", 2: \"IronHouse\"}\n    self.__secure_mode = 0  # handles ZMQ security layer status\n    auth_cert_dir = \"\"  # handles valid ZMQ certificates dir\n    self.__auth_publickeys_dir = \"\"  # handles valid ZMQ public certificates dir\n    self.__auth_secretkeys_dir = \"\"  # handles valid ZMQ private certificates dir\n    overwrite_cert = False  # checks if certificates overwriting allowed\n    custom_cert_location = \"\"  # handles custom ZMQ certificates path\n\n    # define frame-compression handler\n    self.__jpeg_compression = (\n        True if not (simplejpeg is None) else False\n    )  # enabled by default for all connections if simplejpeg is installed\n    self.__jpeg_compression_quality = 90  # 90% quality\n    self.__jpeg_compression_fastdct = True  # fastest DCT on by default\n    self.__jpeg_compression_fastupsample = False  # fastupsample off by default\n    self.__jpeg_compression_colorspace = \"BGR\"  # use BGR colorspace by default\n\n    # defines frame compression on return data\n    self.__ex_compression_params = None\n\n    # define receiver return data handler\n    self.__return_data = None\n\n    # generate 8-digit random system id\n    self.__id = \"\".join(\n        secrets.choice(string.ascii_uppercase + string.digits) for i in range(8)\n    )\n\n    # define termination flag\n    self.__terminate = False\n\n    # additional settings for reliability\n    if pattern < 2:\n        # define zmq poller for reliable transmission\n        self.__poll = zmq.Poller()\n        # define max retries\n        self.__max_retries = 3\n        # request timeout\n        self.__request_timeout = 4000  # 4 secs\n    else:\n        # subscriber timeout\n        self.__subscriber_timeout = None\n\n    # Handle user-defined options dictionary values\n    # reformat dictionary\n    options = {str(k).strip(): v for k, v in options.items()}\n\n    # loop over dictionary key & values and assign to global variables if valid\n    for key, value in options.items():\n        # handle multi-server mode\n        if key == \"multiserver_mode\" and isinstance(value, bool):\n            # check if valid pattern assigned\n            if pattern > 0:\n                # activate Multi-server mode\n                self.__multiserver_mode = value\n            else:\n                # otherwise disable it and raise error\n                self.__multiserver_mode = False\n                logger.critical(\"Multi-Server Mode is disabled!\")\n                raise ValueError(\n                    \"[NetGear:ERROR] :: `{}` pattern is not valid when Multi-Server Mode is enabled. Kindly refer Docs for more Information.\".format(\n                        pattern\n                    )\n                )\n\n        # handle multi-client mode\n        elif key == \"multiclient_mode\" and isinstance(value, bool):\n            # check if valid pattern assigned\n            if pattern > 0:\n                # activate Multi-client mode\n                self.__multiclient_mode = value\n            else:\n                # otherwise disable it and raise error\n                self.__multiclient_mode = False\n                logger.critical(\"Multi-Client Mode is disabled!\")\n                raise ValueError(\n                    \"[NetGear:ERROR] :: `{}` pattern is not valid when Multi-Client Mode is enabled. Kindly refer Docs for more Information.\".format(\n                        pattern\n                    )\n                )\n\n        # handle bidirectional mode\n        elif key == \"bidirectional_mode\" and isinstance(value, bool):\n            # check if pattern is valid\n            if pattern < 2:\n                # activate Bidirectional mode if specified\n                self.__bi_mode = value\n            else:\n                # otherwise disable it and raise error\n                self.__bi_mode = False\n                logger.warning(\"Bidirectional data transmission is disabled!\")\n                raise ValueError(\n                    \"[NetGear:ERROR] :: `{}` pattern is not valid when Bidirectional Mode is enabled. Kindly refer Docs for more Information!\".format(\n                        pattern\n                    )\n                )\n\n        # handle secure mode\n        elif (\n            key == \"secure_mode\"\n            and isinstance(value, int)\n            and (value in valid_security_mech)\n        ):\n            self.__secure_mode = value\n\n        elif key == \"custom_cert_location\" and isinstance(value, str):\n            # verify custom auth certificates path for secure mode\n            custom_cert_location = os.path.abspath(value)\n            assert os.path.isdir(\n                custom_cert_location\n            ), \"[NetGear:ERROR] :: `custom_cert_location` value must be the path to a valid directory!\"\n            assert check_WriteAccess(\n                custom_cert_location,\n                is_windows=True if os.name == \"nt\" else False,\n                logging=self.__logging,\n            ), \"[NetGear:ERROR] :: Permission Denied!, cannot write ZMQ authentication certificates to '{}' directory!\".format(\n                value\n            )\n        elif key == \"overwrite_cert\" and isinstance(value, bool):\n            # enable/disable auth certificate overwriting in secure mode\n            overwrite_cert = value\n\n        # handle ssh-tunneling mode\n        elif key == \"ssh_tunnel_mode\" and isinstance(value, str):\n            # enable SSH Tunneling Mode\n            self.__ssh_tunnel_mode = value.strip()\n        elif key == \"ssh_tunnel_pwd\" and isinstance(value, str):\n            # add valid SSH Tunneling password\n            self.__ssh_tunnel_pwd = value\n        elif key == \"ssh_tunnel_keyfile\" and isinstance(value, str):\n            # add valid SSH Tunneling key-file\n            self.__ssh_tunnel_keyfile = value if os.path.isfile(value) else None\n            if self.__ssh_tunnel_keyfile is None:\n                logger.warning(\n                    \"Discarded invalid or non-existential SSH Tunnel Key-file at {}!\".format(\n                        value\n                    )\n                )\n\n        # handle jpeg compression\n        elif (\n            key == \"jpeg_compression\"\n            and not (simplejpeg is None)\n            and isinstance(value, (bool, str))\n        ):\n            if isinstance(value, str) and value.strip().upper() in [\n                \"RGB\",\n                \"BGR\",\n                \"RGBX\",\n                \"BGRX\",\n                \"XBGR\",\n                \"XRGB\",\n                \"GRAY\",\n                \"RGBA\",\n                \"BGRA\",\n                \"ABGR\",\n                \"ARGB\",\n                \"CMYK\",\n            ]:\n                # set encoding colorspace\n                self.__jpeg_compression_colorspace = value.strip().upper()\n                # enable frame-compression encoding value\n                self.__jpeg_compression = True\n            else:\n                # enable frame-compression encoding value\n                self.__jpeg_compression = value\n        elif key == \"jpeg_compression_quality\" and isinstance(value, (int, float)):\n            # set valid jpeg quality\n            if value >= 10 and value <= 100:\n                self.__jpeg_compression_quality = int(value)\n            else:\n                logger.warning(\"Skipped invalid `jpeg_compression_quality` value!\")\n        elif key == \"jpeg_compression_fastdct\" and isinstance(value, bool):\n            # enable jpeg fastdct\n            self.__jpeg_compression_fastdct = value\n        elif key == \"jpeg_compression_fastupsample\" and isinstance(value, bool):\n            # enable jpeg  fastupsample\n            self.__jpeg_compression_fastupsample = value\n\n        # assign maximum retries in synchronous patterns\n        elif key == \"max_retries\" and isinstance(value, int) and pattern < 2:\n            if value >= 0:\n                self.__max_retries = value\n            else:\n                logger.warning(\"Invalid `max_retries` value skipped!\")\n\n        # assign request timeout in synchronous patterns\n        elif key == \"request_timeout\" and isinstance(value, int) and pattern < 2:\n            if value >= 4:\n                self.__request_timeout = value * 1000  # covert to milliseconds\n            else:\n                logger.warning(\"Invalid `request_timeout` value skipped!\")\n\n        # assign subscriber timeout\n        elif (\n            key == \"subscriber_timeout\" and isinstance(value, int) and pattern == 2\n        ):\n            if value > 0:\n                self.__subscriber_timeout = value * 1000  # covert to milliseconds\n            else:\n                logger.warning(\"Invalid `request_timeout` value skipped!\")\n\n        # handle ZMQ flags\n        elif key == \"flag\" and isinstance(value, int):\n            self.__msg_flag = value\n            self.__msg_flag and logger.warning(\n                \"The flag optional value is set to `1` (NOBLOCK) for this run. This might cause NetGear to not terminate gracefully.\"\n            )\n        elif key == \"copy\" and isinstance(value, bool):\n            self.__msg_copy = value\n        elif key == \"track\" and isinstance(value, bool):\n            self.__msg_track = value\n            self.__msg_copy and self.__msg_track and logger.info(\n                \"The `track` optional value will be ignored for this run because `copy=True` is also defined.\"\n            )\n        else:\n            pass\n\n    # Handle ssh tunneling if enabled\n    if not (self.__ssh_tunnel_mode is None):\n        # SSH Tunnel Mode only available for server mode\n        if receive_mode:\n            logger.error(\"SSH Tunneling cannot be enabled for Client-end!\")\n        else:\n            # check if SSH tunneling possible\n            ssh_address = self.__ssh_tunnel_mode\n            ssh_address, ssh_port = (\n                ssh_address.split(\":\")\n                if \":\" in ssh_address\n                else [ssh_address, \"22\"]\n            )  # default to port 22\n            if \"47\" in ssh_port:\n                self.__ssh_tunnel_mode = self.__ssh_tunnel_mode.replace(\n                    \":47\", \"\"\n                )  # port-47 is reserved for testing\n            else:\n                # extract ip for validation\n                ssh_user, ssh_ip = (\n                    ssh_address.split(\"@\")\n                    if \"@\" in ssh_address\n                    else [\"\", ssh_address]\n                )\n                # validate ip specified port\n                assert check_open_port(\n                    ssh_ip, port=int(ssh_port)\n                ), \"[NetGear:ERROR] :: Host `{}` is not available for SSH Tunneling at port-{}!\".format(\n                    ssh_address, ssh_port\n                )\n\n    # Handle multiple exclusive modes if enabled\n    if self.__multiclient_mode and self.__multiserver_mode:\n        raise ValueError(\n            \"[NetGear:ERROR] :: Multi-Client and Multi-Server Mode cannot be enabled simultaneously!\"\n        )\n    elif self.__multiserver_mode or self.__multiclient_mode:\n        # check if Bidirectional Mode also enabled\n        if self.__bi_mode:\n            # log it\n            self.__logging and logger.debug(\n                \"Bidirectional Data Transmission is also enabled for this connection!\"\n            )\n        # check if SSH Tunneling Mode also enabled\n        if self.__ssh_tunnel_mode:\n            # raise error\n            raise ValueError(\n                \"[NetGear:ERROR] :: SSH Tunneling and {} Mode cannot be enabled simultaneously. Kindly refer docs!\".format(\n                    \"Multi-Server\" if self.__multiserver_mode else \"Multi-Client\"\n                )\n            )\n    elif self.__bi_mode:\n        # log Bidirectional mode activation\n        self.__logging and logger.debug(\n            \"Bidirectional Data Transmission is enabled for this connection!\"\n        )\n    elif self.__ssh_tunnel_mode:\n        # log Bidirectional mode activation\n        self.__logging and logger.debug(\n            \"SSH Tunneling is enabled for host:`{}` with `{}` back-end.\".format(\n                self.__ssh_tunnel_mode,\n                \"paramiko\" if self.__paramiko_present else \"pexpect\",\n            )\n        )\n\n    # On Windows, NetGear requires the ``WindowsSelectorEventLoop`` but Python 3.8 and above,\n    # defaults to an ``ProactorEventLoop`` loop that is not compatible with it. Thereby,\n    # we had to set it manually.\n    platform.system() == \"Windows\" and asyncio.set_event_loop_policy(\n        asyncio.WindowsSelectorEventLoopPolicy()\n    )\n\n    # define ZMQ messaging context instance\n    self.__msg_context = zmq.Context.instance()\n\n    # initialize and assign receive mode to global variable\n    self.__receive_mode = receive_mode\n\n    # Handle Secure mode\n    if self.__secure_mode > 0:\n        # activate and log if overwriting is enabled\n        if receive_mode:\n            overwrite_cert = False\n            overwrite_cert and logger.warning(\n                \"Overwriting ZMQ Authentication certificates is disabled for Client's end!\"\n            )\n        else:\n            overwrite_cert and self.__logging and logger.info(\n                \"Overwriting ZMQ Authentication certificates over previous ones!\"\n            )\n\n        # Validate certificate generation paths\n        # Start threaded authenticator for this context\n        try:\n            # check if custom certificates path is specified\n            if custom_cert_location:\n                (\n                    auth_cert_dir,\n                    self.__auth_secretkeys_dir,\n                    self.__auth_publickeys_dir,\n                ) = generate_auth_certificates(\n                    custom_cert_location, overwrite=overwrite_cert, logging=logging\n                )\n            else:\n                # otherwise auto-generate suitable path\n                (\n                    auth_cert_dir,\n                    self.__auth_secretkeys_dir,\n                    self.__auth_publickeys_dir,\n                ) = generate_auth_certificates(\n                    os.path.join(expanduser(\"~\"), \".vidgear\"),\n                    overwrite=overwrite_cert,\n                    logging=logging,\n                )\n            # log it\n            self.__logging and logger.debug(\n                \"`{}` is the default location for storing ZMQ authentication certificates/keys.\".format(\n                    auth_cert_dir\n                )\n            )\n\n            # start an authenticator for this context\n            self.__z_auth = ThreadAuthenticator(self.__msg_context)\n            self.__z_auth.start()\n            self.__z_auth.allow(str(address))  # allow current address\n\n            # check if `IronHouse` is activated\n            if self.__secure_mode == 2:\n                # tell authenticator to use the certificate from given valid dir\n                self.__z_auth.configure_curve(\n                    domain=\"*\", location=self.__auth_publickeys_dir\n                )\n            else:\n                # otherwise tell the authenticator how to handle the CURVE requests, if `StoneHouse` is activated\n                self.__z_auth.configure_curve(\n                    domain=\"*\", location=auth.CURVE_ALLOW_ANY\n                )\n        except zmq.ZMQError as e:\n            if \"Address in use\" in str(e):\n                logger.info(\"ZMQ Authenticator already running.\")\n            else:\n                # catch if any error occurred and disable Secure mode\n                logger.exception(str(e))\n                self.__secure_mode = 0\n                logger.error(\n                    \"ZMQ Security Mechanism is disabled for this connection due to errors!\"\n                )\n\n    # check whether `receive_mode` is enabled\n    if self.__receive_mode:\n        # define connection address\n        address = \"*\" if address is None else address\n\n        # check if multiserver_mode is enabled\n        if self.__multiserver_mode:\n            # check if unique server port address list/tuple is assigned or not in multiserver_mode\n            if port is None or not isinstance(port, (tuple, list)):\n                # raise error if not\n                raise ValueError(\n                    \"[NetGear:ERROR] :: Incorrect port value! Kindly provide a list/tuple of Server ports while Multi-Server mode is enabled. For more information refer VidGear docs.\"\n                )\n            else:\n                # otherwise log it\n                logger.debug(\n                    \"Enabling Multi-Server Mode at PORTS: {}!\".format(port)\n                )\n            # create port address buffer for keeping track of connected client's port(s)\n            self.__port_buffer = []\n        # check if multiclient_mode is enabled\n        elif self.__multiclient_mode:\n            # check if unique server port address is assigned or not in multiclient_mode\n            if port is None:\n                # raise error if not\n                raise ValueError(\n                    \"[NetGear:ERROR] :: Kindly provide a unique & valid port value at Client-end. For more information refer VidGear docs.\"\n                )\n            else:\n                # otherwise log it\n                logger.debug(\n                    \"Enabling Multi-Client Mode at PORT: {} on this device!\".format(\n                        port\n                    )\n                )\n            # assign value to global variable\n            self.__port = port\n        else:\n            # otherwise assign local port address if None\n            port = \"5555\" if port is None else port\n\n        try:\n            # define thread-safe messaging socket\n            self.__msg_socket = self.__msg_context.socket(msg_pattern[1])\n\n            # define pub-sub flag\n            self.__pattern == 2 and self.__msg_socket.set_hwm(1)\n\n            # enable specified secure mode for the socket\n            if self.__secure_mode > 0:\n                # load server key\n                server_secret_file = os.path.join(\n                    self.__auth_secretkeys_dir, \"server.key_secret\"\n                )\n                server_public, server_secret = auth.load_certificate(\n                    server_secret_file\n                )\n                # load  all CURVE keys\n                self.__msg_socket.curve_secretkey = server_secret\n                self.__msg_socket.curve_publickey = server_public\n                # enable CURVE connection for this socket\n                self.__msg_socket.curve_server = True\n\n            # define exclusive socket options for `patterns=2`\n            if self.__pattern == 2:\n                self.__msg_socket.setsockopt_string(zmq.SUBSCRIBE, \"\")\n                self.__subscriber_timeout and self.__msg_socket.setsockopt(\n                    zmq.RCVTIMEO, self.__subscriber_timeout\n                )\n                self.__subscriber_timeout and self.__msg_socket.setsockopt(\n                    zmq.LINGER, 0\n                )\n\n            # if multiserver_mode is enabled, then assign port addresses to zmq socket\n            if self.__multiserver_mode:\n                # bind socket to given server protocol, address and ports\n                for pt in port:\n                    self.__msg_socket.bind(\n                        protocol + \"://\" + str(address) + \":\" + str(pt)\n                    )\n            else:\n                # bind socket to given protocol, address and port normally\n                self.__msg_socket.bind(\n                    protocol + \"://\" + str(address) + \":\" + str(port)\n                )\n\n            # additional settings\n            if pattern < 2:\n                if self.__multiserver_mode:\n                    self.__connection_address = []\n                    for pt in port:\n                        self.__connection_address.append(\n                            protocol + \"://\" + str(address) + \":\" + str(pt)\n                        )\n                else:\n                    self.__connection_address = (\n                        protocol + \"://\" + str(address) + \":\" + str(port)\n                    )\n                self.__msg_pattern = msg_pattern[1]\n                self.__poll.register(self.__msg_socket, zmq.POLLIN)\n                self.__logging and logger.debug(\n                    \"Reliable transmission is enabled for this pattern with max-retries: {} and timeout: {} secs.\".format(\n                        self.__max_retries, self.__request_timeout / 1000\n                    )\n                )\n            else:\n                self.__logging and self.__subscriber_timeout and logger.debug(\n                    \"Timeout: {} secs is enabled for this system.\".format(\n                        self.__subscriber_timeout / 1000\n                    )\n                )\n\n        except Exception as e:\n            # otherwise log and raise error\n            logger.exception(str(e))\n            # Handle Secure Mode\n            self.__secure_mode and logger.critical(\n                \"Failed to activate Secure Mode: `{}` for this connection!\".format(\n                    valid_security_mech[self.__secure_mode]\n                )\n            )\n            # raise errors for exclusive modes\n            if self.__multiserver_mode or self.__multiclient_mode:\n                raise RuntimeError(\n                    \"[NetGear:ERROR] :: Receive Mode failed to activate {} Mode at address: {} with pattern: {}! Kindly recheck all parameters.\".format(\n                        (\n                            \"Multi-Server\"\n                            if self.__multiserver_mode\n                            else \"Multi-Client\"\n                        ),\n                        (protocol + \"://\" + str(address) + \":\" + str(port)),\n                        pattern,\n                    )\n                )\n            else:\n                self.__bi_mode and logger.critical(\n                    \"Failed to activate Bidirectional Mode for this connection!\"\n                )\n                raise RuntimeError(\n                    \"[NetGear:ERROR] :: Receive Mode failed to bind address: {} and pattern: {}! Kindly recheck all parameters.\".format(\n                        (protocol + \"://\" + str(address) + \":\" + str(port)), pattern\n                    )\n                )\n\n        # Handle threaded queue mode\n        self.__logging and logger.debug(\n            \"Threaded Queue Mode is enabled by default for this connection.\"\n        )\n\n        # define deque and assign it to global var\n        self.__queue = deque(maxlen=96)  # max len 96 to check overflow\n\n        # initialize and start threaded recv_handler\n        self.__thread = Thread(target=self.__recv_handler, name=\"NetGear\", args=())\n        self.__thread.daemon = True\n        self.__thread.start()\n\n        if self.__logging:\n            # finally log progress\n            logger.debug(\n                \"Successfully Binded to address: {} with pattern: {}.\".format(\n                    (protocol + \"://\" + str(address) + \":\" + str(port)), pattern\n                )\n            )\n            self.__jpeg_compression and logger.debug(\n                \"JPEG Frame-Compression is activated for this connection with Colorspace:`{}`, Quality:`{}`%, Fastdct:`{}`, and Fastupsample:`{}`.\".format(\n                    self.__jpeg_compression_colorspace,\n                    self.__jpeg_compression_quality,\n                    (\"enabled\" if self.__jpeg_compression_fastdct else \"disabled\"),\n                    (\n                        \"enabled\"\n                        if self.__jpeg_compression_fastupsample\n                        else \"disabled\"\n                    ),\n                )\n            )\n            self.__secure_mode and logger.debug(\n                \"Successfully enabled ZMQ Security Mechanism: `{}` for this connection.\".format(\n                    valid_security_mech[self.__secure_mode]\n                )\n            )\n            logger.debug(\"Multi-threaded Receive Mode is successfully enabled.\")\n            logger.debug(\"Unique System ID is {}.\".format(self.__id))\n            logger.debug(\"Receive Mode is now activated.\")\n\n    else:\n        # otherwise default to `Send Mode`\n        # define connection address\n        address = \"localhost\" if address is None else address\n\n        # check if multiserver_mode is enabled\n        if self.__multiserver_mode:\n            # check if unique server port address is assigned or not in multiserver_mode\n            if port is None:\n                # raise error if not\n                raise ValueError(\n                    \"[NetGear:ERROR] :: Kindly provide a unique & valid port value at Server-end. For more information refer VidGear docs.\"\n                )\n            else:\n                # otherwise log it\n                logger.debug(\n                    \"Enabling Multi-Server Mode at PORT: {} on this device!\".format(\n                        port\n                    )\n                )\n            # assign value to global variable\n            self.__port = port\n        # check if multiclient_mode is enabled\n        elif self.__multiclient_mode:\n            # check if unique client port address list/tuple is assigned or not in multiclient_mode\n            if port is None or not isinstance(port, (tuple, list)):\n                # raise error if not\n                raise ValueError(\n                    \"[NetGear:ERROR] :: Incorrect port value! Kindly provide a list/tuple of Client ports while Multi-Client mode is enabled. For more information refer VidGear docs.\"\n                )\n            else:\n                # otherwise log it\n                logger.debug(\n                    \"Enabling Multi-Client Mode at PORTS: {}!\".format(port)\n                )\n            # create port address buffer for keeping track of connected client ports\n            self.__port_buffer = []\n        else:\n            # otherwise assign local port address if None\n            port = \"5555\" if port is None else port\n\n        try:\n            # define thread-safe messaging socket\n            self.__msg_socket = self.__msg_context.socket(msg_pattern[0])\n\n            # if req/rep pattern, define additional flags\n            if self.__pattern == 1:\n                self.__msg_socket.REQ_RELAXED = True\n                self.__msg_socket.REQ_CORRELATE = True\n\n            # if pub/sub pattern, define additional optimizer\n            if self.__pattern == 2:\n                self.__msg_socket.set_hwm(1)\n\n            # enable specified secure mode for the socket\n            if self.__secure_mode > 0:\n                # load client key\n                client_secret_file = os.path.join(\n                    self.__auth_secretkeys_dir, \"client.key_secret\"\n                )\n                client_public, client_secret = auth.load_certificate(\n                    client_secret_file\n                )\n                # load  all CURVE keys\n                self.__msg_socket.curve_secretkey = client_secret\n                self.__msg_socket.curve_publickey = client_public\n                # load server key\n                server_public_file = os.path.join(\n                    self.__auth_publickeys_dir, \"server.key\"\n                )\n                server_public, _ = auth.load_certificate(server_public_file)\n                # inject public key to make a CURVE connection.\n                self.__msg_socket.curve_serverkey = server_public\n\n            # check if multi-client_mode is enabled\n            if self.__multiclient_mode:\n                # bind socket to given server protocol, address and ports\n                for pt in port:\n                    self.__msg_socket.connect(\n                        protocol + \"://\" + str(address) + \":\" + str(pt)\n                    )\n            else:\n                # handle SSH tunneling if enabled\n                if self.__ssh_tunnel_mode:\n                    # establish tunnel connection\n                    ssh.tunnel_connection(\n                        self.__msg_socket,\n                        protocol + \"://\" + str(address) + \":\" + str(port),\n                        self.__ssh_tunnel_mode,\n                        keyfile=self.__ssh_tunnel_keyfile,\n                        password=self.__ssh_tunnel_pwd,\n                        paramiko=self.__paramiko_present,\n                    )\n                else:\n                    # connect socket to given protocol, address and port\n                    self.__msg_socket.connect(\n                        protocol + \"://\" + str(address) + \":\" + str(port)\n                    )\n\n            # additional settings\n            if pattern < 2:\n                if self.__multiclient_mode:\n                    self.__connection_address = []\n                    for pt in port:\n                        self.__connection_address.append(\n                            protocol + \"://\" + str(address) + \":\" + str(pt)\n                        )\n                else:\n                    self.__connection_address = (\n                        protocol + \"://\" + str(address) + \":\" + str(port)\n                    )\n                self.__msg_pattern = msg_pattern[0]\n                self.__poll.register(self.__msg_socket, zmq.POLLIN)\n\n                self.__logging and logger.debug(\n                    \"Reliable transmission is enabled for this pattern with max-retries: {} and timeout: {} secs.\".format(\n                        self.__max_retries, self.__request_timeout / 1000\n                    )\n                )\n\n        except Exception as e:\n            # otherwise log and raise error\n            logger.exception(str(e))\n            # Handle Secure Mode\n            self.__secure_mode and logger.critical(\n                \"Failed to activate Secure Mode: `{}` for this connection!\".format(\n                    valid_security_mech[self.__secure_mode]\n                )\n            )\n            # raise errors for exclusive modes\n            if self.__multiserver_mode or self.__multiclient_mode:\n                raise RuntimeError(\n                    \"[NetGear:ERROR] :: Send Mode failed to activate {} Mode at address: {} with pattern: {}! Kindly recheck all parameters.\".format(\n                        (\n                            \"Multi-Server\"\n                            if self.__multiserver_mode\n                            else \"Multi-Client\"\n                        ),\n                        (protocol + \"://\" + str(address) + \":\" + str(port)),\n                        pattern,\n                    )\n                )\n            else:\n                self.__bi_mode and logger.critical(\n                    \"Failed to activate Bidirectional Mode for this connection!\"\n                )\n                self.__ssh_tunnel_mode and logger.critical(\n                    \"Failed to initiate SSH Tunneling Mode for this server with `{}` back-end!\".format(\n                        \"paramiko\" if self.__paramiko_present else \"pexpect\"\n                    )\n                )\n                raise RuntimeError(\n                    \"[NetGear:ERROR] :: Send Mode failed to connect address: {} and pattern: {}! Kindly recheck all parameters.\".format(\n                        (protocol + \"://\" + str(address) + \":\" + str(port)), pattern\n                    )\n                )\n\n        if self.__logging:\n            # finally log progress\n            logger.debug(\n                \"Successfully connected to address: {} with pattern: {}.\".format(\n                    (protocol + \"://\" + str(address) + \":\" + str(port)), pattern\n                )\n            )\n            self.__jpeg_compression and logger.debug(\n                \"JPEG Frame-Compression is activated for this connection with Colorspace:`{}`, Quality:`{}`%, Fastdct:`{}`, and Fastupsample:`{}`.\".format(\n                    self.__jpeg_compression_colorspace,\n                    self.__jpeg_compression_quality,\n                    (\"enabled\" if self.__jpeg_compression_fastdct else \"disabled\"),\n                    (\n                        \"enabled\"\n                        if self.__jpeg_compression_fastupsample\n                        else \"disabled\"\n                    ),\n                )\n            )\n            self.__secure_mode and logger.debug(\n                \"Enabled ZMQ Security Mechanism: `{}` for this connection.\".format(\n                    valid_security_mech[self.__secure_mode]\n                )\n            )\n            logger.debug(\"Unique System ID is {}.\".format(self.__id))\n            logger.debug(\n                \"Send Mode is successfully activated and ready to send data.\"\n            )\n
"},{"location":"bonus/reference/netgear/#vidgear.gears.netgear.NetGear.close","title":"close(self, kill=False)","text":"

Safely terminates the threads, and NetGear resources.

Parameters:

Name Type Description Default kill bool

Kills ZMQ context instead of graceful exiting in receive mode.

False Source code in vidgear/gears/netgear.py
def close(self, kill=False):\n    \"\"\"\n    Safely terminates the threads, and NetGear resources.\n\n    Parameters:\n        kill (bool): Kills ZMQ context instead of graceful exiting in receive mode.\n    \"\"\"\n    # log it\n    self.__logging and logger.debug(\n        \"Terminating various {} Processes.\".format(\n            \"Receive Mode\" if self.__receive_mode else \"Send Mode\"\n        )\n    )\n    #  whether `receive_mode` is enabled or not\n    if self.__receive_mode:\n        # check whether queue mode is empty\n        if not (self.__queue is None) and self.__queue:\n            self.__queue.clear()\n        # call immediate termination\n        self.__terminate = True\n        # properly close the socket\n        self.__logging and logger.debug(\"Terminating. Please wait...\")\n        # Handle Secure Mode Thread\n        if self.__z_auth:\n            self.__logging and logger.debug(\"Terminating Authenticator Thread.\")\n            self.__z_auth.stop()\n            while self.__z_auth.is_alive():\n                pass\n        # wait until stream resources are released\n        # (producer thread might be still grabbing frame)\n        if self.__thread is not None:\n            self.__logging and logger.debug(\"Terminating Main Thread.\")\n            # properly handle thread exit\n            if self.__thread.is_alive() and kill:\n                # force close if still alive\n                logger.warning(\"Thread still running...Killing it forcefully!\")\n                self.__msg_context.destroy()\n                self.__thread.join()\n            else:\n                self.__msg_socket.close(linger=0)\n                self.__thread.join()\n            self.__thread = None\n        self.__logging and logger.debug(\"Terminated Successfully!\")\n    else:\n        # indicate that process should be terminated\n        self.__terminate = True\n        # log if kill enabled\n        kill and logger.warning(\n            \"`kill` parmeter is only available in the receive mode.\"\n        )\n        # Handle Secure Mode Thread\n        if self.__z_auth:\n            self.__logging and logger.debug(\"Terminating Authenticator Thread.\")\n            self.__z_auth.stop()\n            while self.__z_auth.is_alive():\n                pass\n        # check if all attempts of reconnecting failed, then skip to closure\n        if (self.__pattern < 2 and not self.__max_retries) or (\n            self.__multiclient_mode and not self.__port_buffer\n        ):\n            try:\n                # properly close the socket\n                self.__msg_socket.setsockopt(zmq.LINGER, 0)\n                self.__msg_socket.close()\n            except ZMQError:\n                pass\n            finally:\n                # exit\n                return\n\n        if self.__multiserver_mode:\n            # check if multiserver_mode\n            # send termination flag to client with its unique port\n            term_dict = dict(terminate_flag=True, port=self.__port)\n        else:\n            # otherwise send termination flag to client\n            term_dict = dict(terminate_flag=True)\n\n        try:\n            if self.__multiclient_mode:\n                for _ in self.__port_buffer:\n                    self.__msg_socket.send_json(term_dict)\n            else:\n                self.__msg_socket.send_json(term_dict)\n\n            # check for confirmation if available within 1/5 timeout\n            if self.__pattern < 2:\n                self.__logging and logger.debug(\"Terminating. Please wait...\")\n                if self.__msg_socket.poll(self.__request_timeout // 5, zmq.POLLIN):\n                    self.__msg_socket.recv()\n        except Exception as e:\n            if not isinstance(e, ZMQError):\n                logger.exception(str(e))\n        finally:\n            # properly close the socket\n            self.__msg_socket.setsockopt(zmq.LINGER, 0)\n            self.__msg_socket.close()\n            self.__logging and logger.debug(\"Terminated Successfully!\")\n
"},{"location":"bonus/reference/netgear/#vidgear.gears.netgear.NetGear.recv","title":"recv(self, return_data=None)","text":"

A Receiver end method, that extracts received frames synchronously from monitored deque, while maintaining a fixed-length frame buffer in the memory, and blocks the thread if the deque is full.

Parameters:

Name Type Description Default return_data any

inputs return data (of any datatype), for sending back to Server.

None

Returns: A n-dimensional numpy array.

Source code in vidgear/gears/netgear.py
def recv(self, return_data=None):\n    \"\"\"\n    A Receiver end method, that extracts received frames synchronously from monitored deque, while maintaining a\n    fixed-length frame buffer in the memory, and blocks the thread if the deque is full.\n\n    Parameters:\n        return_data (any): inputs return data _(of any datatype)_, for sending back to Server.\n\n    **Returns:** A n-dimensional numpy array.\n    \"\"\"\n    # check whether `receive mode` is activated\n    if not (self.__receive_mode):\n        # raise value error and exit\n        self.__terminate = True\n        raise ValueError(\n            \"[NetGear:ERROR] :: `recv()` function cannot be used while receive_mode is disabled. Kindly refer vidgear docs!\"\n        )\n\n    # handle Bidirectional return data\n    if (self.__bi_mode or self.__multiclient_mode) and not (return_data is None):\n        self.__return_data = return_data\n\n    # check whether or not termination flag is enabled\n    while not self.__terminate:\n        try:\n            # check if queue is empty\n            if len(self.__queue) > 0:\n                return self.__queue.popleft()\n            else:\n                time.sleep(0.00001)\n                continue\n        except KeyboardInterrupt:\n            self.__terminate = True\n            break\n    # otherwise return NoneType\n    return None\n
"},{"location":"bonus/reference/netgear/#vidgear.gears.netgear.NetGear.send","title":"send(self, frame, message=None)","text":"

A Server end method, that sends the data and frames over the network to Client(s).

Parameters:

Name Type Description Default frame numpy.ndarray

inputs numpy array(frame).

required message any

input for sending additional data (of any datatype except numpy.ndarray) to Client(s).

None

Returns: Data (of any datatype) in selected exclusive modes, otherwise None-type.

Source code in vidgear/gears/netgear.py
def send(self, frame, message=None):\n    \"\"\"\n    A Server end method, that sends the data and frames over the network to Client(s).\n\n    Parameters:\n        frame (numpy.ndarray): inputs numpy array(frame).\n        message (any): input for sending additional data _(of any datatype except `numpy.ndarray`)_ to Client(s).\n\n    **Returns:** Data _(of any datatype)_ in selected exclusive modes, otherwise None-type.\n\n    \"\"\"\n    # check whether `receive_mode` is disabled\n    if self.__receive_mode:\n        # raise value error and exit\n        self.__terminate = True\n        raise ValueError(\n            \"[NetGear:ERROR] :: `send()` function cannot be used while receive_mode is enabled. Kindly refer vidgear docs!\"\n        )\n\n    if not (message is None) and isinstance(message, np.ndarray):\n        logger.warning(\n            \"Skipped unsupported `message` of datatype: {}!\".format(\n                type(message).__name__\n            )\n        )\n        message = None\n\n    # define exit_flag and assign value\n    exit_flag = True if (frame is None or self.__terminate) else False\n\n    # check whether exit_flag is False\n    if not (exit_flag) and not (frame.flags[\"C_CONTIGUOUS\"]):\n        # check whether the incoming frame is contiguous\n        frame = np.ascontiguousarray(frame, dtype=frame.dtype)\n\n    # handle JPEG compression encoding\n    if self.__jpeg_compression:\n        if self.__jpeg_compression_colorspace == \"GRAY\":\n            if frame.ndim == 2:\n                # patch for https://gitlab.com/jfolz/simplejpeg/-/issues/11\n                frame = np.expand_dims(frame, axis=2)\n            frame = simplejpeg.encode_jpeg(\n                frame,\n                quality=self.__jpeg_compression_quality,\n                colorspace=self.__jpeg_compression_colorspace,\n                fastdct=self.__jpeg_compression_fastdct,\n            )\n        else:\n            frame = simplejpeg.encode_jpeg(\n                frame,\n                quality=self.__jpeg_compression_quality,\n                colorspace=self.__jpeg_compression_colorspace,\n                colorsubsampling=\"422\",\n                fastdct=self.__jpeg_compression_fastdct,\n            )\n\n    # check if multiserver_mode is activated and assign values with unique port\n    msg_dict = dict(port=self.__port) if self.__multiserver_mode else dict()\n\n    # prepare the exclusive json dict\n    msg_dict.update(\n        dict(\n            terminate_flag=exit_flag,\n            compression=(\n                {\n                    \"dct\": self.__jpeg_compression_fastdct,\n                    \"ups\": self.__jpeg_compression_fastupsample,\n                    \"colorspace\": self.__jpeg_compression_colorspace,\n                }\n                if self.__jpeg_compression\n                else False\n            ),\n            message=message,\n            pattern=str(self.__pattern),\n            dtype=str(frame.dtype) if not (self.__jpeg_compression) else \"\",\n            shape=frame.shape if not (self.__jpeg_compression) else \"\",\n        )\n    )\n\n    # send the json dict\n    self.__msg_socket.send_json(msg_dict, self.__msg_flag | zmq.SNDMORE)\n    # send the frame array with correct flags\n    self.__msg_socket.send(\n        frame, flags=self.__msg_flag, copy=self.__msg_copy, track=self.__msg_track\n    )\n\n    # check if synchronous patterns, then wait for confirmation\n    if self.__pattern < 2:\n        # check if Bidirectional data transmission is enabled\n        if self.__bi_mode or self.__multiclient_mode:\n            # handles return data\n            recvd_data = None\n\n            socks = dict(self.__poll.poll(self.__request_timeout))\n            if socks.get(self.__msg_socket) == zmq.POLLIN:\n                # handle return data\n                recv_json = self.__msg_socket.recv_json(flags=self.__msg_flag)\n            else:\n                logger.critical(\"No response from Client, Reconnecting again...\")\n                # Socket is confused. Close and remove it.\n                self.__msg_socket.setsockopt(zmq.LINGER, 0)\n                self.__msg_socket.close()\n                self.__poll.unregister(self.__msg_socket)\n                self.__max_retries -= 1\n\n                if not (self.__max_retries):\n                    if self.__multiclient_mode:\n                        logger.error(\n                            \"All Clients failed to respond on multiple attempts.\"\n                        )\n                    else:\n                        logger.error(\n                            \"Client failed to respond on multiple attempts.\"\n                        )\n                    self.__terminate = True\n                    raise RuntimeError(\n                        \"[NetGear:ERROR] :: Client(s) seems to be offline, Abandoning.\"\n                    )\n\n                # Create new connection\n                self.__msg_socket = self.__msg_context.socket(self.__msg_pattern)\n                if isinstance(self.__connection_address, list):\n                    for _connection in self.__connection_address:\n                        self.__msg_socket.connect(_connection)\n                else:\n                    # handle SSH tunneling if enabled\n                    if self.__ssh_tunnel_mode:\n                        # establish tunnel connection\n                        ssh.tunnel_connection(\n                            self.__msg_socket,\n                            self.__connection_address,\n                            self.__ssh_tunnel_mode,\n                            keyfile=self.__ssh_tunnel_keyfile,\n                            password=self.__ssh_tunnel_pwd,\n                            paramiko=self.__paramiko_present,\n                        )\n                    else:\n                        # connect normally\n                        self.__msg_socket.connect(self.__connection_address)\n                self.__poll.register(self.__msg_socket, zmq.POLLIN)\n                # return None for mean-time\n                return None\n\n            # save the unique port addresses\n            if (\n                self.__multiclient_mode\n                and not recv_json[\"port\"] in self.__port_buffer\n            ):\n                self.__port_buffer.append(recv_json[\"port\"])\n\n            if recv_json[\"return_type\"] == \"ndarray\":\n                recv_array = self.__msg_socket.recv(\n                    flags=self.__msg_flag,\n                    copy=self.__msg_copy,\n                    track=self.__msg_track,\n                )\n                # check if encoding was enabled\n                if recv_json[\"compression\"]:\n                    # decode JPEG frame\n                    recvd_data = simplejpeg.decode_jpeg(\n                        recv_array,\n                        colorspace=recv_json[\"compression\"][\"colorspace\"],\n                        fastdct=self.__jpeg_compression_fastdct\n                        or recv_json[\"compression\"][\"dct\"],\n                        fastupsample=self.__jpeg_compression_fastupsample\n                        or recv_json[\"compression\"][\"ups\"],\n                    )\n                    # check if valid frame returned\n                    if recvd_data is None:\n                        self.__terminate = True\n                        # otherwise raise error and exit\n                        raise RuntimeError(\n                            \"[NetGear:ERROR] :: Received compressed frame `{}` decoding failed with flag: {}.\".format(\n                                recv_json[\"compression\"],\n                                self.__ex_compression_params,\n                            )\n                        )\n\n                    if (\n                        recv_json[\"compression\"][\"colorspace\"] == \"GRAY\"\n                        and recvd_data.ndim == 3\n                    ):\n                        # patch for https://gitlab.com/jfolz/simplejpeg/-/issues/11\n                        recvd_data = np.squeeze(recvd_data, axis=2)\n                else:\n                    recvd_data = np.frombuffer(\n                        recv_array, dtype=recv_json[\"array_dtype\"]\n                    ).reshape(recv_json[\"array_shape\"])\n            else:\n                recvd_data = recv_json[\"data\"]\n\n            return (\n                (recv_json[\"port\"], recvd_data)\n                if self.__multiclient_mode\n                else recvd_data\n            )\n        else:\n            # otherwise log normally\n            socks = dict(self.__poll.poll(self.__request_timeout))\n            if socks.get(self.__msg_socket) == zmq.POLLIN:\n                recv_confirmation = self.__msg_socket.recv()\n            else:\n                logger.critical(\"No response from Client, Reconnecting again...\")\n                # Socket is confused. Close and remove it.\n                self.__msg_socket.setsockopt(zmq.LINGER, 0)\n                self.__msg_socket.close()\n                self.__poll.unregister(self.__msg_socket)\n                self.__max_retries -= 1\n\n                if not (self.__max_retries):\n                    logger.error(\"Client failed to respond on repeated attempts.\")\n                    self.__terminate = True\n                    raise RuntimeError(\n                        \"[NetGear:ERROR] :: Client seems to be offline, Abandoning!\"\n                    )\n\n                # Create new connection\n                self.__msg_socket = self.__msg_context.socket(self.__msg_pattern)\n                # handle SSH tunneling if enabled\n                if self.__ssh_tunnel_mode:\n                    # establish tunnel connection\n                    ssh.tunnel_connection(\n                        self.__msg_socket,\n                        self.__connection_address,\n                        self.__ssh_tunnel_mode,\n                        keyfile=self.__ssh_tunnel_keyfile,\n                        password=self.__ssh_tunnel_pwd,\n                        paramiko=self.__paramiko_present,\n                    )\n                else:\n                    # connect normally\n                    self.__msg_socket.connect(self.__connection_address)\n                self.__poll.register(self.__msg_socket, zmq.POLLIN)\n                return None\n\n            # log confirmation\n            self.__logging and logger.debug(recv_confirmation)\n
"},{"location":"bonus/reference/netgear_async/","title":"NetGear_Async API References","text":"

NetGear_Async API usage examples can be found here \u27b6

NetGear_Async API parameters are explained here \u27b6

NetGear_Async can generate the same performance as NetGear API at about one-third the memory consumption, and also provide complete server-client handling with various options to use variable protocols/patterns similar to NetGear, but lacks in term of flexibility as it supports only a few NetGear's Exclusive Modes.

NetGear_Async is built on zmq.asyncio, and powered by a high-performance asyncio event loop called uvloop to achieve unwatchable high-speed and lag-free video streaming over the network with minimal resource constraints. NetGear_Async can transfer thousands of frames in just a few seconds without causing any significant load on your system.

NetGear_Async provides complete server-client handling and options to use variable protocols/patterns similar to NetGear API. Furthermore, NetGear_Async allows us to define our custom Server as source to transform frames easily before sending them across the network.

NetGear_Async now supports additional bidirectional data transmission between receiver(client) and sender(server) while transferring frames. Users can easily build complex applications such as like Real-Time Video Chat in just few lines of code.

In addition to all this, NetGear_Async API also provides internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs, thereby granting it exclusive power for transferring frames incoming from any source to the network.

NetGear_Async as of now supports four ZeroMQ messaging patterns:

Whereas supported protocol are: tcp and ipc.

Source code in vidgear/gears/asyncio/netgear_async.py
class NetGear_Async:\n    \"\"\"\n    NetGear_Async can generate the same performance as NetGear API at about one-third the memory consumption, and also provide complete server-client handling with various\n    options to use variable protocols/patterns similar to NetGear, but lacks in term of flexibility as it supports only a few NetGear's Exclusive Modes.\n\n    NetGear_Async is built on `zmq.asyncio`, and powered by a high-performance asyncio event loop called uvloop to achieve unwatchable high-speed and lag-free video streaming\n    over the network with minimal resource constraints. NetGear_Async can transfer thousands of frames in just a few seconds without causing any significant load on your\n    system.\n\n    NetGear_Async provides complete server-client handling and options to use variable protocols/patterns similar to NetGear API. Furthermore, NetGear_Async allows us to define\n     our custom Server as source to transform frames easily before sending them across the network.\n\n    NetGear_Async now supports additional **bidirectional data transmission** between receiver(client) and sender(server) while transferring frames.\n    Users can easily build complex applications such as like _Real-Time Video Chat_ in just few lines of code.\n\n    In addition to all this, NetGear_Async API also provides internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs, thereby\n    granting it exclusive power for transferring frames incoming from any source to the network.\n\n    NetGear_Async as of now supports four ZeroMQ messaging patterns:\n\n    - `zmq.PAIR` _(ZMQ Pair Pattern)_\n    - `zmq.REQ/zmq.REP` _(ZMQ Request/Reply Pattern)_\n    - `zmq.PUB/zmq.SUB` _(ZMQ Publish/Subscribe Pattern)_\n    - `zmq.PUSH/zmq.PULL` _(ZMQ Push/Pull Pattern)_\n\n    Whereas supported protocol are: `tcp` and `ipc`.\n    \"\"\"\n\n    def __init__(\n        self,\n        # NetGear_Async parameters\n        address=None,\n        port=None,\n        protocol=\"tcp\",\n        pattern=0,\n        receive_mode=False,\n        timeout=0.0,\n        # Videogear parameters\n        enablePiCamera=False,\n        stabilize=False,\n        source=None,\n        camera_num=0,\n        stream_mode=False,\n        backend=0,\n        colorspace=None,\n        resolution=(640, 480),\n        framerate=25,\n        time_delay=0,\n        # common parameters\n        logging=False,\n        **options\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the NetGear_Async class.\n\n        Parameters:\n            address (str): sets the valid network address of the Server/Client.\n            port (str): sets the valid Network Port of the Server/Client.\n            protocol (str): sets the valid messaging protocol between Server/Client.\n            pattern (int): sets the supported messaging pattern(flow of communication) between Server/Client\n            receive_mode (bool): select the NetGear_Async's Mode of operation.\n            timeout (int/float): controls the maximum waiting time(in sec) after which Client throws `TimeoutError`.\n            enablePiCamera (bool): provide access to PiGear(if True) or CamGear(if False) APIs respectively.\n            stabilize (bool): enable access to Stabilizer Class for stabilizing frames.\n            camera_num (int): selects the camera module index which will be used as Rpi source.\n            resolution (tuple): sets the resolution (i.e. `(width,height)`) of the Rpi source.\n            framerate (int/float): sets the framerate of the Rpi source.\n            source (based on input): defines the source for the input stream.\n            stream_mode (bool): controls the exclusive YouTube Mode.\n            backend (int): selects the backend for OpenCV's VideoCapture class.\n            colorspace (str): selects the colorspace of the input stream.\n            logging (bool): enables/disables logging.\n            time_delay (int): time delay (in sec) before start reading the frames.\n            options (dict): provides ability to alter Tweak Parameters of NetGear_Async, CamGear, PiGear & Stabilizer.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # raise error(s) for critical Class imports\n        import_dependency_safe(\n            \"zmq\" if zmq is None else \"\", min_version=\"4.0\", pkg_name=\"pyzmq\"\n        )\n        import_dependency_safe(\"msgpack\" if msgpack is None else \"\")\n        import_dependency_safe(\"msgpack_numpy\" if m is None else \"\")\n\n        # define valid messaging patterns => `0`: PAIR, `1`:(REQ, REP), `2`:(SUB, PUB), `3`:(PUSH, PULL)\n        valid_messaging_patterns = {\n            0: (zmq.PAIR, zmq.PAIR),\n            1: (zmq.REQ, zmq.REP),\n            2: (zmq.PUB, zmq.SUB),\n            3: (zmq.PUSH, zmq.PULL),\n        }\n\n        # check whether user-defined messaging pattern is valid\n        if isinstance(pattern, int) and pattern in valid_messaging_patterns:\n            # assign value\n            self.__msg_pattern = pattern\n            self.__pattern = valid_messaging_patterns[pattern]\n        else:\n            # otherwise default to 0:`zmq.PAIR`\n            self.__msg_pattern = 0\n            self.__pattern = valid_messaging_patterns[self.__msg_pattern]\n            self.__logging and logger.warning(\n                \"Invalid pattern {pattern}. Defaulting to `zmq.PAIR`!\".format(\n                    pattern=pattern\n                )\n            )\n\n        # check  whether user-defined messaging protocol is valid\n        if isinstance(protocol, str) and protocol in [\"tcp\", \"ipc\"]:\n            # assign value\n            self.__protocol = protocol\n        else:\n            # else default to `tcp` protocol\n            self.__protocol = \"tcp\"\n            self.__logging and logger.warning(\"Invalid protocol. Defaulting to `tcp`!\")\n\n        # initialize Termination flag\n        self.__terminate = False\n        # initialize and assign `Receive Mode`\n        self.__receive_mode = receive_mode\n        # initialize stream handler\n        self.__stream = None\n        # initialize Messaging Socket\n        self.__msg_socket = None\n        # initialize NetGear_Async's configuration dictionary\n        self.config = {}\n        # asyncio queue handler\n        self.__queue = None\n        # define Bidirectional mode\n        self.__bi_mode = False  # handles Bidirectional mode state\n\n        # assign timeout for Receiver end\n        if timeout and isinstance(timeout, (int, float)):\n            self.__timeout = float(timeout)\n        else:\n            self.__timeout = 15.0\n\n        # generate 8-digit random system id\n        self.__id = \"\".join(\n            secrets.choice(string.ascii_uppercase + string.digits) for i in range(8)\n        )\n\n        # Handle user-defined options dictionary values\n        # reformat dictionary\n        options = {str(k).strip(): v for k, v in options.items()}\n        # handle bidirectional mode\n        if \"bidirectional_mode\" in options:\n            value = options[\"bidirectional_mode\"]\n            # also check if pattern and source is valid\n            if isinstance(value, bool) and pattern < 2 and source is None:\n                # activate Bidirectional mode if specified\n                self.__bi_mode = value\n            else:\n                # otherwise disable it\n                self.__bi_mode = False\n                logger.warning(\"Bidirectional data transmission is disabled!\")\n            # handle errors and logging\n            if pattern >= 2:\n                # raise error\n                raise ValueError(\n                    \"[NetGear_Async:ERROR] :: `{}` pattern is not valid when Bidirectional Mode is enabled. Kindly refer Docs for more Information!\".format(\n                        pattern\n                    )\n                )\n            elif not (source is None):\n                raise ValueError(\n                    \"[NetGear_Async:ERROR] :: Custom source must be used when Bidirectional Mode is enabled. Kindly refer Docs for more Information!\".format(\n                        pattern\n                    )\n                )\n            elif isinstance(value, bool) and self.__logging:\n                # log Bidirectional mode activation\n                logger.debug(\n                    \"Bidirectional Data Transmission is {} for this connection!\".format(\n                        \"enabled\" if value else \"disabled\"\n                    )\n                )\n            else:\n                logger.error(\"`bidirectional_mode` value is invalid!\")\n            # clean\n            del options[\"bidirectional_mode\"]\n\n        # Setup and assign event loop policy\n        if platform.system() == \"Windows\":\n            # On Windows, VidGear requires the ``WindowsSelectorEventLoop``, but Python 3.8 and above,\n            # defaults to an ``ProactorEventLoop`` loop that is not compatible with it. Thereby,\n            # we had to set it manually.\n            asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())\n        else:\n            if not (uvloop is None):\n                # Latest uvloop eventloop is only available for UNIX machines.\n                asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())\n            else:\n                # log if not present\n                import_dependency_safe(\"uvloop\", error=\"log\")\n\n        # Retrieve event loop and assign it\n        try:\n            self.loop = asyncio.get_running_loop()\n        except RuntimeError:\n            # otherwise create one\n            logger.critical(\"No running event loop found. Creating a new one.\")\n            self.loop = asyncio.new_event_loop()\n\n        # log eventloop for debugging\n        self.__logging and logger.info(\n            \"Using ``{}`` event loop for this process.\".format(\n                self.loop.__class__.__name__\n            )\n        )\n\n        # define messaging asynchronous Context\n        self.__msg_context = zmq.asyncio.Context()\n\n        # check whether `Receive Mode` is enabled\n        if receive_mode:\n            # assign local IP address if None\n            if address is None:\n                self.__address = \"*\"  # define address\n            else:\n                self.__address = address\n            # assign default port address if None\n            if port is None:\n                self.__port = \"5555\"\n            else:\n                self.__port = port\n        else:\n            # Handle video source\n            if source is None:\n                self.config = {\"generator\": None}\n                self.__logging and logger.warning(\"Given source is of NoneType!\")\n            else:\n                # define stream with necessary params\n                self.__stream = VideoGear(\n                    enablePiCamera=enablePiCamera,\n                    stabilize=stabilize,\n                    source=source,\n                    camera_num=camera_num,\n                    stream_mode=stream_mode,\n                    backend=backend,\n                    colorspace=colorspace,\n                    resolution=resolution,\n                    framerate=framerate,\n                    logging=logging,\n                    time_delay=time_delay,\n                    **options\n                )\n                # define default frame generator in configuration\n                self.config = {\"generator\": self.__frame_generator()}\n            # assign local ip address if None\n            if address is None:\n                self.__address = \"localhost\"\n            else:\n                self.__address = address\n            # assign default port address if None\n            if port is None:\n                self.__port = \"5555\"\n            else:\n                self.__port = port\n            # add server task handler\n            self.task = None\n\n        # create asyncio queue if bidirectional mode activated\n        self.__queue = asyncio.Queue() if self.__bi_mode else None\n\n    def launch(self):\n        \"\"\"\n        Launches an asynchronous generators and loop executors for respective task.\n        \"\"\"\n        # check if receive mode enabled\n        if self.__receive_mode:\n            self.__logging and logger.debug(\n                \"Launching NetGear_Async asynchronous generator!\"\n            )\n            # run loop executor for Receiver asynchronous generator\n            self.loop.run_in_executor(None, self.recv_generator)\n        else:\n            # Otherwise launch Server handler\n            self.__logging and logger.debug(\n                \"Creating NetGear_Async asynchronous server handler!\"\n            )\n            # create task for Server Handler\n            self.task = self.loop.create_task(self.__server_handler())\n        # return instance\n        return self\n\n    async def __server_handler(self):\n        \"\"\"\n        Handles various Server-end processes/tasks.\n        \"\"\"\n        # validate assigned frame generator in NetGear_Async configuration\n        if isinstance(self.config, dict) and \"generator\" in self.config:\n            # check if its  assigned value is a asynchronous generator\n            if self.config[\"generator\"] is None or not inspect.isasyncgen(\n                self.config[\"generator\"]\n            ):\n                # otherwise raise error\n                raise ValueError(\n                    \"[NetGear_Async:ERROR] :: Invalid configuration. Assigned generator must be a asynchronous generator function/method only!\"\n                )\n        else:\n            # raise error if validation fails\n            raise RuntimeError(\n                \"[NetGear_Async:ERROR] :: Assigned NetGear_Async configuration is invalid!\"\n            )\n\n        # define our messaging socket\n        self.__msg_socket = self.__msg_context.socket(self.__pattern[0])\n\n        # if req/rep pattern, define additional flags\n        if self.__msg_pattern == 1:\n            self.__msg_socket.REQ_RELAXED = True\n            self.__msg_socket.REQ_CORRELATE = True\n\n        # if pub/sub pattern, define additional optimizer\n        if self.__msg_pattern == 2:\n            self.__msg_socket.set_hwm(1)\n\n        # try connecting socket to assigned protocol, address and port\n        try:\n            self.__msg_socket.connect(\n                self.__protocol + \"://\" + str(self.__address) + \":\" + str(self.__port)\n            )\n            # finally log if successful\n            self.__logging and logger.debug(\n                \"Successfully connected to address: {} with pattern: {}.\".format(\n                    (\n                        self.__protocol\n                        + \"://\"\n                        + str(self.__address)\n                        + \":\"\n                        + str(self.__port)\n                    ),\n                    self.__msg_pattern,\n                )\n            )\n            logger.critical(\n                \"Send Mode is successfully activated and ready to send data!\"\n            )\n        except Exception as e:\n            # log ad raise error if failed\n            logger.exception(str(e))\n            if self.__bi_mode:\n                logger.error(\n                    \"Failed to activate Bidirectional Mode for this connection!\"\n                )\n            raise ValueError(\n                \"[NetGear_Async:ERROR] :: Failed to connect address: {} and pattern: {}!\".format(\n                    (\n                        self.__protocol\n                        + \"://\"\n                        + str(self.__address)\n                        + \":\"\n                        + str(self.__port)\n                    ),\n                    self.__msg_pattern,\n                )\n            )\n\n        # loop over our Asynchronous frame generator\n        async for dataframe in self.config[\"generator\"]:\n            # extract data if bidirectional mode\n            if self.__bi_mode and len(dataframe) == 2:\n                (data, frame) = dataframe\n                if not (data is None) and isinstance(data, np.ndarray):\n                    logger.warning(\n                        \"Skipped unsupported `data` of datatype: {}!\".format(\n                            type(data).__name__\n                        )\n                    )\n                    data = None\n                assert isinstance(\n                    frame, np.ndarray\n                ), \"[NetGear_Async:ERROR] :: Invalid data received from server end!\"\n            elif self.__bi_mode:\n                # raise error for invalid data\n                raise ValueError(\n                    \"[NetGear_Async:ERROR] :: Send Mode only accepts tuple(data, frame) as input in Bidirectional Mode. \\\n                    Kindly refer vidgear docs!\"\n                )\n            else:\n                # otherwise just make a copy of frame\n                frame = np.copy(dataframe)\n                data = None\n\n            # check if retrieved frame is `CONTIGUOUS`\n            if not (frame.flags[\"C_CONTIGUOUS\"]):\n                # otherwise make it\n                frame = np.ascontiguousarray(frame, dtype=frame.dtype)\n\n            # create data dict\n            data_dict = dict(\n                terminate=False,\n                bi_mode=self.__bi_mode,\n                data=data if not (data is None) else \"\",\n            )\n            # encode it\n            data_enc = msgpack.packb(data_dict)\n            # send the encoded data with correct flags\n            await self.__msg_socket.send(data_enc, flags=zmq.SNDMORE)\n\n            # encode frame\n            frame_enc = msgpack.packb(frame, default=m.encode)\n            # send the encoded frame\n            await self.__msg_socket.send_multipart([frame_enc])\n\n            # check if bidirectional patterns used\n            if self.__msg_pattern < 2:\n                # handle bidirectional data transfer if enabled\n                if self.__bi_mode:\n                    # get receiver encoded message withing timeout limit\n                    recvdmsg_encoded = await asyncio.wait_for(\n                        self.__msg_socket.recv(), timeout=self.__timeout\n                    )\n                    # retrieve receiver data from encoded message\n                    recvd_data = msgpack.unpackb(recvdmsg_encoded, use_list=False)\n                    # check message type\n                    if recvd_data[\"return_type\"] == \"ndarray\":  # numpy.ndarray\n                        # get encoded frame from receiver\n                        recvdframe_encoded = await asyncio.wait_for(\n                            self.__msg_socket.recv_multipart(), timeout=self.__timeout\n                        )\n                        # retrieve frame and put in queue\n                        await self.__queue.put(\n                            msgpack.unpackb(\n                                recvdframe_encoded[0],\n                                use_list=False,\n                                object_hook=m.decode,\n                            )\n                        )\n                    else:\n                        # otherwise put data directly in queue\n                        await self.__queue.put(\n                            recvd_data[\"return_data\"]\n                            if recvd_data[\"return_data\"]\n                            else None\n                        )\n                else:\n                    # otherwise log received confirmation\n                    recv_confirmation = await asyncio.wait_for(\n                        self.__msg_socket.recv(), timeout=self.__timeout\n                    )\n                    self.__logging and logger.debug(recv_confirmation)\n\n    async def recv_generator(self):\n        \"\"\"\n        A default Asynchronous Frame Generator for NetGear_Async's Receiver-end.\n        \"\"\"\n        # check whether `receive mode` is activated\n        if not (self.__receive_mode):\n            # raise Value error and exit\n            self.__terminate = True\n            raise ValueError(\n                \"[NetGear_Async:ERROR] :: `recv_generator()` function cannot be accessed while `receive_mode` is disabled. Kindly refer vidgear docs!\"\n            )\n\n        # initialize and define messaging socket\n        self.__msg_socket = self.__msg_context.socket(self.__pattern[1])\n\n        # define exclusive socket options for patterns\n        if self.__msg_pattern == 2:\n            self.__msg_socket.set_hwm(1)\n            self.__msg_socket.setsockopt(zmq.SUBSCRIBE, b\"\")\n\n        try:\n            # bind socket to the assigned protocol, address and port\n            self.__msg_socket.bind(\n                self.__protocol + \"://\" + str(self.__address) + \":\" + str(self.__port)\n            )\n            # finally log progress\n            self.__logging and logger.debug(\n                \"Successfully binded to address: {} with pattern: {}.\".format(\n                    (\n                        self.__protocol\n                        + \"://\"\n                        + str(self.__address)\n                        + \":\"\n                        + str(self.__port)\n                    ),\n                    self.__msg_pattern,\n                )\n            )\n            logger.critical(\"Receive Mode is activated successfully!\")\n        except Exception as e:\n            logger.exception(str(e))\n            raise RuntimeError(\n                \"[NetGear_Async:ERROR] :: Failed to bind address: {} and pattern: {}{}!\".format(\n                    (\n                        self.__protocol\n                        + \"://\"\n                        + str(self.__address)\n                        + \":\"\n                        + str(self.__port)\n                    ),\n                    self.__msg_pattern,\n                    \" and Bidirectional Mode enabled\" if self.__bi_mode else \"\",\n                )\n            )\n\n        # loop until terminated\n        while not self.__terminate:\n            # get encoded data message from server withing timeout limit\n            datamsg_encoded = await asyncio.wait_for(\n                self.__msg_socket.recv(), timeout=self.__timeout\n            )\n            # retrieve data from message\n            data = msgpack.unpackb(datamsg_encoded, use_list=False)\n            # terminate if exit` flag received from server\n            if data[\"terminate\"]:\n                # send confirmation message to server if bidirectional patterns\n                if self.__msg_pattern < 2:\n                    # create termination confirmation message\n                    return_dict = dict(\n                        terminated=\"Client-`{}` successfully terminated!\".format(\n                            self.__id\n                        ),\n                    )\n                    # encode message\n                    retdata_enc = msgpack.packb(return_dict)\n                    # send message back to server\n                    await self.__msg_socket.send(retdata_enc)\n                self.__logging and logger.info(\n                    \"Termination signal received from server!\"\n                )\n                # break loop and terminate\n                self.__terminate = True\n                break\n            # get encoded frame message from server withing timeout limit\n            framemsg_encoded = await asyncio.wait_for(\n                self.__msg_socket.recv_multipart(), timeout=self.__timeout\n            )\n            # retrieve frame from message\n            frame = msgpack.unpackb(\n                framemsg_encoded[0], use_list=False, object_hook=m.decode\n            )\n\n            # check if bidirectional patterns\n            if self.__msg_pattern < 2:\n                # handle bidirectional data transfer if enabled\n                if self.__bi_mode and data[\"bi_mode\"]:\n                    # handle empty queue\n                    if not self.__queue.empty():\n                        return_data = await self.__queue.get()\n                        self.__queue.task_done()\n                    else:\n                        return_data = None\n                    # check if we are returning `ndarray` frames\n                    if not (return_data is None) and isinstance(\n                        return_data, np.ndarray\n                    ):\n                        # check whether the incoming frame is contiguous\n                        if not (return_data.flags[\"C_CONTIGUOUS\"]):\n                            return_data = np.ascontiguousarray(\n                                return_data, dtype=return_data.dtype\n                            )\n\n                        # create return type dict without data\n                        rettype_dict = dict(\n                            return_type=(type(return_data).__name__),\n                            return_data=None,\n                        )\n                        # encode it\n                        rettype_enc = msgpack.packb(rettype_dict)\n                        # send it to server with correct flags\n                        await self.__msg_socket.send(rettype_enc, flags=zmq.SNDMORE)\n\n                        # encode return ndarray data\n                        retframe_enc = msgpack.packb(return_data, default=m.encode)\n                        # send it over network to server\n                        await self.__msg_socket.send_multipart([retframe_enc])\n                    else:\n                        # otherwise create type and data dict\n                        return_dict = dict(\n                            return_type=(type(return_data).__name__),\n                            return_data=(\n                                return_data if not (return_data is None) else \"\"\n                            ),\n                        )\n                        # encode it\n                        retdata_enc = msgpack.packb(return_dict)\n                        # send it over network to server\n                        await self.__msg_socket.send(retdata_enc)\n                elif self.__bi_mode or data[\"bi_mode\"]:\n                    # raise error if bidirectional mode is disabled at server or client but not both\n                    raise RuntimeError(\n                        \"[NetGear_Async:ERROR] :: Invalid configuration! Bidirectional Mode is not activate on {} end.\".format(\n                            \"client\" if self.__bi_mode else \"server\"\n                        )\n                    )\n                else:\n                    # otherwise just send confirmation message to server\n                    await self.__msg_socket.send(\n                        bytes(\n                            \"Data received on client: {} !\".format(self.__id), \"utf-8\"\n                        )\n                    )\n            # yield received tuple(data-frame) if bidirectional mode or else just frame\n            if self.__bi_mode:\n                yield (data[\"data\"], frame) if data[\"data\"] else (None, frame)\n            else:\n                yield frame\n            # sleep for sometime\n            await asyncio.sleep(0)\n\n    async def __frame_generator(self):\n        \"\"\"\n        Returns a default frame-generator for NetGear_Async's Server Handler.\n        \"\"\"\n        # start stream\n        self.__stream.start()\n        # loop over stream until its terminated\n        while not self.__terminate:\n            # read frames\n            frame = self.__stream.read()\n            # break if NoneType\n            if frame is None:\n                break\n            # yield frame\n            yield frame\n            # sleep for sometime\n            await asyncio.sleep(0)\n\n    async def transceive_data(self, data=None):\n        \"\"\"\n        Bidirectional Mode exclusive method to Transmit data _(in Receive mode)_ and Receive data _(in Send mode)_.\n\n        Parameters:\n            data (any): inputs data _(of any datatype)_ for sending back to Server.\n        \"\"\"\n        recvd_data = None\n        if not self.__terminate:\n            if self.__bi_mode:\n                if self.__receive_mode:\n                    await self.__queue.put(data)\n                else:\n                    if not self.__queue.empty():\n                        recvd_data = await self.__queue.get()\n                        self.__queue.task_done()\n            else:\n                logger.error(\n                    \"`transceive_data()` function cannot be used when Bidirectional Mode is disabled.\"\n                )\n        return recvd_data\n\n    async def __terminate_connection(self, disable_confirmation=False):\n        \"\"\"\n        Internal asyncio method to safely terminate ZMQ connection and queues\n\n        Parameters:\n            disable_confirmation (boolean): Force disable termination confirmation from client in bidirectional patterns.\n        \"\"\"\n        # log termination\n        self.__logging and logger.debug(\n            \"Terminating various {} Processes. Please wait.\".format(\n                \"Receive Mode\" if self.__receive_mode else \"Send Mode\"\n            )\n        )\n\n        # check whether `receive_mode` is enabled or not\n        if self.__receive_mode:\n            # indicate that process should be terminated\n            self.__terminate = True\n        else:\n            # indicate that process should be terminated\n            self.__terminate = True\n            # terminate stream\n            if not (self.__stream is None):\n                self.__stream.stop()\n            # signal `exit` flag for termination!\n            data_dict = dict(terminate=True)\n            data_enc = msgpack.packb(data_dict)\n            await self.__msg_socket.send(data_enc)\n            # check if bidirectional patterns\n            if self.__msg_pattern < 2 and not disable_confirmation:\n                # then receive and log confirmation\n                recv_confirmation = await self.__msg_socket.recv()\n                recvd_conf = msgpack.unpackb(recv_confirmation, use_list=False)\n                self.__logging and \"terminated\" in recvd_conf and logger.debug(\n                    recvd_conf[\"terminated\"]\n                )\n        # close socket\n        self.__msg_socket.setsockopt(zmq.LINGER, 0)\n        self.__msg_socket.close()\n        # handle asyncio queues in bidirectional mode\n        if self.__bi_mode:\n            # empty queue if not\n            while not self.__queue.empty():\n                try:\n                    self.__queue.get_nowait()\n                except asyncio.QueueEmpty:\n                    continue\n                self.__queue.task_done()\n            # join queues\n            await self.__queue.join()\n\n        logger.critical(\n            \"{} successfully terminated!\".format(\n                \"Receive Mode\" if self.__receive_mode else \"Send Mode\"\n            )\n        )\n\n    def close(self, skip_loop=False):\n        \"\"\"\n        Terminates all NetGear_Async Asynchronous processes gracefully.\n\n        Parameters:\n            skip_loop (Boolean): (optional)used only if don't want to close eventloop(required in pytest).\n        \"\"\"\n        # close event loop if specified\n        if not (skip_loop):\n            # close connection gracefully\n            self.loop.run_until_complete(self.__terminate_connection())\n            self.loop.close()\n        else:\n            # otherwise create a task\n            asyncio.ensure_future(\n                self.__terminate_connection(disable_confirmation=True)\n            )\n

"},{"location":"bonus/reference/netgear_async/#vidgear.gears.asyncio.netgear_async.NetGear_Async.__init__","title":"__init__(self, address=None, port=None, protocol='tcp', pattern=0, receive_mode=False, timeout=0.0, enablePiCamera=False, stabilize=False, source=None, camera_num=0, stream_mode=False, backend=0, colorspace=None, resolution=(640, 480), framerate=25, time_delay=0, logging=False, **options) special","text":"

This constructor method initializes the object state and attributes of the NetGear_Async class.

Parameters:

Name Type Description Default address str

sets the valid network address of the Server/Client.

None port str

sets the valid Network Port of the Server/Client.

None protocol str

sets the valid messaging protocol between Server/Client.

'tcp' pattern int

sets the supported messaging pattern(flow of communication) between Server/Client

0 receive_mode bool

select the NetGear_Async's Mode of operation.

False timeout int/float

controls the maximum waiting time(in sec) after which Client throws TimeoutError.

0.0 enablePiCamera bool

provide access to PiGear(if True) or CamGear(if False) APIs respectively.

False stabilize bool

enable access to Stabilizer Class for stabilizing frames.

False camera_num int

selects the camera module index which will be used as Rpi source.

0 resolution tuple

sets the resolution (i.e. (width,height)) of the Rpi source.

(640, 480) framerate int/float

sets the framerate of the Rpi source.

25 source based on input

defines the source for the input stream.

None stream_mode bool

controls the exclusive YouTube Mode.

False backend int

selects the backend for OpenCV's VideoCapture class.

0 colorspace str

selects the colorspace of the input stream.

None logging bool

enables/disables logging.

False time_delay int

time delay (in sec) before start reading the frames.

0 options dict

provides ability to alter Tweak Parameters of NetGear_Async, CamGear, PiGear & Stabilizer.

{} Source code in vidgear/gears/asyncio/netgear_async.py
def __init__(\n    self,\n    # NetGear_Async parameters\n    address=None,\n    port=None,\n    protocol=\"tcp\",\n    pattern=0,\n    receive_mode=False,\n    timeout=0.0,\n    # Videogear parameters\n    enablePiCamera=False,\n    stabilize=False,\n    source=None,\n    camera_num=0,\n    stream_mode=False,\n    backend=0,\n    colorspace=None,\n    resolution=(640, 480),\n    framerate=25,\n    time_delay=0,\n    # common parameters\n    logging=False,\n    **options\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the NetGear_Async class.\n\n    Parameters:\n        address (str): sets the valid network address of the Server/Client.\n        port (str): sets the valid Network Port of the Server/Client.\n        protocol (str): sets the valid messaging protocol between Server/Client.\n        pattern (int): sets the supported messaging pattern(flow of communication) between Server/Client\n        receive_mode (bool): select the NetGear_Async's Mode of operation.\n        timeout (int/float): controls the maximum waiting time(in sec) after which Client throws `TimeoutError`.\n        enablePiCamera (bool): provide access to PiGear(if True) or CamGear(if False) APIs respectively.\n        stabilize (bool): enable access to Stabilizer Class for stabilizing frames.\n        camera_num (int): selects the camera module index which will be used as Rpi source.\n        resolution (tuple): sets the resolution (i.e. `(width,height)`) of the Rpi source.\n        framerate (int/float): sets the framerate of the Rpi source.\n        source (based on input): defines the source for the input stream.\n        stream_mode (bool): controls the exclusive YouTube Mode.\n        backend (int): selects the backend for OpenCV's VideoCapture class.\n        colorspace (str): selects the colorspace of the input stream.\n        logging (bool): enables/disables logging.\n        time_delay (int): time delay (in sec) before start reading the frames.\n        options (dict): provides ability to alter Tweak Parameters of NetGear_Async, CamGear, PiGear & Stabilizer.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # raise error(s) for critical Class imports\n    import_dependency_safe(\n        \"zmq\" if zmq is None else \"\", min_version=\"4.0\", pkg_name=\"pyzmq\"\n    )\n    import_dependency_safe(\"msgpack\" if msgpack is None else \"\")\n    import_dependency_safe(\"msgpack_numpy\" if m is None else \"\")\n\n    # define valid messaging patterns => `0`: PAIR, `1`:(REQ, REP), `2`:(SUB, PUB), `3`:(PUSH, PULL)\n    valid_messaging_patterns = {\n        0: (zmq.PAIR, zmq.PAIR),\n        1: (zmq.REQ, zmq.REP),\n        2: (zmq.PUB, zmq.SUB),\n        3: (zmq.PUSH, zmq.PULL),\n    }\n\n    # check whether user-defined messaging pattern is valid\n    if isinstance(pattern, int) and pattern in valid_messaging_patterns:\n        # assign value\n        self.__msg_pattern = pattern\n        self.__pattern = valid_messaging_patterns[pattern]\n    else:\n        # otherwise default to 0:`zmq.PAIR`\n        self.__msg_pattern = 0\n        self.__pattern = valid_messaging_patterns[self.__msg_pattern]\n        self.__logging and logger.warning(\n            \"Invalid pattern {pattern}. Defaulting to `zmq.PAIR`!\".format(\n                pattern=pattern\n            )\n        )\n\n    # check  whether user-defined messaging protocol is valid\n    if isinstance(protocol, str) and protocol in [\"tcp\", \"ipc\"]:\n        # assign value\n        self.__protocol = protocol\n    else:\n        # else default to `tcp` protocol\n        self.__protocol = \"tcp\"\n        self.__logging and logger.warning(\"Invalid protocol. Defaulting to `tcp`!\")\n\n    # initialize Termination flag\n    self.__terminate = False\n    # initialize and assign `Receive Mode`\n    self.__receive_mode = receive_mode\n    # initialize stream handler\n    self.__stream = None\n    # initialize Messaging Socket\n    self.__msg_socket = None\n    # initialize NetGear_Async's configuration dictionary\n    self.config = {}\n    # asyncio queue handler\n    self.__queue = None\n    # define Bidirectional mode\n    self.__bi_mode = False  # handles Bidirectional mode state\n\n    # assign timeout for Receiver end\n    if timeout and isinstance(timeout, (int, float)):\n        self.__timeout = float(timeout)\n    else:\n        self.__timeout = 15.0\n\n    # generate 8-digit random system id\n    self.__id = \"\".join(\n        secrets.choice(string.ascii_uppercase + string.digits) for i in range(8)\n    )\n\n    # Handle user-defined options dictionary values\n    # reformat dictionary\n    options = {str(k).strip(): v for k, v in options.items()}\n    # handle bidirectional mode\n    if \"bidirectional_mode\" in options:\n        value = options[\"bidirectional_mode\"]\n        # also check if pattern and source is valid\n        if isinstance(value, bool) and pattern < 2 and source is None:\n            # activate Bidirectional mode if specified\n            self.__bi_mode = value\n        else:\n            # otherwise disable it\n            self.__bi_mode = False\n            logger.warning(\"Bidirectional data transmission is disabled!\")\n        # handle errors and logging\n        if pattern >= 2:\n            # raise error\n            raise ValueError(\n                \"[NetGear_Async:ERROR] :: `{}` pattern is not valid when Bidirectional Mode is enabled. Kindly refer Docs for more Information!\".format(\n                    pattern\n                )\n            )\n        elif not (source is None):\n            raise ValueError(\n                \"[NetGear_Async:ERROR] :: Custom source must be used when Bidirectional Mode is enabled. Kindly refer Docs for more Information!\".format(\n                    pattern\n                )\n            )\n        elif isinstance(value, bool) and self.__logging:\n            # log Bidirectional mode activation\n            logger.debug(\n                \"Bidirectional Data Transmission is {} for this connection!\".format(\n                    \"enabled\" if value else \"disabled\"\n                )\n            )\n        else:\n            logger.error(\"`bidirectional_mode` value is invalid!\")\n        # clean\n        del options[\"bidirectional_mode\"]\n\n    # Setup and assign event loop policy\n    if platform.system() == \"Windows\":\n        # On Windows, VidGear requires the ``WindowsSelectorEventLoop``, but Python 3.8 and above,\n        # defaults to an ``ProactorEventLoop`` loop that is not compatible with it. Thereby,\n        # we had to set it manually.\n        asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())\n    else:\n        if not (uvloop is None):\n            # Latest uvloop eventloop is only available for UNIX machines.\n            asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())\n        else:\n            # log if not present\n            import_dependency_safe(\"uvloop\", error=\"log\")\n\n    # Retrieve event loop and assign it\n    try:\n        self.loop = asyncio.get_running_loop()\n    except RuntimeError:\n        # otherwise create one\n        logger.critical(\"No running event loop found. Creating a new one.\")\n        self.loop = asyncio.new_event_loop()\n\n    # log eventloop for debugging\n    self.__logging and logger.info(\n        \"Using ``{}`` event loop for this process.\".format(\n            self.loop.__class__.__name__\n        )\n    )\n\n    # define messaging asynchronous Context\n    self.__msg_context = zmq.asyncio.Context()\n\n    # check whether `Receive Mode` is enabled\n    if receive_mode:\n        # assign local IP address if None\n        if address is None:\n            self.__address = \"*\"  # define address\n        else:\n            self.__address = address\n        # assign default port address if None\n        if port is None:\n            self.__port = \"5555\"\n        else:\n            self.__port = port\n    else:\n        # Handle video source\n        if source is None:\n            self.config = {\"generator\": None}\n            self.__logging and logger.warning(\"Given source is of NoneType!\")\n        else:\n            # define stream with necessary params\n            self.__stream = VideoGear(\n                enablePiCamera=enablePiCamera,\n                stabilize=stabilize,\n                source=source,\n                camera_num=camera_num,\n                stream_mode=stream_mode,\n                backend=backend,\n                colorspace=colorspace,\n                resolution=resolution,\n                framerate=framerate,\n                logging=logging,\n                time_delay=time_delay,\n                **options\n            )\n            # define default frame generator in configuration\n            self.config = {\"generator\": self.__frame_generator()}\n        # assign local ip address if None\n        if address is None:\n            self.__address = \"localhost\"\n        else:\n            self.__address = address\n        # assign default port address if None\n        if port is None:\n            self.__port = \"5555\"\n        else:\n            self.__port = port\n        # add server task handler\n        self.task = None\n\n    # create asyncio queue if bidirectional mode activated\n    self.__queue = asyncio.Queue() if self.__bi_mode else None\n
"},{"location":"bonus/reference/netgear_async/#vidgear.gears.asyncio.netgear_async.NetGear_Async.close","title":"close(self, skip_loop=False)","text":"

Terminates all NetGear_Async Asynchronous processes gracefully.

Parameters:

Name Type Description Default skip_loop Boolean

(optional)used only if don't want to close eventloop(required in pytest).

False Source code in vidgear/gears/asyncio/netgear_async.py
def close(self, skip_loop=False):\n    \"\"\"\n    Terminates all NetGear_Async Asynchronous processes gracefully.\n\n    Parameters:\n        skip_loop (Boolean): (optional)used only if don't want to close eventloop(required in pytest).\n    \"\"\"\n    # close event loop if specified\n    if not (skip_loop):\n        # close connection gracefully\n        self.loop.run_until_complete(self.__terminate_connection())\n        self.loop.close()\n    else:\n        # otherwise create a task\n        asyncio.ensure_future(\n            self.__terminate_connection(disable_confirmation=True)\n        )\n
"},{"location":"bonus/reference/netgear_async/#vidgear.gears.asyncio.netgear_async.NetGear_Async.launch","title":"launch(self)","text":"

Launches an asynchronous generators and loop executors for respective task.

Source code in vidgear/gears/asyncio/netgear_async.py
def launch(self):\n    \"\"\"\n    Launches an asynchronous generators and loop executors for respective task.\n    \"\"\"\n    # check if receive mode enabled\n    if self.__receive_mode:\n        self.__logging and logger.debug(\n            \"Launching NetGear_Async asynchronous generator!\"\n        )\n        # run loop executor for Receiver asynchronous generator\n        self.loop.run_in_executor(None, self.recv_generator)\n    else:\n        # Otherwise launch Server handler\n        self.__logging and logger.debug(\n            \"Creating NetGear_Async asynchronous server handler!\"\n        )\n        # create task for Server Handler\n        self.task = self.loop.create_task(self.__server_handler())\n    # return instance\n    return self\n
"},{"location":"bonus/reference/netgear_async/#vidgear.gears.asyncio.netgear_async.NetGear_Async.recv_generator","title":"recv_generator(self)","text":"

A default Asynchronous Frame Generator for NetGear_Async's Receiver-end.

Source code in vidgear/gears/asyncio/netgear_async.py
async def recv_generator(self):\n    \"\"\"\n    A default Asynchronous Frame Generator for NetGear_Async's Receiver-end.\n    \"\"\"\n    # check whether `receive mode` is activated\n    if not (self.__receive_mode):\n        # raise Value error and exit\n        self.__terminate = True\n        raise ValueError(\n            \"[NetGear_Async:ERROR] :: `recv_generator()` function cannot be accessed while `receive_mode` is disabled. Kindly refer vidgear docs!\"\n        )\n\n    # initialize and define messaging socket\n    self.__msg_socket = self.__msg_context.socket(self.__pattern[1])\n\n    # define exclusive socket options for patterns\n    if self.__msg_pattern == 2:\n        self.__msg_socket.set_hwm(1)\n        self.__msg_socket.setsockopt(zmq.SUBSCRIBE, b\"\")\n\n    try:\n        # bind socket to the assigned protocol, address and port\n        self.__msg_socket.bind(\n            self.__protocol + \"://\" + str(self.__address) + \":\" + str(self.__port)\n        )\n        # finally log progress\n        self.__logging and logger.debug(\n            \"Successfully binded to address: {} with pattern: {}.\".format(\n                (\n                    self.__protocol\n                    + \"://\"\n                    + str(self.__address)\n                    + \":\"\n                    + str(self.__port)\n                ),\n                self.__msg_pattern,\n            )\n        )\n        logger.critical(\"Receive Mode is activated successfully!\")\n    except Exception as e:\n        logger.exception(str(e))\n        raise RuntimeError(\n            \"[NetGear_Async:ERROR] :: Failed to bind address: {} and pattern: {}{}!\".format(\n                (\n                    self.__protocol\n                    + \"://\"\n                    + str(self.__address)\n                    + \":\"\n                    + str(self.__port)\n                ),\n                self.__msg_pattern,\n                \" and Bidirectional Mode enabled\" if self.__bi_mode else \"\",\n            )\n        )\n\n    # loop until terminated\n    while not self.__terminate:\n        # get encoded data message from server withing timeout limit\n        datamsg_encoded = await asyncio.wait_for(\n            self.__msg_socket.recv(), timeout=self.__timeout\n        )\n        # retrieve data from message\n        data = msgpack.unpackb(datamsg_encoded, use_list=False)\n        # terminate if exit` flag received from server\n        if data[\"terminate\"]:\n            # send confirmation message to server if bidirectional patterns\n            if self.__msg_pattern < 2:\n                # create termination confirmation message\n                return_dict = dict(\n                    terminated=\"Client-`{}` successfully terminated!\".format(\n                        self.__id\n                    ),\n                )\n                # encode message\n                retdata_enc = msgpack.packb(return_dict)\n                # send message back to server\n                await self.__msg_socket.send(retdata_enc)\n            self.__logging and logger.info(\n                \"Termination signal received from server!\"\n            )\n            # break loop and terminate\n            self.__terminate = True\n            break\n        # get encoded frame message from server withing timeout limit\n        framemsg_encoded = await asyncio.wait_for(\n            self.__msg_socket.recv_multipart(), timeout=self.__timeout\n        )\n        # retrieve frame from message\n        frame = msgpack.unpackb(\n            framemsg_encoded[0], use_list=False, object_hook=m.decode\n        )\n\n        # check if bidirectional patterns\n        if self.__msg_pattern < 2:\n            # handle bidirectional data transfer if enabled\n            if self.__bi_mode and data[\"bi_mode\"]:\n                # handle empty queue\n                if not self.__queue.empty():\n                    return_data = await self.__queue.get()\n                    self.__queue.task_done()\n                else:\n                    return_data = None\n                # check if we are returning `ndarray` frames\n                if not (return_data is None) and isinstance(\n                    return_data, np.ndarray\n                ):\n                    # check whether the incoming frame is contiguous\n                    if not (return_data.flags[\"C_CONTIGUOUS\"]):\n                        return_data = np.ascontiguousarray(\n                            return_data, dtype=return_data.dtype\n                        )\n\n                    # create return type dict without data\n                    rettype_dict = dict(\n                        return_type=(type(return_data).__name__),\n                        return_data=None,\n                    )\n                    # encode it\n                    rettype_enc = msgpack.packb(rettype_dict)\n                    # send it to server with correct flags\n                    await self.__msg_socket.send(rettype_enc, flags=zmq.SNDMORE)\n\n                    # encode return ndarray data\n                    retframe_enc = msgpack.packb(return_data, default=m.encode)\n                    # send it over network to server\n                    await self.__msg_socket.send_multipart([retframe_enc])\n                else:\n                    # otherwise create type and data dict\n                    return_dict = dict(\n                        return_type=(type(return_data).__name__),\n                        return_data=(\n                            return_data if not (return_data is None) else \"\"\n                        ),\n                    )\n                    # encode it\n                    retdata_enc = msgpack.packb(return_dict)\n                    # send it over network to server\n                    await self.__msg_socket.send(retdata_enc)\n            elif self.__bi_mode or data[\"bi_mode\"]:\n                # raise error if bidirectional mode is disabled at server or client but not both\n                raise RuntimeError(\n                    \"[NetGear_Async:ERROR] :: Invalid configuration! Bidirectional Mode is not activate on {} end.\".format(\n                        \"client\" if self.__bi_mode else \"server\"\n                    )\n                )\n            else:\n                # otherwise just send confirmation message to server\n                await self.__msg_socket.send(\n                    bytes(\n                        \"Data received on client: {} !\".format(self.__id), \"utf-8\"\n                    )\n                )\n        # yield received tuple(data-frame) if bidirectional mode or else just frame\n        if self.__bi_mode:\n            yield (data[\"data\"], frame) if data[\"data\"] else (None, frame)\n        else:\n            yield frame\n        # sleep for sometime\n        await asyncio.sleep(0)\n
"},{"location":"bonus/reference/netgear_async/#vidgear.gears.asyncio.netgear_async.NetGear_Async.transceive_data","title":"transceive_data(self, data=None) async","text":"

Bidirectional Mode exclusive method to Transmit data (in Receive mode) and Receive data (in Send mode).

Parameters:

Name Type Description Default data any

inputs data (of any datatype) for sending back to Server.

None Source code in vidgear/gears/asyncio/netgear_async.py
async def transceive_data(self, data=None):\n    \"\"\"\n    Bidirectional Mode exclusive method to Transmit data _(in Receive mode)_ and Receive data _(in Send mode)_.\n\n    Parameters:\n        data (any): inputs data _(of any datatype)_ for sending back to Server.\n    \"\"\"\n    recvd_data = None\n    if not self.__terminate:\n        if self.__bi_mode:\n            if self.__receive_mode:\n                await self.__queue.put(data)\n            else:\n                if not self.__queue.empty():\n                    recvd_data = await self.__queue.get()\n                    self.__queue.task_done()\n        else:\n            logger.error(\n                \"`transceive_data()` function cannot be used when Bidirectional Mode is disabled.\"\n            )\n    return recvd_data\n
"},{"location":"bonus/reference/pigear/","title":"PiGear API References","text":"

PiGear API usage examples can be found here \u27b6

PiGear API parameters are explained here \u27b6

PiGear implements a seamless and robust wrapper around the picamera2 python library, simplifying integration with minimal code changes and ensuring a smooth transition for developers already familiar with the Picamera2 API. PiGear leverages the libcamera API under the hood with multi-threading, providing high-performance , enhanced control and functionality for Raspberry Pi camera modules.

PiGear handles common configuration parameters and non-standard settings for various camera types, simplifying the integration process. PiGear currently supports picamera2 API parameters such as sensor, controls, transform, and stride, with internal type and sanity checks for robust performance.

While primarily focused on Raspberry Pi camera modules, PiGear also provides basic functionality for USB webcams only with Picamera2 API, along with the ability to accurately differentiate between USB and Raspberry Pi cameras using metadata.

Backward compatibility with picamera library

PiGear seamlessly switches to the legacy picamera library if the picamera2 library is unavailable, ensuring seamless backward compatibility. For this, PiGear also provides a flexible multi-threaded framework around complete picamera API, allowing developers to effortlessly exploit a wide range of parameters, such as brightness, saturation, sensor_mode, iso, exposure, and more.

Furthermore, PiGear supports the use of multiple camera modules, including those found on Raspberry Pi Compute Module IO boards and USB cameras (only with Picamera2 API).

Threaded Internal Timer

PiGear ensures proper resource release during the termination of the API, preventing potential issues or resource leaks. PiGear API internally implements a Threaded Internal Timer that silently keeps active track of any frozen-threads or hardware-failures and exits safely if any do occur. This means that if you're running the PiGear API in your script and someone accidentally pulls the Camera-Module cable out, instead of going into a possible kernel panic, the API will exit safely to save resources.

Make sure to enable Raspberry Pi hardware-specific settings prior using this API, otherwise nothing will work.

Source code in vidgear/gears/pigear.py
class PiGear:\n    \"\"\"\n    PiGear implements a seamless and robust wrapper around the [picamera2](https://github.com/raspberrypi/picamera2) python library, simplifying integration with minimal code changes and ensuring a\n    smooth transition for developers already familiar with the Picamera2 API. PiGear leverages the `libcamera` API under the hood with multi-threading, providing high-performance :fire:, enhanced\n    control and functionality for Raspberry Pi camera modules.\n\n    PiGear handles common configuration parameters and non-standard settings for various camera types, simplifying the integration process. PiGear currently supports picamera2 API parameters such as\n    `sensor`, `controls`, `transform`, and `stride`, with internal type and sanity checks for robust performance.\n\n    While primarily focused on Raspberry Pi camera modules, PiGear also provides basic functionality for USB webcams only with Picamera2 API, along with the ability to accurately differentiate between\n    USB and Raspberry Pi cameras using metadata.\n\n    ???+ info \"Backward compatibility with `picamera` library\"\n        PiGear seamlessly switches to the legacy [picamera](https://picamera.readthedocs.io/en/release-1.13/index.html) library if the `picamera2` library is unavailable, ensuring seamless backward\n        compatibility. For this, PiGear also provides a flexible multi-threaded framework around complete `picamera` API, allowing developers to effortlessly exploit a wide range of parameters, such\n        as `brightness`, `saturation`, `sensor_mode`, `iso`, `exposure`, and more.\n\n    Furthermore, PiGear supports the use of multiple camera modules, including those found on Raspberry Pi Compute Module IO boards and USB cameras _(only with Picamera2 API)_.\n\n    ??? new \"Threaded Internal Timer :material-camera-timer:\"\n        PiGear ensures proper resource release during the termination of the API, preventing potential issues or resource leaks. PiGear API internally implements a\n        ==Threaded Internal Timer== that silently keeps active track of any frozen-threads or hardware-failures and exits safely if any do occur. This means that if\n        you're running the PiGear API in your script and someone accidentally pulls the Camera-Module cable out, instead of going into a possible kernel panic,\n        the API will exit safely to save resources.\n\n    !!! failure \"Make sure to [enable Raspberry Pi hardware-specific settings](https://picamera.readthedocs.io/en/release-1.13/quickstart.html) prior using this API, otherwise nothing will work.\"\n    \"\"\"\n\n    def __init__(\n        self,\n        camera_num=0,\n        resolution=(640, 480),\n        framerate=30,\n        colorspace=None,\n        logging=False,\n        time_delay=0,\n        **options\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the PiGear class.\n\n        Parameters:\n            camera_num (int): selects the camera module index which will be used as source.\n            resolution (tuple): sets the resolution (i.e. `(width,height)`) of the source..\n            framerate (int/float): sets the framerate of the source.\n            colorspace (str): selects the colorspace of the input stream.\n            logging (bool): enables/disables logging.\n            time_delay (int): time delay (in sec) before start reading the frames.\n            options (dict): provides ability to alter Source Tweak Parameters.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # raise error(s) for critical Class imports\n        global picamera, picamera2\n        if picamera2:\n            # log if picamera2\n            self.__logging and logger.info(\"picamera2 API is currently being accessed.\")\n        elif picamera:\n            # switch to picamera otherwise\n            logger.critical(\n                \"picamera2 library not installed on this system. Defaulting to legacy picamera API.\"\n            )\n        else:\n            # raise error if none\n            import_dependency_safe(\"picamera\")\n\n        assert (\n            isinstance(framerate, (int, float)) and framerate > 0.0\n        ), \"[PiGear:ERROR] :: Input framerate value `{}` is a Invalid! Kindly read docs.\".format(\n            framerate\n        )\n        assert (\n            isinstance(resolution, (tuple, list)) and len(resolution) == 2\n        ), \"[PiGear:ERROR] :: Input resolution value `{}` is a Invalid! Kindly read docs.\".format(\n            resolution\n        )\n        if not (isinstance(camera_num, int) and camera_num >= 0):\n            camera_num = 0\n            logger.warning(\n                \"Input camera_num value `{}` is invalid, Defaulting to index 0!\"\n            )\n\n        # reformat dict\n        options = {str(k).strip(): v for k, v in options.items()}\n\n        # check if legacy picamera backend is enforced\n        enforce_legacy_picamera = options.pop(\"enforce_legacy_picamera\", False)\n        if isinstance(enforce_legacy_picamera, bool) and enforce_legacy_picamera:\n            # check if picamera library is available.\n            if picamera:\n                logger.critical(\n                    \"Enforcing legacy picamera API for this run. picamera2 API access will be disabled!\"\n                )\n                # disable picamera2\n                picamera2 = None\n            else:\n                # raise error otherwise\n                logger.error(\n                    \"`picamera` is unavailable or unsupported on this system, `enforce_legacy_picamera` will be discarded!\"\n                )\n                import_dependency_safe(\"picamera\")\n\n        if picamera2:\n            # handle logging\n            not (self.__logging) and not os.getenv(\n                \"LIBCAMERA_LOG_LEVELS\", False\n            ) and logger.info(\n                \"Kindly set `LIBCAMERA_LOG_LEVELS=2` environment variable to disable common libcamera API messages.\"\n            )\n            # collect metadata\n            cameras_metadata = Picamera2.global_camera_info()\n            # initialize the picamera stream at given index\n            self.__camera = Picamera2(camera_num=camera_num)\n            # extract metadata for current camera\n            camera_metadata = [x for x in cameras_metadata if x[\"Num\"] == camera_num][0]\n            # check connected camera is USB or I2C\n            self.__camera_is_usb = True if \"usb\" in camera_metadata[\"Id\"] else False\n            # handle framerate control\n            if not self.__camera_is_usb:\n                self.__camera.set_controls({\"FrameRate\": framerate})\n            else:\n                logger.warning(\n                    \"USB camera detected. Setting input framerate is NOT supported with Picamera2 API!\"\n                )\n            # log\n            self.__logging and logger.debug(\n                \"Activating Picamera2 API for `{}` camera at index: {} with resolution: {} & framerate: {}\".format(\n                    camera_metadata[\"Model\"],\n                    camera_num,\n                    resolution if not self.__camera_is_usb else \"default\",\n                    framerate,\n                )\n            )\n        else:\n            # initialize the picamera stream at given index\n            self.__camera = PiCamera(camera_num=camera_num)\n            self.__camera.resolution = tuple(resolution)\n            self.__camera.framerate = framerate\n            self.__logging and logger.debug(\n                \"Activating Picamera API at index: {} with resolution: {} & framerate: {}\".format(\n                    camera_num, resolution, framerate\n                )\n            )\n\n        # initialize framerate (Read-only) variable\n        self.framerate = framerate\n\n        # initializing colorspace variable\n        self.color_space = None\n\n        # define timeout variable default value(handles hardware failures)\n        self.__failure_timeout = options.pop(\"HWFAILURE_TIMEOUT\", 2.0)\n        if isinstance(self.__failure_timeout, (int, float)):\n            if not (10.0 > self.__failure_timeout > 1.0):\n                raise ValueError(\n                    \"[PiGear:ERROR] :: `HWFAILURE_TIMEOUT` value can only be between 1.0 ~ 10.0\"\n                )\n            self.__logging and logger.debug(\n                \"Setting HW Failure Timeout: {} seconds\".format(self.__failure_timeout)\n            )\n        else:\n            # reset improper values\n            self.__failure_timeout = 2.0\n\n        try:\n            if picamera2:\n                # define common supported picamera2 config parameters\n                valid_config_options = [\n                    \"auto_align_output_size\",  # internal\n                    \"enable_verbose_logs\",  # internal\n                    \"format\",\n                    \"sensor\",\n                ]\n\n                # define non-USB supported picamera2 config parameters\n                non_usb_options = [\n                    \"controls\",  # not-supported on USB\n                    \"transform\",  # not-working on USB\n                    \"buffer_count\",  # not-supported on USB\n                    \"queue\",  # not-supported on USB\n                ]  # Less are supported (will be changed in future)\n\n                # filter parameter supported with non-USB cameras only\n                if self.__camera_is_usb:\n                    unsupported_config_keys = set(list(options.keys())).intersection(\n                        set(non_usb_options)\n                    )\n                    unsupported_config_keys and logger.warning(\n                        \"Setting parameters: `{}` for USB camera is NOT supported with Picamera2 API!\".format(\n                            \"`, `\".join(unsupported_config_keys)\n                        )\n                    )\n                else:\n                    valid_config_options += non_usb_options\n\n                # log all invalid keys\n                invalid_config_keys = set(list(options.keys())) - set(\n                    valid_config_options\n                )\n                invalid_config_keys and logger.warning(\n                    \"Discarding invalid options NOT supported by Picamera2 API for current Camera Sensor: `{}`\".format(\n                        \"`, `\".join(invalid_config_keys)\n                    )\n                )\n                # delete all unsupported options\n                options = {\n                    x: y for x, y in options.items() if x in valid_config_options\n                }\n\n                # setting size, already defined\n                options.update({\"size\": tuple(resolution)})\n\n                # set 24-bit, BGR format by default\n                if not \"format\" in options:\n                    # auto defaults for USB cameras\n                    not self.__camera_is_usb and options.update({\"format\": \"RGB888\"})\n                elif self.__camera_is_usb:\n                    # check the supported formats, if USB camera\n                    avail_formats = [\n                        mode[\"format\"] for mode in self.__camera.sensor_modes\n                    ]\n                    # handle unsupported formats\n                    if not options[\"format\"] in avail_formats:\n                        logger.warning(\n                            \"Discarding `format={}`. `{}` are the only available formats for USB camera in use!\".format(\n                                options[\"format\"], \"`, `\".join(avail_formats)\n                            )\n                        )\n                        del options[\"format\"]\n                    else:\n                        # `colorspace` parameter must define with  `format` optional parameter\n                        # unless format is MPEG (tested)\n                        (\n                            not (colorspace is None) or options[\"format\"] == \"MPEG\"\n                        ) and logger.warning(\n                            \"Custom Output frames `format={}` detected. It is advised to define `colorspace` parameter or handle this format manually in your code!\".format(\n                                options[\"format\"]\n                            )\n                        )\n                else:\n                    # `colorspace` parameter must define with  `format` optional parameter\n                    # unless format is either BGR or BGRA\n                    (\n                        not (colorspace is None)\n                        or options[\"format\"]\n                        in [\n                            \"RGB888\",\n                            \"XRGB8888\",\n                        ]\n                    ) and logger.warning(\n                        \"Custom Output frames `format={}` detected. It is advised to define `colorspace` parameter or handle this format manually in your code!\".format(\n                            options[\"format\"]\n                        )\n                    )\n\n                # enable verbose logging mode (handled by Picamera2 API)\n                verbose = options.pop(\"enable_verbose_logs\", False)\n                if self.__logging and isinstance(verbose, bool) and verbose:\n                    self.__camera.set_logging(Picamera2.DEBUG)\n                else:\n                    # setup logging\n                    self.__camera.set_logging(Picamera2.WARNING)\n\n                # handle transformations, if specified\n                transform = options.pop(\"transform\", Transform())\n                if not isinstance(transform, Transform):\n                    logger.warning(\"`transform` value is of invalid type, Discarding!\")\n                    transform = Transform()\n\n                # handle sensor configurations, if specified\n                sensor = options.pop(\"sensor\", {})\n                if isinstance(sensor, dict):\n                    # extract all valid sensor keys\n                    valid_sensor = [\"output_size\", \"bit_depth\"]\n                    # log all invalid keys\n                    invalid_sensor_keys = set(list(sensor)) - set(valid_sensor)\n                    invalid_sensor_keys and logger.warning(\n                        \"Discarding sensor properties NOT supported by current Camera Sensor: `{}`. Only supported are: (`{}`)\".format(\n                            \"`, `\".join(invalid_sensor_keys),\n                            \"`, `\".join(valid_sensor),\n                        )\n                    )\n                    # delete all unsupported control keys\n                    sensor = {x: y for x, y in sensor.items() if x in valid_sensor}\n                    # remove size if output size is defined\n                    if \"output_size\" in sensor:\n                        del options[\"size\"]\n                        logger.critical(\n                            \"Overriding output frame size with `output_size={}!\".format(\n                                sensor[\"output_size\"]\n                            )\n                        )\n                else:\n                    logger.warning(\"`sensor` value is of invalid type, Discarding!\")\n                    sensor = {}\n\n                # handle controls, if specified\n                controls = options.pop(\"controls\", {})\n                if isinstance(controls, dict):\n                    # extract all valid control keys\n                    valid_controls = self.__camera.camera_controls\n                    # remove any fps controls, assigned already\n                    valid_controls.pop(\"FrameDuration\", None)\n                    valid_controls.pop(\"FrameDurationLimits\", None)\n                    # log all invalid keys\n                    invalid_control_keys = set(list(controls.keys())) - set(\n                        list(valid_controls.keys())\n                    )\n                    invalid_control_keys and logger.warning(\n                        \"Discarding control properties NOT supported by current Camera Sensor: `{}`. Only supported are: (`{}`)\".format(\n                            \"`, `\".join(invalid_control_keys),\n                            \"`, `\".join(list(valid_controls.keys())),\n                        )\n                    )\n                    # delete all unsupported control keys\n                    controls = {\n                        x: y for x, y in controls.items() if x in valid_controls.keys()\n                    }\n                else:\n                    logger.warning(\"`controls` value is of invalid type, Discarding!\")\n                    controls = {}\n\n                # handle buffer_count, if specified\n                buffer_count = options.pop(\"buffer_count\", 4)\n                if (\n                    not isinstance(buffer_count, int) or buffer_count < 1\n                ):  # must be greater than 1\n                    logger.warning(\n                        \"`buffer_count` value is of invalid type, Discarding!\"\n                    )\n                    # `create_preview_configuration` requests 4 sets of buffers\n                    buffer_count = 4\n\n                # handle queue, if specified\n                queue = options.pop(\"queue\", True)\n                if not isinstance(queue, bool):\n                    logger.warning(\"`queue` value is of invalid type, Discarding!\")\n                    queue = True\n\n                # check if auto-align camera configuration is specified\n                auto_align_output_size = options.pop(\"auto_align_output_size\", False)\n\n                # create default configuration for camera\n                config = self.__camera.create_preview_configuration(\n                    main=options,\n                    transform=transform,\n                    sensor=sensor,\n                    controls=controls,\n                    buffer_count=buffer_count,\n                    queue=queue,\n                )\n\n                # auto-align camera configuration, if specified\n                if isinstance(auto_align_output_size, bool) and auto_align_output_size:\n                    self.__logging and logger.debug(\n                        \"Re-aligning Output frames to optimal size supported by current Camera Sensor.\"\n                    )\n                    self.__camera.align_configuration(config)\n\n                # configure camera\n                self.__camera.configure(config)\n                self.__logging and logger.debug(\n                    \"Setting Picamera2 API Parameters: `{}`, controls: `{}`, sensor: `{}`, buffer_count: `{}`, and queue: `{}`.\".format(\n                        self.__camera.camera_configuration()[\"main\"],\n                        controls,\n                        sensor,\n                        buffer_count,\n                        queue,\n                    )\n                )\n            else:\n                # apply attributes to source if specified\n                for key, value in options.items():\n                    self.__logging and logger.debug(\n                        \"Setting {} API Parameter for Picamera: `{}`\".format(key, value)\n                    )\n                    setattr(self.__camera, key, value)\n        except Exception as e:\n            # Catch if any error occurred\n            logger.exception(str(e))\n\n        # separately handle colorspace value to int conversion\n        if not (colorspace is None):\n            self.color_space = capPropId(colorspace.strip())\n            if self.__logging and not (self.color_space is None):\n                logger.debug(\n                    \"Enabling `{}` colorspace for this video stream!\".format(\n                        colorspace.strip()\n                    )\n                )\n\n        # enable rgb capture array thread and capture stream\n        if not picamera2:\n            self.__rawCapture = PiRGBArray(self.__camera, size=resolution)\n            self.stream = self.__camera.capture_continuous(\n                self.__rawCapture, format=\"bgr\", use_video_port=True\n            )\n\n        # initialize frame variable\n        # with captured frame\n        try:\n            if picamera2:\n                # start camera thread\n                self.__camera.start()\n                # capture frame array\n                self.frame = self.__camera.capture_array(\"main\")\n                # assign camera as stream for setting\n                # parameters after starting the camera\n                self.stream = self.__camera\n            else:\n                # capture frame array from stream\n                stream = next(self.stream)\n                self.frame = stream.array\n                self.__rawCapture.seek(0)\n                self.__rawCapture.truncate()\n            # render colorspace if defined\n            if not (self.frame is None) and not (self.color_space is None):\n                self.frame = cv2.cvtColor(self.frame, self.color_space)\n        except Exception as e:\n            logger.exception(str(e))\n            raise RuntimeError(\"[PiGear:ERROR] :: Camera Module failed to initialize!\")\n\n        # applying time delay to warm-up picamera only if specified\n        if time_delay and isinstance(time_delay, (int, float)):\n            time.sleep(time_delay)\n\n        # thread initialization\n        self.__thread = None\n\n        # timer thread initialization(Keeps check on frozen thread)\n        self.__timer = None\n        self.__t_elapsed = 0.0  # records time taken by thread\n\n        # catching thread exceptions\n        self.__exceptions = None\n\n        # initialize termination flag\n        self.__terminate = False\n\n    def start(self):\n        \"\"\"\n        Launches the internal *Threaded Frames Extractor* daemon\n\n        **Returns:** A reference to the PiGear class object.\n        \"\"\"\n        # Start frame producer thread\n        self.__thread = Thread(target=self.__update, name=\"PiGear\", args=())\n        self.__thread.daemon = True\n        self.__thread.start()\n\n        # Start internal timer thread\n        self.__timer = Thread(target=self.__timeit, name=\"PiTimer\", args=())\n        self.__timer.daemon = True\n        self.__timer.start()\n\n        return self\n\n    def __timeit(self):\n        \"\"\"\n        Threaded Internal Timer that keep checks on thread execution timing\n        \"\"\"\n        # assign current time\n        self.__t_elapsed = time.time()\n\n        # loop until terminated\n        while not (self.__terminate):\n            # check for frozen thread\n            if time.time() - self.__t_elapsed > self.__failure_timeout:\n                # log failure\n                self.__logging and logger.critical(\"Camera Module Disconnected!\")\n                # prepare for clean exit\n                self.__exceptions = True\n                self.__terminate = True  # self-terminate\n\n    def __update(self):\n        \"\"\"\n        A **Threaded Frames Extractor**, that keep iterating frames from PiCamera API to a internal monitored deque,\n        until the thread is terminated, or frames runs out.\n        \"\"\"\n        # keep looping infinitely until the thread is terminated\n        while not (self.__terminate):\n            if not picamera2:\n                try:\n                    # Try to iterate next frame from generator\n                    stream = next(self.stream)\n                except Exception:\n                    # catch and save any exceptions\n                    self.__exceptions = sys.exc_info()\n                    break  # exit\n\n            # __update timer\n            self.__t_elapsed = time.time()\n\n            # grab the frame from the stream\n            if picamera2:\n                frame = self.__camera.capture_array(\"main\")\n            else:\n                frame = stream.array\n                # clear the stream in preparation\n                # for the next frame\n                self.__rawCapture.seek(0)\n                self.__rawCapture.truncate()\n\n            # apply colorspace if specified\n            if not (self.color_space is None):\n                # apply colorspace to frames\n                color_frame = None\n                try:\n                    color_frame = cv2.cvtColor(frame, self.color_space)\n                except Exception as e:\n                    # Catch if any error occurred\n                    color_frame = None\n                    self.color_space = None\n                    self.__logging and logger.exception(str(e))\n                    logger.warning(\"Assigned colorspace value is invalid. Discarding!\")\n                self.frame = color_frame if not (color_frame is None) else frame\n            else:\n                self.frame = frame\n\n        # terminate processes\n        if not (self.__terminate):\n            self.__terminate = True\n\n        # release resources\n        if picamera2:\n            self.__camera.stop()\n        else:\n            self.__rawCapture.close()\n            self.__camera.close()\n\n    def read(self):\n        \"\"\"\n        Extracts frames synchronously from monitored deque, while maintaining a fixed-length frame buffer in the memory,\n        and blocks the thread if the deque is full.\n\n        **Returns:** A n-dimensional numpy array.\n        \"\"\"\n        # check if there are any thread exceptions\n        if not (self.__exceptions is None):\n            if isinstance(self.__exceptions, bool):\n                # clear frame\n                self.frame = None\n                # notify user about hardware failure\n                raise SystemError(\n                    \"[PiGear:ERROR] :: Hardware failure occurred, Kindly reconnect Camera Module and restart your Pi!\"\n                )\n            else:\n                # clear frame\n                self.frame = None\n                # re-raise error for debugging\n                error_msg = (\n                    \"[PiGear:ERROR] :: Camera Module API failure occurred: {}\".format(\n                        self.__exceptions[1]\n                    )\n                )\n                raise RuntimeError(error_msg).with_traceback(self.__exceptions[2])\n        # return the frame\n        return self.frame\n\n    def stop(self):\n        \"\"\"\n        Safely terminates the thread, and release the multi-threaded resources.\n        \"\"\"\n        # log termination\n        self.__logging and logger.debug(\"Terminating PiGear Processes.\")\n\n        # make sure that the threads should be terminated\n        self.__terminate = True\n\n        # stop timer thread\n        if not (self.__timer is None):\n            self.__timer.join()\n            self.__timer = None\n\n        # handle camera thread\n        if not (self.__thread is None):\n            # check if hardware failure occurred\n            if not (self.__exceptions is None) and isinstance(self.__exceptions, bool):\n                if picamera2:\n                    # release picamera2 resources\n                    self.__camera.stop()\n                else:\n                    # force release picamera resources\n                    self.__rawCapture.close()\n                    self.__camera.close()\n            # properly handle thread exit\n            # wait if still process is still\n            # processing some information\n            self.__thread.join()\n            # remove any threads\n            self.__thread = None\n

"},{"location":"bonus/reference/pigear/#vidgear.gears.pigear.PiGear.__init__","title":"__init__(self, camera_num=0, resolution=(640, 480), framerate=30, colorspace=None, logging=False, time_delay=0, **options) special","text":"

This constructor method initializes the object state and attributes of the PiGear class.

Parameters:

Name Type Description Default camera_num int

selects the camera module index which will be used as source.

0 resolution tuple

sets the resolution (i.e. (width,height)) of the source..

(640, 480) framerate int/float

sets the framerate of the source.

30 colorspace str

selects the colorspace of the input stream.

None logging bool

enables/disables logging.

False time_delay int

time delay (in sec) before start reading the frames.

0 options dict

provides ability to alter Source Tweak Parameters.

{} Source code in vidgear/gears/pigear.py
def __init__(\n    self,\n    camera_num=0,\n    resolution=(640, 480),\n    framerate=30,\n    colorspace=None,\n    logging=False,\n    time_delay=0,\n    **options\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the PiGear class.\n\n    Parameters:\n        camera_num (int): selects the camera module index which will be used as source.\n        resolution (tuple): sets the resolution (i.e. `(width,height)`) of the source..\n        framerate (int/float): sets the framerate of the source.\n        colorspace (str): selects the colorspace of the input stream.\n        logging (bool): enables/disables logging.\n        time_delay (int): time delay (in sec) before start reading the frames.\n        options (dict): provides ability to alter Source Tweak Parameters.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # raise error(s) for critical Class imports\n    global picamera, picamera2\n    if picamera2:\n        # log if picamera2\n        self.__logging and logger.info(\"picamera2 API is currently being accessed.\")\n    elif picamera:\n        # switch to picamera otherwise\n        logger.critical(\n            \"picamera2 library not installed on this system. Defaulting to legacy picamera API.\"\n        )\n    else:\n        # raise error if none\n        import_dependency_safe(\"picamera\")\n\n    assert (\n        isinstance(framerate, (int, float)) and framerate > 0.0\n    ), \"[PiGear:ERROR] :: Input framerate value `{}` is a Invalid! Kindly read docs.\".format(\n        framerate\n    )\n    assert (\n        isinstance(resolution, (tuple, list)) and len(resolution) == 2\n    ), \"[PiGear:ERROR] :: Input resolution value `{}` is a Invalid! Kindly read docs.\".format(\n        resolution\n    )\n    if not (isinstance(camera_num, int) and camera_num >= 0):\n        camera_num = 0\n        logger.warning(\n            \"Input camera_num value `{}` is invalid, Defaulting to index 0!\"\n        )\n\n    # reformat dict\n    options = {str(k).strip(): v for k, v in options.items()}\n\n    # check if legacy picamera backend is enforced\n    enforce_legacy_picamera = options.pop(\"enforce_legacy_picamera\", False)\n    if isinstance(enforce_legacy_picamera, bool) and enforce_legacy_picamera:\n        # check if picamera library is available.\n        if picamera:\n            logger.critical(\n                \"Enforcing legacy picamera API for this run. picamera2 API access will be disabled!\"\n            )\n            # disable picamera2\n            picamera2 = None\n        else:\n            # raise error otherwise\n            logger.error(\n                \"`picamera` is unavailable or unsupported on this system, `enforce_legacy_picamera` will be discarded!\"\n            )\n            import_dependency_safe(\"picamera\")\n\n    if picamera2:\n        # handle logging\n        not (self.__logging) and not os.getenv(\n            \"LIBCAMERA_LOG_LEVELS\", False\n        ) and logger.info(\n            \"Kindly set `LIBCAMERA_LOG_LEVELS=2` environment variable to disable common libcamera API messages.\"\n        )\n        # collect metadata\n        cameras_metadata = Picamera2.global_camera_info()\n        # initialize the picamera stream at given index\n        self.__camera = Picamera2(camera_num=camera_num)\n        # extract metadata for current camera\n        camera_metadata = [x for x in cameras_metadata if x[\"Num\"] == camera_num][0]\n        # check connected camera is USB or I2C\n        self.__camera_is_usb = True if \"usb\" in camera_metadata[\"Id\"] else False\n        # handle framerate control\n        if not self.__camera_is_usb:\n            self.__camera.set_controls({\"FrameRate\": framerate})\n        else:\n            logger.warning(\n                \"USB camera detected. Setting input framerate is NOT supported with Picamera2 API!\"\n            )\n        # log\n        self.__logging and logger.debug(\n            \"Activating Picamera2 API for `{}` camera at index: {} with resolution: {} & framerate: {}\".format(\n                camera_metadata[\"Model\"],\n                camera_num,\n                resolution if not self.__camera_is_usb else \"default\",\n                framerate,\n            )\n        )\n    else:\n        # initialize the picamera stream at given index\n        self.__camera = PiCamera(camera_num=camera_num)\n        self.__camera.resolution = tuple(resolution)\n        self.__camera.framerate = framerate\n        self.__logging and logger.debug(\n            \"Activating Picamera API at index: {} with resolution: {} & framerate: {}\".format(\n                camera_num, resolution, framerate\n            )\n        )\n\n    # initialize framerate (Read-only) variable\n    self.framerate = framerate\n\n    # initializing colorspace variable\n    self.color_space = None\n\n    # define timeout variable default value(handles hardware failures)\n    self.__failure_timeout = options.pop(\"HWFAILURE_TIMEOUT\", 2.0)\n    if isinstance(self.__failure_timeout, (int, float)):\n        if not (10.0 > self.__failure_timeout > 1.0):\n            raise ValueError(\n                \"[PiGear:ERROR] :: `HWFAILURE_TIMEOUT` value can only be between 1.0 ~ 10.0\"\n            )\n        self.__logging and logger.debug(\n            \"Setting HW Failure Timeout: {} seconds\".format(self.__failure_timeout)\n        )\n    else:\n        # reset improper values\n        self.__failure_timeout = 2.0\n\n    try:\n        if picamera2:\n            # define common supported picamera2 config parameters\n            valid_config_options = [\n                \"auto_align_output_size\",  # internal\n                \"enable_verbose_logs\",  # internal\n                \"format\",\n                \"sensor\",\n            ]\n\n            # define non-USB supported picamera2 config parameters\n            non_usb_options = [\n                \"controls\",  # not-supported on USB\n                \"transform\",  # not-working on USB\n                \"buffer_count\",  # not-supported on USB\n                \"queue\",  # not-supported on USB\n            ]  # Less are supported (will be changed in future)\n\n            # filter parameter supported with non-USB cameras only\n            if self.__camera_is_usb:\n                unsupported_config_keys = set(list(options.keys())).intersection(\n                    set(non_usb_options)\n                )\n                unsupported_config_keys and logger.warning(\n                    \"Setting parameters: `{}` for USB camera is NOT supported with Picamera2 API!\".format(\n                        \"`, `\".join(unsupported_config_keys)\n                    )\n                )\n            else:\n                valid_config_options += non_usb_options\n\n            # log all invalid keys\n            invalid_config_keys = set(list(options.keys())) - set(\n                valid_config_options\n            )\n            invalid_config_keys and logger.warning(\n                \"Discarding invalid options NOT supported by Picamera2 API for current Camera Sensor: `{}`\".format(\n                    \"`, `\".join(invalid_config_keys)\n                )\n            )\n            # delete all unsupported options\n            options = {\n                x: y for x, y in options.items() if x in valid_config_options\n            }\n\n            # setting size, already defined\n            options.update({\"size\": tuple(resolution)})\n\n            # set 24-bit, BGR format by default\n            if not \"format\" in options:\n                # auto defaults for USB cameras\n                not self.__camera_is_usb and options.update({\"format\": \"RGB888\"})\n            elif self.__camera_is_usb:\n                # check the supported formats, if USB camera\n                avail_formats = [\n                    mode[\"format\"] for mode in self.__camera.sensor_modes\n                ]\n                # handle unsupported formats\n                if not options[\"format\"] in avail_formats:\n                    logger.warning(\n                        \"Discarding `format={}`. `{}` are the only available formats for USB camera in use!\".format(\n                            options[\"format\"], \"`, `\".join(avail_formats)\n                        )\n                    )\n                    del options[\"format\"]\n                else:\n                    # `colorspace` parameter must define with  `format` optional parameter\n                    # unless format is MPEG (tested)\n                    (\n                        not (colorspace is None) or options[\"format\"] == \"MPEG\"\n                    ) and logger.warning(\n                        \"Custom Output frames `format={}` detected. It is advised to define `colorspace` parameter or handle this format manually in your code!\".format(\n                            options[\"format\"]\n                        )\n                    )\n            else:\n                # `colorspace` parameter must define with  `format` optional parameter\n                # unless format is either BGR or BGRA\n                (\n                    not (colorspace is None)\n                    or options[\"format\"]\n                    in [\n                        \"RGB888\",\n                        \"XRGB8888\",\n                    ]\n                ) and logger.warning(\n                    \"Custom Output frames `format={}` detected. It is advised to define `colorspace` parameter or handle this format manually in your code!\".format(\n                        options[\"format\"]\n                    )\n                )\n\n            # enable verbose logging mode (handled by Picamera2 API)\n            verbose = options.pop(\"enable_verbose_logs\", False)\n            if self.__logging and isinstance(verbose, bool) and verbose:\n                self.__camera.set_logging(Picamera2.DEBUG)\n            else:\n                # setup logging\n                self.__camera.set_logging(Picamera2.WARNING)\n\n            # handle transformations, if specified\n            transform = options.pop(\"transform\", Transform())\n            if not isinstance(transform, Transform):\n                logger.warning(\"`transform` value is of invalid type, Discarding!\")\n                transform = Transform()\n\n            # handle sensor configurations, if specified\n            sensor = options.pop(\"sensor\", {})\n            if isinstance(sensor, dict):\n                # extract all valid sensor keys\n                valid_sensor = [\"output_size\", \"bit_depth\"]\n                # log all invalid keys\n                invalid_sensor_keys = set(list(sensor)) - set(valid_sensor)\n                invalid_sensor_keys and logger.warning(\n                    \"Discarding sensor properties NOT supported by current Camera Sensor: `{}`. Only supported are: (`{}`)\".format(\n                        \"`, `\".join(invalid_sensor_keys),\n                        \"`, `\".join(valid_sensor),\n                    )\n                )\n                # delete all unsupported control keys\n                sensor = {x: y for x, y in sensor.items() if x in valid_sensor}\n                # remove size if output size is defined\n                if \"output_size\" in sensor:\n                    del options[\"size\"]\n                    logger.critical(\n                        \"Overriding output frame size with `output_size={}!\".format(\n                            sensor[\"output_size\"]\n                        )\n                    )\n            else:\n                logger.warning(\"`sensor` value is of invalid type, Discarding!\")\n                sensor = {}\n\n            # handle controls, if specified\n            controls = options.pop(\"controls\", {})\n            if isinstance(controls, dict):\n                # extract all valid control keys\n                valid_controls = self.__camera.camera_controls\n                # remove any fps controls, assigned already\n                valid_controls.pop(\"FrameDuration\", None)\n                valid_controls.pop(\"FrameDurationLimits\", None)\n                # log all invalid keys\n                invalid_control_keys = set(list(controls.keys())) - set(\n                    list(valid_controls.keys())\n                )\n                invalid_control_keys and logger.warning(\n                    \"Discarding control properties NOT supported by current Camera Sensor: `{}`. Only supported are: (`{}`)\".format(\n                        \"`, `\".join(invalid_control_keys),\n                        \"`, `\".join(list(valid_controls.keys())),\n                    )\n                )\n                # delete all unsupported control keys\n                controls = {\n                    x: y for x, y in controls.items() if x in valid_controls.keys()\n                }\n            else:\n                logger.warning(\"`controls` value is of invalid type, Discarding!\")\n                controls = {}\n\n            # handle buffer_count, if specified\n            buffer_count = options.pop(\"buffer_count\", 4)\n            if (\n                not isinstance(buffer_count, int) or buffer_count < 1\n            ):  # must be greater than 1\n                logger.warning(\n                    \"`buffer_count` value is of invalid type, Discarding!\"\n                )\n                # `create_preview_configuration` requests 4 sets of buffers\n                buffer_count = 4\n\n            # handle queue, if specified\n            queue = options.pop(\"queue\", True)\n            if not isinstance(queue, bool):\n                logger.warning(\"`queue` value is of invalid type, Discarding!\")\n                queue = True\n\n            # check if auto-align camera configuration is specified\n            auto_align_output_size = options.pop(\"auto_align_output_size\", False)\n\n            # create default configuration for camera\n            config = self.__camera.create_preview_configuration(\n                main=options,\n                transform=transform,\n                sensor=sensor,\n                controls=controls,\n                buffer_count=buffer_count,\n                queue=queue,\n            )\n\n            # auto-align camera configuration, if specified\n            if isinstance(auto_align_output_size, bool) and auto_align_output_size:\n                self.__logging and logger.debug(\n                    \"Re-aligning Output frames to optimal size supported by current Camera Sensor.\"\n                )\n                self.__camera.align_configuration(config)\n\n            # configure camera\n            self.__camera.configure(config)\n            self.__logging and logger.debug(\n                \"Setting Picamera2 API Parameters: `{}`, controls: `{}`, sensor: `{}`, buffer_count: `{}`, and queue: `{}`.\".format(\n                    self.__camera.camera_configuration()[\"main\"],\n                    controls,\n                    sensor,\n                    buffer_count,\n                    queue,\n                )\n            )\n        else:\n            # apply attributes to source if specified\n            for key, value in options.items():\n                self.__logging and logger.debug(\n                    \"Setting {} API Parameter for Picamera: `{}`\".format(key, value)\n                )\n                setattr(self.__camera, key, value)\n    except Exception as e:\n        # Catch if any error occurred\n        logger.exception(str(e))\n\n    # separately handle colorspace value to int conversion\n    if not (colorspace is None):\n        self.color_space = capPropId(colorspace.strip())\n        if self.__logging and not (self.color_space is None):\n            logger.debug(\n                \"Enabling `{}` colorspace for this video stream!\".format(\n                    colorspace.strip()\n                )\n            )\n\n    # enable rgb capture array thread and capture stream\n    if not picamera2:\n        self.__rawCapture = PiRGBArray(self.__camera, size=resolution)\n        self.stream = self.__camera.capture_continuous(\n            self.__rawCapture, format=\"bgr\", use_video_port=True\n        )\n\n    # initialize frame variable\n    # with captured frame\n    try:\n        if picamera2:\n            # start camera thread\n            self.__camera.start()\n            # capture frame array\n            self.frame = self.__camera.capture_array(\"main\")\n            # assign camera as stream for setting\n            # parameters after starting the camera\n            self.stream = self.__camera\n        else:\n            # capture frame array from stream\n            stream = next(self.stream)\n            self.frame = stream.array\n            self.__rawCapture.seek(0)\n            self.__rawCapture.truncate()\n        # render colorspace if defined\n        if not (self.frame is None) and not (self.color_space is None):\n            self.frame = cv2.cvtColor(self.frame, self.color_space)\n    except Exception as e:\n        logger.exception(str(e))\n        raise RuntimeError(\"[PiGear:ERROR] :: Camera Module failed to initialize!\")\n\n    # applying time delay to warm-up picamera only if specified\n    if time_delay and isinstance(time_delay, (int, float)):\n        time.sleep(time_delay)\n\n    # thread initialization\n    self.__thread = None\n\n    # timer thread initialization(Keeps check on frozen thread)\n    self.__timer = None\n    self.__t_elapsed = 0.0  # records time taken by thread\n\n    # catching thread exceptions\n    self.__exceptions = None\n\n    # initialize termination flag\n    self.__terminate = False\n
"},{"location":"bonus/reference/pigear/#vidgear.gears.pigear.PiGear.read","title":"read(self)","text":"

Extracts frames synchronously from monitored deque, while maintaining a fixed-length frame buffer in the memory, and blocks the thread if the deque is full.

Returns: A n-dimensional numpy array.

Source code in vidgear/gears/pigear.py
def read(self):\n    \"\"\"\n    Extracts frames synchronously from monitored deque, while maintaining a fixed-length frame buffer in the memory,\n    and blocks the thread if the deque is full.\n\n    **Returns:** A n-dimensional numpy array.\n    \"\"\"\n    # check if there are any thread exceptions\n    if not (self.__exceptions is None):\n        if isinstance(self.__exceptions, bool):\n            # clear frame\n            self.frame = None\n            # notify user about hardware failure\n            raise SystemError(\n                \"[PiGear:ERROR] :: Hardware failure occurred, Kindly reconnect Camera Module and restart your Pi!\"\n            )\n        else:\n            # clear frame\n            self.frame = None\n            # re-raise error for debugging\n            error_msg = (\n                \"[PiGear:ERROR] :: Camera Module API failure occurred: {}\".format(\n                    self.__exceptions[1]\n                )\n            )\n            raise RuntimeError(error_msg).with_traceback(self.__exceptions[2])\n    # return the frame\n    return self.frame\n
"},{"location":"bonus/reference/pigear/#vidgear.gears.pigear.PiGear.start","title":"start(self)","text":"

Launches the internal Threaded Frames Extractor daemon

Returns: A reference to the PiGear class object.

Source code in vidgear/gears/pigear.py
def start(self):\n    \"\"\"\n    Launches the internal *Threaded Frames Extractor* daemon\n\n    **Returns:** A reference to the PiGear class object.\n    \"\"\"\n    # Start frame producer thread\n    self.__thread = Thread(target=self.__update, name=\"PiGear\", args=())\n    self.__thread.daemon = True\n    self.__thread.start()\n\n    # Start internal timer thread\n    self.__timer = Thread(target=self.__timeit, name=\"PiTimer\", args=())\n    self.__timer.daemon = True\n    self.__timer.start()\n\n    return self\n
"},{"location":"bonus/reference/pigear/#vidgear.gears.pigear.PiGear.stop","title":"stop(self)","text":"

Safely terminates the thread, and release the multi-threaded resources.

Source code in vidgear/gears/pigear.py
def stop(self):\n    \"\"\"\n    Safely terminates the thread, and release the multi-threaded resources.\n    \"\"\"\n    # log termination\n    self.__logging and logger.debug(\"Terminating PiGear Processes.\")\n\n    # make sure that the threads should be terminated\n    self.__terminate = True\n\n    # stop timer thread\n    if not (self.__timer is None):\n        self.__timer.join()\n        self.__timer = None\n\n    # handle camera thread\n    if not (self.__thread is None):\n        # check if hardware failure occurred\n        if not (self.__exceptions is None) and isinstance(self.__exceptions, bool):\n            if picamera2:\n                # release picamera2 resources\n                self.__camera.stop()\n            else:\n                # force release picamera resources\n                self.__rawCapture.close()\n                self.__camera.close()\n        # properly handle thread exit\n        # wait if still process is still\n        # processing some information\n        self.__thread.join()\n        # remove any threads\n        self.__thread = None\n
"},{"location":"bonus/reference/screengear/","title":"ScreenGear API References","text":"

ScreenGear API usage examples can be found here \u27b6

ScreenGear API parameters are explained here \u27b6

ScreenGear is designed exclusively for targeting rapid Screencasting Capabilities, which means it can grab frames from your monitor in real-time, either by defining an area on the computer screen or full-screen, at the expense of inconsiderable latency. ScreenGear also seamlessly support frame capturing from multiple monitors as well as supports multiple backends.

ScreenGear API implements a multi-threaded wrapper around dxcam, pyscreenshot, python-mss python library, and also flexibly supports its internal parameter.

Source code in vidgear/gears/screengear.py
class ScreenGear:\n    \"\"\"\n    ScreenGear is designed exclusively for targeting rapid Screencasting Capabilities, which means it can\n    grab frames from your monitor in real-time, either by defining an area on the computer screen or full-screen,\n    at the expense of inconsiderable latency. ScreenGear also seamlessly support frame capturing from multiple\n    monitors as well as supports multiple backends.\n\n    ScreenGear API implements a multi-threaded wrapper around dxcam, pyscreenshot, python-mss python library,\n    and also flexibly supports its internal parameter.\n    \"\"\"\n\n    def __init__(\n        self, monitor=None, backend=None, colorspace=None, logging=False, **options\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the ScreenGear class.\n\n        Parameters:\n            monitor (int): enables `mss` backend and sets the index of the monitor screen.\n            backend (str): select suitable backend for extracting frames.\n            colorspace (str): selects the colorspace of the input stream.\n            logging (bool): enables/disables logging.\n            options (dict): provides the flexibility to easily alter backend library parameters. Such as, manually set the dimensions of capture screen area etc.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # create instances for the user-defined monitor\n        self.__monitor_instance = None\n        self.__backend = None\n\n        # validate monitor instance\n        assert (\n            monitor is None or monitor and isinstance(monitor, (int, tuple))\n        ), \"[ScreenGear:ERROR] :: Invalid `monitor` value detected!\"\n\n        # initialize backend\n        if backend and monitor is None:\n            self.__backend = backend.lower().strip()\n        else:\n            # enforce `dxcam` for Windows machines if undefined (or monitor is defined)\n            self.__backend = (\n                \"dxcam\" if platform.system() == \"Windows\" and dxcam else None\n            )\n\n        # initiate screen dimension handler\n        screen_dims = {}\n        # reformat proper mss dict and assign to screen dimension handler\n        screen_dims = {\n            k.strip(): v\n            for k, v in options.items()\n            if k.strip() in [\"top\", \"left\", \"width\", \"height\"]\n        }\n        # check whether user-defined dimensions are provided\n        if screen_dims and len(screen_dims) == 4:\n            key_order = (\n                (\"top\", \"left\", \"width\", \"height\")\n                if self.__backend != \"dxcam\"\n                else (\"left\", \"top\", \"width\", \"height\")\n            )\n            screen_dims = OrderedDict((k, screen_dims[k]) for k in key_order)\n            self.__logging and logger.debug(\n                \"Setting Capture-Area dimensions: {}\".format(json.dumps(screen_dims))\n            )\n        else:\n            screen_dims.clear()\n\n        # handle backends\n        if self.__backend == \"dxcam\":\n            # get target fps in case of DXcam\n            self.__target_fps = options.pop(\"dxcam_target_fps\", 0)\n            if self.__target_fps and isinstance(self.__target_fps, (int, float)):\n                # set values\n                self.__target_fps = int(self.__target_fps)\n                self.__logging and logger.debug(\n                    \"Setting Target FPS: {}\".format(self.__target_fps)\n                )\n            else:\n                # defaults to 0fps\n                self.__target_fps = 0\n            # check if platform is windows\n            assert (\n                platform.system() == \"Windows\"\n            ), \"`dxcam` backend is only available for Windows Machines.\"\n            # verify monitor values if tuple\n            assert (\n                monitor is None\n                or isinstance(monitor, int)\n                or (\n                    isinstance(monitor, tuple)\n                    and len(monitor) == 2\n                    and all(isinstance(x, int) for x in monitor)\n                )\n            ), \"For dxcam` backend, monitor` tuple value must be format `int` or `(int, int)` only.\"\n            # raise error(s) for critical Class imports\n            import_dependency_safe(\"dxcam\" if dxcam is None else \"\")\n            if monitor is None:\n                self.__capture_object = dxcam.create(\n                    region=tuple(screen_dims.values()) if screen_dims else None\n                )\n            else:\n                self.__capture_object = (\n                    dxcam.create(\n                        device_idx=monitor[0],\n                        output_idx=monitor[1],\n                        region=tuple(screen_dims.values()) if screen_dims else None,\n                    )\n                    if isinstance(monitor, tuple)\n                    else dxcam.create(\n                        device_idx=monitor,\n                        region=tuple(screen_dims.values()) if screen_dims else None,\n                    )\n                )\n        else:\n            if monitor is None:\n                # raise error(s) for critical Class imports\n                import_dependency_safe(\"pyscreenshot\" if pysct is None else \"\")\n                # reset backend if not provided\n                self.__backend = \"pil\" if self.__backend is None else self.__backend\n                # check if valid backend\n                assert (\n                    self.__backend in pysct.backends()\n                ), \"Unsupported backend {} provided!\".format(backend)\n                # create capture object\n                self.__capture_object = pysct\n            else:\n                # monitor value must be integer\n                assert monitor and isinstance(\n                    monitor, int\n                ), \"[ScreenGear:ERROR] :: Invalid `monitor` value must be integer with mss backend.\"\n                # raise error(s) for critical Class imports\n                import_dependency_safe(\n                    \"from mss import mss\" if mss is None else \"\", pkg_name=\"mss\"\n                )\n                # create capture object\n                self.__capture_object = mss()\n                self.__backend and logger.warning(\n                    \"Backends are disabled for Monitor Indexing(monitor>=0)!\"\n                )\n                self.__monitor_instance = self.__capture_object.monitors[monitor]\n\n        # log backend\n        self.__backend and self.__logging and logger.debug(\n            \"Setting Backend: {}\".format(self.__backend.upper())\n        )\n\n        # assigns special parameter to global variable and clear\n        # separately handle colorspace value to int conversion\n        if colorspace:\n            self.color_space = capPropId(colorspace.strip())\n            self.__logging and not (self.color_space is None) and logger.debug(\n                \"Enabling `{}` colorspace for this video stream!\".format(\n                    colorspace.strip()\n                )\n            )\n        else:\n            self.color_space = None\n\n        # initialize mss capture instance\n        self.__mss_capture_instance = None\n        try:\n            if self.__backend == \"dxcam\":\n                # extract global frame from instance\n                self.frame = self.__capture_object.grab()\n            else:\n                if self.__monitor_instance is None:\n                    if screen_dims:\n                        self.__mss_capture_instance = tuple(screen_dims.values())\n                    # extract global frame from instance\n                    self.frame = np.asanyarray(\n                        self.__capture_object.grab(\n                            bbox=self.__mss_capture_instance,\n                            childprocess=False,\n                            backend=self.__backend,\n                        )\n                    )\n                else:\n                    if screen_dims:\n                        self.__mss_capture_instance = {\n                            \"top\": self.__monitor_instance[\"top\"] + screen_dims[\"top\"],\n                            \"left\": self.__monitor_instance[\"left\"]\n                            + screen_dims[\"left\"],\n                            \"width\": screen_dims[\"width\"],\n                            \"height\": screen_dims[\"height\"],\n                            \"mon\": monitor,\n                        }\n                    else:\n                        self.__mss_capture_instance = (\n                            self.__monitor_instance  # otherwise create instance from monitor\n                        )\n                    # extract global frame from instance\n                    self.frame = np.asanyarray(\n                        self.__capture_object.grab(self.__mss_capture_instance)\n                    )\n            # convert to bgr frame if applicable\n            self.frame = (\n                self.frame[:, :, ::-1]\n                if self.__backend == \"dxcam\" or not (pysct is None)\n                else self.frame\n            )\n            # render colorspace if defined\n            if not (self.frame is None) and not (self.color_space is None):\n                self.frame = cv2.cvtColor(self.frame, self.color_space)\n        except Exception as e:\n            if isinstance(e, ScreenShotError):\n                # otherwise catch and log errors\n                self.__logging and logger.exception(\n                    self.__capture_object.get_error_details()\n                )\n                raise ValueError(\n                    \"[ScreenGear:ERROR] :: ScreenShotError caught, Wrong dimensions passed to python-mss, Kindly Refer Docs!\"\n                )\n            else:\n                raise SystemError(\n                    \"[ScreenGear:ERROR] :: Unable to grab any instance on this system, Are you running headless?\"\n                )\n        # thread initialization\n        self.__thread = None\n        # initialize termination flag\n        self.__terminate = Event()\n\n    def start(self):\n        \"\"\"\n        Launches the internal *Threaded Frames Extractor* daemon\n\n        **Returns:** A reference to the ScreenGear class object.\n        \"\"\"\n        self.__thread = Thread(target=self.__update, name=\"ScreenGear\", args=())\n        self.__thread.daemon = True\n        self.__thread.start()\n        if self.__backend == \"dxcam\":\n            self.__capture_object.start(\n                target_fps=self.__target_fps,\n                video_mode=True,\n            )\n            self.__logging and self.__target_fps and logger.debug(\n                \"Targeting FPS: {}\".format(self.__target_fps)\n            )\n        return self\n\n    def __update(self):\n        \"\"\"\n        A **Threaded Frames Extractor**, that keep iterating frames from `mss` API to a internal monitored deque,\n        until the thread is terminated, or frames runs out.\n        \"\"\"\n        # initialize frame variable\n        frame = None\n        # keep looping infinitely until the thread is terminated\n        while not self.__terminate.is_set():\n            try:\n                if self.__backend == \"dxcam\":\n                    # extract global frame from instance\n                    frame = self.__capture_object.get_latest_frame()\n                else:\n                    if self.__monitor_instance:\n                        frame = np.asanyarray(\n                            self.__capture_object.grab(self.__mss_capture_instance)\n                        )\n                    else:\n                        frame = np.asanyarray(\n                            self.__capture_object.grab(\n                                bbox=self.__mss_capture_instance,\n                                childprocess=False,\n                                backend=self.__backend,\n                            )\n                        )\n                # check if valid frame\n                assert not (\n                    frame is None or np.shape(frame) == ()\n                ), \"[ScreenGear:ERROR] :: Failed to retrieve valid frame!\"\n                # convert to bgr frame if applicable\n                frame = (\n                    frame[:, :, ::-1]\n                    if self.__backend == \"dxcam\" or not (pysct is None)\n                    else frame\n                )\n            except Exception as e:\n                if isinstance(e, ScreenShotError):\n                    raise RuntimeError(self.__capture_object.get_error_details())\n                else:\n                    logger.exception(str(e))\n                self.__terminate.set()\n                continue\n\n            if not (self.color_space is None):\n                # apply colorspace to frames\n                color_frame = None\n                try:\n                    color_frame = cv2.cvtColor(frame, self.color_space)\n                except Exception as e:\n                    # Catch if any error occurred\n                    color_frame = None\n                    self.color_space = None\n                    self.__logging and logger.exception(str(e))\n                    logger.warning(\"Assigned colorspace value is invalid. Discarding!\")\n                self.frame = color_frame if not (color_frame is None) else frame\n            else:\n                self.frame = frame\n\n        # indicate immediate termination\n        self.__terminate.set()\n\n        # finally release mss resources\n        if self.__monitor_instance:\n            self.__capture_object.close()\n        if self.__backend == \"dxcam\":\n            self.__capture_object.stop()\n            del self.__capture_object\n\n    def read(self):\n        \"\"\"\n        Extracts frames synchronously from monitored deque, while maintaining a fixed-length frame buffer in the memory,\n        and blocks the thread if the deque is full.\n\n        **Returns:** A n-dimensional numpy array.\n        \"\"\"\n        # return the frame\n        return self.frame\n\n    def stop(self):\n        \"\"\"\n        Safely terminates the thread, and release the resources.\n        \"\"\"\n        self.__logging and logger.debug(\"Terminating ScreenGear Processes.\")\n\n        # indicate that the thread should be terminate\n        self.__terminate.set()\n\n        # wait until stream resources are released (producer thread might be still grabbing frame)\n        not (self.__thread is None) and self.__thread.join()\n

"},{"location":"bonus/reference/screengear/#vidgear.gears.screengear.ScreenGear.__init__","title":"__init__(self, monitor=None, backend=None, colorspace=None, logging=False, **options) special","text":"

This constructor method initializes the object state and attributes of the ScreenGear class.

Parameters:

Name Type Description Default monitor int

enables mss backend and sets the index of the monitor screen.

None backend str

select suitable backend for extracting frames.

None colorspace str

selects the colorspace of the input stream.

None logging bool

enables/disables logging.

False options dict

provides the flexibility to easily alter backend library parameters. Such as, manually set the dimensions of capture screen area etc.

{} Source code in vidgear/gears/screengear.py
def __init__(\n    self, monitor=None, backend=None, colorspace=None, logging=False, **options\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the ScreenGear class.\n\n    Parameters:\n        monitor (int): enables `mss` backend and sets the index of the monitor screen.\n        backend (str): select suitable backend for extracting frames.\n        colorspace (str): selects the colorspace of the input stream.\n        logging (bool): enables/disables logging.\n        options (dict): provides the flexibility to easily alter backend library parameters. Such as, manually set the dimensions of capture screen area etc.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # create instances for the user-defined monitor\n    self.__monitor_instance = None\n    self.__backend = None\n\n    # validate monitor instance\n    assert (\n        monitor is None or monitor and isinstance(monitor, (int, tuple))\n    ), \"[ScreenGear:ERROR] :: Invalid `monitor` value detected!\"\n\n    # initialize backend\n    if backend and monitor is None:\n        self.__backend = backend.lower().strip()\n    else:\n        # enforce `dxcam` for Windows machines if undefined (or monitor is defined)\n        self.__backend = (\n            \"dxcam\" if platform.system() == \"Windows\" and dxcam else None\n        )\n\n    # initiate screen dimension handler\n    screen_dims = {}\n    # reformat proper mss dict and assign to screen dimension handler\n    screen_dims = {\n        k.strip(): v\n        for k, v in options.items()\n        if k.strip() in [\"top\", \"left\", \"width\", \"height\"]\n    }\n    # check whether user-defined dimensions are provided\n    if screen_dims and len(screen_dims) == 4:\n        key_order = (\n            (\"top\", \"left\", \"width\", \"height\")\n            if self.__backend != \"dxcam\"\n            else (\"left\", \"top\", \"width\", \"height\")\n        )\n        screen_dims = OrderedDict((k, screen_dims[k]) for k in key_order)\n        self.__logging and logger.debug(\n            \"Setting Capture-Area dimensions: {}\".format(json.dumps(screen_dims))\n        )\n    else:\n        screen_dims.clear()\n\n    # handle backends\n    if self.__backend == \"dxcam\":\n        # get target fps in case of DXcam\n        self.__target_fps = options.pop(\"dxcam_target_fps\", 0)\n        if self.__target_fps and isinstance(self.__target_fps, (int, float)):\n            # set values\n            self.__target_fps = int(self.__target_fps)\n            self.__logging and logger.debug(\n                \"Setting Target FPS: {}\".format(self.__target_fps)\n            )\n        else:\n            # defaults to 0fps\n            self.__target_fps = 0\n        # check if platform is windows\n        assert (\n            platform.system() == \"Windows\"\n        ), \"`dxcam` backend is only available for Windows Machines.\"\n        # verify monitor values if tuple\n        assert (\n            monitor is None\n            or isinstance(monitor, int)\n            or (\n                isinstance(monitor, tuple)\n                and len(monitor) == 2\n                and all(isinstance(x, int) for x in monitor)\n            )\n        ), \"For dxcam` backend, monitor` tuple value must be format `int` or `(int, int)` only.\"\n        # raise error(s) for critical Class imports\n        import_dependency_safe(\"dxcam\" if dxcam is None else \"\")\n        if monitor is None:\n            self.__capture_object = dxcam.create(\n                region=tuple(screen_dims.values()) if screen_dims else None\n            )\n        else:\n            self.__capture_object = (\n                dxcam.create(\n                    device_idx=monitor[0],\n                    output_idx=monitor[1],\n                    region=tuple(screen_dims.values()) if screen_dims else None,\n                )\n                if isinstance(monitor, tuple)\n                else dxcam.create(\n                    device_idx=monitor,\n                    region=tuple(screen_dims.values()) if screen_dims else None,\n                )\n            )\n    else:\n        if monitor is None:\n            # raise error(s) for critical Class imports\n            import_dependency_safe(\"pyscreenshot\" if pysct is None else \"\")\n            # reset backend if not provided\n            self.__backend = \"pil\" if self.__backend is None else self.__backend\n            # check if valid backend\n            assert (\n                self.__backend in pysct.backends()\n            ), \"Unsupported backend {} provided!\".format(backend)\n            # create capture object\n            self.__capture_object = pysct\n        else:\n            # monitor value must be integer\n            assert monitor and isinstance(\n                monitor, int\n            ), \"[ScreenGear:ERROR] :: Invalid `monitor` value must be integer with mss backend.\"\n            # raise error(s) for critical Class imports\n            import_dependency_safe(\n                \"from mss import mss\" if mss is None else \"\", pkg_name=\"mss\"\n            )\n            # create capture object\n            self.__capture_object = mss()\n            self.__backend and logger.warning(\n                \"Backends are disabled for Monitor Indexing(monitor>=0)!\"\n            )\n            self.__monitor_instance = self.__capture_object.monitors[monitor]\n\n    # log backend\n    self.__backend and self.__logging and logger.debug(\n        \"Setting Backend: {}\".format(self.__backend.upper())\n    )\n\n    # assigns special parameter to global variable and clear\n    # separately handle colorspace value to int conversion\n    if colorspace:\n        self.color_space = capPropId(colorspace.strip())\n        self.__logging and not (self.color_space is None) and logger.debug(\n            \"Enabling `{}` colorspace for this video stream!\".format(\n                colorspace.strip()\n            )\n        )\n    else:\n        self.color_space = None\n\n    # initialize mss capture instance\n    self.__mss_capture_instance = None\n    try:\n        if self.__backend == \"dxcam\":\n            # extract global frame from instance\n            self.frame = self.__capture_object.grab()\n        else:\n            if self.__monitor_instance is None:\n                if screen_dims:\n                    self.__mss_capture_instance = tuple(screen_dims.values())\n                # extract global frame from instance\n                self.frame = np.asanyarray(\n                    self.__capture_object.grab(\n                        bbox=self.__mss_capture_instance,\n                        childprocess=False,\n                        backend=self.__backend,\n                    )\n                )\n            else:\n                if screen_dims:\n                    self.__mss_capture_instance = {\n                        \"top\": self.__monitor_instance[\"top\"] + screen_dims[\"top\"],\n                        \"left\": self.__monitor_instance[\"left\"]\n                        + screen_dims[\"left\"],\n                        \"width\": screen_dims[\"width\"],\n                        \"height\": screen_dims[\"height\"],\n                        \"mon\": monitor,\n                    }\n                else:\n                    self.__mss_capture_instance = (\n                        self.__monitor_instance  # otherwise create instance from monitor\n                    )\n                # extract global frame from instance\n                self.frame = np.asanyarray(\n                    self.__capture_object.grab(self.__mss_capture_instance)\n                )\n        # convert to bgr frame if applicable\n        self.frame = (\n            self.frame[:, :, ::-1]\n            if self.__backend == \"dxcam\" or not (pysct is None)\n            else self.frame\n        )\n        # render colorspace if defined\n        if not (self.frame is None) and not (self.color_space is None):\n            self.frame = cv2.cvtColor(self.frame, self.color_space)\n    except Exception as e:\n        if isinstance(e, ScreenShotError):\n            # otherwise catch and log errors\n            self.__logging and logger.exception(\n                self.__capture_object.get_error_details()\n            )\n            raise ValueError(\n                \"[ScreenGear:ERROR] :: ScreenShotError caught, Wrong dimensions passed to python-mss, Kindly Refer Docs!\"\n            )\n        else:\n            raise SystemError(\n                \"[ScreenGear:ERROR] :: Unable to grab any instance on this system, Are you running headless?\"\n            )\n    # thread initialization\n    self.__thread = None\n    # initialize termination flag\n    self.__terminate = Event()\n
"},{"location":"bonus/reference/screengear/#vidgear.gears.screengear.ScreenGear.read","title":"read(self)","text":"

Extracts frames synchronously from monitored deque, while maintaining a fixed-length frame buffer in the memory, and blocks the thread if the deque is full.

Returns: A n-dimensional numpy array.

Source code in vidgear/gears/screengear.py
def read(self):\n    \"\"\"\n    Extracts frames synchronously from monitored deque, while maintaining a fixed-length frame buffer in the memory,\n    and blocks the thread if the deque is full.\n\n    **Returns:** A n-dimensional numpy array.\n    \"\"\"\n    # return the frame\n    return self.frame\n
"},{"location":"bonus/reference/screengear/#vidgear.gears.screengear.ScreenGear.start","title":"start(self)","text":"

Launches the internal Threaded Frames Extractor daemon

Returns: A reference to the ScreenGear class object.

Source code in vidgear/gears/screengear.py
def start(self):\n    \"\"\"\n    Launches the internal *Threaded Frames Extractor* daemon\n\n    **Returns:** A reference to the ScreenGear class object.\n    \"\"\"\n    self.__thread = Thread(target=self.__update, name=\"ScreenGear\", args=())\n    self.__thread.daemon = True\n    self.__thread.start()\n    if self.__backend == \"dxcam\":\n        self.__capture_object.start(\n            target_fps=self.__target_fps,\n            video_mode=True,\n        )\n        self.__logging and self.__target_fps and logger.debug(\n            \"Targeting FPS: {}\".format(self.__target_fps)\n        )\n    return self\n
"},{"location":"bonus/reference/screengear/#vidgear.gears.screengear.ScreenGear.stop","title":"stop(self)","text":"

Safely terminates the thread, and release the resources.

Source code in vidgear/gears/screengear.py
def stop(self):\n    \"\"\"\n    Safely terminates the thread, and release the resources.\n    \"\"\"\n    self.__logging and logger.debug(\"Terminating ScreenGear Processes.\")\n\n    # indicate that the thread should be terminate\n    self.__terminate.set()\n\n    # wait until stream resources are released (producer thread might be still grabbing frame)\n    not (self.__thread is None) and self.__thread.join()\n
"},{"location":"bonus/reference/stabilizer/","title":"API References","text":"

Stabilizer API usage examples can be found here \u27b6

Stabilizer API parameters are explained here \u27b6

This is an auxiliary class that enables Video Stabilization for vidgear with minimalistic latency, and at the expense of little to no additional computational requirements.

The basic idea behind it is to tracks and save the salient feature array for the given number of frames and then uses these anchor point to cancel out all perturbations relative to it for the incoming frames in the queue. This class relies heavily on Threaded Queue mode for error-free & ultra-fast frame handling.

Source code in vidgear/gears/stabilizer.py
class Stabilizer:\n    \"\"\"\n    This is an auxiliary class that enables Video Stabilization for vidgear with minimalistic latency, and at the expense\n    of little to no additional computational requirements.\n\n    The basic idea behind it is to tracks and save the salient feature array for the given number of frames and then uses\n    these anchor point to cancel out all perturbations relative to it for the incoming frames in the queue. This class relies\n    heavily on **Threaded Queue mode** for error-free & ultra-fast frame handling.\n    \"\"\"\n\n    def __init__(\n        self,\n        smoothing_radius=25,\n        border_type=\"black\",\n        border_size=0,\n        crop_n_zoom=False,\n        logging=False,\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the Stabilizer class.\n\n        Parameters:\n            smoothing_radius (int): alter averaging window size.\n            border_type (str): changes the extended border type.\n            border_size (int): enables and set the value for extended border size to reduce the black borders.\n            crop_n_zoom (bool): enables cropping and zooming of frames(to original size) to reduce the black borders.\n            logging (bool): enables/disables logging.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # initialize deques for handling input frames and its indexes\n        self.__frame_queue = deque(maxlen=smoothing_radius)\n        self.__frame_queue_indexes = deque(maxlen=smoothing_radius)\n\n        # define and create Adaptive histogram equalization (AHE) object for optimizations\n        self.__clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))\n\n        # initialize global vars\n        self.__smoothing_radius = smoothing_radius  # averaging window, handles the quality of stabilization at expense of latency and sudden panning\n        self.__smoothed_path = None  # handles the smoothed path with box filter\n        self.__path = None  # handles path i.e cumulative sum of previous_2_current transformations along a axis\n        self.__transforms = []  # handles previous_2_current transformations [dx,dy,da]\n        self.__frame_transforms_smoothed = None  # handles smoothed array of previous_2_current transformations w.r.t to frames\n        self.__previous_gray = None  # handles previous gray frame\n        self.__previous_keypoints = (\n            None  # handles previous detect_GFTTed keypoints w.r.t previous gray frame\n        )\n        self.__frame_height, self.frame_width = (\n            0,\n            0,\n        )  # handles width and height of input frames\n        self.__crop_n_zoom = 0  # handles cropping and zooms frames to reduce the black borders from stabilization being too noticeable.\n\n        # if check if crop_n_zoom defined\n        if crop_n_zoom and border_size:\n            self.__crop_n_zoom = border_size  # crops and zoom frame to original size\n            self.__border_size = 0  # zero out border size\n            self.__frame_size = None  # handles frame size for zooming\n            self.__logging and logger.debug(\n                \"Setting Cropping margin {} pixels\".format(border_size)\n            )\n        else:\n            # Add output borders to frame\n            self.__border_size = border_size\n            self.__logging and border_size and logger.debug(\n                \"Setting Border size {} pixels\".format(border_size)\n            )\n\n        # define valid border modes\n        border_modes = {\n            \"black\": cv2.BORDER_CONSTANT,\n            \"reflect\": cv2.BORDER_REFLECT,\n            \"reflect_101\": cv2.BORDER_REFLECT_101,\n            \"replicate\": cv2.BORDER_REPLICATE,\n            \"wrap\": cv2.BORDER_WRAP,\n        }\n        # choose valid border_mode from border_type\n        if border_type in [\"black\", \"reflect\", \"reflect_101\", \"replicate\", \"wrap\"]:\n            if not crop_n_zoom:\n                # initialize global border mode variable\n                self.__border_mode = border_modes[border_type]\n                self.__logging and border_type != \"black\" and logger.info(\n                    \"Setting Border type: {}\".format(border_type)\n                )\n            else:\n                # log and reset to default\n                self.__logging and border_type != \"black\" and logger.debug(\n                    \"Setting border type is disabled if cropping is enabled!\"\n                )\n                self.__border_mode = border_modes[\"black\"]\n        else:\n            # otherwise log if not\n            self.__logging and logger.debug(\"Invalid input border type!\")\n            self.__border_mode = border_modes[\"black\"]  # reset to default mode\n\n        # define OpenCV version\n        self.__cv2_version = check_CV_version()\n\n        # retrieve best interpolation\n        self.__interpolation = retrieve_best_interpolation(\n            [\"INTER_LINEAR_EXACT\", \"INTER_LINEAR\", \"INTER_AREA\"]\n        )\n\n        # define normalized box filter\n        self.__box_filter = np.ones(smoothing_radius) / smoothing_radius\n\n    def stabilize(self, frame):\n        \"\"\"\n        This method takes an unstabilized video frame, and returns a stabilized one.\n\n        Parameters:\n            frame (numpy.ndarray): inputs unstabilized video frames.\n        \"\"\"\n        # check if frame is None\n        if frame is None:\n            # return if it does\n            return\n\n        # save frame size for zooming\n        if self.__crop_n_zoom and self.__frame_size == None:\n            self.__frame_size = frame.shape[:2]\n\n        # initiate transformations capturing\n        if not self.__frame_queue:\n            # for first frame\n            previous_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)  # convert to gray\n            previous_gray = self.__clahe.apply(previous_gray)  # optimize gray frame\n            self.__previous_keypoints = cv2.goodFeaturesToTrack(\n                previous_gray,\n                maxCorners=200,\n                qualityLevel=0.05,\n                minDistance=30.0,\n                blockSize=3,\n                mask=None,\n                useHarrisDetector=False,\n                k=0.04,\n            )  # track features using GFTT\n            self.__frame_height, self.frame_width = frame.shape[\n                :2\n            ]  # save input frame height and width\n            self.__frame_queue.append(frame)  # save frame to deque\n            self.__frame_queue_indexes.append(0)  # save frame index to deque\n            self.__previous_gray = previous_gray[\n                :\n            ]  # save gray frame clone for further processing\n\n        elif self.__frame_queue_indexes[-1] < self.__smoothing_radius - 1:\n            # for rest of frames\n            self.__frame_queue.append(frame)  # save frame to deque\n            self.__frame_queue_indexes.append(\n                self.__frame_queue_indexes[-1] + 1\n            )  # save frame index\n            self.__generate_transformations()  # generate transformations\n        else:\n            # start applying transformations\n            self.__frame_queue.append(frame)  # save frame to deque\n            self.__frame_queue_indexes.append(\n                self.__frame_queue_indexes[-1] + 1\n            )  # save frame index\n            self.__generate_transformations()  # generate transformations\n            # calculate smooth path once transformation capturing is completed\n            for i in range(3):\n                # apply normalized box filter to the path\n                self.__smoothed_path[:, i] = self.__box_filter_convolve(\n                    (self.__path[:, i]), window_size=self.__smoothing_radius\n                )\n            # calculate deviation of path from smoothed path\n            deviation = self.__smoothed_path - self.__path\n            # save smoothed transformation\n            self.__frame_transforms_smoothed = self.frame_transform + deviation\n            # return transformation applied stabilized frame\n            return self.__apply_transformations()\n\n    def __generate_transformations(self):\n        \"\"\"\n        An internal method that generate previous-to-current transformations [dx,dy,da].\n        \"\"\"\n        frame_gray = cv2.cvtColor(\n            self.__frame_queue[-1], cv2.COLOR_BGR2GRAY\n        )  # retrieve current frame and convert to gray\n        frame_gray = self.__clahe.apply(frame_gray)  # optimize it\n\n        transformation = None\n        try:\n            # calculate optical flow using Lucas-Kanade differential method\n            curr_kps, status, error = cv2.calcOpticalFlowPyrLK(\n                self.__previous_gray, frame_gray, self.__previous_keypoints, None\n            )\n\n            # select only valid key-points\n            valid_curr_kps = curr_kps[status == 1]  # current\n            valid_previous_keypoints = self.__previous_keypoints[\n                status == 1\n            ]  # previous\n\n            # calculate optimal affine transformation between previous_2_current key-points\n            if self.__cv2_version == 3:\n                # backward compatibility with OpenCV3\n                transformation = cv2.estimateRigidTransform(\n                    valid_previous_keypoints, valid_curr_kps, False\n                )\n            else:\n                transformation = cv2.estimateAffinePartial2D(\n                    valid_previous_keypoints, valid_curr_kps\n                )[0]\n        except cv2.error as e:\n            # catch any OpenCV assertion errors and warn user\n            logger.warning(\"Video-Frame is too dark to generate any transformations!\")\n            transformation = None\n\n        # check if transformation is not None\n        if not (transformation is None):\n            # previous_2_current translation in x direction\n            dx = transformation[0, 2]\n            # previous_2_current translation in y direction\n            dy = transformation[1, 2]\n            # previous_2_current rotation in angle\n            da = np.arctan2(transformation[1, 0], transformation[0, 0])\n        else:\n            # otherwise zero it\n            dx = dy = da = 0\n\n        # save this transformation\n        self.__transforms.append([dx, dy, da])\n\n        # calculate path from cumulative transformations sum\n        self.frame_transform = np.array(self.__transforms, dtype=\"float32\")\n        self.__path = np.cumsum(self.frame_transform, axis=0)\n        # create smoothed path from a copy of path\n        self.__smoothed_path = np.copy(self.__path)\n\n        # re-calculate and save GFTT key-points for current gray frame\n        self.__previous_keypoints = cv2.goodFeaturesToTrack(\n            frame_gray,\n            maxCorners=200,\n            qualityLevel=0.05,\n            minDistance=30.0,\n            blockSize=3,\n            mask=None,\n            useHarrisDetector=False,\n            k=0.04,\n        )\n        # save this gray frame for further processing\n        self.__previous_gray = frame_gray[:]\n\n    def __box_filter_convolve(self, path, window_size):\n        \"\"\"\n        An internal method that applies *normalized linear box filter* to path w.r.t averaging window\n\n        Parameters:\n\n        * path (numpy.ndarray): a cumulative sum of transformations\n        * window_size (int): averaging window size\n        \"\"\"\n        # pad path to size of averaging window\n        path_padded = np.pad(path, (window_size, window_size), \"median\")\n        # apply linear box filter to path\n        path_smoothed = np.convolve(path_padded, self.__box_filter, mode=\"same\")\n        # crop the smoothed path to original path\n        path_smoothed = path_smoothed[window_size:-window_size]\n        # assert if cropping is completed\n        assert path.shape == path_smoothed.shape\n        # return smoothed path\n        return path_smoothed\n\n    def __apply_transformations(self):\n        \"\"\"\n        An internal method that applies affine transformation to the given frame\n        from previously calculated transformations\n        \"\"\"\n        # extract frame and its index from deque\n        queue_frame = self.__frame_queue.popleft()\n        queue_frame_index = self.__frame_queue_indexes.popleft()\n\n        # create border around extracted frame w.r.t border_size\n        bordered_frame = cv2.copyMakeBorder(\n            queue_frame,\n            top=self.__border_size,\n            bottom=self.__border_size,\n            left=self.__border_size,\n            right=self.__border_size,\n            borderType=self.__border_mode,\n            value=[0, 0, 0],\n        )\n        alpha_bordered_frame = cv2.cvtColor(\n            bordered_frame, cv2.COLOR_BGR2BGRA\n        )  # create alpha channel\n        # extract alpha channel\n        alpha_bordered_frame[:, :, 3] = 0\n        alpha_bordered_frame[\n            self.__border_size : self.__border_size + self.__frame_height,\n            self.__border_size : self.__border_size + self.frame_width,\n            3,\n        ] = 255\n\n        # extracting Transformations w.r.t frame index\n        dx = self.__frame_transforms_smoothed[queue_frame_index, 0]  # x-axis\n        dy = self.__frame_transforms_smoothed[queue_frame_index, 1]  # y-axis\n        da = self.__frame_transforms_smoothed[queue_frame_index, 2]  # angle\n\n        # building 2x3 transformation matrix from extracted transformations\n        queue_frame_transform = np.zeros((2, 3), np.float32)\n        queue_frame_transform[0, 0] = np.cos(da)\n        queue_frame_transform[0, 1] = -np.sin(da)\n        queue_frame_transform[1, 0] = np.sin(da)\n        queue_frame_transform[1, 1] = np.cos(da)\n        queue_frame_transform[0, 2] = dx\n        queue_frame_transform[1, 2] = dy\n\n        # Applying an affine transformation to the frame\n        frame_wrapped = cv2.warpAffine(\n            alpha_bordered_frame,\n            queue_frame_transform,\n            alpha_bordered_frame.shape[:2][::-1],\n            borderMode=self.__border_mode,\n        )\n\n        # drop alpha channel\n        frame_stabilized = frame_wrapped[:, :, :3]\n\n        # crop and zoom\n        if self.__crop_n_zoom:\n            # crop stabilized frame\n            frame_cropped = frame_stabilized[\n                self.__crop_n_zoom : -self.__crop_n_zoom,\n                self.__crop_n_zoom : -self.__crop_n_zoom,\n            ]\n            # zoom stabilized frame\n            frame_stabilized = cv2.resize(\n                frame_cropped,\n                self.__frame_size[::-1],\n                interpolation=self.__interpolation,\n            )\n\n        # finally return stabilized frame\n        return frame_stabilized\n\n    def clean(self):\n        \"\"\"\n        Cleans Stabilizer resources\n        \"\"\"\n        # check if deque present\n        if self.__frame_queue:\n            # clear frame deque\n            self.__frame_queue.clear()\n            # clear frame indexes deque\n            self.__frame_queue_indexes.clear()\n

"},{"location":"bonus/reference/stabilizer/#vidgear.gears.stabilizer.Stabilizer.__init__","title":"__init__(self, smoothing_radius=25, border_type='black', border_size=0, crop_n_zoom=False, logging=False) special","text":"

This constructor method initializes the object state and attributes of the Stabilizer class.

Parameters:

Name Type Description Default smoothing_radius int

alter averaging window size.

25 border_type str

changes the extended border type.

'black' border_size int

enables and set the value for extended border size to reduce the black borders.

0 crop_n_zoom bool

enables cropping and zooming of frames(to original size) to reduce the black borders.

False logging bool

enables/disables logging.

False Source code in vidgear/gears/stabilizer.py
def __init__(\n    self,\n    smoothing_radius=25,\n    border_type=\"black\",\n    border_size=0,\n    crop_n_zoom=False,\n    logging=False,\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the Stabilizer class.\n\n    Parameters:\n        smoothing_radius (int): alter averaging window size.\n        border_type (str): changes the extended border type.\n        border_size (int): enables and set the value for extended border size to reduce the black borders.\n        crop_n_zoom (bool): enables cropping and zooming of frames(to original size) to reduce the black borders.\n        logging (bool): enables/disables logging.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # initialize deques for handling input frames and its indexes\n    self.__frame_queue = deque(maxlen=smoothing_radius)\n    self.__frame_queue_indexes = deque(maxlen=smoothing_radius)\n\n    # define and create Adaptive histogram equalization (AHE) object for optimizations\n    self.__clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))\n\n    # initialize global vars\n    self.__smoothing_radius = smoothing_radius  # averaging window, handles the quality of stabilization at expense of latency and sudden panning\n    self.__smoothed_path = None  # handles the smoothed path with box filter\n    self.__path = None  # handles path i.e cumulative sum of previous_2_current transformations along a axis\n    self.__transforms = []  # handles previous_2_current transformations [dx,dy,da]\n    self.__frame_transforms_smoothed = None  # handles smoothed array of previous_2_current transformations w.r.t to frames\n    self.__previous_gray = None  # handles previous gray frame\n    self.__previous_keypoints = (\n        None  # handles previous detect_GFTTed keypoints w.r.t previous gray frame\n    )\n    self.__frame_height, self.frame_width = (\n        0,\n        0,\n    )  # handles width and height of input frames\n    self.__crop_n_zoom = 0  # handles cropping and zooms frames to reduce the black borders from stabilization being too noticeable.\n\n    # if check if crop_n_zoom defined\n    if crop_n_zoom and border_size:\n        self.__crop_n_zoom = border_size  # crops and zoom frame to original size\n        self.__border_size = 0  # zero out border size\n        self.__frame_size = None  # handles frame size for zooming\n        self.__logging and logger.debug(\n            \"Setting Cropping margin {} pixels\".format(border_size)\n        )\n    else:\n        # Add output borders to frame\n        self.__border_size = border_size\n        self.__logging and border_size and logger.debug(\n            \"Setting Border size {} pixels\".format(border_size)\n        )\n\n    # define valid border modes\n    border_modes = {\n        \"black\": cv2.BORDER_CONSTANT,\n        \"reflect\": cv2.BORDER_REFLECT,\n        \"reflect_101\": cv2.BORDER_REFLECT_101,\n        \"replicate\": cv2.BORDER_REPLICATE,\n        \"wrap\": cv2.BORDER_WRAP,\n    }\n    # choose valid border_mode from border_type\n    if border_type in [\"black\", \"reflect\", \"reflect_101\", \"replicate\", \"wrap\"]:\n        if not crop_n_zoom:\n            # initialize global border mode variable\n            self.__border_mode = border_modes[border_type]\n            self.__logging and border_type != \"black\" and logger.info(\n                \"Setting Border type: {}\".format(border_type)\n            )\n        else:\n            # log and reset to default\n            self.__logging and border_type != \"black\" and logger.debug(\n                \"Setting border type is disabled if cropping is enabled!\"\n            )\n            self.__border_mode = border_modes[\"black\"]\n    else:\n        # otherwise log if not\n        self.__logging and logger.debug(\"Invalid input border type!\")\n        self.__border_mode = border_modes[\"black\"]  # reset to default mode\n\n    # define OpenCV version\n    self.__cv2_version = check_CV_version()\n\n    # retrieve best interpolation\n    self.__interpolation = retrieve_best_interpolation(\n        [\"INTER_LINEAR_EXACT\", \"INTER_LINEAR\", \"INTER_AREA\"]\n    )\n\n    # define normalized box filter\n    self.__box_filter = np.ones(smoothing_radius) / smoothing_radius\n
"},{"location":"bonus/reference/stabilizer/#vidgear.gears.stabilizer.Stabilizer.clean","title":"clean(self)","text":"

Cleans Stabilizer resources

Source code in vidgear/gears/stabilizer.py
def clean(self):\n    \"\"\"\n    Cleans Stabilizer resources\n    \"\"\"\n    # check if deque present\n    if self.__frame_queue:\n        # clear frame deque\n        self.__frame_queue.clear()\n        # clear frame indexes deque\n        self.__frame_queue_indexes.clear()\n
"},{"location":"bonus/reference/stabilizer/#vidgear.gears.stabilizer.Stabilizer.stabilize","title":"stabilize(self, frame)","text":"

This method takes an unstabilized video frame, and returns a stabilized one.

Parameters:

Name Type Description Default frame numpy.ndarray

inputs unstabilized video frames.

required Source code in vidgear/gears/stabilizer.py
def stabilize(self, frame):\n    \"\"\"\n    This method takes an unstabilized video frame, and returns a stabilized one.\n\n    Parameters:\n        frame (numpy.ndarray): inputs unstabilized video frames.\n    \"\"\"\n    # check if frame is None\n    if frame is None:\n        # return if it does\n        return\n\n    # save frame size for zooming\n    if self.__crop_n_zoom and self.__frame_size == None:\n        self.__frame_size = frame.shape[:2]\n\n    # initiate transformations capturing\n    if not self.__frame_queue:\n        # for first frame\n        previous_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)  # convert to gray\n        previous_gray = self.__clahe.apply(previous_gray)  # optimize gray frame\n        self.__previous_keypoints = cv2.goodFeaturesToTrack(\n            previous_gray,\n            maxCorners=200,\n            qualityLevel=0.05,\n            minDistance=30.0,\n            blockSize=3,\n            mask=None,\n            useHarrisDetector=False,\n            k=0.04,\n        )  # track features using GFTT\n        self.__frame_height, self.frame_width = frame.shape[\n            :2\n        ]  # save input frame height and width\n        self.__frame_queue.append(frame)  # save frame to deque\n        self.__frame_queue_indexes.append(0)  # save frame index to deque\n        self.__previous_gray = previous_gray[\n            :\n        ]  # save gray frame clone for further processing\n\n    elif self.__frame_queue_indexes[-1] < self.__smoothing_radius - 1:\n        # for rest of frames\n        self.__frame_queue.append(frame)  # save frame to deque\n        self.__frame_queue_indexes.append(\n            self.__frame_queue_indexes[-1] + 1\n        )  # save frame index\n        self.__generate_transformations()  # generate transformations\n    else:\n        # start applying transformations\n        self.__frame_queue.append(frame)  # save frame to deque\n        self.__frame_queue_indexes.append(\n            self.__frame_queue_indexes[-1] + 1\n        )  # save frame index\n        self.__generate_transformations()  # generate transformations\n        # calculate smooth path once transformation capturing is completed\n        for i in range(3):\n            # apply normalized box filter to the path\n            self.__smoothed_path[:, i] = self.__box_filter_convolve(\n                (self.__path[:, i]), window_size=self.__smoothing_radius\n            )\n        # calculate deviation of path from smoothed path\n        deviation = self.__smoothed_path - self.__path\n        # save smoothed transformation\n        self.__frame_transforms_smoothed = self.frame_transform + deviation\n        # return transformation applied stabilized frame\n        return self.__apply_transformations()\n
"},{"location":"bonus/reference/streamgear/","title":"StreamGear API References","text":"

StreamGear API usage examples for: Single-Source Mode \u27b6 and Real-time Frames Mode \u27b6

StreamGear API parameters are explained here \u27b6

StreamGear automates transcoding workflow for generating Ultra-Low Latency, High-Quality, Dynamic & Adaptive Streaming Formats (such as MPEG-DASH and HLS) in just few lines of python code. StreamGear provides a standalone, highly extensible, and flexible wrapper around FFmpeg multimedia framework for generating chunked-encoded media segments of the content.

SteamGear easily transcodes source videos/audio files & real-time video-frames and breaks them into a sequence of multiple smaller chunks/segments of suitable length. These segments make it possible to stream videos at different quality levels (different bitrate or spatial resolutions) and can be switched in the middle of a video from one quality level to another - if bandwidth permits - on a per-segment basis. A user can serve these segments on a web server that makes it easier to download them through HTTP standard-compliant GET requests.

SteamGear also creates a Manifest/Playlist file (such as MPD in-case of DASH and M3U8 in-case of HLS) besides segments that describe these segment information (timing, URL, media characteristics like video resolution and bit rates) and is provided to the client before the streaming session.

SteamGear currently supports MPEG-DASH (Dynamic Adaptive Streaming over HTTP, ISO/IEC 23009-1) and Apple HLS (HTTP live streaming).

Source code in vidgear/gears/streamgear.py
class StreamGear:\n    \"\"\"\n    StreamGear automates transcoding workflow for generating Ultra-Low Latency, High-Quality, Dynamic & Adaptive Streaming Formats (such as MPEG-DASH and HLS) in just few lines of python code.\n    StreamGear provides a standalone, highly extensible, and flexible wrapper around FFmpeg multimedia framework for generating chunked-encoded media segments of the content.\n\n    SteamGear easily transcodes source videos/audio files & real-time video-frames and breaks them into a sequence of multiple smaller chunks/segments of suitable length. These segments make it\n    possible to stream videos at different quality levels _(different bitrate or spatial resolutions)_ and can be switched in the middle of a video from one quality level to another - if bandwidth\n    permits - on a per-segment basis. A user can serve these segments on a web server that makes it easier to download them through HTTP standard-compliant GET requests.\n\n    SteamGear also creates a Manifest/Playlist file (such as MPD in-case of DASH and M3U8 in-case of HLS) besides segments that describe these segment information\n    (timing, URL, media characteristics like video resolution and bit rates) and is provided to the client before the streaming session.\n\n    SteamGear currently supports MPEG-DASH (Dynamic Adaptive Streaming over HTTP, ISO/IEC 23009-1) and Apple HLS (HTTP live streaming).\n    \"\"\"\n\n    def __init__(\n        self, output=\"\", format=\"dash\", custom_ffmpeg=\"\", logging=False, **stream_params\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the StreamGear class.\n\n        Parameters:\n            output (str): sets the valid filename/path for generating the StreamGear assets.\n            format (str): select the adaptive HTTP streaming format(DASH and HLS).\n            custom_ffmpeg (str): assigns the location of custom path/directory for custom FFmpeg executables.\n            logging (bool): enables/disables logging.\n            stream_params (dict): provides the flexibility to control supported internal parameters and FFmpeg properties.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # checks if machine in-use is running windows os or not\n        self.__os_windows = True if os.name == \"nt\" else False\n\n        # initialize various class variables\n        # handles user-defined parameters\n        self.__params = {}\n        # handle input video/frame resolution and channels\n        self.__inputheight = None\n        self.__inputwidth = None\n        self.__inputchannels = None\n        self.__sourceframerate = None\n        # handle process to be frames written\n        self.__process = None\n        # handle valid FFmpeg assets location\n        self.__ffmpeg = \"\"\n        # handle one time process for valid process initialization\n        self.__initiate_stream = True\n\n        # cleans and reformat user-defined parameters\n        self.__params = {\n            str(k).strip(): (v.strip() if isinstance(v, str) else v)\n            for k, v in stream_params.items()\n        }\n\n        # handle where to save the downloaded FFmpeg Static assets on Windows(if specified)\n        __ffmpeg_download_path = self.__params.pop(\"-ffmpeg_download_path\", \"\")\n        if not isinstance(__ffmpeg_download_path, (str)):\n            # reset improper values\n            __ffmpeg_download_path = \"\"\n\n        # validate the FFmpeg assets and return location (also downloads static assets on windows)\n        self.__ffmpeg = get_valid_ffmpeg_path(\n            str(custom_ffmpeg),\n            self.__os_windows,\n            ffmpeg_download_path=__ffmpeg_download_path,\n            logging=self.__logging,\n        )\n\n        # check if valid FFmpeg path returned\n        if self.__ffmpeg:\n            self.__logging and logger.debug(\n                \"Found valid FFmpeg executables: `{}`.\".format(self.__ffmpeg)\n            )\n        else:\n            # else raise error\n            raise RuntimeError(\n                \"[StreamGear:ERROR] :: Failed to find FFmpeg assets on this system. Kindly compile/install FFmpeg or provide a valid custom FFmpeg binary path!\"\n            )\n\n        # handle streaming format\n        supported_formats = [\"dash\", \"hls\"]  # TODO will be extended in future\n        if format and isinstance(format, str):\n            _format = format.strip().lower()\n            if _format in supported_formats:\n                self.__format = _format\n                logger.info(\n                    \"StreamGear will generate asset files for {} streaming format.\".format(\n                        self.__format.upper()\n                    )\n                )\n            elif difflib.get_close_matches(_format, supported_formats):\n                raise ValueError(\n                    \"[StreamGear:ERROR] :: Incorrect `format` parameter value! Did you mean `{}`?\".format(\n                        difflib.get_close_matches(_format, supported_formats)[0]\n                    )\n                )\n            else:\n                raise ValueError(\n                    \"[StreamGear:ERROR] :: The `format` parameter value `{}` not valid/supported!\".format(\n                        format\n                    )\n                )\n        else:\n            raise ValueError(\n                \"[StreamGear:ERROR] :: The `format` parameter value is Missing or Invalid!\"\n            )\n\n        # handle Audio-Input\n        audio = self.__params.pop(\"-audio\", False)\n        if audio and isinstance(audio, str):\n            if os.path.isfile(audio):\n                self.__audio = os.path.abspath(audio)\n            elif is_valid_url(self.__ffmpeg, url=audio, logging=self.__logging):\n                self.__audio = audio\n            else:\n                self.__audio = False\n        elif audio and isinstance(audio, list):\n            self.__audio = audio\n        else:\n            self.__audio = False\n        # log external audio source\n        self.__audio and self.__logging and logger.debug(\n            \"External audio source `{}` detected.\".format(self.__audio)\n        )\n\n        # handle Video-Source input\n        source = self.__params.pop(\"-video_source\", False)\n        # Check if input is valid string\n        if source and isinstance(source, str) and len(source) > 1:\n            # Differentiate input\n            if os.path.isfile(source):\n                self.__video_source = os.path.abspath(source)\n            elif is_valid_url(self.__ffmpeg, url=source, logging=self.__logging):\n                self.__video_source = source\n            else:\n                # discard the value otherwise\n                self.__video_source = False\n\n            # Validate input\n            if self.__video_source:\n                validation_results = validate_video(\n                    self.__ffmpeg, video_path=self.__video_source\n                )\n                assert not (\n                    validation_results is None\n                ), \"[StreamGear:ERROR] :: Given `{}` video_source is Invalid, Check Again!\".format(\n                    self.__video_source\n                )\n                self.__aspect_source = validation_results[\"resolution\"]\n                self.__fps_source = validation_results[\"framerate\"]\n                # log it\n                self.__logging and logger.debug(\n                    \"Given video_source is valid and has {}x{} resolution, and a framerate of {} fps.\".format(\n                        self.__aspect_source[0],\n                        self.__aspect_source[1],\n                        self.__fps_source,\n                    )\n                )\n            else:\n                # log warning\n                logger.warning(\"Discarded invalid `-video_source` value provided.\")\n        else:\n            if source:\n                # log warning if source provided\n                logger.warning(\"Invalid `-video_source` value provided.\")\n            else:\n                # log normally\n                logger.info(\"No `-video_source` value provided.\")\n            # discard the value otherwise\n            self.__video_source = False\n\n        # handle user-defined framerate\n        self.__inputframerate = self.__params.pop(\"-input_framerate\", 0.0)\n        if isinstance(self.__inputframerate, (float, int)):\n            # must be float\n            self.__inputframerate = float(self.__inputframerate)\n        else:\n            # reset improper values\n            self.__inputframerate = 0.0\n\n        # handle old assets\n        clear_assets = self.__params.pop(\"-clear_prev_assets\", False)\n        if isinstance(clear_assets, bool):\n            self.__clear_assets = clear_assets\n            # log if clearing assets is enabled\n            clear_assets and logger.info(\n                \"The `-clear_prev_assets` parameter is enabled successfully. All previous StreamGear API assets for `{}` format will be removed for this run.\".format(\n                    self.__format.upper()\n                )\n            )\n        else:\n            # reset improper values\n            self.__clear_assets = False\n\n        # handle whether to livestream?\n        livestreaming = self.__params.pop(\"-livestream\", False)\n        if isinstance(livestreaming, bool) and livestreaming:\n            # NOTE:  `livestream` is only available with real-time mode.\n            self.__livestreaming = livestreaming if not (self.__video_source) else False\n            if self.__video_source:\n                logger.error(\n                    \"Live-Streaming is only available with Real-time Mode. Refer docs for more information.\"\n                )\n            else:\n                # log if live streaming is enabled\n                livestreaming and logger.info(\n                    \"Live-Streaming is successfully enabled for this run.\"\n                )\n        else:\n            # reset improper values\n            self.__livestreaming = False\n\n        # handle the special-case of forced-termination\n        enable_force_termination = self.__params.pop(\"-enable_force_termination\", False)\n        # check if value is valid\n        if isinstance(enable_force_termination, bool):\n            self.__forced_termination = enable_force_termination\n            # log if forced termination is enabled\n            self.__forced_termination and logger.warning(\n                \"Forced termination is enabled for this run. This may result in corrupted output in certain scenarios!\"\n            )\n        else:\n            # handle improper values\n            self.__forced_termination = False\n\n        # handle streaming format\n        supported_formats = [\"dash\", \"hls\"]  # TODO will be extended in future\n        if format and isinstance(format, str):\n            _format = format.strip().lower()\n            if _format in supported_formats:\n                self.__format = _format\n                logger.info(\n                    \"StreamGear will generate asset files for {} streaming format.\".format(\n                        self.__format.upper()\n                    )\n                )\n            elif difflib.get_close_matches(_format, supported_formats):\n                raise ValueError(\n                    \"[StreamGear:ERROR] :: Incorrect `format` parameter value! Did you mean `{}`?\".format(\n                        difflib.get_close_matches(_format, supported_formats)[0]\n                    )\n                )\n            else:\n                raise ValueError(\n                    \"[StreamGear:ERROR] :: The `format` parameter value `{}` not valid/supported!\".format(\n                        format\n                    )\n                )\n        else:\n            raise ValueError(\n                \"[StreamGear:ERROR] :: The `format` parameter value is Missing or Invalid!\"\n            )\n\n        # handles output asset filenames\n        if output:\n            # validate this class has the access rights to specified directory or not\n            abs_path = os.path.abspath(output)\n            # check if given output is a valid system path\n            if check_WriteAccess(\n                os.path.dirname(abs_path),\n                is_windows=self.__os_windows,\n                logging=self.__logging,\n            ):\n                # get all assets extensions\n                valid_extension = \"mpd\" if self.__format == \"dash\" else \"m3u8\"\n                assets_exts = [\n                    (\"chunk-stream\", \".m4s\"),  # filename prefix, extension\n                    (\"chunk-stream\", \".ts\"),  # filename prefix, extension\n                    \".{}\".format(valid_extension),\n                ]\n                # add source file extension too\n                self.__video_source and assets_exts.append(\n                    (\n                        \"chunk-stream\",\n                        os.path.splitext(self.__video_source)[1],\n                    )  # filename prefix, extension\n                )\n                # handle output\n                # check if path is a directory\n                if os.path.isdir(abs_path):\n                    # clear previous assets if specified\n                    self.__clear_assets and delete_ext_safe(\n                        abs_path, assets_exts, logging=self.__logging\n                    )\n                    # auto-assign valid name and adds it to path\n                    abs_path = os.path.join(\n                        abs_path,\n                        \"{}-{}.{}\".format(\n                            self.__format,\n                            time.strftime(\"%Y%m%d-%H%M%S\"),\n                            valid_extension,\n                        ),\n                    )\n                # or check if path is a file\n                elif os.path.isfile(abs_path) and self.__clear_assets:\n                    # clear previous assets if specified\n                    delete_ext_safe(\n                        os.path.dirname(abs_path),\n                        assets_exts,\n                        logging=self.__logging,\n                    )\n                # check if path has valid file extension\n                assert abs_path.endswith(\n                    valid_extension\n                ), \"Given `{}` path has invalid file-extension w.r.t selected format: `{}`!\".format(\n                    output, self.__format.upper()\n                )\n                self.__logging and logger.debug(\n                    \"Output Path:`{}` is successfully configured for generating streaming assets.\".format(\n                        abs_path\n                    )\n                )\n                # workaround patch for Windows only,\n                # others platforms will not be affected\n                self.__out_file = abs_path.replace(\"\\\\\", \"/\")\n            # check if given output is a valid URL\n            elif is_valid_url(self.__ffmpeg, url=output, logging=self.__logging):\n                self.__logging and logger.debug(\n                    \"URL:`{}` is valid and successfully configured for generating streaming assets.\".format(\n                        output\n                    )\n                )\n                self.__out_file = output\n            # raise ValueError otherwise\n            else:\n                raise ValueError(\n                    \"[StreamGear:ERROR] :: The output parameter value:`{}` is not valid/supported!\".format(\n                        output\n                    )\n                )\n        else:\n            # raise ValueError otherwise\n            raise ValueError(\n                \"[StreamGear:ERROR] :: Kindly provide a valid `output` parameter value. Refer Docs for more information.\"\n            )\n\n        # log Mode of operation\n        self.__video_source and logger.info(\n            \"StreamGear has been successfully configured for {} Mode.\".format(\n                \"Single-Source\" if self.__video_source else \"Real-time Frames\"\n            )\n        )\n\n    @deprecated(\n        parameter=\"rgb_mode\",\n        message=\"The `rgb_mode` parameter is deprecated and will be removed in a future version. Only BGR format frames will be supported going forward.\",\n    )\n    def stream(self, frame, rgb_mode=False):\n        \"\"\"\n        Pipes `ndarray` frames to FFmpeg Pipeline for transcoding them into chunked-encoded media segments of\n        streaming formats such as MPEG-DASH and HLS.\n\n        !!! warning \"[DEPRECATION NOTICE]: The `rgb_mode` parameter is deprecated and will be removed in a future version.\"\n\n        Parameters:\n            frame (ndarray): a valid numpy frame\n            rgb_mode (boolean): enable this flag to activate RGB mode _(i.e. specifies that incoming frames are of RGB format instead of default BGR)_.\n        \"\"\"\n        # check if function is called in correct context\n        if self.__video_source:\n            raise RuntimeError(\n                \"[StreamGear:ERROR] :: The `stream()` method cannot be used when streaming from a `-video_source` input file. Kindly refer vidgear docs!\"\n            )\n        # None-Type frames will be skipped\n        if frame is None:\n            return\n        # extract height, width and number of channels of frame\n        height, width = frame.shape[:2]\n        channels = frame.shape[-1] if frame.ndim == 3 else 1\n        # assign values to class variables on first run\n        if self.__initiate_stream:\n            self.__inputheight = height\n            self.__inputwidth = width\n            self.__inputchannels = channels\n            self.__sourceframerate = (\n                25.0 if not (self.__inputframerate) else self.__inputframerate\n            )\n            self.__logging and logger.debug(\n                \"InputFrame => Height:{} Width:{} Channels:{}\".format(\n                    self.__inputheight, self.__inputwidth, self.__inputchannels\n                )\n            )\n        # validate size of frame\n        if height != self.__inputheight or width != self.__inputwidth:\n            raise ValueError(\"[StreamGear:ERROR] :: All frames must have same size!\")\n        # validate number of channels\n        if channels != self.__inputchannels:\n            raise ValueError(\n                \"[StreamGear:ERROR] :: All frames must have same number of channels!\"\n            )\n        # initiate FFmpeg process on first run\n        if self.__initiate_stream:\n            # launch pre-processing\n            self.__PreProcess(channels=channels, rgb=rgb_mode)\n            # Check status of the process\n            assert self.__process is not None\n\n        # write the frame to pipeline\n        try:\n            self.__process.stdin.write(frame.tobytes())\n        except (OSError, IOError):\n            # log something is wrong!\n            logger.error(\n                \"BrokenPipeError caught, Wrong values passed to FFmpeg Pipe, Kindly Refer Docs!\"\n            )\n            raise ValueError  # for testing purpose only\n\n    def transcode_source(self):\n        \"\"\"\n        Transcodes an entire video file _(with or without audio)_ into chunked-encoded media segments of\n        streaming formats such as MPEG-DASH and HLS.\n        \"\"\"\n        # check if function is called in correct context\n        if not (self.__video_source):\n            raise RuntimeError(\n                \"[StreamGear:ERROR] :: The `transcode_source()` method cannot be used without a valid `-video_source` input. Kindly refer vidgear docs!\"\n            )\n        # assign height, width and framerate\n        self.__inputheight = int(self.__aspect_source[1])\n        self.__inputwidth = int(self.__aspect_source[0])\n        self.__sourceframerate = float(self.__fps_source)\n        # launch pre-processing\n        self.__PreProcess()\n\n    def __PreProcess(self, channels=0, rgb=False):\n        \"\"\"\n        Internal method that pre-processes default FFmpeg parameters before starting pipelining.\n\n        Parameters:\n            channels (int): Number of channels\n            rgb (boolean): activates RGB mode _(if enabled)_.\n        \"\"\"\n        # turn off initiate flag\n        self.__initiate_stream = False\n        # initialize I/O parameters\n        input_parameters = OrderedDict()\n        output_parameters = OrderedDict()\n        # pre-assign default codec parameters (if not assigned by user).\n        default_codec = \"libx264rgb\" if rgb else \"libx264\"\n        output_vcodec = self.__params.pop(\"-vcodec\", default_codec)\n        # enforce default encoder if stream copy specified\n        # in Real-time Frames Mode\n        output_parameters[\"-vcodec\"] = (\n            default_codec\n            if output_vcodec == \"copy\"\n            and (not (self.__video_source) or \"-streams\" in self.__params)\n            else output_vcodec\n        )\n        # enforce compatibility with stream copy\n        if output_parameters[\"-vcodec\"] != \"copy\":\n            # NOTE: these parameters only supported when stream copy not defined\n            output_parameters[\"-vf\"] = self.__params.pop(\"-vf\", \"format=yuv420p\")\n            # Non-essential `-aspect` parameter is removed from the default pipeline.\n        else:\n            # log warnings if stream copy specified in Real-time Frames Mode\n            not (self.__video_source) and logger.error(\n                \"Stream copy is not compatible with Real-time Frames Mode as it require re-encoding of incoming frames. Discarding the `-vcodec copy` parameter!\"\n            )\n            (\"-streams\" in self.__params) and logger.error(\n                \"Stream copying is incompatible with Custom Streams as it require re-encoding for each additional stream. Discarding the `-vcodec copy` parameter!\"\n            )\n            # log warnings for these parameters\n            self.__params.pop(\"-vf\", False) and logger.warning(\n                \"Filtering and stream copy cannot be used together. Discarding specified `-vf` parameter!\"\n            )\n            self.__params.pop(\"-aspect\", False) and logger.warning(\n                \"Overriding aspect ratio with stream copy may produce invalid files. Discarding specified `-aspect` parameter!\"\n            )\n\n        # enable optimizations w.r.t selected codec\n        ### OPTIMIZATION-1 ###\n        if output_parameters[\"-vcodec\"] in [\n            \"libx264\",\n            \"libx264rgb\",\n            \"libx265\",\n            \"libvpx-vp9\",\n        ]:\n            output_parameters[\"-crf\"] = self.__params.pop(\"-crf\", \"20\")\n        ### OPTIMIZATION-2 ###\n        if output_parameters[\"-vcodec\"] == \"libx264\":\n            if not (self.__video_source):\n                output_parameters[\"-profile:v\"] = self.__params.pop(\n                    \"-profile:v\", \"high\"\n                )\n        ### OPTIMIZATION-3 ###\n        if output_parameters[\"-vcodec\"] in [\"libx264\", \"libx264rgb\"]:\n            output_parameters[\"-tune\"] = self.__params.pop(\"-tune\", \"zerolatency\")\n            output_parameters[\"-preset\"] = self.__params.pop(\"-preset\", \"veryfast\")\n        ### OPTIMIZATION-4 ###\n        if output_parameters[\"-vcodec\"] == \"libx265\":\n            output_parameters[\"-x265-params\"] = self.__params.pop(\n                \"-x265-params\", \"lossless=1\"\n            )\n\n        # enable audio (if present)\n        if self.__audio:\n            # validate audio source\n            bitrate = validate_audio(self.__ffmpeg, source=self.__audio)\n            if bitrate:\n                logger.info(\n                    \"Detected External Audio Source is valid, and will be used for generating streams.\"\n                )\n                # assign audio source\n                output_parameters[\n                    \"{}\".format(\n                        \"-core_asource\" if isinstance(self.__audio, list) else \"-i\"\n                    )\n                ] = self.__audio\n                # assign audio codec\n                output_parameters[\"-acodec\"] = self.__params.pop(\"-acodec\", \"aac\")\n                output_parameters[\"a_bitrate\"] = bitrate  # temporary handler\n                output_parameters[\"-core_audio\"] = (\n                    [\"-map\", \"1:a:0\"] if self.__format == \"dash\" else []\n                )\n            else:\n                # discard invalid audio\n                logger.warning(\n                    \"Audio source `{}` is not valid, Skipped!\".format(self.__audio)\n                )\n                self.__audio = False\n        # validate input video's audio source if available\n        elif self.__video_source:\n            bitrate = validate_audio(self.__ffmpeg, source=self.__video_source)\n            if bitrate:\n                logger.info(\"Input video's audio source will be used for this run.\")\n                # assign audio codec\n                output_parameters[\"-acodec\"] = self.__params.pop(\n                    \"-acodec\",\n                    \"aac\" if (\"-streams\" in self.__params) else \"copy\",\n                )\n                if output_parameters[\"-acodec\"] != \"copy\":\n                    output_parameters[\"a_bitrate\"] = bitrate  # temporary handler\n            else:\n                logger.info(\n                    \"No valid audio source available in the input video. Disabling audio while generating streams.\"\n                )\n        else:\n            logger.info(\n                \"No valid audio source provided. Disabling audio while generating streams.\"\n            )\n        # enable audio optimizations based on audio codec\n        if \"-acodec\" in output_parameters and output_parameters[\"-acodec\"] == \"aac\":\n            output_parameters[\"-movflags\"] = \"+faststart\"\n\n        # set input framerate\n        if self.__sourceframerate > 0.0 and not (self.__video_source):\n            # set input framerate\n            self.__logging and logger.debug(\n                \"Setting Input framerate: {}\".format(self.__sourceframerate)\n            )\n            input_parameters[\"-framerate\"] = str(self.__sourceframerate)\n\n        # handle input resolution and pixel format\n        if not (self.__video_source):\n            dimensions = \"{}x{}\".format(self.__inputwidth, self.__inputheight)\n            input_parameters[\"-video_size\"] = str(dimensions)\n            # handles pix_fmt based on channels(HACK)\n            if channels == 1:\n                input_parameters[\"-pix_fmt\"] = \"gray\"\n            elif channels == 2:\n                input_parameters[\"-pix_fmt\"] = \"ya8\"\n            elif channels == 3:\n                input_parameters[\"-pix_fmt\"] = \"rgb24\" if rgb else \"bgr24\"\n            elif channels == 4:\n                input_parameters[\"-pix_fmt\"] = \"rgba\" if rgb else \"bgra\"\n            else:\n                raise ValueError(\n                    \"[StreamGear:ERROR] :: Frames with channels outside range 1-to-4 are not supported!\"\n                )\n        # process assigned format parameters\n        process_params = self.__handle_streams(\n            input_params=input_parameters, output_params=output_parameters\n        )\n        # check if processing completed successfully\n        assert not (\n            process_params is None\n        ), \"[StreamGear:ERROR] :: `{}` stream cannot be initiated properly!\".format(\n            self.__format.upper()\n        )\n        # Finally start FFmpeg pipeline and process everything\n        self.__Build_n_Execute(process_params[0], process_params[1])\n\n    def __handle_streams(self, input_params, output_params):\n        \"\"\"\n        An internal function that parses various streams and its parameters.\n\n        Parameters:\n            input_params (dict): Input FFmpeg parameters\n            output_params (dict): Output FFmpeg parameters\n        \"\"\"\n        # handle bit-per-pixels\n        bpp = self.__params.pop(\"-bpp\", 0.1000)\n        if isinstance(bpp, float) and bpp >= 0.001:\n            bpp = float(bpp)\n        else:\n            # reset to default if invalid\n            bpp = 0.1000\n        # log it\n        bpp and self.__logging and logger.debug(\n            \"Setting bit-per-pixels: {} for this stream.\".format(bpp)\n        )\n\n        # handle gop\n        gop = self.__params.pop(\"-gop\", 2 * int(self.__sourceframerate))\n        if isinstance(gop, (int, float)) and gop >= 0:\n            gop = int(gop)\n        else:\n            # reset to some recommended value\n            gop = 2 * int(self.__sourceframerate)\n        # log it\n        gop and self.__logging and logger.debug(\n            \"Setting GOP: {} for this stream.\".format(gop)\n        )\n\n        # define default stream and its mapping\n        if self.__format == \"hls\":\n            output_params[\"-corev0\"] = [\"-map\", \"0:v\"]\n            if \"-acodec\" in output_params:\n                output_params[\"-corea0\"] = [\n                    \"-map\",\n                    \"{}:a\".format(1 if \"-core_audio\" in output_params else 0),\n                ]\n        else:\n            output_params[\"-map\"] = 0\n\n        # assign default output resolution\n        if \"-s:v:0\" in self.__params:\n            # prevent duplicates\n            del self.__params[\"-s:v:0\"]\n        if output_params[\"-vcodec\"] != \"copy\":\n            output_params[\"-s:v:0\"] = \"{}x{}\".format(\n                self.__inputwidth, self.__inputheight\n            )\n        # assign default output video-bitrate\n        if \"-b:v:0\" in self.__params:\n            # prevent duplicates\n            del self.__params[\"-b:v:0\"]\n        if output_params[\"-vcodec\"] != \"copy\":\n            output_params[\"-b:v:0\"] = (\n                str(\n                    get_video_bitrate(\n                        int(self.__inputwidth),\n                        int(self.__inputheight),\n                        self.__sourceframerate,\n                        bpp,\n                    )\n                )\n                + \"k\"\n            )\n\n        # assign default output audio-bitrate\n        if \"-b:a:0\" in self.__params:\n            # prevent duplicates\n            del self.__params[\"-b:a:0\"]\n        # extract and assign audio-bitrate from temporary handler\n        a_bitrate = output_params.pop(\"a_bitrate\", False)\n        if \"-acodec\" in output_params and a_bitrate:\n            output_params[\"-b:a:0\"] = a_bitrate\n\n        # handle user-defined streams\n        streams = self.__params.pop(\"-streams\", {})\n        output_params = self.__evaluate_streams(streams, output_params, bpp)\n\n        # define additional streams optimization parameters\n        if output_params[\"-vcodec\"] in [\"libx264\", \"libx264rgb\"]:\n            if not \"-bf\" in self.__params:\n                output_params[\"-bf\"] = 1\n            if not \"-sc_threshold\" in self.__params:\n                output_params[\"-sc_threshold\"] = 0\n            if not \"-keyint_min\" in self.__params:\n                output_params[\"-keyint_min\"] = gop\n        if (\n            output_params[\"-vcodec\"] in [\"libx264\", \"libx264rgb\", \"libvpx-vp9\"]\n            and not \"-g\" in self.__params\n        ):\n            output_params[\"-g\"] = gop\n        if output_params[\"-vcodec\"] == \"libx265\":\n            output_params[\"-core_x265\"] = [\n                \"-x265-params\",\n                \"keyint={}:min-keyint={}\".format(gop, gop),\n            ]\n\n        # process given dash/hls stream and return it\n        if self.__format == \"dash\":\n            processed_params = self.__generate_dash_stream(\n                input_params=input_params,\n                output_params=output_params,\n            )\n        else:\n            processed_params = self.__generate_hls_stream(\n                input_params=input_params,\n                output_params=output_params,\n            )\n        return processed_params\n\n    def __evaluate_streams(self, streams, output_params, bpp):\n        \"\"\"\n        Internal function that Extracts, Evaluates & Validates user-defined streams\n\n        Parameters:\n            streams (dict): Individual streams formatted as list of dict.\n            output_params (dict): Output FFmpeg parameters\n        \"\"\"\n        # temporary streams count variable\n        output_params[\"stream_count\"] = 1  # default is 1\n\n        # check if streams are empty\n        if not streams:\n            logger.info(\"No additional `-streams` are provided.\")\n            return output_params\n\n        # check if streams are valid\n        if isinstance(streams, list) and all(isinstance(x, dict) for x in streams):\n            # keep track of streams\n            stream_count = 1\n            # calculate source aspect-ratio\n            source_aspect_ratio = self.__inputwidth / self.__inputheight\n            # log the process\n            self.__logging and logger.debug(\n                \"Processing {} streams.\".format(len(streams))\n            )\n            # iterate over given streams\n            for idx, stream in enumerate(streams):\n                # log stream processing\n                self.__logging and logger.debug(\"Processing Stream: #{}\".format(idx))\n                # make copy\n                stream_copy = stream.copy()\n                # handle intermediate stream data as dictionary\n                intermediate_dict = {}\n                # define and map stream to intermediate dict\n                if self.__format == \"hls\":\n                    intermediate_dict[\"-corev{}\".format(stream_count)] = [\"-map\", \"0:v\"]\n                    if \"-acodec\" in output_params:\n                        intermediate_dict[\"-corea{}\".format(stream_count)] = [\n                            \"-map\",\n                            \"{}:a\".format(1 if \"-core_audio\" in output_params else 0),\n                        ]\n                else:\n                    intermediate_dict[\"-core{}\".format(stream_count)] = [\"-map\", \"0\"]\n\n                # extract resolution & individual dimension of stream\n                resolution = stream.pop(\"-resolution\", \"\")\n                dimensions = (\n                    resolution.lower().split(\"x\")\n                    if (resolution and isinstance(resolution, str))\n                    else []\n                )\n                # validate resolution\n                if (\n                    len(dimensions) == 2\n                    and dimensions[0].isnumeric()\n                    and dimensions[1].isnumeric()\n                ):\n                    # verify resolution is w.r.t source aspect-ratio\n                    expected_width = math.floor(\n                        int(dimensions[1]) * source_aspect_ratio\n                    )\n                    if int(dimensions[0]) != expected_width:\n                        logger.warning(\n                            \"The provided stream resolution '{}' does not align with the source aspect ratio. Output stream may appear distorted!\".format(\n                                resolution\n                            )\n                        )\n                    # assign stream resolution to intermediate dict\n                    intermediate_dict[\"-s:v:{}\".format(stream_count)] = resolution\n                else:\n                    # otherwise log error and skip stream\n                    logger.error(\n                        \"Missing `-resolution` value. Invalid stream `{}` Skipped!\".format(\n                            stream_copy\n                        )\n                    )\n                    continue\n\n                # verify given stream video-bitrate\n                video_bitrate = stream.pop(\"-video_bitrate\", \"\")\n                if (\n                    video_bitrate\n                    and isinstance(video_bitrate, str)\n                    and video_bitrate.endswith((\"k\", \"M\"))\n                ):\n                    # assign it\n                    intermediate_dict[\"-b:v:{}\".format(stream_count)] = video_bitrate\n                else:\n                    # otherwise calculate video-bitrate\n                    fps = stream.pop(\"-framerate\", 0.0)\n                    if dimensions and isinstance(fps, (float, int)) and fps > 0:\n                        intermediate_dict[\"-b:v:{}\".format(stream_count)] = (\n                            \"{}k\".format(\n                                get_video_bitrate(\n                                    int(dimensions[0]), int(dimensions[1]), fps, bpp\n                                )\n                            )\n                        )\n                    else:\n                        # If everything fails, log and skip the stream!\n                        logger.error(\n                            \"Unable to determine Video-Bitrate for the stream `{}`. Skipped!\".format(\n                                stream_copy\n                            )\n                        )\n                        continue\n                # verify given stream audio-bitrate\n                audio_bitrate = stream.pop(\"-audio_bitrate\", \"\")\n                if \"-acodec\" in output_params:\n                    if audio_bitrate and audio_bitrate.endswith((\"k\", \"M\")):\n                        intermediate_dict[\"-b:a:{}\".format(stream_count)] = (\n                            audio_bitrate\n                        )\n                    else:\n                        # otherwise calculate audio-bitrate\n                        if dimensions:\n                            aspect_width = int(dimensions[0])\n                            intermediate_dict[\"-b:a:{}\".format(stream_count)] = (\n                                \"{}k\".format(128 if (aspect_width > 800) else 96)\n                            )\n                # update output parameters\n                output_params.update(intermediate_dict)\n                # clear intermediate dict\n                intermediate_dict.clear()\n                # clear stream copy\n                stream_copy.clear()\n                # increment to next stream\n                stream_count += 1\n                # log stream processing\n                self.__logging and logger.debug(\n                    \"Processed #{} stream successfully.\".format(idx)\n                )\n            # store stream count\n            output_params[\"stream_count\"] = stream_count\n            # log streams processing\n            self.__logging and logger.debug(\"All streams processed successfully!\")\n        else:\n            # skip and log\n            logger.warning(\"Invalid type `-streams` skipped!\")\n\n        return output_params\n\n    def __generate_hls_stream(self, input_params, output_params):\n        \"\"\"\n        An internal function that parses user-defined parameters and generates\n        suitable FFmpeg Terminal Command for transcoding input into HLS Stream.\n\n        Parameters:\n            input_params (dict): Input FFmpeg parameters\n            output_params (dict): Output FFmpeg parameters\n        \"\"\"\n        # validate `hls_segment_type`\n        default_hls_segment_type = self.__params.pop(\"-hls_segment_type\", \"mpegts\")\n        if isinstance(\n            default_hls_segment_type, str\n        ) and default_hls_segment_type.strip() in [\"fmp4\", \"mpegts\"]:\n            output_params[\"-hls_segment_type\"] = default_hls_segment_type.strip()\n        else:\n            # otherwise reset to default\n            logger.warning(\"Invalid `-hls_segment_type` value skipped!\")\n            output_params[\"-hls_segment_type\"] = \"mpegts\"\n        # gather required parameters\n        if self.__livestreaming:\n            # `hls_list_size` must be greater than or equal to 0\n            default_hls_list_size = self.__params.pop(\"-hls_list_size\", 6)\n            if isinstance(default_hls_list_size, int) and default_hls_list_size >= 0:\n                output_params[\"-hls_list_size\"] = default_hls_list_size\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-hls_list_size` value skipped!\")\n                output_params[\"-hls_list_size\"] = 6\n            # `hls_init_time` must be greater than or equal to 0\n            default_hls_init_time = self.__params.pop(\"-hls_init_time\", 4)\n            if isinstance(default_hls_init_time, int) and default_hls_init_time >= 0:\n                output_params[\"-hls_init_time\"] = default_hls_init_time\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-hls_init_time` value skipped!\")\n                output_params[\"-hls_init_time\"] = 4\n            # `hls_time` must be greater than or equal to 0\n            default_hls_time = self.__params.pop(\"-hls_time\", 4)\n            if isinstance(default_hls_time, int) and default_hls_time >= 0:\n                output_params[\"-hls_time\"] = default_hls_time\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-hls_time` value skipped!\")\n                output_params[\"-hls_time\"] = 6\n            # `hls_flags` must be string\n            default_hls_flags = self.__params.pop(\n                \"-hls_flags\", \"delete_segments+discont_start+split_by_time\"\n            )\n            if isinstance(default_hls_flags, str):\n                output_params[\"-hls_flags\"] = default_hls_flags\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-hls_flags` value skipped!\")\n                output_params[\"-hls_flags\"] = (\n                    \"delete_segments+discont_start+split_by_time\"\n                )\n            # clean everything at exit?\n            remove_at_exit = self.__params.pop(\"-remove_at_exit\", 0)\n            if isinstance(remove_at_exit, int) and remove_at_exit in [\n                0,\n                1,\n            ]:\n                output_params[\"-remove_at_exit\"] = remove_at_exit\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-remove_at_exit` value skipped!\")\n                output_params[\"-remove_at_exit\"] = 0\n        else:\n            # enforce \"contain all the segments\"\n            output_params[\"-hls_list_size\"] = 0\n            output_params[\"-hls_playlist_type\"] = \"vod\"\n\n        # handle base URL for absolute paths\n        hls_base_url = self.__params.pop(\"-hls_base_url\", \"\")\n        if isinstance(hls_base_url, str):\n            output_params[\"-hls_base_url\"] = hls_base_url\n        else:\n            # otherwise reset to default\n            logger.warning(\"Invalid `-hls_base_url` value skipped!\")\n            output_params[\"-hls_base_url\"] = \"\"\n\n        # Hardcoded HLS parameters (Refer FFmpeg docs for more info.)\n        output_params[\"-allowed_extensions\"] = \"ALL\"\n        # Handling <hls_segment_filename>\n        # Here filename will be based on `stream_count` dict parameter that\n        # would be used to check whether stream is multi-variant(>1) or single(0-1)\n        segment_template = (\n            \"{}-stream%v-%03d.{}\"\n            if output_params[\"stream_count\"] > 1\n            else \"{}-stream-%03d.{}\"\n        )\n        output_params[\"-hls_segment_filename\"] = segment_template.format(\n            os.path.join(os.path.dirname(self.__out_file), \"chunk\"),\n            \"m4s\" if output_params[\"-hls_segment_type\"] == \"fmp4\" else \"ts\",\n        )\n        # Hardcoded HLS parameters (Refer FFmpeg docs for more info.)\n        output_params[\"-hls_allow_cache\"] = 0\n        # enable hls formatting\n        output_params[\"-f\"] = \"hls\"\n        # return HLS params\n        return (input_params, output_params)\n\n    def __generate_dash_stream(self, input_params, output_params):\n        \"\"\"\n        An internal function that parses user-defined parameters and generates\n        suitable FFmpeg Terminal Command for transcoding input into MPEG-dash Stream.\n\n        Parameters:\n            input_params (dict): Input FFmpeg parameters\n            output_params (dict): Output FFmpeg parameters\n        \"\"\"\n\n        # Check if live-streaming or not?\n        if self.__livestreaming:\n            # `extra_window_size` must be greater than or equal to 0\n            window_size = self.__params.pop(\"-window_size\", 5)\n            if isinstance(window_size, int) and window_size >= 0:\n                output_params[\"-window_size\"] = window_size\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-window_size` value skipped!\")\n                output_params[\"-window_size\"] = 5\n            # `extra_window_size` must be greater than or equal to 0\n            extra_window_size = self.__params.pop(\"-extra_window_size\", 5)\n            if isinstance(extra_window_size, int) and extra_window_size >= 0:\n                output_params[\"-extra_window_size\"] = window_size\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-extra_window_size` value skipped!\")\n                output_params[\"-extra_window_size\"] = 5\n            # clean everything at exit?\n            remove_at_exit = self.__params.pop(\"-remove_at_exit\", 0)\n            if isinstance(remove_at_exit, int) and remove_at_exit in [\n                0,\n                1,\n            ]:\n                output_params[\"-remove_at_exit\"] = remove_at_exit\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-remove_at_exit` value skipped!\")\n                output_params[\"-remove_at_exit\"] = 0\n            # `seg_duration` must be greater than or equal to 0\n            seg_duration = self.__params.pop(\"-seg_duration\", 20)\n            if isinstance(seg_duration, int) and seg_duration >= 0:\n                output_params[\"-seg_duration\"] = seg_duration\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-seg_duration` value skipped!\")\n                output_params[\"-seg_duration\"] = 20\n            # Disable (0) the use of a SegmentTimeline inside a SegmentTemplate.\n            output_params[\"-use_timeline\"] = 0\n        else:\n            # `seg_duration` must be greater than or equal to 0\n            seg_duration = self.__params.pop(\"-seg_duration\", 5)\n            if isinstance(seg_duration, int) and seg_duration >= 0:\n                output_params[\"-seg_duration\"] = seg_duration\n            else:\n                # otherwise reset to default\n                logger.warning(\"Invalid `-seg_duration` value skipped!\")\n                output_params[\"-seg_duration\"] = 5\n            # Enable (1) the use of a SegmentTimeline inside a SegmentTemplate.\n            output_params[\"-use_timeline\"] = 1\n\n        # Finally, some hardcoded DASH parameters (Refer FFmpeg docs for more info.)\n        output_params[\"-use_template\"] = 1\n        output_params[\"-adaptation_sets\"] = \"id=0,streams=v {}\".format(\n            \"id=1,streams=a\" if (\"-acodec\" in output_params) else \"\"\n        )\n        # enable dash formatting\n        output_params[\"-f\"] = \"dash\"\n        # return DASH params\n        return (input_params, output_params)\n\n    def __Build_n_Execute(self, input_params, output_params):\n        \"\"\"\n        An Internal function that launches FFmpeg subprocess and pipelines commands.\n\n        Parameters:\n            input_params (dict): Input FFmpeg parameters\n            output_params (dict): Output FFmpeg parameters\n        \"\"\"\n        # handle audio source if present\n        \"-core_asource\" in output_params and output_params.move_to_end(\n            \"-core_asource\", last=False\n        )\n        # handle `-i` parameter\n        \"-i\" in output_params and output_params.move_to_end(\"-i\", last=False)\n        # copy streams count\n        stream_count = output_params.pop(\"stream_count\", 1)\n\n        # convert input parameters to list\n        input_commands = dict2Args(input_params)\n        # convert output parameters to list\n        output_commands = dict2Args(output_params)\n        # convert any additional parameters to list\n        stream_commands = dict2Args(self.__params)\n\n        # create exclusive HLS params\n        hls_commands = []\n        # handle HLS multi-variant streams\n        if self.__format == \"hls\" and stream_count > 1:\n            stream_map = \"\"\n            for count in range(0, stream_count):\n                stream_map += \"v:{}{} \".format(\n                    count, \",a:{}\".format(count) if \"-acodec\" in output_params else \",\"\n                )\n            hls_commands += [\n                \"-master_pl_name\",\n                os.path.basename(self.__out_file),\n                \"-var_stream_map\",\n                stream_map.strip(),\n                os.path.join(os.path.dirname(self.__out_file), \"stream_%v.m3u8\"),\n            ]\n\n        # log it if enabled\n        self.__logging and logger.debug(\n            \"User-Defined Output parameters: `{}`\".format(\n                \" \".join(output_commands) if output_commands else None\n            )\n        )\n        self.__logging and logger.debug(\n            \"Additional parameters: `{}`\".format(\n                \" \".join(stream_commands) if stream_commands else None\n            )\n        )\n        # build FFmpeg command from parameters\n        ffmpeg_cmd = None\n        # ensuring less cluttering if silent mode\n        hide_banner = [] if self.__logging else [\"-hide_banner\"]\n        # format commands\n        if self.__video_source:\n            ffmpeg_cmd = (\n                [self.__ffmpeg, \"-y\"]\n                + ([\"-re\"] if self.__livestreaming else [])  # pseudo live-streaming\n                + hide_banner\n                + [\"-i\", self.__video_source]\n                + input_commands\n                + output_commands\n                + stream_commands\n            )\n        else:\n            ffmpeg_cmd = (\n                [self.__ffmpeg, \"-y\"]\n                + hide_banner\n                + [\"-f\", \"rawvideo\", \"-vcodec\", \"rawvideo\"]\n                + input_commands\n                + [\"-i\", \"-\"]\n                + output_commands\n                + stream_commands\n            )\n        # format outputs\n        ffmpeg_cmd.extend([self.__out_file] if not (hls_commands) else hls_commands)\n        # Launch the FFmpeg pipeline with built command\n        logger.critical(\"Transcoding streaming chunks. Please wait...\")  # log it\n        self.__process = sp.Popen(\n            ffmpeg_cmd,\n            stdin=sp.PIPE,\n            stdout=(\n                sp.DEVNULL\n                if (not self.__video_source and not self.__logging)\n                else sp.PIPE\n            ),\n            stderr=None if self.__logging else sp.STDOUT,\n        )\n        # post handle progress bar and runtime errors in case of video_source\n        if self.__video_source:\n            return_code = 0\n            pbar = None\n            sec_prev = 0\n            if self.__logging:\n                self.__process.communicate()\n                return_code = self.__process.returncode\n            else:\n                # iterate until stdout runs out\n                while True:\n                    # read and process data\n                    data = self.__process.stdout.readline()\n                    if data:\n                        data = data.decode(\"utf-8\")\n                        # extract duration and time-left\n                        if pbar is None and \"Duration:\" in data:\n                            # extract time in seconds\n                            sec_duration = extract_time(data)\n                            # initiate progress bar\n                            pbar = tqdm(\n                                total=sec_duration,\n                                desc=\"Processing Frames\",\n                                unit=\"frame\",\n                            )\n                        elif \"time=\" in data:\n                            # extract time in seconds\n                            sec_current = extract_time(data)\n                            # update progress bar\n                            if sec_current:\n                                pbar.update(sec_current - sec_prev)\n                                sec_prev = sec_current\n                    else:\n                        # poll if no data\n                        if self.__process.poll() is not None:\n                            break\n                return_code = self.__process.poll()\n            # close progress bar\n            not (pbar is None) and pbar.close()\n            # handle return_code\n            if return_code != 0:\n                # log and raise error if return_code is `1`\n                logger.error(\n                    \"StreamGear failed to initiate stream for this video source!\"\n                )\n                raise sp.CalledProcessError(return_code, ffmpeg_cmd)\n            else:\n                # log if successful\n                logger.critical(\n                    \"Transcoding Ended. {} Streaming assets are successfully generated at specified path.\".format(\n                        self.__format.upper()\n                    )\n                )\n\n    def __enter__(self):\n        \"\"\"\n        Handles entry with the `with` statement. See [PEP343 -- The 'with' statement'](https://peps.python.org/pep-0343/).\n\n        **Returns:** Returns a reference to the StreamGear Class\n        \"\"\"\n        return self\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        \"\"\"\n        Handles exit with the `with` statement. See [PEP343 -- The 'with' statement'](https://peps.python.org/pep-0343/).\n        \"\"\"\n        self.close()\n\n    @deprecated(\n        message=\"The `terminate()` method will be removed in the next release. Kindly use `close()` method instead.\"\n    )\n    def terminate(self):\n        \"\"\"\n        !!! warning \"[DEPRECATION NOTICE]: This method is now deprecated and will be removed in a future release.\"\n\n        This function ensures backward compatibility for the `terminate()` method to maintain the API on existing systems.\n        It achieves this by calling the new `close()` method to terminate various\n        StreamGear processes.\n        \"\"\"\n\n        self.close()\n\n    def close(self):\n        \"\"\"\n        Safely terminates various StreamGear process.\n        \"\"\"\n        # log termination\n        self.__logging and logger.debug(\"Terminating StreamGear Processes.\")\n\n        # return if no process was initiated at first place\n        if self.__process is None or not (self.__process.poll() is None):\n            return\n        # close `stdin` output\n        self.__process.stdin and self.__process.stdin.close()\n        # close `stdout` output\n        self.__process.stdout and self.__process.stdout.close()\n        # forced termination if specified.\n        if self.__forced_termination:\n            self.__process.terminate()\n        # handle device audio streams\n        elif self.__audio and isinstance(self.__audio, list):\n            # send `CTRL_BREAK_EVENT` signal if Windows else `SIGINT`\n            self.__process.send_signal(\n                signal.CTRL_BREAK_EVENT if self.__os_windows else signal.SIGINT\n            )\n        # wait if process is still processing\n        self.__process.wait()\n        # discard process\n        self.__process = None\n

"},{"location":"bonus/reference/streamgear/#vidgear.gears.streamgear.StreamGear.__enter__","title":"__enter__(self) special","text":"

Handles entry with the with statement. See PEP343 -- The 'with' statement'.

Returns: Returns a reference to the StreamGear Class

Source code in vidgear/gears/streamgear.py
def __enter__(self):\n    \"\"\"\n    Handles entry with the `with` statement. See [PEP343 -- The 'with' statement'](https://peps.python.org/pep-0343/).\n\n    **Returns:** Returns a reference to the StreamGear Class\n    \"\"\"\n    return self\n
"},{"location":"bonus/reference/streamgear/#vidgear.gears.streamgear.StreamGear.__exit__","title":"__exit__(self, exc_type, exc_val, exc_tb) special","text":"

Handles exit with the with statement. See PEP343 -- The 'with' statement'.

Source code in vidgear/gears/streamgear.py
def __exit__(self, exc_type, exc_val, exc_tb):\n    \"\"\"\n    Handles exit with the `with` statement. See [PEP343 -- The 'with' statement'](https://peps.python.org/pep-0343/).\n    \"\"\"\n    self.close()\n
"},{"location":"bonus/reference/streamgear/#vidgear.gears.streamgear.StreamGear.__init__","title":"__init__(self, output='', format='dash', custom_ffmpeg='', logging=False, **stream_params) special","text":"

This constructor method initializes the object state and attributes of the StreamGear class.

Parameters:

Name Type Description Default output str

sets the valid filename/path for generating the StreamGear assets.

'' format str

select the adaptive HTTP streaming format(DASH and HLS).

'dash' custom_ffmpeg str

assigns the location of custom path/directory for custom FFmpeg executables.

'' logging bool

enables/disables logging.

False stream_params dict

provides the flexibility to control supported internal parameters and FFmpeg properties.

{} Source code in vidgear/gears/streamgear.py
def __init__(\n    self, output=\"\", format=\"dash\", custom_ffmpeg=\"\", logging=False, **stream_params\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the StreamGear class.\n\n    Parameters:\n        output (str): sets the valid filename/path for generating the StreamGear assets.\n        format (str): select the adaptive HTTP streaming format(DASH and HLS).\n        custom_ffmpeg (str): assigns the location of custom path/directory for custom FFmpeg executables.\n        logging (bool): enables/disables logging.\n        stream_params (dict): provides the flexibility to control supported internal parameters and FFmpeg properties.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # checks if machine in-use is running windows os or not\n    self.__os_windows = True if os.name == \"nt\" else False\n\n    # initialize various class variables\n    # handles user-defined parameters\n    self.__params = {}\n    # handle input video/frame resolution and channels\n    self.__inputheight = None\n    self.__inputwidth = None\n    self.__inputchannels = None\n    self.__sourceframerate = None\n    # handle process to be frames written\n    self.__process = None\n    # handle valid FFmpeg assets location\n    self.__ffmpeg = \"\"\n    # handle one time process for valid process initialization\n    self.__initiate_stream = True\n\n    # cleans and reformat user-defined parameters\n    self.__params = {\n        str(k).strip(): (v.strip() if isinstance(v, str) else v)\n        for k, v in stream_params.items()\n    }\n\n    # handle where to save the downloaded FFmpeg Static assets on Windows(if specified)\n    __ffmpeg_download_path = self.__params.pop(\"-ffmpeg_download_path\", \"\")\n    if not isinstance(__ffmpeg_download_path, (str)):\n        # reset improper values\n        __ffmpeg_download_path = \"\"\n\n    # validate the FFmpeg assets and return location (also downloads static assets on windows)\n    self.__ffmpeg = get_valid_ffmpeg_path(\n        str(custom_ffmpeg),\n        self.__os_windows,\n        ffmpeg_download_path=__ffmpeg_download_path,\n        logging=self.__logging,\n    )\n\n    # check if valid FFmpeg path returned\n    if self.__ffmpeg:\n        self.__logging and logger.debug(\n            \"Found valid FFmpeg executables: `{}`.\".format(self.__ffmpeg)\n        )\n    else:\n        # else raise error\n        raise RuntimeError(\n            \"[StreamGear:ERROR] :: Failed to find FFmpeg assets on this system. Kindly compile/install FFmpeg or provide a valid custom FFmpeg binary path!\"\n        )\n\n    # handle streaming format\n    supported_formats = [\"dash\", \"hls\"]  # TODO will be extended in future\n    if format and isinstance(format, str):\n        _format = format.strip().lower()\n        if _format in supported_formats:\n            self.__format = _format\n            logger.info(\n                \"StreamGear will generate asset files for {} streaming format.\".format(\n                    self.__format.upper()\n                )\n            )\n        elif difflib.get_close_matches(_format, supported_formats):\n            raise ValueError(\n                \"[StreamGear:ERROR] :: Incorrect `format` parameter value! Did you mean `{}`?\".format(\n                    difflib.get_close_matches(_format, supported_formats)[0]\n                )\n            )\n        else:\n            raise ValueError(\n                \"[StreamGear:ERROR] :: The `format` parameter value `{}` not valid/supported!\".format(\n                    format\n                )\n            )\n    else:\n        raise ValueError(\n            \"[StreamGear:ERROR] :: The `format` parameter value is Missing or Invalid!\"\n        )\n\n    # handle Audio-Input\n    audio = self.__params.pop(\"-audio\", False)\n    if audio and isinstance(audio, str):\n        if os.path.isfile(audio):\n            self.__audio = os.path.abspath(audio)\n        elif is_valid_url(self.__ffmpeg, url=audio, logging=self.__logging):\n            self.__audio = audio\n        else:\n            self.__audio = False\n    elif audio and isinstance(audio, list):\n        self.__audio = audio\n    else:\n        self.__audio = False\n    # log external audio source\n    self.__audio and self.__logging and logger.debug(\n        \"External audio source `{}` detected.\".format(self.__audio)\n    )\n\n    # handle Video-Source input\n    source = self.__params.pop(\"-video_source\", False)\n    # Check if input is valid string\n    if source and isinstance(source, str) and len(source) > 1:\n        # Differentiate input\n        if os.path.isfile(source):\n            self.__video_source = os.path.abspath(source)\n        elif is_valid_url(self.__ffmpeg, url=source, logging=self.__logging):\n            self.__video_source = source\n        else:\n            # discard the value otherwise\n            self.__video_source = False\n\n        # Validate input\n        if self.__video_source:\n            validation_results = validate_video(\n                self.__ffmpeg, video_path=self.__video_source\n            )\n            assert not (\n                validation_results is None\n            ), \"[StreamGear:ERROR] :: Given `{}` video_source is Invalid, Check Again!\".format(\n                self.__video_source\n            )\n            self.__aspect_source = validation_results[\"resolution\"]\n            self.__fps_source = validation_results[\"framerate\"]\n            # log it\n            self.__logging and logger.debug(\n                \"Given video_source is valid and has {}x{} resolution, and a framerate of {} fps.\".format(\n                    self.__aspect_source[0],\n                    self.__aspect_source[1],\n                    self.__fps_source,\n                )\n            )\n        else:\n            # log warning\n            logger.warning(\"Discarded invalid `-video_source` value provided.\")\n    else:\n        if source:\n            # log warning if source provided\n            logger.warning(\"Invalid `-video_source` value provided.\")\n        else:\n            # log normally\n            logger.info(\"No `-video_source` value provided.\")\n        # discard the value otherwise\n        self.__video_source = False\n\n    # handle user-defined framerate\n    self.__inputframerate = self.__params.pop(\"-input_framerate\", 0.0)\n    if isinstance(self.__inputframerate, (float, int)):\n        # must be float\n        self.__inputframerate = float(self.__inputframerate)\n    else:\n        # reset improper values\n        self.__inputframerate = 0.0\n\n    # handle old assets\n    clear_assets = self.__params.pop(\"-clear_prev_assets\", False)\n    if isinstance(clear_assets, bool):\n        self.__clear_assets = clear_assets\n        # log if clearing assets is enabled\n        clear_assets and logger.info(\n            \"The `-clear_prev_assets` parameter is enabled successfully. All previous StreamGear API assets for `{}` format will be removed for this run.\".format(\n                self.__format.upper()\n            )\n        )\n    else:\n        # reset improper values\n        self.__clear_assets = False\n\n    # handle whether to livestream?\n    livestreaming = self.__params.pop(\"-livestream\", False)\n    if isinstance(livestreaming, bool) and livestreaming:\n        # NOTE:  `livestream` is only available with real-time mode.\n        self.__livestreaming = livestreaming if not (self.__video_source) else False\n        if self.__video_source:\n            logger.error(\n                \"Live-Streaming is only available with Real-time Mode. Refer docs for more information.\"\n            )\n        else:\n            # log if live streaming is enabled\n            livestreaming and logger.info(\n                \"Live-Streaming is successfully enabled for this run.\"\n            )\n    else:\n        # reset improper values\n        self.__livestreaming = False\n\n    # handle the special-case of forced-termination\n    enable_force_termination = self.__params.pop(\"-enable_force_termination\", False)\n    # check if value is valid\n    if isinstance(enable_force_termination, bool):\n        self.__forced_termination = enable_force_termination\n        # log if forced termination is enabled\n        self.__forced_termination and logger.warning(\n            \"Forced termination is enabled for this run. This may result in corrupted output in certain scenarios!\"\n        )\n    else:\n        # handle improper values\n        self.__forced_termination = False\n\n    # handle streaming format\n    supported_formats = [\"dash\", \"hls\"]  # TODO will be extended in future\n    if format and isinstance(format, str):\n        _format = format.strip().lower()\n        if _format in supported_formats:\n            self.__format = _format\n            logger.info(\n                \"StreamGear will generate asset files for {} streaming format.\".format(\n                    self.__format.upper()\n                )\n            )\n        elif difflib.get_close_matches(_format, supported_formats):\n            raise ValueError(\n                \"[StreamGear:ERROR] :: Incorrect `format` parameter value! Did you mean `{}`?\".format(\n                    difflib.get_close_matches(_format, supported_formats)[0]\n                )\n            )\n        else:\n            raise ValueError(\n                \"[StreamGear:ERROR] :: The `format` parameter value `{}` not valid/supported!\".format(\n                    format\n                )\n            )\n    else:\n        raise ValueError(\n            \"[StreamGear:ERROR] :: The `format` parameter value is Missing or Invalid!\"\n        )\n\n    # handles output asset filenames\n    if output:\n        # validate this class has the access rights to specified directory or not\n        abs_path = os.path.abspath(output)\n        # check if given output is a valid system path\n        if check_WriteAccess(\n            os.path.dirname(abs_path),\n            is_windows=self.__os_windows,\n            logging=self.__logging,\n        ):\n            # get all assets extensions\n            valid_extension = \"mpd\" if self.__format == \"dash\" else \"m3u8\"\n            assets_exts = [\n                (\"chunk-stream\", \".m4s\"),  # filename prefix, extension\n                (\"chunk-stream\", \".ts\"),  # filename prefix, extension\n                \".{}\".format(valid_extension),\n            ]\n            # add source file extension too\n            self.__video_source and assets_exts.append(\n                (\n                    \"chunk-stream\",\n                    os.path.splitext(self.__video_source)[1],\n                )  # filename prefix, extension\n            )\n            # handle output\n            # check if path is a directory\n            if os.path.isdir(abs_path):\n                # clear previous assets if specified\n                self.__clear_assets and delete_ext_safe(\n                    abs_path, assets_exts, logging=self.__logging\n                )\n                # auto-assign valid name and adds it to path\n                abs_path = os.path.join(\n                    abs_path,\n                    \"{}-{}.{}\".format(\n                        self.__format,\n                        time.strftime(\"%Y%m%d-%H%M%S\"),\n                        valid_extension,\n                    ),\n                )\n            # or check if path is a file\n            elif os.path.isfile(abs_path) and self.__clear_assets:\n                # clear previous assets if specified\n                delete_ext_safe(\n                    os.path.dirname(abs_path),\n                    assets_exts,\n                    logging=self.__logging,\n                )\n            # check if path has valid file extension\n            assert abs_path.endswith(\n                valid_extension\n            ), \"Given `{}` path has invalid file-extension w.r.t selected format: `{}`!\".format(\n                output, self.__format.upper()\n            )\n            self.__logging and logger.debug(\n                \"Output Path:`{}` is successfully configured for generating streaming assets.\".format(\n                    abs_path\n                )\n            )\n            # workaround patch for Windows only,\n            # others platforms will not be affected\n            self.__out_file = abs_path.replace(\"\\\\\", \"/\")\n        # check if given output is a valid URL\n        elif is_valid_url(self.__ffmpeg, url=output, logging=self.__logging):\n            self.__logging and logger.debug(\n                \"URL:`{}` is valid and successfully configured for generating streaming assets.\".format(\n                    output\n                )\n            )\n            self.__out_file = output\n        # raise ValueError otherwise\n        else:\n            raise ValueError(\n                \"[StreamGear:ERROR] :: The output parameter value:`{}` is not valid/supported!\".format(\n                    output\n                )\n            )\n    else:\n        # raise ValueError otherwise\n        raise ValueError(\n            \"[StreamGear:ERROR] :: Kindly provide a valid `output` parameter value. Refer Docs for more information.\"\n        )\n\n    # log Mode of operation\n    self.__video_source and logger.info(\n        \"StreamGear has been successfully configured for {} Mode.\".format(\n            \"Single-Source\" if self.__video_source else \"Real-time Frames\"\n        )\n    )\n
"},{"location":"bonus/reference/streamgear/#vidgear.gears.streamgear.StreamGear.close","title":"close(self)","text":"

Safely terminates various StreamGear process.

Source code in vidgear/gears/streamgear.py
def close(self):\n    \"\"\"\n    Safely terminates various StreamGear process.\n    \"\"\"\n    # log termination\n    self.__logging and logger.debug(\"Terminating StreamGear Processes.\")\n\n    # return if no process was initiated at first place\n    if self.__process is None or not (self.__process.poll() is None):\n        return\n    # close `stdin` output\n    self.__process.stdin and self.__process.stdin.close()\n    # close `stdout` output\n    self.__process.stdout and self.__process.stdout.close()\n    # forced termination if specified.\n    if self.__forced_termination:\n        self.__process.terminate()\n    # handle device audio streams\n    elif self.__audio and isinstance(self.__audio, list):\n        # send `CTRL_BREAK_EVENT` signal if Windows else `SIGINT`\n        self.__process.send_signal(\n            signal.CTRL_BREAK_EVENT if self.__os_windows else signal.SIGINT\n        )\n    # wait if process is still processing\n    self.__process.wait()\n    # discard process\n    self.__process = None\n
"},{"location":"bonus/reference/streamgear/#vidgear.gears.streamgear.StreamGear.stream","title":"stream(self, frame, rgb_mode=False)","text":"

Pipes ndarray frames to FFmpeg Pipeline for transcoding them into chunked-encoded media segments of streaming formats such as MPEG-DASH and HLS.

[DEPRECATION NOTICE]: The rgb_mode parameter is deprecated and will be removed in a future version.

Parameters:

Name Type Description Default frame ndarray

a valid numpy frame

required rgb_mode boolean

enable this flag to activate RGB mode (i.e. specifies that incoming frames are of RGB format instead of default BGR).

False Source code in vidgear/gears/streamgear.py
@deprecated(\n    parameter=\"rgb_mode\",\n    message=\"The `rgb_mode` parameter is deprecated and will be removed in a future version. Only BGR format frames will be supported going forward.\",\n)\ndef stream(self, frame, rgb_mode=False):\n    \"\"\"\n    Pipes `ndarray` frames to FFmpeg Pipeline for transcoding them into chunked-encoded media segments of\n    streaming formats such as MPEG-DASH and HLS.\n\n    !!! warning \"[DEPRECATION NOTICE]: The `rgb_mode` parameter is deprecated and will be removed in a future version.\"\n\n    Parameters:\n        frame (ndarray): a valid numpy frame\n        rgb_mode (boolean): enable this flag to activate RGB mode _(i.e. specifies that incoming frames are of RGB format instead of default BGR)_.\n    \"\"\"\n    # check if function is called in correct context\n    if self.__video_source:\n        raise RuntimeError(\n            \"[StreamGear:ERROR] :: The `stream()` method cannot be used when streaming from a `-video_source` input file. Kindly refer vidgear docs!\"\n        )\n    # None-Type frames will be skipped\n    if frame is None:\n        return\n    # extract height, width and number of channels of frame\n    height, width = frame.shape[:2]\n    channels = frame.shape[-1] if frame.ndim == 3 else 1\n    # assign values to class variables on first run\n    if self.__initiate_stream:\n        self.__inputheight = height\n        self.__inputwidth = width\n        self.__inputchannels = channels\n        self.__sourceframerate = (\n            25.0 if not (self.__inputframerate) else self.__inputframerate\n        )\n        self.__logging and logger.debug(\n            \"InputFrame => Height:{} Width:{} Channels:{}\".format(\n                self.__inputheight, self.__inputwidth, self.__inputchannels\n            )\n        )\n    # validate size of frame\n    if height != self.__inputheight or width != self.__inputwidth:\n        raise ValueError(\"[StreamGear:ERROR] :: All frames must have same size!\")\n    # validate number of channels\n    if channels != self.__inputchannels:\n        raise ValueError(\n            \"[StreamGear:ERROR] :: All frames must have same number of channels!\"\n        )\n    # initiate FFmpeg process on first run\n    if self.__initiate_stream:\n        # launch pre-processing\n        self.__PreProcess(channels=channels, rgb=rgb_mode)\n        # Check status of the process\n        assert self.__process is not None\n\n    # write the frame to pipeline\n    try:\n        self.__process.stdin.write(frame.tobytes())\n    except (OSError, IOError):\n        # log something is wrong!\n        logger.error(\n            \"BrokenPipeError caught, Wrong values passed to FFmpeg Pipe, Kindly Refer Docs!\"\n        )\n        raise ValueError  # for testing purpose only\n
"},{"location":"bonus/reference/streamgear/#vidgear.gears.streamgear.StreamGear.terminate","title":"terminate(self)","text":"

[DEPRECATION NOTICE]: This method is now deprecated and will be removed in a future release.

This function ensures backward compatibility for the terminate() method to maintain the API on existing systems. It achieves this by calling the new close() method to terminate various StreamGear processes.

Source code in vidgear/gears/streamgear.py
@deprecated(\n    message=\"The `terminate()` method will be removed in the next release. Kindly use `close()` method instead.\"\n)\ndef terminate(self):\n    \"\"\"\n    !!! warning \"[DEPRECATION NOTICE]: This method is now deprecated and will be removed in a future release.\"\n\n    This function ensures backward compatibility for the `terminate()` method to maintain the API on existing systems.\n    It achieves this by calling the new `close()` method to terminate various\n    StreamGear processes.\n    \"\"\"\n\n    self.close()\n
"},{"location":"bonus/reference/streamgear/#vidgear.gears.streamgear.StreamGear.transcode_source","title":"transcode_source(self)","text":"

Transcodes an entire video file (with or without audio) into chunked-encoded media segments of streaming formats such as MPEG-DASH and HLS.

Source code in vidgear/gears/streamgear.py
def transcode_source(self):\n    \"\"\"\n    Transcodes an entire video file _(with or without audio)_ into chunked-encoded media segments of\n    streaming formats such as MPEG-DASH and HLS.\n    \"\"\"\n    # check if function is called in correct context\n    if not (self.__video_source):\n        raise RuntimeError(\n            \"[StreamGear:ERROR] :: The `transcode_source()` method cannot be used without a valid `-video_source` input. Kindly refer vidgear docs!\"\n        )\n    # assign height, width and framerate\n    self.__inputheight = int(self.__aspect_source[1])\n    self.__inputwidth = int(self.__aspect_source[0])\n    self.__sourceframerate = float(self.__fps_source)\n    # launch pre-processing\n    self.__PreProcess()\n
"},{"location":"bonus/reference/videogear/","title":"VideoGear API References","text":"

VideoGear API usage examples can be found here \u27b6

VideoGear API parameters are explained here \u27b6

VideoGear API provides a special internal wrapper around VidGear's exclusive Video Stabilizer class. VideoGear also acts as a Common Video-Capture API that provides internal access for both CamGear and PiGear APIs and their parameters with an exclusive enablePiCamera boolean flag.

VideoGear is ideal when you need to switch to different video sources without changing your code much. Also, it enables easy stabilization for various video-streams (real-time or not) with minimum effort and writing way fewer lines of code.

Source code in vidgear/gears/videogear.py
class VideoGear:\n    \"\"\"\n    VideoGear API provides a special internal wrapper around VidGear's exclusive Video Stabilizer class.\n    VideoGear also acts as a Common Video-Capture API that provides internal access for both CamGear and PiGear APIs and\n    their parameters with an exclusive enablePiCamera boolean flag.\n\n    VideoGear is ideal when you need to switch to different video sources without changing your code much. Also, it enables\n    easy stabilization for various video-streams (real-time or not)\n    with minimum effort and writing way fewer lines of code.\n    \"\"\"\n\n    def __init__(\n        self,\n        # VideoGear parameters\n        enablePiCamera=False,\n        stabilize=False,\n        # PiGear parameters\n        camera_num=0,\n        resolution=(640, 480),\n        framerate=30,\n        # CamGear parameters\n        source=0,\n        stream_mode=False,\n        backend=0,\n        # common parameters\n        time_delay=0,\n        colorspace=None,\n        logging=False,\n        **options\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the VideoGear class.\n\n        Parameters:\n            enablePiCamera (bool): provide access to PiGear(if True) or CamGear(if False) APIs respectively.\n            stabilize (bool): enable access to Stabilizer Class for stabilizing frames.\n            camera_num (int): selects the camera module index which will be used as Rpi source.\n            resolution (tuple): sets the resolution (i.e. `(width,height)`) of the Rpi source.\n            framerate (int/float): sets the framerate of the Rpi source.\n            source (based on input): defines the source for the input stream.\n            stream_mode (bool): controls the exclusive YouTube Mode.\n            backend (int): selects the backend for OpenCV's VideoCapture class.\n            colorspace (str): selects the colorspace of the input stream.\n            logging (bool): enables/disables logging.\n            time_delay (int): time delay (in sec) before start reading the frames.\n            options (dict): provides ability to alter Tweak Parameters of CamGear, PiGear & Stabilizer.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # initialize stabilizer\n        self.__stabilization_mode = stabilize\n\n        # reformat dictionary\n        options = {str(k).strip(): v for k, v in options.items()}\n\n        if self.__stabilization_mode:\n            from .stabilizer import Stabilizer\n\n            s_radius = options.pop(\"SMOOTHING_RADIUS\", 25)\n            if not isinstance(s_radius, int):\n                s_radius = 25\n\n            border_size = options.pop(\"BORDER_SIZE\", 0)\n            if not isinstance(border_size, int):\n                border_size = 0\n\n            border_type = options.pop(\"BORDER_TYPE\", \"black\")\n            if not isinstance(border_type, str):\n                border_type = \"black\"\n\n            crop_n_zoom = options.pop(\"CROP_N_ZOOM\", False)\n            if not isinstance(crop_n_zoom, bool):\n                crop_n_zoom = False\n\n            self.__stabilizer_obj = Stabilizer(\n                smoothing_radius=s_radius,\n                border_type=border_type,\n                border_size=border_size,\n                crop_n_zoom=crop_n_zoom,\n                logging=logging,\n            )\n            self.__logging and logger.debug(\n                \"Enabling Stabilization Mode for the current video source!\"\n            )  # log info\n\n        if enablePiCamera:\n            # only import the pigear module only if required\n            from .pigear import PiGear\n\n            # initialize the picamera stream by enabling PiGear API\n            self.stream = PiGear(\n                camera_num=camera_num,\n                resolution=resolution,\n                framerate=framerate,\n                colorspace=colorspace,\n                logging=logging,\n                time_delay=time_delay,\n                **options\n            )\n        else:\n            # otherwise, we are using OpenCV so initialize the webcam\n            # stream by activating CamGear API\n            self.stream = CamGear(\n                source=source,\n                stream_mode=stream_mode,\n                backend=backend,\n                colorspace=colorspace,\n                logging=logging,\n                time_delay=time_delay,\n                **options\n            )\n\n        # initialize framerate variable\n        self.framerate = self.stream.framerate\n\n    def start(self):\n        \"\"\"\n        Launches the internal *Threaded Frames Extractor* daemon of API in use.\n\n        **Returns:** A reference to the selected class object.\n        \"\"\"\n        self.stream.start()\n        return self\n\n    def read(self):\n        \"\"\"\n        Extracts frames synchronously from selected API's monitored deque, while maintaining a fixed-length frame\n        buffer in the memory, and blocks the thread if the deque is full.\n\n        **Returns:** A n-dimensional numpy array.\n        \"\"\"\n        while self.__stabilization_mode:\n            frame = self.stream.read()\n            if frame is None:\n                break\n            frame_stab = self.__stabilizer_obj.stabilize(frame)\n            if not (frame_stab is None):\n                return frame_stab\n        return self.stream.read()\n\n    def stop(self):\n        \"\"\"\n        Safely terminates the thread, and release the respective multi-threaded resources.\n        \"\"\"\n        self.stream.stop()\n        # logged\n        self.__logging and logger.debug(\"Terminating VideoGear.\")\n        # clean queue\n        self.__stabilization_mode and self.__stabilizer_obj.clean()\n

"},{"location":"bonus/reference/videogear/#vidgear.gears.videogear.VideoGear.__init__","title":"__init__(self, enablePiCamera=False, stabilize=False, camera_num=0, resolution=(640, 480), framerate=30, source=0, stream_mode=False, backend=0, time_delay=0, colorspace=None, logging=False, **options) special","text":"

This constructor method initializes the object state and attributes of the VideoGear class.

Parameters:

Name Type Description Default enablePiCamera bool

provide access to PiGear(if True) or CamGear(if False) APIs respectively.

False stabilize bool

enable access to Stabilizer Class for stabilizing frames.

False camera_num int

selects the camera module index which will be used as Rpi source.

0 resolution tuple

sets the resolution (i.e. (width,height)) of the Rpi source.

(640, 480) framerate int/float

sets the framerate of the Rpi source.

30 source based on input

defines the source for the input stream.

0 stream_mode bool

controls the exclusive YouTube Mode.

False backend int

selects the backend for OpenCV's VideoCapture class.

0 colorspace str

selects the colorspace of the input stream.

None logging bool

enables/disables logging.

False time_delay int

time delay (in sec) before start reading the frames.

0 options dict

provides ability to alter Tweak Parameters of CamGear, PiGear & Stabilizer.

{} Source code in vidgear/gears/videogear.py
def __init__(\n    self,\n    # VideoGear parameters\n    enablePiCamera=False,\n    stabilize=False,\n    # PiGear parameters\n    camera_num=0,\n    resolution=(640, 480),\n    framerate=30,\n    # CamGear parameters\n    source=0,\n    stream_mode=False,\n    backend=0,\n    # common parameters\n    time_delay=0,\n    colorspace=None,\n    logging=False,\n    **options\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the VideoGear class.\n\n    Parameters:\n        enablePiCamera (bool): provide access to PiGear(if True) or CamGear(if False) APIs respectively.\n        stabilize (bool): enable access to Stabilizer Class for stabilizing frames.\n        camera_num (int): selects the camera module index which will be used as Rpi source.\n        resolution (tuple): sets the resolution (i.e. `(width,height)`) of the Rpi source.\n        framerate (int/float): sets the framerate of the Rpi source.\n        source (based on input): defines the source for the input stream.\n        stream_mode (bool): controls the exclusive YouTube Mode.\n        backend (int): selects the backend for OpenCV's VideoCapture class.\n        colorspace (str): selects the colorspace of the input stream.\n        logging (bool): enables/disables logging.\n        time_delay (int): time delay (in sec) before start reading the frames.\n        options (dict): provides ability to alter Tweak Parameters of CamGear, PiGear & Stabilizer.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # initialize stabilizer\n    self.__stabilization_mode = stabilize\n\n    # reformat dictionary\n    options = {str(k).strip(): v for k, v in options.items()}\n\n    if self.__stabilization_mode:\n        from .stabilizer import Stabilizer\n\n        s_radius = options.pop(\"SMOOTHING_RADIUS\", 25)\n        if not isinstance(s_radius, int):\n            s_radius = 25\n\n        border_size = options.pop(\"BORDER_SIZE\", 0)\n        if not isinstance(border_size, int):\n            border_size = 0\n\n        border_type = options.pop(\"BORDER_TYPE\", \"black\")\n        if not isinstance(border_type, str):\n            border_type = \"black\"\n\n        crop_n_zoom = options.pop(\"CROP_N_ZOOM\", False)\n        if not isinstance(crop_n_zoom, bool):\n            crop_n_zoom = False\n\n        self.__stabilizer_obj = Stabilizer(\n            smoothing_radius=s_radius,\n            border_type=border_type,\n            border_size=border_size,\n            crop_n_zoom=crop_n_zoom,\n            logging=logging,\n        )\n        self.__logging and logger.debug(\n            \"Enabling Stabilization Mode for the current video source!\"\n        )  # log info\n\n    if enablePiCamera:\n        # only import the pigear module only if required\n        from .pigear import PiGear\n\n        # initialize the picamera stream by enabling PiGear API\n        self.stream = PiGear(\n            camera_num=camera_num,\n            resolution=resolution,\n            framerate=framerate,\n            colorspace=colorspace,\n            logging=logging,\n            time_delay=time_delay,\n            **options\n        )\n    else:\n        # otherwise, we are using OpenCV so initialize the webcam\n        # stream by activating CamGear API\n        self.stream = CamGear(\n            source=source,\n            stream_mode=stream_mode,\n            backend=backend,\n            colorspace=colorspace,\n            logging=logging,\n            time_delay=time_delay,\n            **options\n        )\n\n    # initialize framerate variable\n    self.framerate = self.stream.framerate\n
"},{"location":"bonus/reference/videogear/#vidgear.gears.videogear.VideoGear.read","title":"read(self)","text":"

Extracts frames synchronously from selected API's monitored deque, while maintaining a fixed-length frame buffer in the memory, and blocks the thread if the deque is full.

Returns: A n-dimensional numpy array.

Source code in vidgear/gears/videogear.py
def read(self):\n    \"\"\"\n    Extracts frames synchronously from selected API's monitored deque, while maintaining a fixed-length frame\n    buffer in the memory, and blocks the thread if the deque is full.\n\n    **Returns:** A n-dimensional numpy array.\n    \"\"\"\n    while self.__stabilization_mode:\n        frame = self.stream.read()\n        if frame is None:\n            break\n        frame_stab = self.__stabilizer_obj.stabilize(frame)\n        if not (frame_stab is None):\n            return frame_stab\n    return self.stream.read()\n
"},{"location":"bonus/reference/videogear/#vidgear.gears.videogear.VideoGear.start","title":"start(self)","text":"

Launches the internal Threaded Frames Extractor daemon of API in use.

Returns: A reference to the selected class object.

Source code in vidgear/gears/videogear.py
def start(self):\n    \"\"\"\n    Launches the internal *Threaded Frames Extractor* daemon of API in use.\n\n    **Returns:** A reference to the selected class object.\n    \"\"\"\n    self.stream.start()\n    return self\n
"},{"location":"bonus/reference/videogear/#vidgear.gears.videogear.VideoGear.stop","title":"stop(self)","text":"

Safely terminates the thread, and release the respective multi-threaded resources.

Source code in vidgear/gears/videogear.py
def stop(self):\n    \"\"\"\n    Safely terminates the thread, and release the respective multi-threaded resources.\n    \"\"\"\n    self.stream.stop()\n    # logged\n    self.__logging and logger.debug(\"Terminating VideoGear.\")\n    # clean queue\n    self.__stabilization_mode and self.__stabilizer_obj.clean()\n
"},{"location":"bonus/reference/webgear/","title":"WebGear API References","text":"

WebGear API usage examples can be found here \u27b6

WebGear API parameters are explained here \u27b6

WebGear is a powerful ASGI Video-Broadcaster API ideal for transmitting Motion-JPEG-frames from a single source to multiple recipients via the browser.

WebGear API works on Starlette's ASGI application and provides a highly extensible and flexible async wrapper around its complete framework. WebGear can flexibly interact with Starlette's ecosystem of shared middleware, mountable applications, Response classes, Routing tables, Static Files, Templating engine(with Jinja2), etc.

WebGear API uses an intraframe-only compression scheme under the hood where the sequence of video-frames are first encoded as JPEG-DIB (JPEG with Device-Independent Bit compression) and then streamed over HTTP using Starlette's Multipart Streaming Response and a Uvicorn ASGI Server. This method imposes lower processing and memory requirements, but the quality is not the best, since JPEG compression is not very efficient for motion video.

In layman's terms, WebGear acts as a powerful Video Broadcaster that transmits live video-frames to any web-browser in the network. Additionally, WebGear API also provides internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs, thereby granting it exclusive power for transferring frames incoming from any source to the network.

Source code in vidgear/gears/asyncio/webgear.py
class WebGear:\n    \"\"\"\n    WebGear is a powerful ASGI Video-Broadcaster API ideal for transmitting Motion-JPEG-frames from a single source to multiple recipients via the browser.\n\n    WebGear API works on Starlette's ASGI application and provides a highly extensible and flexible async wrapper around its complete framework. WebGear can\n    flexibly interact with Starlette's ecosystem of shared middleware, mountable applications, Response classes, Routing tables, Static Files, Templating\n    engine(with Jinja2), etc.\n\n    WebGear API uses an intraframe-only compression scheme under the hood where the sequence of video-frames are first encoded as JPEG-DIB (JPEG with Device-Independent Bit compression)\n    and then streamed over HTTP using Starlette's Multipart Streaming Response and a Uvicorn ASGI Server. This method imposes lower processing and memory requirements, but the quality\n    is not the best, since JPEG compression is not very efficient for motion video.\n\n    In layman's terms, WebGear acts as a powerful Video Broadcaster that transmits live video-frames to any web-browser in the network. Additionally, WebGear API also provides internal\n    wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs, thereby granting it exclusive power for transferring frames incoming from any source to the network.\n    \"\"\"\n\n    def __init__(\n        self,\n        enablePiCamera=False,\n        stabilize=False,\n        source=None,\n        camera_num=0,\n        stream_mode=False,\n        backend=0,\n        colorspace=None,\n        resolution=(640, 480),\n        framerate=25,\n        logging=False,\n        time_delay=0,\n        **options\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the WebGear class.\n\n        Parameters:\n            enablePiCamera (bool): provide access to PiGear(if True) or CamGear(if False) APIs respectively.\n            stabilize (bool): enable access to Stabilizer Class for stabilizing frames.\n            camera_num (int): selects the camera module index which will be used as Rpi source.\n            resolution (tuple): sets the resolution (i.e. `(width,height)`) of the Rpi source.\n            framerate (int/float): sets the framerate of the Rpi source.\n            source (based on input): defines the source for the input stream.\n            stream_mode (bool): controls the exclusive YouTube Mode.\n            backend (int): selects the backend for OpenCV's VideoCapture class.\n            colorspace (str): selects the colorspace of the input stream.\n            logging (bool): enables/disables logging.\n            time_delay (int): time delay (in sec) before start reading the frames.\n            options (dict): provides ability to alter Tweak Parameters of WebGear, CamGear, PiGear & Stabilizer.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # raise error(s) for critical Class imports\n        import_dependency_safe(\"starlette\" if starlette is None else \"\")\n        import_dependency_safe(\n            \"simplejpeg\" if simplejpeg is None else \"\", min_version=\"1.6.1\"\n        )\n\n        # initialize global params\n        self.__skip_generate_webdata = False  # generate webgear data by default\n        # define frame-compression handler\n        self.__jpeg_compression_quality = 90  # 90% quality\n        self.__jpeg_compression_fastdct = True  # fastest DCT on by default\n        self.__jpeg_compression_fastupsample = False  # fastupsample off by default\n        self.__jpeg_compression_colorspace = \"BGR\"  # use BGR colorspace by default\n        self.__frame_size_reduction = 25  # use 25% reduction\n        # retrieve interpolation for reduction\n        self.__interpolation = retrieve_best_interpolation(\n            [\"INTER_LINEAR_EXACT\", \"INTER_LINEAR\", \"INTER_AREA\"]\n        )\n\n        custom_video_endpoint = \"\"  # custom video endpoint path\n        custom_data_location = \"\"  # path to save data-files to custom location\n        data_path = \"\"  # path to WebGear data-files\n        overwrite_default = False\n        self.__enable_inf = False  # continue frames even when video ends.\n\n        # reformat dictionary\n        options = {str(k).strip(): v for k, v in options.items()}\n\n        # assign values to global variables if specified and valid\n        if options:\n            # check whether to disable Data-Files Auto-Generation WorkFlow\n            if \"skip_generate_webdata\" in options:\n                value = options[\"skip_generate_webdata\"]\n                # enable jpeg fastdct\n                if isinstance(value, bool):\n                    self.__skip_generate_webdata = value\n                else:\n                    logger.warning(\"Skipped invalid `skip_generate_webdata` value!\")\n                del options[\"skip_generate_webdata\"]  # clean\n\n            if \"jpeg_compression_colorspace\" in options:\n                value = options[\"jpeg_compression_colorspace\"]\n                if isinstance(value, str) and value.strip().upper() in [\n                    \"RGB\",\n                    \"BGR\",\n                    \"RGBX\",\n                    \"BGRX\",\n                    \"XBGR\",\n                    \"XRGB\",\n                    \"GRAY\",\n                    \"RGBA\",\n                    \"BGRA\",\n                    \"ABGR\",\n                    \"ARGB\",\n                    \"CMYK\",\n                ]:\n                    # set encoding colorspace\n                    self.__jpeg_compression_colorspace = value.strip().upper()\n                else:\n                    logger.warning(\n                        \"Skipped invalid `jpeg_compression_colorspace` value!\"\n                    )\n                del options[\"jpeg_compression_colorspace\"]  # clean\n\n            if \"jpeg_compression_quality\" in options:\n                value = options[\"jpeg_compression_quality\"]\n                # set valid jpeg quality\n                if isinstance(value, (int, float)) and value >= 10 and value <= 100:\n                    self.__jpeg_compression_quality = int(value)\n                else:\n                    logger.warning(\"Skipped invalid `jpeg_compression_quality` value!\")\n                del options[\"jpeg_compression_quality\"]  # clean\n\n            if \"jpeg_compression_fastdct\" in options:\n                value = options[\"jpeg_compression_fastdct\"]\n                # enable jpeg fastdct\n                if isinstance(value, bool):\n                    self.__jpeg_compression_fastdct = value\n                else:\n                    logger.warning(\"Skipped invalid `jpeg_compression_fastdct` value!\")\n                del options[\"jpeg_compression_fastdct\"]  # clean\n\n            if \"jpeg_compression_fastupsample\" in options:\n                value = options[\"jpeg_compression_fastupsample\"]\n                # enable jpeg  fastupsample\n                if isinstance(value, bool):\n                    self.__jpeg_compression_fastupsample = value\n                else:\n                    logger.warning(\n                        \"Skipped invalid `jpeg_compression_fastupsample` value!\"\n                    )\n                del options[\"jpeg_compression_fastupsample\"]  # clean\n\n            if \"frame_size_reduction\" in options:\n                value = options[\"frame_size_reduction\"]\n                if isinstance(value, (int, float)) and value >= 0 and value <= 90:\n                    self.__frame_size_reduction = value\n                else:\n                    logger.warning(\"Skipped invalid `frame_size_reduction` value!\")\n                del options[\"frame_size_reduction\"]  # clean\n\n            if \"custom_video_endpoint\" in options:\n                value = options[\"custom_video_endpoint\"]\n                if value and isinstance(value, str) and value.strip().isalnum():\n                    custom_video_endpoint = value.strip()\n                    logging and logger.critical(\n                        \"Using custom video endpoint path: `/{}`\".format(\n                            custom_video_endpoint\n                        )\n                    )\n                else:\n                    logger.warning(\"Skipped invalid `custom_video_endpoint` value!\")\n                del options[\"custom_video_endpoint\"]  # clean\n\n            if \"custom_data_location\" in options:\n                value = options[\"custom_data_location\"]\n                if value and isinstance(value, str):\n                    assert os.access(\n                        value, os.W_OK\n                    ), \"[WebGear:ERROR] :: Permission Denied!, cannot write WebGear data-files to '{}' directory!\".format(\n                        value\n                    )\n                    assert os.path.isdir(\n                        os.path.abspath(value)\n                    ), \"[WebGear:ERROR] :: `custom_data_location` value must be the path to a directory and not to a file!\"\n                    custom_data_location = os.path.abspath(value)\n                else:\n                    logger.warning(\"Skipped invalid `custom_data_location` value!\")\n                del options[\"custom_data_location\"]  # clean\n\n            if \"overwrite_default_files\" in options:\n                value = options[\"overwrite_default_files\"]\n                if isinstance(value, bool):\n                    overwrite_default = value\n                else:\n                    logger.warning(\"Skipped invalid `overwrite_default_files` value!\")\n                del options[\"overwrite_default_files\"]  # clean\n\n            if \"enable_infinite_frames\" in options:\n                value = options[\"enable_infinite_frames\"]\n                if isinstance(value, bool):\n                    self.__enable_inf = value\n                else:\n                    logger.warning(\"Skipped invalid `enable_infinite_frames` value!\")\n                del options[\"enable_infinite_frames\"]  # clean\n\n        # check if disable Data-Files Auto-Generation WorkFlow is disabled\n        if not self.__skip_generate_webdata:\n            # check if custom data path is specified\n            if custom_data_location:\n                data_path = generate_webdata(\n                    custom_data_location,\n                    c_name=\"webgear\",\n                    overwrite_default=overwrite_default,\n                    logging=logging,\n                )\n            else:\n                # otherwise generate suitable path\n                data_path = generate_webdata(\n                    os.path.join(expanduser(\"~\"), \".vidgear\"),\n                    c_name=\"webgear\",\n                    overwrite_default=overwrite_default,\n                    logging=logging,\n                )\n\n            # log it\n            self.__logging and logger.debug(\n                \"`{}` is the default location for saving WebGear data-files.\".format(\n                    data_path\n                )\n            )\n            # define Jinja2 templates handler\n            self.__templates = Jinja2Templates(\n                directory=\"{}/templates\".format(data_path)\n            )\n            # define routing tables\n            self.routes = [\n                Route(\"/\", endpoint=self.__homepage),\n                Route(\n                    \"/{}\".format(\n                        custom_video_endpoint if custom_video_endpoint else \"video\"\n                    ),\n                    endpoint=self.__video,\n                ),\n                Mount(\n                    \"/static\",\n                    app=StaticFiles(directory=\"{}/static\".format(data_path)),\n                    name=\"static\",\n                ),\n            ]\n        else:\n            # log it\n            self.__logging and logger.critical(\n                \"WebGear Data-Files Auto-Generation WorkFlow has been manually disabled.\"\n            )\n            # define routing tables\n            self.routes = [\n                Route(\n                    \"/{}\".format(\n                        custom_video_endpoint if custom_video_endpoint else \"video\"\n                    ),\n                    endpoint=self.__video,\n                ),\n            ]\n            # log exceptions\n            self.__logging and logger.warning(\n                \"Only `/video` route is available for this instance.\"\n            )\n\n        # define custom exception handlers\n        self.__exception_handlers = {404: self.__not_found, 500: self.__server_error}\n        # define middleware support\n        self.middleware = []\n        # Handle video source\n        if source is None:\n            self.config = {\"generator\": None}\n            self.__stream = None\n        else:\n            # define stream with necessary params\n            self.__stream = VideoGear(\n                enablePiCamera=enablePiCamera,\n                stabilize=stabilize,\n                source=source,\n                camera_num=camera_num,\n                stream_mode=stream_mode,\n                backend=backend,\n                colorspace=colorspace,\n                resolution=resolution,\n                framerate=framerate,\n                logging=logging,\n                time_delay=time_delay,\n                **options\n            )\n            # define default frame generator in configuration\n            self.config = {\"generator\": self.__producer}\n\n        # log if specified\n        if self.__logging:\n            if source is None:\n                logger.warning(\n                    \"Given source is of NoneType. Therefore, JPEG Frame-Compression is disabled!\"\n                )\n            else:\n                logger.debug(\n                    \"Enabling JPEG Frame-Compression with Colorspace:`{}`, Quality:`{}`%, Fastdct:`{}`, and Fastupsample:`{}`.\".format(\n                        self.__jpeg_compression_colorspace,\n                        self.__jpeg_compression_quality,\n                        \"enabled\" if self.__jpeg_compression_fastdct else \"disabled\",\n                        (\n                            \"enabled\"\n                            if self.__jpeg_compression_fastupsample\n                            else \"disabled\"\n                        ),\n                    )\n                )\n\n        # copying original routing tables for further validation\n        self.__rt_org_copy = self.routes[:]\n        # initialize blank frame\n        self.blank_frame = None\n        # keeps check if producer loop should be running\n        self.__isrunning = True\n\n    def __call__(self):\n        \"\"\"\n        Implements a custom Callable method for WebGear application.\n        \"\"\"\n        # validate routing tables\n        assert not (self.routes is None), \"Routing tables are NoneType!\"\n        if not isinstance(self.routes, list) or not all(\n            x in self.routes for x in self.__rt_org_copy\n        ):\n            raise RuntimeError(\"[WebGear:ERROR] :: Routing tables are not valid!\")\n\n        # validate middlewares\n        assert not (self.middleware is None), \"Middlewares are NoneType!\"\n        if self.middleware and (\n            not isinstance(self.middleware, list)\n            or not all(isinstance(x, Middleware) for x in self.middleware)\n        ):\n            raise RuntimeError(\"[WebGear:ERROR] :: Middlewares are not valid!\")\n\n        # validate assigned frame generator in WebGear configuration\n        if isinstance(self.config, dict) and \"generator\" in self.config:\n            # check if its  assigned value is a asynchronous generator\n            if self.config[\"generator\"] is None or not inspect.isasyncgen(\n                self.config[\"generator\"]()\n            ):\n                # otherwise raise error\n                raise ValueError(\n                    \"[WebGear:ERROR] :: Invalid configuration. Assigned generator must be a asynchronous generator function/method only!\"\n                )\n        else:\n            # raise error if validation fails\n            raise RuntimeError(\"[WebGear:ERROR] :: Assigned configuration is invalid!\")\n\n        # initiate stream\n        self.__logging and logger.debug(\"Initiating Video Streaming.\")\n        if not (self.__stream is None):\n            self.__stream.start()\n        # return Starlette application\n        self.__logging and logger.debug(\"Running Starlette application.\")\n        return Starlette(\n            debug=(True if self.__logging else False),\n            routes=self.routes,\n            middleware=self.middleware,\n            exception_handlers=self.__exception_handlers,\n            lifespan=self.__lifespan,\n        )\n\n    async def __producer(self):\n        \"\"\"\n        WebGear's default asynchronous frame producer/generator.\n        \"\"\"\n        # loop over frames\n        while self.__isrunning:\n            # read frame\n            frame = self.__stream.read()\n\n            # display blank if NoneType\n            if frame is None:\n                frame = (\n                    self.blank_frame\n                    if self.blank_frame is None\n                    else self.blank_frame[:]\n                )\n                if not self.__enable_inf:\n                    self.__isrunning = False\n            else:\n                # create blank\n                if self.blank_frame is None:\n                    self.blank_frame = create_blank_frame(\n                        frame=frame,\n                        text=\"No Input\" if self.__enable_inf else \"The End\",\n                        logging=self.__logging,\n                    )\n\n            # reducer frames size if specified\n            if self.__frame_size_reduction:\n                frame = await reducer(\n                    frame,\n                    percentage=self.__frame_size_reduction,\n                    interpolation=self.__interpolation,\n                )\n\n            # handle JPEG encoding\n            if self.__jpeg_compression_colorspace == \"GRAY\":\n                if frame.ndim == 2:\n                    # patch for https://gitlab.com/jfolz/simplejpeg/-/issues/11\n                    frame = np.expand_dims(frame, axis=2)\n                encodedImage = simplejpeg.encode_jpeg(\n                    frame,\n                    quality=self.__jpeg_compression_quality,\n                    colorspace=self.__jpeg_compression_colorspace,\n                    fastdct=self.__jpeg_compression_fastdct,\n                )\n            else:\n                encodedImage = simplejpeg.encode_jpeg(\n                    frame,\n                    quality=self.__jpeg_compression_quality,\n                    colorspace=self.__jpeg_compression_colorspace,\n                    colorsubsampling=\"422\",\n                    fastdct=self.__jpeg_compression_fastdct,\n                )\n\n            # yield frame in byte format\n            yield (\n                b\"--frame\\r\\nContent-Type:image/jpeg\\r\\n\\r\\n\" + encodedImage + b\"\\r\\n\"\n            )\n            # sleep for sometime.\n            await asyncio.sleep(0)\n\n    async def __video(self, scope):\n        \"\"\"\n        Returns a async video streaming response.\n        \"\"\"\n        assert scope[\"type\"] in [\"http\", \"https\"]\n        return StreamingResponse(\n            self.config[\"generator\"](),\n            media_type=\"multipart/x-mixed-replace; boundary=frame\",\n        )\n\n    async def __homepage(self, request):\n        \"\"\"\n        Returns an HTML index page.\n        \"\"\"\n        return (\n            self.__templates.TemplateResponse(request, \"index.html\")\n            if not self.__skip_generate_webdata\n            else JSONResponse(\n                {\n                    \"detail\": \"MESSAGE : WebGear Data-Files Auto-Generation WorkFlow is disabled!\"\n                },\n                status_code=404,\n            )\n        )\n\n    async def __not_found(self, request, exc):\n        \"\"\"\n        Returns an HTML 404 page.\n        \"\"\"\n        return (\n            self.__templates.TemplateResponse(request, \"404.html\", status_code=404)\n            if not self.__skip_generate_webdata\n            else JSONResponse(\n                {\n                    \"detail\": \"ERROR : {} :: MESSAGE : WebGear Data-Files Auto-Generation WorkFlow is disabled.\".format(\n                        exc.detail\n                    )\n                },\n                status_code=404,\n            )\n        )\n\n    async def __server_error(self, request, exc):\n        \"\"\"\n        Returns an HTML 500 page.\n        \"\"\"\n        return (\n            self.__templates.TemplateResponse(request, \"500.html\", status_code=500)\n            if not self.__skip_generate_webdata\n            else JSONResponse(\n                {\n                    \"detail\": \"ERROR : {} :: MESSAGE : WebGear Data-Files Auto-Generation WorkFlow is disabled.\".format(\n                        exc.detail if hasattr(exc, \"detail\") else repr(exc)\n                    )\n                },\n                status_code=500,\n            )\n        )\n\n    @contextlib.asynccontextmanager\n    async def __lifespan(self, context):\n        try:\n            yield\n        finally:\n            # close Video Server\n            self.shutdown()\n\n    def shutdown(self):\n        \"\"\"\n        Implements a Callable to be run on application shutdown\n        \"\"\"\n        if not (self.__stream is None):\n            self.__logging and logger.debug(\"Closing Video Streaming.\")\n            # stops producer\n            self.__isrunning = False\n            # stops VideoGear stream\n            self.__stream.stop()\n            # prevent any re-iteration\n            self.__stream = None\n

"},{"location":"bonus/reference/webgear/#vidgear.gears.asyncio.webgear.WebGear.__call__","title":"__call__(self) special","text":"

Implements a custom Callable method for WebGear application.

Source code in vidgear/gears/asyncio/webgear.py
def __call__(self):\n    \"\"\"\n    Implements a custom Callable method for WebGear application.\n    \"\"\"\n    # validate routing tables\n    assert not (self.routes is None), \"Routing tables are NoneType!\"\n    if not isinstance(self.routes, list) or not all(\n        x in self.routes for x in self.__rt_org_copy\n    ):\n        raise RuntimeError(\"[WebGear:ERROR] :: Routing tables are not valid!\")\n\n    # validate middlewares\n    assert not (self.middleware is None), \"Middlewares are NoneType!\"\n    if self.middleware and (\n        not isinstance(self.middleware, list)\n        or not all(isinstance(x, Middleware) for x in self.middleware)\n    ):\n        raise RuntimeError(\"[WebGear:ERROR] :: Middlewares are not valid!\")\n\n    # validate assigned frame generator in WebGear configuration\n    if isinstance(self.config, dict) and \"generator\" in self.config:\n        # check if its  assigned value is a asynchronous generator\n        if self.config[\"generator\"] is None or not inspect.isasyncgen(\n            self.config[\"generator\"]()\n        ):\n            # otherwise raise error\n            raise ValueError(\n                \"[WebGear:ERROR] :: Invalid configuration. Assigned generator must be a asynchronous generator function/method only!\"\n            )\n    else:\n        # raise error if validation fails\n        raise RuntimeError(\"[WebGear:ERROR] :: Assigned configuration is invalid!\")\n\n    # initiate stream\n    self.__logging and logger.debug(\"Initiating Video Streaming.\")\n    if not (self.__stream is None):\n        self.__stream.start()\n    # return Starlette application\n    self.__logging and logger.debug(\"Running Starlette application.\")\n    return Starlette(\n        debug=(True if self.__logging else False),\n        routes=self.routes,\n        middleware=self.middleware,\n        exception_handlers=self.__exception_handlers,\n        lifespan=self.__lifespan,\n    )\n
"},{"location":"bonus/reference/webgear/#vidgear.gears.asyncio.webgear.WebGear.__init__","title":"__init__(self, enablePiCamera=False, stabilize=False, source=None, camera_num=0, stream_mode=False, backend=0, colorspace=None, resolution=(640, 480), framerate=25, logging=False, time_delay=0, **options) special","text":"

This constructor method initializes the object state and attributes of the WebGear class.

Parameters:

Name Type Description Default enablePiCamera bool

provide access to PiGear(if True) or CamGear(if False) APIs respectively.

False stabilize bool

enable access to Stabilizer Class for stabilizing frames.

False camera_num int

selects the camera module index which will be used as Rpi source.

0 resolution tuple

sets the resolution (i.e. (width,height)) of the Rpi source.

(640, 480) framerate int/float

sets the framerate of the Rpi source.

25 source based on input

defines the source for the input stream.

None stream_mode bool

controls the exclusive YouTube Mode.

False backend int

selects the backend for OpenCV's VideoCapture class.

0 colorspace str

selects the colorspace of the input stream.

None logging bool

enables/disables logging.

False time_delay int

time delay (in sec) before start reading the frames.

0 options dict

provides ability to alter Tweak Parameters of WebGear, CamGear, PiGear & Stabilizer.

{} Source code in vidgear/gears/asyncio/webgear.py
def __init__(\n    self,\n    enablePiCamera=False,\n    stabilize=False,\n    source=None,\n    camera_num=0,\n    stream_mode=False,\n    backend=0,\n    colorspace=None,\n    resolution=(640, 480),\n    framerate=25,\n    logging=False,\n    time_delay=0,\n    **options\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the WebGear class.\n\n    Parameters:\n        enablePiCamera (bool): provide access to PiGear(if True) or CamGear(if False) APIs respectively.\n        stabilize (bool): enable access to Stabilizer Class for stabilizing frames.\n        camera_num (int): selects the camera module index which will be used as Rpi source.\n        resolution (tuple): sets the resolution (i.e. `(width,height)`) of the Rpi source.\n        framerate (int/float): sets the framerate of the Rpi source.\n        source (based on input): defines the source for the input stream.\n        stream_mode (bool): controls the exclusive YouTube Mode.\n        backend (int): selects the backend for OpenCV's VideoCapture class.\n        colorspace (str): selects the colorspace of the input stream.\n        logging (bool): enables/disables logging.\n        time_delay (int): time delay (in sec) before start reading the frames.\n        options (dict): provides ability to alter Tweak Parameters of WebGear, CamGear, PiGear & Stabilizer.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # raise error(s) for critical Class imports\n    import_dependency_safe(\"starlette\" if starlette is None else \"\")\n    import_dependency_safe(\n        \"simplejpeg\" if simplejpeg is None else \"\", min_version=\"1.6.1\"\n    )\n\n    # initialize global params\n    self.__skip_generate_webdata = False  # generate webgear data by default\n    # define frame-compression handler\n    self.__jpeg_compression_quality = 90  # 90% quality\n    self.__jpeg_compression_fastdct = True  # fastest DCT on by default\n    self.__jpeg_compression_fastupsample = False  # fastupsample off by default\n    self.__jpeg_compression_colorspace = \"BGR\"  # use BGR colorspace by default\n    self.__frame_size_reduction = 25  # use 25% reduction\n    # retrieve interpolation for reduction\n    self.__interpolation = retrieve_best_interpolation(\n        [\"INTER_LINEAR_EXACT\", \"INTER_LINEAR\", \"INTER_AREA\"]\n    )\n\n    custom_video_endpoint = \"\"  # custom video endpoint path\n    custom_data_location = \"\"  # path to save data-files to custom location\n    data_path = \"\"  # path to WebGear data-files\n    overwrite_default = False\n    self.__enable_inf = False  # continue frames even when video ends.\n\n    # reformat dictionary\n    options = {str(k).strip(): v for k, v in options.items()}\n\n    # assign values to global variables if specified and valid\n    if options:\n        # check whether to disable Data-Files Auto-Generation WorkFlow\n        if \"skip_generate_webdata\" in options:\n            value = options[\"skip_generate_webdata\"]\n            # enable jpeg fastdct\n            if isinstance(value, bool):\n                self.__skip_generate_webdata = value\n            else:\n                logger.warning(\"Skipped invalid `skip_generate_webdata` value!\")\n            del options[\"skip_generate_webdata\"]  # clean\n\n        if \"jpeg_compression_colorspace\" in options:\n            value = options[\"jpeg_compression_colorspace\"]\n            if isinstance(value, str) and value.strip().upper() in [\n                \"RGB\",\n                \"BGR\",\n                \"RGBX\",\n                \"BGRX\",\n                \"XBGR\",\n                \"XRGB\",\n                \"GRAY\",\n                \"RGBA\",\n                \"BGRA\",\n                \"ABGR\",\n                \"ARGB\",\n                \"CMYK\",\n            ]:\n                # set encoding colorspace\n                self.__jpeg_compression_colorspace = value.strip().upper()\n            else:\n                logger.warning(\n                    \"Skipped invalid `jpeg_compression_colorspace` value!\"\n                )\n            del options[\"jpeg_compression_colorspace\"]  # clean\n\n        if \"jpeg_compression_quality\" in options:\n            value = options[\"jpeg_compression_quality\"]\n            # set valid jpeg quality\n            if isinstance(value, (int, float)) and value >= 10 and value <= 100:\n                self.__jpeg_compression_quality = int(value)\n            else:\n                logger.warning(\"Skipped invalid `jpeg_compression_quality` value!\")\n            del options[\"jpeg_compression_quality\"]  # clean\n\n        if \"jpeg_compression_fastdct\" in options:\n            value = options[\"jpeg_compression_fastdct\"]\n            # enable jpeg fastdct\n            if isinstance(value, bool):\n                self.__jpeg_compression_fastdct = value\n            else:\n                logger.warning(\"Skipped invalid `jpeg_compression_fastdct` value!\")\n            del options[\"jpeg_compression_fastdct\"]  # clean\n\n        if \"jpeg_compression_fastupsample\" in options:\n            value = options[\"jpeg_compression_fastupsample\"]\n            # enable jpeg  fastupsample\n            if isinstance(value, bool):\n                self.__jpeg_compression_fastupsample = value\n            else:\n                logger.warning(\n                    \"Skipped invalid `jpeg_compression_fastupsample` value!\"\n                )\n            del options[\"jpeg_compression_fastupsample\"]  # clean\n\n        if \"frame_size_reduction\" in options:\n            value = options[\"frame_size_reduction\"]\n            if isinstance(value, (int, float)) and value >= 0 and value <= 90:\n                self.__frame_size_reduction = value\n            else:\n                logger.warning(\"Skipped invalid `frame_size_reduction` value!\")\n            del options[\"frame_size_reduction\"]  # clean\n\n        if \"custom_video_endpoint\" in options:\n            value = options[\"custom_video_endpoint\"]\n            if value and isinstance(value, str) and value.strip().isalnum():\n                custom_video_endpoint = value.strip()\n                logging and logger.critical(\n                    \"Using custom video endpoint path: `/{}`\".format(\n                        custom_video_endpoint\n                    )\n                )\n            else:\n                logger.warning(\"Skipped invalid `custom_video_endpoint` value!\")\n            del options[\"custom_video_endpoint\"]  # clean\n\n        if \"custom_data_location\" in options:\n            value = options[\"custom_data_location\"]\n            if value and isinstance(value, str):\n                assert os.access(\n                    value, os.W_OK\n                ), \"[WebGear:ERROR] :: Permission Denied!, cannot write WebGear data-files to '{}' directory!\".format(\n                    value\n                )\n                assert os.path.isdir(\n                    os.path.abspath(value)\n                ), \"[WebGear:ERROR] :: `custom_data_location` value must be the path to a directory and not to a file!\"\n                custom_data_location = os.path.abspath(value)\n            else:\n                logger.warning(\"Skipped invalid `custom_data_location` value!\")\n            del options[\"custom_data_location\"]  # clean\n\n        if \"overwrite_default_files\" in options:\n            value = options[\"overwrite_default_files\"]\n            if isinstance(value, bool):\n                overwrite_default = value\n            else:\n                logger.warning(\"Skipped invalid `overwrite_default_files` value!\")\n            del options[\"overwrite_default_files\"]  # clean\n\n        if \"enable_infinite_frames\" in options:\n            value = options[\"enable_infinite_frames\"]\n            if isinstance(value, bool):\n                self.__enable_inf = value\n            else:\n                logger.warning(\"Skipped invalid `enable_infinite_frames` value!\")\n            del options[\"enable_infinite_frames\"]  # clean\n\n    # check if disable Data-Files Auto-Generation WorkFlow is disabled\n    if not self.__skip_generate_webdata:\n        # check if custom data path is specified\n        if custom_data_location:\n            data_path = generate_webdata(\n                custom_data_location,\n                c_name=\"webgear\",\n                overwrite_default=overwrite_default,\n                logging=logging,\n            )\n        else:\n            # otherwise generate suitable path\n            data_path = generate_webdata(\n                os.path.join(expanduser(\"~\"), \".vidgear\"),\n                c_name=\"webgear\",\n                overwrite_default=overwrite_default,\n                logging=logging,\n            )\n\n        # log it\n        self.__logging and logger.debug(\n            \"`{}` is the default location for saving WebGear data-files.\".format(\n                data_path\n            )\n        )\n        # define Jinja2 templates handler\n        self.__templates = Jinja2Templates(\n            directory=\"{}/templates\".format(data_path)\n        )\n        # define routing tables\n        self.routes = [\n            Route(\"/\", endpoint=self.__homepage),\n            Route(\n                \"/{}\".format(\n                    custom_video_endpoint if custom_video_endpoint else \"video\"\n                ),\n                endpoint=self.__video,\n            ),\n            Mount(\n                \"/static\",\n                app=StaticFiles(directory=\"{}/static\".format(data_path)),\n                name=\"static\",\n            ),\n        ]\n    else:\n        # log it\n        self.__logging and logger.critical(\n            \"WebGear Data-Files Auto-Generation WorkFlow has been manually disabled.\"\n        )\n        # define routing tables\n        self.routes = [\n            Route(\n                \"/{}\".format(\n                    custom_video_endpoint if custom_video_endpoint else \"video\"\n                ),\n                endpoint=self.__video,\n            ),\n        ]\n        # log exceptions\n        self.__logging and logger.warning(\n            \"Only `/video` route is available for this instance.\"\n        )\n\n    # define custom exception handlers\n    self.__exception_handlers = {404: self.__not_found, 500: self.__server_error}\n    # define middleware support\n    self.middleware = []\n    # Handle video source\n    if source is None:\n        self.config = {\"generator\": None}\n        self.__stream = None\n    else:\n        # define stream with necessary params\n        self.__stream = VideoGear(\n            enablePiCamera=enablePiCamera,\n            stabilize=stabilize,\n            source=source,\n            camera_num=camera_num,\n            stream_mode=stream_mode,\n            backend=backend,\n            colorspace=colorspace,\n            resolution=resolution,\n            framerate=framerate,\n            logging=logging,\n            time_delay=time_delay,\n            **options\n        )\n        # define default frame generator in configuration\n        self.config = {\"generator\": self.__producer}\n\n    # log if specified\n    if self.__logging:\n        if source is None:\n            logger.warning(\n                \"Given source is of NoneType. Therefore, JPEG Frame-Compression is disabled!\"\n            )\n        else:\n            logger.debug(\n                \"Enabling JPEG Frame-Compression with Colorspace:`{}`, Quality:`{}`%, Fastdct:`{}`, and Fastupsample:`{}`.\".format(\n                    self.__jpeg_compression_colorspace,\n                    self.__jpeg_compression_quality,\n                    \"enabled\" if self.__jpeg_compression_fastdct else \"disabled\",\n                    (\n                        \"enabled\"\n                        if self.__jpeg_compression_fastupsample\n                        else \"disabled\"\n                    ),\n                )\n            )\n\n    # copying original routing tables for further validation\n    self.__rt_org_copy = self.routes[:]\n    # initialize blank frame\n    self.blank_frame = None\n    # keeps check if producer loop should be running\n    self.__isrunning = True\n
"},{"location":"bonus/reference/webgear/#vidgear.gears.asyncio.webgear.WebGear.shutdown","title":"shutdown(self)","text":"

Implements a Callable to be run on application shutdown

Source code in vidgear/gears/asyncio/webgear.py
def shutdown(self):\n    \"\"\"\n    Implements a Callable to be run on application shutdown\n    \"\"\"\n    if not (self.__stream is None):\n        self.__logging and logger.debug(\"Closing Video Streaming.\")\n        # stops producer\n        self.__isrunning = False\n        # stops VideoGear stream\n        self.__stream.stop()\n        # prevent any re-iteration\n        self.__stream = None\n
"},{"location":"bonus/reference/webgear_rtc/","title":"WebGear_RTC API References","text":"

WebGear_RTC API usage examples can be found here \u27b6

WebGear_RTC API parameters are explained here \u27b6

WebGear_RTC is similar to WeGear API in many aspects but utilizes WebRTC technology under the hood instead of Motion JPEG, which makes it suitable for building powerful video-streaming solutions for all modern browsers as well as native clients available on all major platforms.

WebGear_RTC is implemented with the help of aiortc library which is built on top of asynchronous I/O framework for Web Real-Time Communication (WebRTC) and Object Real-Time Communication (ORTC) and supports many features like SDP generation/parsing, Interactive Connectivity Establishment with half-trickle and mDNS support, DTLS key and certificate generation, DTLS handshake, etc.

WebGear_RTC can handle multiple consumers seamlessly and provides native support for ICE (Interactive Connectivity Establishment) protocol, STUN (Session Traversal Utilities for NAT), and TURN (Traversal Using Relays around NAT) servers that help us to easily establish direct media connection with the remote peers for uninterrupted data flow. It also allows us to define our custom Server as a source to transform frames easily before sending them across the network(see this doc example).

WebGear_RTC API works in conjunction with Starlette ASGI application and can also flexibly interact with Starlette's ecosystem of shared middleware, mountable applications, Response classes, Routing tables, Static Files, Templating engine(with Jinja2), etc.

Additionally, WebGear_RTC API also provides internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs.

Source code in vidgear/gears/asyncio/webgear_rtc.py
class WebGear_RTC:\n    \"\"\"\n    WebGear_RTC is similar to WeGear API in many aspects but utilizes WebRTC technology under the hood instead of Motion JPEG, which\n    makes it suitable for building powerful video-streaming solutions for all modern browsers as well as native clients available on\n    all major platforms.\n\n    WebGear_RTC is implemented with the help of aiortc library which is built on top of asynchronous I/O framework for Web Real-Time\n    Communication (WebRTC) and Object Real-Time Communication (ORTC) and supports many features like SDP generation/parsing, Interactive\n    Connectivity Establishment with half-trickle and mDNS support, DTLS key and certificate generation, DTLS handshake, etc.\n\n    WebGear_RTC can handle multiple consumers seamlessly and provides native support for ICE (Interactive Connectivity Establishment)\n    protocol, STUN (Session Traversal Utilities for NAT), and TURN (Traversal Using Relays around NAT) servers that help us to easily\n    establish direct media connection with the remote peers for uninterrupted data flow. It also allows us to define our custom Server\n    as a source to transform frames easily before sending them across the network(see this doc example).\n\n    WebGear_RTC API works in conjunction with Starlette ASGI application and can also flexibly interact with Starlette's ecosystem of\n    shared middleware, mountable applications, Response classes, Routing tables, Static Files, Templating engine(with Jinja2), etc.\n\n    Additionally, WebGear_RTC API also provides internal wrapper around VideoGear, which itself provides internal access to both\n    CamGear and PiGear APIs.\n    \"\"\"\n\n    def __init__(\n        self,\n        enablePiCamera=False,\n        stabilize=False,\n        source=None,\n        camera_num=0,\n        stream_mode=False,\n        backend=0,\n        colorspace=None,\n        resolution=(640, 480),\n        framerate=25,\n        logging=False,\n        time_delay=0,\n        **options\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the WebGear_RTC class.\n\n        Parameters:\n            enablePiCamera (bool): provide access to PiGear(if True) or CamGear(if False) APIs respectively.\n            stabilize (bool): enable access to Stabilizer Class for stabilizing frames.\n            camera_num (int): selects the camera module index which will be used as Rpi source.\n            resolution (tuple): sets the resolution (i.e. `(width,height)`) of the Rpi source.\n            framerate (int/float): sets the framerate of the Rpi source.\n            source (based on input): defines the source for the input stream.\n            stream_mode (bool): controls the exclusive YouTube Mode.\n            backend (int): selects the backend for OpenCV's VideoCapture class.\n            colorspace (str): selects the colorspace of the input stream.\n            logging (bool): enables/disables logging.\n            time_delay (int): time delay (in sec) before start reading the frames.\n            options (dict): provides ability to alter Tweak Parameters of WebGear_RTC, CamGear, PiGear & Stabilizer.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # raise error(s) for critical Class imports\n        import_dependency_safe(\"starlette\" if starlette is None else \"\")\n        import_dependency_safe(\"aiortc\" if aiortc is None else \"\")\n\n        # initialize global params\n        custom_data_location = \"\"  # path to save data-files to custom location\n        data_path = \"\"  # path to WebGear_RTC data-files\n        overwrite_default = False\n        self.__relay = None  # act as broadcaster\n\n        # reformat dictionary\n        options = {str(k).strip(): v for k, v in options.items()}\n\n        # assign values to global variables if specified and valid\n        if options:\n            if \"custom_data_location\" in options:\n                value = options[\"custom_data_location\"]\n                if isinstance(value, str):\n                    assert os.access(\n                        value, os.W_OK\n                    ), \"[WebGear_RTC:ERROR] :: Permission Denied!, cannot write WebGear_RTC data-files to '{}' directory!\".format(\n                        value\n                    )\n                    assert os.path.isdir(\n                        os.path.abspath(value)\n                    ), \"[WebGear_RTC:ERROR] :: `custom_data_location` value must be the path to a directory and not to a file!\"\n                    custom_data_location = os.path.abspath(value)\n                else:\n                    logger.warning(\"Skipped invalid `custom_data_location` value!\")\n                del options[\"custom_data_location\"]  # clean\n\n            if \"overwrite_default_files\" in options:\n                value = options[\"overwrite_default_files\"]\n                if isinstance(value, bool):\n                    overwrite_default = value\n                else:\n                    logger.warning(\"Skipped invalid `overwrite_default_files` value!\")\n                del options[\"overwrite_default_files\"]  # clean\n\n            if \"enable_live_broadcast\" in options:\n                value = options[\"enable_live_broadcast\"]\n                if isinstance(value, bool):\n                    if value:\n                        self.__relay = MediaRelay()\n                        options[\"enable_infinite_frames\"] = (\n                            True  # enforce infinite frames\n                        )\n                        logger.critical(\n                            \"Enabled live broadcasting for Peer connection(s).\"\n                        )\n                    else:\n                        None\n                else:\n                    logger.warning(\"Skipped invalid `enable_live_broadcast` value!\")\n                del options[\"enable_live_broadcast\"]  # clean\n\n        # check if custom certificates path is specified\n        if custom_data_location:\n            data_path = generate_webdata(\n                custom_data_location,\n                c_name=\"webgear_rtc\",\n                overwrite_default=overwrite_default,\n                logging=logging,\n            )\n        else:\n            # otherwise generate suitable path\n            data_path = generate_webdata(\n                os.path.join(expanduser(\"~\"), \".vidgear\"),\n                c_name=\"webgear_rtc\",\n                overwrite_default=overwrite_default,\n                logging=logging,\n            )\n\n        # log it\n        self.__logging and logger.debug(\n            \"`{}` is the default location for saving WebGear_RTC data-files.\".format(\n                data_path\n            )\n        )\n\n        # define Jinja2 templates handler\n        self.__templates = Jinja2Templates(directory=\"{}/templates\".format(data_path))\n\n        # define custom exception handlers\n        self.__exception_handlers = {404: self.__not_found, 500: self.__server_error}\n        # define routing tables\n        self.routes = [\n            Route(\"/\", endpoint=self.__homepage),\n            Route(\"/offer\", self.__offer, methods=[\"GET\", \"POST\"]),\n            Mount(\n                \"/static\",\n                app=StaticFiles(directory=\"{}/static\".format(data_path)),\n                name=\"static\",\n            ),\n        ]\n\n        # define middleware support\n        self.middleware = []\n\n        # Handle RTC video server\n        if \"custom_stream\" in options or not (source is None):\n            # Handle video source\n            self.__default_rtc_server = RTC_VideoServer(\n                enablePiCamera=enablePiCamera,\n                stabilize=stabilize,\n                source=source,\n                camera_num=camera_num,\n                stream_mode=stream_mode,\n                backend=backend,\n                colorspace=colorspace,\n                resolution=resolution,\n                framerate=framerate,\n                logging=logging,\n                time_delay=time_delay,\n                **options\n            )\n            # add exclusive reset connection node\n            self.routes.append(\n                Route(\"/close_connection\", self.__reset_connections, methods=[\"POST\"])\n            )\n        else:\n            raise ValueError(\n                \"[WebGear_RTC:ERROR] :: Source cannot be NoneType without Custom Stream(`custom_stream`) defined!\"\n            )\n\n        # copying original routing tables for further validation\n        self.__rt_org_copy = self.routes[:]\n        # collects peer RTC connections\n        self.__pcs = set()\n\n    def __call__(self):\n        \"\"\"\n        Implements a custom Callable method for WebGear_RTC application.\n        \"\"\"\n        # validate routing tables\n        assert not (self.routes is None), \"Routing tables are NoneType!\"\n        if not isinstance(self.routes, list) or not all(\n            x in self.routes for x in self.__rt_org_copy\n        ):\n            raise RuntimeError(\"[WebGear_RTC:ERROR] :: Routing tables are not valid!\")\n\n        # validate middlewares\n        assert not (self.middleware is None), \"Middlewares are NoneType!\"\n        if self.middleware and (\n            not isinstance(self.middleware, list)\n            or not all(isinstance(x, Middleware) for x in self.middleware)\n        ):\n            raise RuntimeError(\"[WebGear_RTC:ERROR] :: Middlewares are not valid!\")\n\n        # return Starlette application\n        self.__logging and logger.debug(\"Running Starlette application.\")\n        return Starlette(\n            debug=(True if self.__logging else False),\n            routes=self.routes,\n            middleware=self.middleware,\n            exception_handlers=self.__exception_handlers,\n            lifespan=self.__lifespan,\n        )\n\n    async def __offer(self, request):\n        \"\"\"\n        Generates JSON Response with a WebRTC Peer Connection of Video Server.\n        \"\"\"\n        # get offer from params\n        params = await request.json()\n        offer = RTCSessionDescription(sdp=params[\"sdp\"], type=params[\"type\"])\n\n        # initiate stream\n        if not (self.__default_rtc_server is None) and not (\n            self.__default_rtc_server.is_launched\n        ):\n            self.__logging and logger.debug(\"Initiating Video Streaming.\")\n            self.__default_rtc_server.launch()\n\n        # setup RTC peer connection - interface represents a WebRTC connection\n        # between the local computer and a remote peer.\n        pc = RTCPeerConnection()\n        self.__pcs.add(pc)\n        self.__logging and logger.info(\"Created WebRTC Peer Connection.\")\n\n        # track ICE connection state changes\n        @pc.on(\"iceconnectionstatechange\")\n        async def on_iceconnectionstatechange():\n            if pc.iceConnectionState == \"failed\":\n                logger.error(\"ICE connection state failed.\")\n                # check if Live Broadcasting is enabled\n                if self.__relay is None:\n                    # if not, close connection.\n                    await pc.close()\n                    self.__pcs.discard(pc)\n            else:\n                logger.debug(\"ICE connection state is %s\" % pc.iceConnectionState)\n\n        # Change the remote description associated with the connection.\n        await pc.setRemoteDescription(offer)\n        # retrieve list of RTCRtpTransceiver objects that are currently attached to the connection\n        for t in pc.getTransceivers():\n            # Increments performance significantly, IDK why this works as H265 codec is not even supported :D\n            capabilities = RTCRtpSender.getCapabilities(\"video\")\n            preferences = list(filter(lambda x: x.name == \"H265\", capabilities.codecs))\n            t.setCodecPreferences(preferences)\n            # add video server to peer track\n            if t.kind == \"video\":\n                pc.addTrack(\n                    self.__relay.subscribe(self.__default_rtc_server)\n                    if not (self.__relay is None)\n                    else self.__default_rtc_server\n                )\n\n        # Create an SDP answer to an offer received from a remote peer\n        answer = await pc.createAnswer()\n\n        # Change the local description for the answer\n        await pc.setLocalDescription(answer)\n\n        # return Starlette json response\n        return JSONResponse(\n            {\"sdp\": pc.localDescription.sdp, \"type\": pc.localDescription.type}\n        )\n\n    async def __homepage(self, request):\n        \"\"\"\n        Return an HTML index page.\n        \"\"\"\n        return self.__templates.TemplateResponse(request, \"index.html\")\n\n    async def __not_found(self, request, exc):\n        \"\"\"\n        Return an HTML 404 page.\n        \"\"\"\n        return self.__templates.TemplateResponse(request, \"404.html\", status_code=404)\n\n    async def __server_error(self, request, exc):\n        \"\"\"\n        Return an HTML 500 page.\n        \"\"\"\n        return self.__templates.TemplateResponse(request, \"500.html\", status_code=500)\n\n    async def __reset_connections(self, request):\n        \"\"\"\n        Resets all connections and recreates VideoServer timestamps\n        \"\"\"\n        # get additional parameter\n        parameter = await request.json()\n        # check if Live Broadcasting is enabled\n        if (\n            self.__relay is None\n            and not (self.__default_rtc_server is None)\n            and (self.__default_rtc_server.is_running)\n        ):\n            logger.critical(\"Resetting Server\")\n            # close old peer connections\n            if parameter != 0:  # disable if specified explicitly\n                coros = [\n                    pc.close() for pc in self.__pcs if pc.iceConnectionState != \"closed\"\n                ]\n                await asyncio.gather(*coros)\n                self.__pcs.clear()\n            await self.__default_rtc_server.reset()\n            return PlainTextResponse(\"OK\")\n        else:\n            # if does, then do nothing\n            return PlainTextResponse(\"DISABLED\")\n\n    @contextlib.asynccontextmanager\n    async def __lifespan(self, context):\n        try:\n            yield\n        finally:\n            # close Video Server\n            self.shutdown()\n            # collects peer RTC connections\n            coros = [\n                pc.close() for pc in self.__pcs if pc.iceConnectionState != \"closed\"\n            ]\n            await asyncio.gather(*coros)\n            self.__pcs.clear()\n\n    def shutdown(self):\n        \"\"\"\n        Gracefully shutdown video-server\n        \"\"\"\n        if not (self.__default_rtc_server is None):\n            self.__logging and logger.debug(\"Closing Video Server.\")\n            self.__default_rtc_server.terminate()\n            self.__default_rtc_server = None\n        # terminate internal server aswell.\n        self.__default_rtc_server = None\n

"},{"location":"bonus/reference/webgear_rtc/#vidgear.gears.asyncio.webgear_rtc.WebGear_RTC.__call__","title":"__call__(self) special","text":"

Implements a custom Callable method for WebGear_RTC application.

Source code in vidgear/gears/asyncio/webgear_rtc.py
def __call__(self):\n    \"\"\"\n    Implements a custom Callable method for WebGear_RTC application.\n    \"\"\"\n    # validate routing tables\n    assert not (self.routes is None), \"Routing tables are NoneType!\"\n    if not isinstance(self.routes, list) or not all(\n        x in self.routes for x in self.__rt_org_copy\n    ):\n        raise RuntimeError(\"[WebGear_RTC:ERROR] :: Routing tables are not valid!\")\n\n    # validate middlewares\n    assert not (self.middleware is None), \"Middlewares are NoneType!\"\n    if self.middleware and (\n        not isinstance(self.middleware, list)\n        or not all(isinstance(x, Middleware) for x in self.middleware)\n    ):\n        raise RuntimeError(\"[WebGear_RTC:ERROR] :: Middlewares are not valid!\")\n\n    # return Starlette application\n    self.__logging and logger.debug(\"Running Starlette application.\")\n    return Starlette(\n        debug=(True if self.__logging else False),\n        routes=self.routes,\n        middleware=self.middleware,\n        exception_handlers=self.__exception_handlers,\n        lifespan=self.__lifespan,\n    )\n
"},{"location":"bonus/reference/webgear_rtc/#vidgear.gears.asyncio.webgear_rtc.WebGear_RTC.__init__","title":"__init__(self, enablePiCamera=False, stabilize=False, source=None, camera_num=0, stream_mode=False, backend=0, colorspace=None, resolution=(640, 480), framerate=25, logging=False, time_delay=0, **options) special","text":"

This constructor method initializes the object state and attributes of the WebGear_RTC class.

Parameters:

Name Type Description Default enablePiCamera bool

provide access to PiGear(if True) or CamGear(if False) APIs respectively.

False stabilize bool

enable access to Stabilizer Class for stabilizing frames.

False camera_num int

selects the camera module index which will be used as Rpi source.

0 resolution tuple

sets the resolution (i.e. (width,height)) of the Rpi source.

(640, 480) framerate int/float

sets the framerate of the Rpi source.

25 source based on input

defines the source for the input stream.

None stream_mode bool

controls the exclusive YouTube Mode.

False backend int

selects the backend for OpenCV's VideoCapture class.

0 colorspace str

selects the colorspace of the input stream.

None logging bool

enables/disables logging.

False time_delay int

time delay (in sec) before start reading the frames.

0 options dict

provides ability to alter Tweak Parameters of WebGear_RTC, CamGear, PiGear & Stabilizer.

{} Source code in vidgear/gears/asyncio/webgear_rtc.py
def __init__(\n    self,\n    enablePiCamera=False,\n    stabilize=False,\n    source=None,\n    camera_num=0,\n    stream_mode=False,\n    backend=0,\n    colorspace=None,\n    resolution=(640, 480),\n    framerate=25,\n    logging=False,\n    time_delay=0,\n    **options\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the WebGear_RTC class.\n\n    Parameters:\n        enablePiCamera (bool): provide access to PiGear(if True) or CamGear(if False) APIs respectively.\n        stabilize (bool): enable access to Stabilizer Class for stabilizing frames.\n        camera_num (int): selects the camera module index which will be used as Rpi source.\n        resolution (tuple): sets the resolution (i.e. `(width,height)`) of the Rpi source.\n        framerate (int/float): sets the framerate of the Rpi source.\n        source (based on input): defines the source for the input stream.\n        stream_mode (bool): controls the exclusive YouTube Mode.\n        backend (int): selects the backend for OpenCV's VideoCapture class.\n        colorspace (str): selects the colorspace of the input stream.\n        logging (bool): enables/disables logging.\n        time_delay (int): time delay (in sec) before start reading the frames.\n        options (dict): provides ability to alter Tweak Parameters of WebGear_RTC, CamGear, PiGear & Stabilizer.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # raise error(s) for critical Class imports\n    import_dependency_safe(\"starlette\" if starlette is None else \"\")\n    import_dependency_safe(\"aiortc\" if aiortc is None else \"\")\n\n    # initialize global params\n    custom_data_location = \"\"  # path to save data-files to custom location\n    data_path = \"\"  # path to WebGear_RTC data-files\n    overwrite_default = False\n    self.__relay = None  # act as broadcaster\n\n    # reformat dictionary\n    options = {str(k).strip(): v for k, v in options.items()}\n\n    # assign values to global variables if specified and valid\n    if options:\n        if \"custom_data_location\" in options:\n            value = options[\"custom_data_location\"]\n            if isinstance(value, str):\n                assert os.access(\n                    value, os.W_OK\n                ), \"[WebGear_RTC:ERROR] :: Permission Denied!, cannot write WebGear_RTC data-files to '{}' directory!\".format(\n                    value\n                )\n                assert os.path.isdir(\n                    os.path.abspath(value)\n                ), \"[WebGear_RTC:ERROR] :: `custom_data_location` value must be the path to a directory and not to a file!\"\n                custom_data_location = os.path.abspath(value)\n            else:\n                logger.warning(\"Skipped invalid `custom_data_location` value!\")\n            del options[\"custom_data_location\"]  # clean\n\n        if \"overwrite_default_files\" in options:\n            value = options[\"overwrite_default_files\"]\n            if isinstance(value, bool):\n                overwrite_default = value\n            else:\n                logger.warning(\"Skipped invalid `overwrite_default_files` value!\")\n            del options[\"overwrite_default_files\"]  # clean\n\n        if \"enable_live_broadcast\" in options:\n            value = options[\"enable_live_broadcast\"]\n            if isinstance(value, bool):\n                if value:\n                    self.__relay = MediaRelay()\n                    options[\"enable_infinite_frames\"] = (\n                        True  # enforce infinite frames\n                    )\n                    logger.critical(\n                        \"Enabled live broadcasting for Peer connection(s).\"\n                    )\n                else:\n                    None\n            else:\n                logger.warning(\"Skipped invalid `enable_live_broadcast` value!\")\n            del options[\"enable_live_broadcast\"]  # clean\n\n    # check if custom certificates path is specified\n    if custom_data_location:\n        data_path = generate_webdata(\n            custom_data_location,\n            c_name=\"webgear_rtc\",\n            overwrite_default=overwrite_default,\n            logging=logging,\n        )\n    else:\n        # otherwise generate suitable path\n        data_path = generate_webdata(\n            os.path.join(expanduser(\"~\"), \".vidgear\"),\n            c_name=\"webgear_rtc\",\n            overwrite_default=overwrite_default,\n            logging=logging,\n        )\n\n    # log it\n    self.__logging and logger.debug(\n        \"`{}` is the default location for saving WebGear_RTC data-files.\".format(\n            data_path\n        )\n    )\n\n    # define Jinja2 templates handler\n    self.__templates = Jinja2Templates(directory=\"{}/templates\".format(data_path))\n\n    # define custom exception handlers\n    self.__exception_handlers = {404: self.__not_found, 500: self.__server_error}\n    # define routing tables\n    self.routes = [\n        Route(\"/\", endpoint=self.__homepage),\n        Route(\"/offer\", self.__offer, methods=[\"GET\", \"POST\"]),\n        Mount(\n            \"/static\",\n            app=StaticFiles(directory=\"{}/static\".format(data_path)),\n            name=\"static\",\n        ),\n    ]\n\n    # define middleware support\n    self.middleware = []\n\n    # Handle RTC video server\n    if \"custom_stream\" in options or not (source is None):\n        # Handle video source\n        self.__default_rtc_server = RTC_VideoServer(\n            enablePiCamera=enablePiCamera,\n            stabilize=stabilize,\n            source=source,\n            camera_num=camera_num,\n            stream_mode=stream_mode,\n            backend=backend,\n            colorspace=colorspace,\n            resolution=resolution,\n            framerate=framerate,\n            logging=logging,\n            time_delay=time_delay,\n            **options\n        )\n        # add exclusive reset connection node\n        self.routes.append(\n            Route(\"/close_connection\", self.__reset_connections, methods=[\"POST\"])\n        )\n    else:\n        raise ValueError(\n            \"[WebGear_RTC:ERROR] :: Source cannot be NoneType without Custom Stream(`custom_stream`) defined!\"\n        )\n\n    # copying original routing tables for further validation\n    self.__rt_org_copy = self.routes[:]\n    # collects peer RTC connections\n    self.__pcs = set()\n
"},{"location":"bonus/reference/webgear_rtc/#vidgear.gears.asyncio.webgear_rtc.WebGear_RTC.shutdown","title":"shutdown(self)","text":"

Gracefully shutdown video-server

Source code in vidgear/gears/asyncio/webgear_rtc.py
def shutdown(self):\n    \"\"\"\n    Gracefully shutdown video-server\n    \"\"\"\n    if not (self.__default_rtc_server is None):\n        self.__logging and logger.debug(\"Closing Video Server.\")\n        self.__default_rtc_server.terminate()\n        self.__default_rtc_server = None\n    # terminate internal server aswell.\n    self.__default_rtc_server = None\n
"},{"location":"bonus/reference/writegear/","title":"WriteGear API References","text":"

WriteGear API usage examples for: Compression Mode \u27b6 and Non-Compression Mode \u27b6

WriteGear API parameters are explained for: Compression Mode \u27b6 and Non-Compression Mode \u27b6

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 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 third-party API.

In addition to this, WriteGear also provides flexible access to OpenCV's VideoWriter API tools for video-frames encoding without compression.

Modes of Operation

WriteGear primarily operates in following modes:

Source code in vidgear/gears/writegear.py
class WriteGear:\n    \"\"\"\n    WriteGear handles various powerful Video-Writer Tools that provide us the freedom to do almost anything imaginable with multimedia data.\n\n    WriteGear API provides a complete, flexible, and robust wrapper around FFmpeg, a leading multimedia framework. WriteGear can process real-time frames into a lossless\n    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\n    Live-Streaming (such as for Twitch) and Multiplexing Video-Audio with real-time frames in way fewer lines of code.\n\n    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\n    third-party API.\n\n    In addition to this, WriteGear also provides flexible access to OpenCV's VideoWriter API tools for video-frames encoding without compression.\n\n    ??? tip \"Modes of Operation\"\n\n        WriteGear primarily operates in following modes:\n\n        * **Compression Mode**: In this mode, WriteGear utilizes powerful **FFmpeg** inbuilt encoders to encode lossless multimedia files.\n                                This mode provides us the ability to exploit almost any parameter available within FFmpeg, effortlessly and flexibly,\n                                and while doing that it robustly handles all errors/warnings quietly.\n\n        * **Non-Compression Mode**: In this mode, WriteGear utilizes basic **OpenCV's inbuilt VideoWriter API** tools. This mode also supports all\n                                    parameters manipulation available within VideoWriter API, but it lacks the ability to manipulate encoding parameters\n                                    and other important features like video compression, audio encoding, etc.\n\n    \"\"\"\n\n    def __init__(\n        self,\n        output=\"\",\n        compression_mode=True,\n        custom_ffmpeg=\"\",\n        logging=False,\n        **output_params\n    ):\n        \"\"\"\n        This constructor method initializes the object state and attributes of the WriteGear class.\n\n        Parameters:\n            output (str): sets the valid filename/path/URL for encoding.\n            compression_mode (bool): selects the WriteGear's Primary Mode of Operation.\n            custom_ffmpeg (str): assigns the location of custom path/directory for custom FFmpeg executables.\n            logging (bool): enables/disables logging.\n            output_params (dict): provides the flexibility to control supported internal parameters and FFmpeg properties.\n        \"\"\"\n        # enable logging if specified\n        self.__logging = logging if isinstance(logging, bool) else False\n\n        # print current version\n        logcurr_vidgear_ver(logging=self.__logging)\n\n        # check if user not using depreciated `output_filename` parameter\n        assert (\n            not \"output_filename\" in output_params\n        ), \"[WriteGear:ERROR] :: The `output_filename` parameter has been renamed to `output`. Refer Docs for more info.\"\n\n        # assign parameter values to class variables\n        # enables compression if enabled\n        self.__compression = (\n            compression_mode if isinstance(compression_mode, bool) else False\n        )\n        # specifies if machine in-use is running Windows OS or not\n        self.__os_windows = True if os.name == \"nt\" else False\n\n        # initialize various important class variables\n        self.__output_parameters = {}  # handles output parameters\n        self.__inputheight = None  # handles input frames height\n        self.__inputwidth = None  # handles input frames width\n        self.__inputchannels = None  # handles input frames channels\n        self.__inputdtype = None  # handles input frames dtype\n        self.__process = None  # handles Encoding class/process\n        self.__ffmpeg = \"\"  # handles valid FFmpeg binaries location\n        self.__initiate_process = (\n            True  # handles initiate one-time process for generating pipeline\n        )\n        self.__ffmpeg_window_disabler_patch = (\n            False  # handles disabling window for ffmpeg subprocess on Windows\n        )\n        self.__out_file = None  # handles output\n        gstpipeline_mode = False  # handles GStreamer Pipeline Mode\n\n        # handles output\n        if not output:\n            # raise error otherwise\n            raise ValueError(\n                \"[WriteGear:ERROR] :: Kindly provide a valid `output` value. Refer Docs for more info.\"\n            )\n        else:\n            # validate output is a system file/directory\n            # and Whether WriteGear has the write rights\n            # to specified file/directory or not\n            abs_path = os.path.abspath(output)\n            if check_WriteAccess(\n                os.path.dirname(abs_path),\n                is_windows=self.__os_windows,\n                logging=self.__logging,\n            ):\n                # check if given path is directory\n                if os.path.isdir(abs_path):\n                    # then, auto-assign valid name and adds it to path\n                    abs_path = os.path.join(\n                        abs_path,\n                        \"VidGear-{}.mp4\".format(time.strftime(\"%Y%m%d-%H%M%S\")),\n                    )\n                # assign output file absolute\n                # path to class variable if valid\n                self.__out_file = abs_path\n            else:\n                # log note otherwise\n                logger.info(\n                    \"`{}` isn't a valid system path or directory. Skipped!\".format(\n                        output\n                    )\n                )\n\n        # cleans and reformat output parameters\n        self.__output_parameters = {\n            str(k).strip(): (v.strip() if isinstance(v, str) else v)\n            for k, v in output_params.items()\n        }\n        # log it if specified\n        self.__logging and logger.debug(\n            \"Output Parameters: `{}`\".format(self.__output_parameters)\n        )\n\n        # handles FFmpeg binaries validity\n        # in Compression mode\n        if self.__compression:\n            # log it if specified\n            self.__logging and logger.debug(\n                \"Compression Mode is enabled therefore checking for valid FFmpeg executable.\"\n            )\n\n            # handles where to save the downloaded FFmpeg Static Binaries\n            # on Windows(if specified)\n            __ffmpeg_download_path = self.__output_parameters.pop(\n                \"-ffmpeg_download_path\", \"\"\n            )\n            # check if value is valid\n            if not isinstance(__ffmpeg_download_path, (str)):\n                # reset improper values\n                __ffmpeg_download_path = \"\"\n\n            # handle user-defined output resolution (must be a tuple or list)\n            # in Compression Mode only.\n            self.__output_dimensions = self.__output_parameters.pop(\n                \"-output_dimensions\", None\n            )\n            # check if value is valid\n            if not isinstance(self.__output_dimensions, (list, tuple)):\n                # reset improper values\n                self.__output_dimensions = None\n\n            # handle user defined input framerate of encoding pipeline\n            # in Compression Mode only.\n            self.__inputframerate = self.__output_parameters.pop(\n                \"-input_framerate\", 0.0\n            )\n            # check if value is valid\n            if not isinstance(self.__inputframerate, (float, int)):\n                # reset improper values\n                self.__inputframerate = 0.0\n            else:\n                # must be float\n                self.__inputframerate = float(self.__inputframerate)\n\n            # handle user-defined input frames pixel-format in Compression Mode only.\n            self.__inputpixfmt = self.__output_parameters.pop(\"-input_pixfmt\", None)\n            # check if value is valid\n            if not isinstance(self.__inputpixfmt, str):\n                # reset improper values\n                self.__inputpixfmt = None\n            else:\n                # must be exact\n                self.__inputpixfmt = self.__inputpixfmt.strip()\n\n            # handle user-defined FFmpeg command pre-headers(must be a list)\n            # in Compression Mode only.\n            self.__ffmpeg_preheaders = self.__output_parameters.pop(\"-ffpreheaders\", [])\n            # check if value is valid\n            if not isinstance(self.__ffmpeg_preheaders, list):\n                # reset improper values\n                self.__ffmpeg_preheaders = []\n\n            # handle the special-case of forced-termination (only for Compression mode)\n            disable_force_termination = self.__output_parameters.pop(\n                \"-disable_force_termination\",\n                False if (\"-i\" in self.__output_parameters) else True,\n            )\n            # check if value is valid\n            if isinstance(disable_force_termination, bool):\n                self.__forced_termination = not (disable_force_termination)\n            else:\n                # handle improper values\n                self.__forced_termination = (\n                    True if (\"-i\" in self.__output_parameters) else False\n                )\n\n            # handles disabling window for ffmpeg subprocess on Windows OS (only for Compression mode)\n            # this patch prevents ffmpeg creation window from opening when building exe files\n            ffmpeg_window_disabler_patch = self.__output_parameters.pop(\n                \"-disable_ffmpeg_window\", False\n            )\n            # check if value is valid\n            if not self.__os_windows or logging:\n                logger.warning(\n                    \"Optional `-disable_ffmpeg_window` flag is only available on Windows OS with `logging=False`. Discarding!\"\n                )\n            elif isinstance(ffmpeg_window_disabler_patch, bool):\n                self.__ffmpeg_window_disabler_patch = ffmpeg_window_disabler_patch\n            else:\n                # handle improper values\n                self.__ffmpeg_window_disabler_patch = False\n\n            # validate the FFmpeg path/binaries and returns valid executable FFmpeg\n            # location/path (also auto-downloads static binaries on Windows OS)\n            self.__ffmpeg = get_valid_ffmpeg_path(\n                custom_ffmpeg,\n                self.__os_windows,\n                ffmpeg_download_path=__ffmpeg_download_path,\n                logging=self.__logging,\n            )\n            # check if valid executable FFmpeg location/path\n            if self.__ffmpeg:\n                # log it if found\n                self.__logging and logger.debug(\n                    \"Found valid FFmpeg executable: `{}`.\".format(self.__ffmpeg)\n                )\n            else:\n                # otherwise disable Compression Mode\n                # and switch to Non-compression mode\n                logger.warning(\n                    \"Disabling Compression Mode since no valid FFmpeg executable found on this machine!\"\n                )\n                if self.__logging and not self.__os_windows:\n                    logger.debug(\n                        \"Kindly install a working FFmpeg module or provide a valid custom FFmpeg binary path. See docs for more info.\"\n                    )\n                # compression mode disabled\n                self.__compression = False\n        else:\n            # handle GStreamer Pipeline Mode (only for Non-compression mode)\n            if \"-gst_pipeline_mode\" in self.__output_parameters:\n                # check if value is valid\n                if isinstance(self.__output_parameters[\"-gst_pipeline_mode\"], bool):\n                    gstpipeline_mode = self.__output_parameters[\n                        \"-gst_pipeline_mode\"\n                    ] and check_gstreamer_support(logging=logging)\n                    self.__logging and logger.debug(\n                        \"GStreamer Pipeline Mode successfully activated!\"\n                    )\n                else:\n                    # reset improper values\n                    gstpipeline_mode = False\n                    # log it\n                    self.__logging and logger.warning(\n                        \"GStreamer Pipeline Mode failed to activate!\"\n                    )\n\n        # handle output differently in Compression/Non-compression Modes\n        if self.__compression and self.__ffmpeg:\n            # check if output falls in exclusive cases\n            if self.__out_file is None:\n                if (\n                    platform.system() == \"Linux\"\n                    and pathlib.Path(output).is_char_device()\n                ):\n                    # check whether output is a Linux video device path (such as `/dev/video0`)\n                    self.__logging and logger.debug(\n                        \"Path:`{}` is a valid Linux Video Device path.\".format(output)\n                    )\n                    self.__out_file = output\n                elif is_valid_url(self.__ffmpeg, url=output, logging=self.__logging):\n                    # check whether output is a valid URL instead\n                    self.__logging and logger.debug(\n                        \"URL:`{}` is valid and successfully configured for streaming.\".format(\n                            output\n                        )\n                    )\n                    self.__out_file = output\n                else:\n                    # raise error otherwise\n                    raise ValueError(\n                        \"[WriteGear:ERROR] :: output value:`{}` is not supported in Compression Mode.\".format(\n                            output\n                        )\n                    )\n            # log if forced termination is enabled\n            self.__forced_termination and logger.debug(\n                \"Forced termination is enabled for this FFmpeg process.\"\n            )\n            # log Compression is enabled\n            self.__logging and logger.debug(\n                \"Compression Mode with FFmpeg backend is configured properly.\"\n            )\n        else:\n            # raise error if not valid input\n            if self.__out_file is None and not gstpipeline_mode:\n                raise ValueError(\n                    \"[WriteGear:ERROR] :: output value:`{}` is not supported in Non-Compression Mode.\".format(\n                        output\n                    )\n                )\n\n            # check if GStreamer Pipeline Mode is enabled\n            if gstpipeline_mode:\n                # enforce GStreamer backend\n                self.__output_parameters[\"-backend\"] = \"CAP_GSTREAMER\"\n                # enforce original output value\n                self.__out_file = output\n\n            # log it\n            self.__logging and logger.debug(\n                \"Non-Compression Mode is successfully configured in GStreamer Pipeline Mode.\"\n            )\n\n            # log if Compression is disabled\n            logger.critical(\n                \"Compression Mode is disabled, Activating OpenCV built-in Writer!\"\n            )\n\n    def write(self, frame, rgb_mode=False):\n        \"\"\"\n        Pipelines `ndarray` frames to respective API _(**FFmpeg** in Compression Mode & **OpenCV's VideoWriter API** in Non-Compression Mode)_.\n\n        Parameters:\n            frame (ndarray): a valid numpy frame\n            rgb_mode (boolean): enable this flag to activate RGB mode _(i.e. specifies that incoming frames are of RGB format(instead of default BGR)_.\n\n        \"\"\"\n        if frame is None:  # None-Type frames will be skipped\n            return\n\n        # get height, width, number of channels, and dtype of current frame\n        height, width = frame.shape[:2]\n        channels = frame.shape[-1] if frame.ndim == 3 else 1\n        dtype = frame.dtype\n\n        # assign values to class variables on first run\n        if self.__initiate_process:\n            self.__inputheight = height\n            self.__inputwidth = width\n            self.__inputchannels = channels\n            self.__inputdtype = dtype\n            self.__logging and logger.debug(\n                \"InputFrame => Height:{} Width:{} Channels:{} Datatype:{}\".format(\n                    self.__inputheight,\n                    self.__inputwidth,\n                    self.__inputchannels,\n                    self.__inputdtype,\n                )\n            )\n\n        # validate frame size\n        if height != self.__inputheight or width != self.__inputwidth:\n            raise ValueError(\n                \"[WriteGear:ERROR] :: All video-frames must have same size!\"\n            )\n        # validate number of channels in frame\n        if channels != self.__inputchannels:\n            raise ValueError(\n                \"[WriteGear:ERROR] :: All video-frames must have same number of channels!\"\n            )\n        # validate frame datatype\n        if dtype != self.__inputdtype:\n            raise ValueError(\n                \"[WriteGear:ERROR] :: All video-frames must have same datatype!\"\n            )\n\n        # checks if compression mode is enabled\n        if self.__compression:\n            # initiate FFmpeg process on first run\n            if self.__initiate_process:\n                # start pre-processing of FFmpeg parameters, and initiate process\n                self.__PreprocessFFParams(channels, dtype=dtype, rgb=rgb_mode)\n                # Check status of the process\n                assert self.__process is not None\n            try:\n                # try writing the frame bytes to the subprocess pipeline\n                self.__process.stdin.write(frame.tobytes())\n            except (OSError, IOError):\n                # log if something is wrong!\n                logger.error(\n                    \"BrokenPipeError caught, Wrong values passed to FFmpeg Pipe. Kindly Refer Docs!\"\n                )\n                raise ValueError  # for testing purpose only\n        else:\n            # otherwise initiate OpenCV's VideoWriter Class process\n            if self.__initiate_process:\n                # start VideoWriter Class process\n                self.__start_CVProcess()\n                # Check status of the process\n                assert self.__process is not None\n                # log one-time OpenCV warning\n                self.__logging and logger.info(\n                    \"RGBA and 16-bit grayscale video frames are not supported by OpenCV yet. Kindly switch on `compression_mode` to use them!\"\n                )\n            # write frame directly to\n            # VideoWriter Class process\n            self.__process.write(frame)\n\n    def __PreprocessFFParams(self, channels, dtype=None, rgb=False):\n        \"\"\"\n        Internal method that pre-processes FFmpeg Parameters before beginning to pipeline frames.\n\n        Parameters:\n            channels (int): Number of channels in input frame.\n            dtype (str): Datatype of input frame.\n            rgb_mode (boolean): Whether to activate `RGB mode`?\n        \"\"\"\n        # turn off initiate flag\n        self.__initiate_process = False\n        # initialize input parameters\n        input_parameters = {}\n\n        # handle output frames dimensions\n        dimensions = \"\"\n        if self.__output_dimensions is None:  # check if dimensions are given\n            dimensions += \"{}x{}\".format(\n                self.__inputwidth, self.__inputheight\n            )  # auto derive from frame\n        else:\n            dimensions += \"{}x{}\".format(\n                self.__output_dimensions[0], self.__output_dimensions[1]\n            )  # apply if defined\n        input_parameters[\"-s\"] = str(dimensions)\n\n        # handles user-defined and auto-assigned input pixel-formats\n        if not (\n            self.__inputpixfmt is None\n        ) and self.__inputpixfmt in get_supported_pixfmts(self.__ffmpeg):\n            # assign directly if valid\n            input_parameters[\"-pix_fmt\"] = self.__inputpixfmt\n        else:\n            # handles pix_fmt based on channels and dtype(HACK)\n            if dtype.kind == \"u\" and dtype.itemsize == 2:\n                # handle pix_fmt for frames with higher than 8-bit depth\n                pix_fmt = None\n                if channels == 1:\n                    pix_fmt = \"gray16\"\n                elif channels == 2:\n                    pix_fmt = \"ya16\"\n                elif channels == 3:\n                    pix_fmt = \"rgb48\" if rgb else \"bgr48\"\n                elif channels == 4:\n                    pix_fmt = \"rgba64\" if rgb else \"bgra64\"\n                else:\n                    # raise error otherwise\n                    raise ValueError(\n                        \"[WriteGear:ERROR] :: Frames with channels outside range 1-to-4 are not supported!\"\n                    )\n                # Add endianness suffix (w.r.t byte-order)\n                input_parameters[\"-pix_fmt\"] = pix_fmt + (\n                    \"be\" if dtype.byteorder == \">\" else \"le\"\n                )\n            else:\n                # handle pix_fmt for frames with exactly 8-bit depth(`uint8`)\n                if channels == 1:\n                    input_parameters[\"-pix_fmt\"] = \"gray\"\n                elif channels == 2:\n                    input_parameters[\"-pix_fmt\"] = \"ya8\"\n                elif channels == 3:\n                    input_parameters[\"-pix_fmt\"] = \"rgb24\" if rgb else \"bgr24\"\n                elif channels == 4:\n                    input_parameters[\"-pix_fmt\"] = \"rgba\" if rgb else \"bgra\"\n                else:\n                    # raise error otherwise\n                    raise ValueError(\n                        \"[WriteGear:ERROR] :: Frames with channels outside range 1-to-4 are not supported!\"\n                    )\n\n        # handles user-defined output video framerate\n        if self.__inputframerate > 0.0:\n            # assign input framerate if valid\n            self.__logging and logger.debug(\n                \"Setting Input framerate: {}\".format(self.__inputframerate)\n            )\n            input_parameters[\"-framerate\"] = str(self.__inputframerate)\n\n        # initiate FFmpeg process\n        self.__start_FFProcess(\n            input_params=input_parameters, output_params=self.__output_parameters\n        )\n\n    def __start_FFProcess(self, input_params, output_params):\n        \"\"\"\n        An Internal method that launches FFmpeg subprocess pipeline in Compression Mode\n        for pipelining frames to `stdin`.\n\n        Parameters:\n            input_params (dict): Input FFmpeg parameters\n            output_params (dict): Output FFmpeg parameters\n        \"\"\"\n        # convert input parameters to argument list\n        input_parameters = dict2Args(input_params)\n\n        # handle output video encoder.\n        # get list of supported video-encoders\n        supported_vcodecs = get_supported_vencoders(self.__ffmpeg)\n        # dynamically select default encoder\n        default_vcodec = [\n            vcodec\n            for vcodec in [\"libx264\", \"libx265\", \"libxvid\", \"mpeg4\"]\n            if vcodec in supported_vcodecs\n        ][0] or \"unknown\"\n        # extract any user-defined encoder\n        if \"-c:v\" in output_params:\n            # assign it to the pipeline\n            output_params[\"-vcodec\"] = output_params.pop(\"-c:v\", default_vcodec)\n        if not \"-vcodec\" in output_params:\n            # auto-assign default video-encoder (if not assigned by user).\n            output_params[\"-vcodec\"] = default_vcodec\n        if (\n            default_vcodec != \"unknown\"\n            and not output_params[\"-vcodec\"] in supported_vcodecs\n        ):\n            # reset to default if not supported\n            logger.critical(\n                \"Provided FFmpeg does not support `{}` video-encoder. Switching to default supported `{}` encoder!\".format(\n                    output_params[\"-vcodec\"], default_vcodec\n                )\n            )\n            output_params[\"-vcodec\"] = default_vcodec\n\n        # assign optimizations based on selected video encoder(if any)\n        if output_params[\"-vcodec\"] in supported_vcodecs:\n            if output_params[\"-vcodec\"] in [\"libx265\", \"libx264\"]:\n                if not \"-crf\" in output_params:\n                    output_params[\"-crf\"] = \"18\"\n                if not \"-preset\" in output_params:\n                    output_params[\"-preset\"] = \"fast\"\n            if output_params[\"-vcodec\"] in [\"libxvid\", \"mpeg4\"]:\n                if not \"-qscale:v\" in output_params:\n                    output_params[\"-qscale:v\"] = \"3\"\n        else:\n            # raise error otherwise\n            raise RuntimeError(\n                \"[WriteGear:ERROR] :: Provided FFmpeg does not support any suitable/usable video-encoders for compression.\"\n                \" Kindly disable compression mode or switch to another FFmpeg binaries(if available).\"\n            )\n\n        # convert output parameters to argument list\n        output_parameters = dict2Args(output_params)\n\n        # format FFmpeg command\n        cmd = (\n            [self.__ffmpeg, \"-y\"]\n            + self.__ffmpeg_preheaders\n            + [\"-f\", \"rawvideo\", \"-vcodec\", \"rawvideo\"]\n            + input_parameters\n            + [\"-i\", \"-\"]\n            + output_parameters\n            + [self.__out_file]\n        )\n        # Launch the process with FFmpeg command\n        if self.__logging:\n            # log command in logging mode\n            logger.debug(\"Executing FFmpeg command: `{}`\".format(\" \".join(cmd)))\n            # In logging mode\n            self.__process = sp.Popen(cmd, stdin=sp.PIPE, stdout=sp.PIPE, stderr=None)\n        else:\n            # In silent mode\n            self.__process = sp.Popen(\n                cmd,\n                stdin=sp.PIPE,\n                stdout=sp.DEVNULL,\n                stderr=sp.STDOUT,\n                creationflags=(  # this prevents ffmpeg creation window from opening when building exe files on Windows\n                    sp.DETACHED_PROCESS if self.__ffmpeg_window_disabler_patch else 0\n                ),\n            )\n\n    def __enter__(self):\n        \"\"\"\n        Handles entry with the `with` statement. See [PEP343 -- The 'with' statement'](https://peps.python.org/pep-0343/).\n\n        **Returns:** Returns a reference to the WriteGear Class\n        \"\"\"\n        return self\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        \"\"\"\n        Handles exit with the `with` statement. See [PEP343 -- The 'with' statement'](https://peps.python.org/pep-0343/).\n        \"\"\"\n        self.close()\n\n    def execute_ffmpeg_cmd(self, command=None):\n        \"\"\"\n\n        Executes user-defined FFmpeg Terminal command, formatted as a python list(in Compression Mode only).\n\n        Parameters:\n            command (list): inputs list data-type command.\n\n        \"\"\"\n        # check if valid command\n        if command is None or not (command):\n            logger.warning(\"Input command is empty, Nothing to execute!\")\n            return\n        else:\n            if not (isinstance(command, list)):\n                raise ValueError(\n                    \"[WriteGear:ERROR] :: Invalid input command datatype! Kindly read docs.\"\n                )\n\n        # check if Compression Mode is enabled\n        if not (self.__compression):\n            # raise error otherwise\n            raise RuntimeError(\n                \"[WriteGear:ERROR] :: Compression Mode is disabled, Kindly enable it to access this function.\"\n            )\n\n        # add configured FFmpeg path\n        cmd = [self.__ffmpeg] + command\n\n        try:\n            # write frames to pipeline\n            if self.__logging:\n                # log command in logging mode\n                logger.debug(\"Executing FFmpeg command: `{}`\".format(\" \".join(cmd)))\n                # In logging mode\n                sp.run(cmd, stdin=sp.PIPE, stdout=sp.PIPE, stderr=None)\n            else:\n                # In silent mode\n                sp.run(cmd, stdin=sp.PIPE, stdout=sp.DEVNULL, stderr=sp.STDOUT)\n        except (OSError, IOError) as e:\n            # re-raise error\n            if self.__logging:\n                raise ValueError(\n                    \"BrokenPipeError caught, Wrong command passed to FFmpeg Pipe, Kindly Refer Docs!\"\n                ) from None\n            else:\n                raise ValueError(\n                    \"BrokenPipeError caught, Wrong command passed to FFmpeg Pipe, Kindly Refer Docs!\"\n                ) from e\n\n    def __start_CVProcess(self):\n        \"\"\"\n        An Internal method that launches OpenCV VideoWriter process in Non-Compression\n        Mode with given settings.\n        \"\"\"\n        # turn off initiate flag\n        self.__initiate_process = False\n\n        # initialize essential variables\n        FPS = 0\n        BACKEND = \"\"\n        FOURCC = 0\n        COLOR = True\n\n        # pre-assign default parameters (if not assigned by user).\n        if \"-fourcc\" not in self.__output_parameters:\n            FOURCC = cv2.VideoWriter_fourcc(*\"MJPG\")\n        if \"-fps\" not in self.__output_parameters:\n            FPS = 25\n\n        # auto-assign frame dimensions\n        HEIGHT = self.__inputheight\n        WIDTH = self.__inputwidth\n\n        # assign dict parameter values to variables\n        try:\n            for key, value in self.__output_parameters.items():\n                if key == \"-fourcc\":\n                    FOURCC = cv2.VideoWriter_fourcc(*(value.upper()))\n                elif key == \"-fps\":\n                    FPS = int(value)\n                elif key == \"-backend\":\n                    BACKEND = capPropId(value.upper())\n                elif key == \"-color\":\n                    COLOR = bool(value)\n                else:\n                    pass\n        except Exception as e:\n            # log and raise error if something is wrong\n            self.__logging and logger.exception(str(e))\n            raise ValueError(\n                \"[WriteGear:ERROR] :: Wrong Values passed to OpenCV Writer, Kindly Refer Docs!\"\n            )\n\n        # log values for debugging\n        self.__logging and logger.debug(\n            \"FILE_PATH: {}, FOURCC = {}, FPS = {}, WIDTH = {}, HEIGHT = {}, BACKEND = {}\".format(\n                self.__out_file, FOURCC, FPS, WIDTH, HEIGHT, BACKEND\n            )\n        )\n        # start different OpenCV VideoCapture processes\n        # for with and without Backend.\n        if BACKEND:\n            self.__process = cv2.VideoWriter(\n                self.__out_file,\n                apiPreference=BACKEND,\n                fourcc=FOURCC,\n                fps=FPS,\n                frameSize=(WIDTH, HEIGHT),\n                isColor=COLOR,\n            )\n        else:\n            self.__process = cv2.VideoWriter(\n                self.__out_file,\n                fourcc=FOURCC,\n                fps=FPS,\n                frameSize=(WIDTH, HEIGHT),\n                isColor=COLOR,\n            )\n        # check if OpenCV VideoCapture is opened successfully\n        assert (\n            self.__process.isOpened()\n        ), \"[WriteGear:ERROR] :: Failed to initialize OpenCV Writer!\"\n\n    def close(self):\n        \"\"\"\n        Safely terminates various WriteGear process.\n        \"\"\"\n        # log termination\n        self.__logging and logger.debug(\"Terminating WriteGear Processes.\")\n        # handle termination separately\n        if self.__compression:\n            # when Compression Mode is enabled\n            if self.__process is None or not (self.__process.poll() is None):\n                # return if no process initiated\n                # at first place\n                return\n            # close `stdin` output\n            self.__process.stdin and self.__process.stdin.close()\n            # close `stdout` output\n            self.__process.stdout and self.__process.stdout.close()\n            # forced termination if specified.\n            self.__forced_termination and self.__process.terminate()\n            # wait if process is still processing\n            self.__process.wait()\n        else:\n            # when Compression Mode is disabled\n            if self.__process is None:\n                # return if no process initiated\n                # at first place\n                return\n            # close it\n            self.__process.release()\n        # discard process\n        self.__process = None\n

"},{"location":"bonus/reference/writegear/#vidgear.gears.writegear.WriteGear.__enter__","title":"__enter__(self) special","text":"

Handles entry with the with statement. See PEP343 -- The 'with' statement'.

Returns: Returns a reference to the WriteGear Class

Source code in vidgear/gears/writegear.py
def __enter__(self):\n    \"\"\"\n    Handles entry with the `with` statement. See [PEP343 -- The 'with' statement'](https://peps.python.org/pep-0343/).\n\n    **Returns:** Returns a reference to the WriteGear Class\n    \"\"\"\n    return self\n
"},{"location":"bonus/reference/writegear/#vidgear.gears.writegear.WriteGear.__exit__","title":"__exit__(self, exc_type, exc_val, exc_tb) special","text":"

Handles exit with the with statement. See PEP343 -- The 'with' statement'.

Source code in vidgear/gears/writegear.py
def __exit__(self, exc_type, exc_val, exc_tb):\n    \"\"\"\n    Handles exit with the `with` statement. See [PEP343 -- The 'with' statement'](https://peps.python.org/pep-0343/).\n    \"\"\"\n    self.close()\n
"},{"location":"bonus/reference/writegear/#vidgear.gears.writegear.WriteGear.__init__","title":"__init__(self, output='', compression_mode=True, custom_ffmpeg='', logging=False, **output_params) special","text":"

This constructor method initializes the object state and attributes of the WriteGear class.

Parameters:

Name Type Description Default output str

sets the valid filename/path/URL for encoding.

'' compression_mode bool

selects the WriteGear's Primary Mode of Operation.

True custom_ffmpeg str

assigns the location of custom path/directory for custom FFmpeg executables.

'' logging bool

enables/disables logging.

False output_params dict

provides the flexibility to control supported internal parameters and FFmpeg properties.

{} Source code in vidgear/gears/writegear.py
def __init__(\n    self,\n    output=\"\",\n    compression_mode=True,\n    custom_ffmpeg=\"\",\n    logging=False,\n    **output_params\n):\n    \"\"\"\n    This constructor method initializes the object state and attributes of the WriteGear class.\n\n    Parameters:\n        output (str): sets the valid filename/path/URL for encoding.\n        compression_mode (bool): selects the WriteGear's Primary Mode of Operation.\n        custom_ffmpeg (str): assigns the location of custom path/directory for custom FFmpeg executables.\n        logging (bool): enables/disables logging.\n        output_params (dict): provides the flexibility to control supported internal parameters and FFmpeg properties.\n    \"\"\"\n    # enable logging if specified\n    self.__logging = logging if isinstance(logging, bool) else False\n\n    # print current version\n    logcurr_vidgear_ver(logging=self.__logging)\n\n    # check if user not using depreciated `output_filename` parameter\n    assert (\n        not \"output_filename\" in output_params\n    ), \"[WriteGear:ERROR] :: The `output_filename` parameter has been renamed to `output`. Refer Docs for more info.\"\n\n    # assign parameter values to class variables\n    # enables compression if enabled\n    self.__compression = (\n        compression_mode if isinstance(compression_mode, bool) else False\n    )\n    # specifies if machine in-use is running Windows OS or not\n    self.__os_windows = True if os.name == \"nt\" else False\n\n    # initialize various important class variables\n    self.__output_parameters = {}  # handles output parameters\n    self.__inputheight = None  # handles input frames height\n    self.__inputwidth = None  # handles input frames width\n    self.__inputchannels = None  # handles input frames channels\n    self.__inputdtype = None  # handles input frames dtype\n    self.__process = None  # handles Encoding class/process\n    self.__ffmpeg = \"\"  # handles valid FFmpeg binaries location\n    self.__initiate_process = (\n        True  # handles initiate one-time process for generating pipeline\n    )\n    self.__ffmpeg_window_disabler_patch = (\n        False  # handles disabling window for ffmpeg subprocess on Windows\n    )\n    self.__out_file = None  # handles output\n    gstpipeline_mode = False  # handles GStreamer Pipeline Mode\n\n    # handles output\n    if not output:\n        # raise error otherwise\n        raise ValueError(\n            \"[WriteGear:ERROR] :: Kindly provide a valid `output` value. Refer Docs for more info.\"\n        )\n    else:\n        # validate output is a system file/directory\n        # and Whether WriteGear has the write rights\n        # to specified file/directory or not\n        abs_path = os.path.abspath(output)\n        if check_WriteAccess(\n            os.path.dirname(abs_path),\n            is_windows=self.__os_windows,\n            logging=self.__logging,\n        ):\n            # check if given path is directory\n            if os.path.isdir(abs_path):\n                # then, auto-assign valid name and adds it to path\n                abs_path = os.path.join(\n                    abs_path,\n                    \"VidGear-{}.mp4\".format(time.strftime(\"%Y%m%d-%H%M%S\")),\n                )\n            # assign output file absolute\n            # path to class variable if valid\n            self.__out_file = abs_path\n        else:\n            # log note otherwise\n            logger.info(\n                \"`{}` isn't a valid system path or directory. Skipped!\".format(\n                    output\n                )\n            )\n\n    # cleans and reformat output parameters\n    self.__output_parameters = {\n        str(k).strip(): (v.strip() if isinstance(v, str) else v)\n        for k, v in output_params.items()\n    }\n    # log it if specified\n    self.__logging and logger.debug(\n        \"Output Parameters: `{}`\".format(self.__output_parameters)\n    )\n\n    # handles FFmpeg binaries validity\n    # in Compression mode\n    if self.__compression:\n        # log it if specified\n        self.__logging and logger.debug(\n            \"Compression Mode is enabled therefore checking for valid FFmpeg executable.\"\n        )\n\n        # handles where to save the downloaded FFmpeg Static Binaries\n        # on Windows(if specified)\n        __ffmpeg_download_path = self.__output_parameters.pop(\n            \"-ffmpeg_download_path\", \"\"\n        )\n        # check if value is valid\n        if not isinstance(__ffmpeg_download_path, (str)):\n            # reset improper values\n            __ffmpeg_download_path = \"\"\n\n        # handle user-defined output resolution (must be a tuple or list)\n        # in Compression Mode only.\n        self.__output_dimensions = self.__output_parameters.pop(\n            \"-output_dimensions\", None\n        )\n        # check if value is valid\n        if not isinstance(self.__output_dimensions, (list, tuple)):\n            # reset improper values\n            self.__output_dimensions = None\n\n        # handle user defined input framerate of encoding pipeline\n        # in Compression Mode only.\n        self.__inputframerate = self.__output_parameters.pop(\n            \"-input_framerate\", 0.0\n        )\n        # check if value is valid\n        if not isinstance(self.__inputframerate, (float, int)):\n            # reset improper values\n            self.__inputframerate = 0.0\n        else:\n            # must be float\n            self.__inputframerate = float(self.__inputframerate)\n\n        # handle user-defined input frames pixel-format in Compression Mode only.\n        self.__inputpixfmt = self.__output_parameters.pop(\"-input_pixfmt\", None)\n        # check if value is valid\n        if not isinstance(self.__inputpixfmt, str):\n            # reset improper values\n            self.__inputpixfmt = None\n        else:\n            # must be exact\n            self.__inputpixfmt = self.__inputpixfmt.strip()\n\n        # handle user-defined FFmpeg command pre-headers(must be a list)\n        # in Compression Mode only.\n        self.__ffmpeg_preheaders = self.__output_parameters.pop(\"-ffpreheaders\", [])\n        # check if value is valid\n        if not isinstance(self.__ffmpeg_preheaders, list):\n            # reset improper values\n            self.__ffmpeg_preheaders = []\n\n        # handle the special-case of forced-termination (only for Compression mode)\n        disable_force_termination = self.__output_parameters.pop(\n            \"-disable_force_termination\",\n            False if (\"-i\" in self.__output_parameters) else True,\n        )\n        # check if value is valid\n        if isinstance(disable_force_termination, bool):\n            self.__forced_termination = not (disable_force_termination)\n        else:\n            # handle improper values\n            self.__forced_termination = (\n                True if (\"-i\" in self.__output_parameters) else False\n            )\n\n        # handles disabling window for ffmpeg subprocess on Windows OS (only for Compression mode)\n        # this patch prevents ffmpeg creation window from opening when building exe files\n        ffmpeg_window_disabler_patch = self.__output_parameters.pop(\n            \"-disable_ffmpeg_window\", False\n        )\n        # check if value is valid\n        if not self.__os_windows or logging:\n            logger.warning(\n                \"Optional `-disable_ffmpeg_window` flag is only available on Windows OS with `logging=False`. Discarding!\"\n            )\n        elif isinstance(ffmpeg_window_disabler_patch, bool):\n            self.__ffmpeg_window_disabler_patch = ffmpeg_window_disabler_patch\n        else:\n            # handle improper values\n            self.__ffmpeg_window_disabler_patch = False\n\n        # validate the FFmpeg path/binaries and returns valid executable FFmpeg\n        # location/path (also auto-downloads static binaries on Windows OS)\n        self.__ffmpeg = get_valid_ffmpeg_path(\n            custom_ffmpeg,\n            self.__os_windows,\n            ffmpeg_download_path=__ffmpeg_download_path,\n            logging=self.__logging,\n        )\n        # check if valid executable FFmpeg location/path\n        if self.__ffmpeg:\n            # log it if found\n            self.__logging and logger.debug(\n                \"Found valid FFmpeg executable: `{}`.\".format(self.__ffmpeg)\n            )\n        else:\n            # otherwise disable Compression Mode\n            # and switch to Non-compression mode\n            logger.warning(\n                \"Disabling Compression Mode since no valid FFmpeg executable found on this machine!\"\n            )\n            if self.__logging and not self.__os_windows:\n                logger.debug(\n                    \"Kindly install a working FFmpeg module or provide a valid custom FFmpeg binary path. See docs for more info.\"\n                )\n            # compression mode disabled\n            self.__compression = False\n    else:\n        # handle GStreamer Pipeline Mode (only for Non-compression mode)\n        if \"-gst_pipeline_mode\" in self.__output_parameters:\n            # check if value is valid\n            if isinstance(self.__output_parameters[\"-gst_pipeline_mode\"], bool):\n                gstpipeline_mode = self.__output_parameters[\n                    \"-gst_pipeline_mode\"\n                ] and check_gstreamer_support(logging=logging)\n                self.__logging and logger.debug(\n                    \"GStreamer Pipeline Mode successfully activated!\"\n                )\n            else:\n                # reset improper values\n                gstpipeline_mode = False\n                # log it\n                self.__logging and logger.warning(\n                    \"GStreamer Pipeline Mode failed to activate!\"\n                )\n\n    # handle output differently in Compression/Non-compression Modes\n    if self.__compression and self.__ffmpeg:\n        # check if output falls in exclusive cases\n        if self.__out_file is None:\n            if (\n                platform.system() == \"Linux\"\n                and pathlib.Path(output).is_char_device()\n            ):\n                # check whether output is a Linux video device path (such as `/dev/video0`)\n                self.__logging and logger.debug(\n                    \"Path:`{}` is a valid Linux Video Device path.\".format(output)\n                )\n                self.__out_file = output\n            elif is_valid_url(self.__ffmpeg, url=output, logging=self.__logging):\n                # check whether output is a valid URL instead\n                self.__logging and logger.debug(\n                    \"URL:`{}` is valid and successfully configured for streaming.\".format(\n                        output\n                    )\n                )\n                self.__out_file = output\n            else:\n                # raise error otherwise\n                raise ValueError(\n                    \"[WriteGear:ERROR] :: output value:`{}` is not supported in Compression Mode.\".format(\n                        output\n                    )\n                )\n        # log if forced termination is enabled\n        self.__forced_termination and logger.debug(\n            \"Forced termination is enabled for this FFmpeg process.\"\n        )\n        # log Compression is enabled\n        self.__logging and logger.debug(\n            \"Compression Mode with FFmpeg backend is configured properly.\"\n        )\n    else:\n        # raise error if not valid input\n        if self.__out_file is None and not gstpipeline_mode:\n            raise ValueError(\n                \"[WriteGear:ERROR] :: output value:`{}` is not supported in Non-Compression Mode.\".format(\n                    output\n                )\n            )\n\n        # check if GStreamer Pipeline Mode is enabled\n        if gstpipeline_mode:\n            # enforce GStreamer backend\n            self.__output_parameters[\"-backend\"] = \"CAP_GSTREAMER\"\n            # enforce original output value\n            self.__out_file = output\n\n        # log it\n        self.__logging and logger.debug(\n            \"Non-Compression Mode is successfully configured in GStreamer Pipeline Mode.\"\n        )\n\n        # log if Compression is disabled\n        logger.critical(\n            \"Compression Mode is disabled, Activating OpenCV built-in Writer!\"\n        )\n
"},{"location":"bonus/reference/writegear/#vidgear.gears.writegear.WriteGear.close","title":"close(self)","text":"

Safely terminates various WriteGear process.

Source code in vidgear/gears/writegear.py
def close(self):\n    \"\"\"\n    Safely terminates various WriteGear process.\n    \"\"\"\n    # log termination\n    self.__logging and logger.debug(\"Terminating WriteGear Processes.\")\n    # handle termination separately\n    if self.__compression:\n        # when Compression Mode is enabled\n        if self.__process is None or not (self.__process.poll() is None):\n            # return if no process initiated\n            # at first place\n            return\n        # close `stdin` output\n        self.__process.stdin and self.__process.stdin.close()\n        # close `stdout` output\n        self.__process.stdout and self.__process.stdout.close()\n        # forced termination if specified.\n        self.__forced_termination and self.__process.terminate()\n        # wait if process is still processing\n        self.__process.wait()\n    else:\n        # when Compression Mode is disabled\n        if self.__process is None:\n            # return if no process initiated\n            # at first place\n            return\n        # close it\n        self.__process.release()\n    # discard process\n    self.__process = None\n
"},{"location":"bonus/reference/writegear/#vidgear.gears.writegear.WriteGear.execute_ffmpeg_cmd","title":"execute_ffmpeg_cmd(self, command=None)","text":"

Executes user-defined FFmpeg Terminal command, formatted as a python list(in Compression Mode only).

Parameters:

Name Type Description Default command list

inputs list data-type command.

None Source code in vidgear/gears/writegear.py
def execute_ffmpeg_cmd(self, command=None):\n    \"\"\"\n\n    Executes user-defined FFmpeg Terminal command, formatted as a python list(in Compression Mode only).\n\n    Parameters:\n        command (list): inputs list data-type command.\n\n    \"\"\"\n    # check if valid command\n    if command is None or not (command):\n        logger.warning(\"Input command is empty, Nothing to execute!\")\n        return\n    else:\n        if not (isinstance(command, list)):\n            raise ValueError(\n                \"[WriteGear:ERROR] :: Invalid input command datatype! Kindly read docs.\"\n            )\n\n    # check if Compression Mode is enabled\n    if not (self.__compression):\n        # raise error otherwise\n        raise RuntimeError(\n            \"[WriteGear:ERROR] :: Compression Mode is disabled, Kindly enable it to access this function.\"\n        )\n\n    # add configured FFmpeg path\n    cmd = [self.__ffmpeg] + command\n\n    try:\n        # write frames to pipeline\n        if self.__logging:\n            # log command in logging mode\n            logger.debug(\"Executing FFmpeg command: `{}`\".format(\" \".join(cmd)))\n            # In logging mode\n            sp.run(cmd, stdin=sp.PIPE, stdout=sp.PIPE, stderr=None)\n        else:\n            # In silent mode\n            sp.run(cmd, stdin=sp.PIPE, stdout=sp.DEVNULL, stderr=sp.STDOUT)\n    except (OSError, IOError) as e:\n        # re-raise error\n        if self.__logging:\n            raise ValueError(\n                \"BrokenPipeError caught, Wrong command passed to FFmpeg Pipe, Kindly Refer Docs!\"\n            ) from None\n        else:\n            raise ValueError(\n                \"BrokenPipeError caught, Wrong command passed to FFmpeg Pipe, Kindly Refer Docs!\"\n            ) from e\n
"},{"location":"bonus/reference/writegear/#vidgear.gears.writegear.WriteGear.write","title":"write(self, frame, rgb_mode=False)","text":"

Pipelines ndarray frames to respective API (FFmpeg in Compression Mode & OpenCV's VideoWriter API in Non-Compression Mode).

Parameters:

Name Type Description Default frame ndarray

a valid numpy frame

required rgb_mode boolean

enable this flag to activate RGB mode (i.e. specifies that incoming frames are of RGB format(instead of default BGR).

False Source code in vidgear/gears/writegear.py
def write(self, frame, rgb_mode=False):\n    \"\"\"\n    Pipelines `ndarray` frames to respective API _(**FFmpeg** in Compression Mode & **OpenCV's VideoWriter API** in Non-Compression Mode)_.\n\n    Parameters:\n        frame (ndarray): a valid numpy frame\n        rgb_mode (boolean): enable this flag to activate RGB mode _(i.e. specifies that incoming frames are of RGB format(instead of default BGR)_.\n\n    \"\"\"\n    if frame is None:  # None-Type frames will be skipped\n        return\n\n    # get height, width, number of channels, and dtype of current frame\n    height, width = frame.shape[:2]\n    channels = frame.shape[-1] if frame.ndim == 3 else 1\n    dtype = frame.dtype\n\n    # assign values to class variables on first run\n    if self.__initiate_process:\n        self.__inputheight = height\n        self.__inputwidth = width\n        self.__inputchannels = channels\n        self.__inputdtype = dtype\n        self.__logging and logger.debug(\n            \"InputFrame => Height:{} Width:{} Channels:{} Datatype:{}\".format(\n                self.__inputheight,\n                self.__inputwidth,\n                self.__inputchannels,\n                self.__inputdtype,\n            )\n        )\n\n    # validate frame size\n    if height != self.__inputheight or width != self.__inputwidth:\n        raise ValueError(\n            \"[WriteGear:ERROR] :: All video-frames must have same size!\"\n        )\n    # validate number of channels in frame\n    if channels != self.__inputchannels:\n        raise ValueError(\n            \"[WriteGear:ERROR] :: All video-frames must have same number of channels!\"\n        )\n    # validate frame datatype\n    if dtype != self.__inputdtype:\n        raise ValueError(\n            \"[WriteGear:ERROR] :: All video-frames must have same datatype!\"\n        )\n\n    # checks if compression mode is enabled\n    if self.__compression:\n        # initiate FFmpeg process on first run\n        if self.__initiate_process:\n            # start pre-processing of FFmpeg parameters, and initiate process\n            self.__PreprocessFFParams(channels, dtype=dtype, rgb=rgb_mode)\n            # Check status of the process\n            assert self.__process is not None\n        try:\n            # try writing the frame bytes to the subprocess pipeline\n            self.__process.stdin.write(frame.tobytes())\n        except (OSError, IOError):\n            # log if something is wrong!\n            logger.error(\n                \"BrokenPipeError caught, Wrong values passed to FFmpeg Pipe. Kindly Refer Docs!\"\n            )\n            raise ValueError  # for testing purpose only\n    else:\n        # otherwise initiate OpenCV's VideoWriter Class process\n        if self.__initiate_process:\n            # start VideoWriter Class process\n            self.__start_CVProcess()\n            # Check status of the process\n            assert self.__process is not None\n            # log one-time OpenCV warning\n            self.__logging and logger.info(\n                \"RGBA and 16-bit grayscale video frames are not supported by OpenCV yet. Kindly switch on `compression_mode` to use them!\"\n            )\n        # write frame directly to\n        # VideoWriter Class process\n        self.__process.write(frame)\n
"},{"location":"contribution/PR/","title":"Submitting Pull Request(PR) Guidelines","text":""},{"location":"contribution/PR/#submitting-pull-requestpr-guidelines","title":"Submitting Pull Request(PR) Guidelines:","text":"

The following guidelines tells you how to submit a valid PR for vidGear:

Working on your first Pull Request for VidGear?

"},{"location":"contribution/PR/#clone-testing-branch","title":"Clone Testing branch","text":"

Base Branch must be testing in your Pull Request

Every PR MUST be pushed against VidGear's testing branch only, in order to trigger must needed CI testing workflows. If your's not, then change the base branch to testing \u27b6

Make sure the testing branch of your Forked repository is up-to-date with VidGear, before starting working on Pull Request.

You can clone your Forked remote git to local and create your PR working branch as a sub-branch of latest testing branch as follows:

Functions of different VidGear's Github Branches

Following are the base branches for VidGear's code in its Github Repository:

Master/MainTestingDevelopment

Branch Features:

Cloning:

# clone your forked repository and `cd` inside\ngit clone https://github.com/abhiTronix/vidgear.git && cd vidgear\n

Branch Features:

Cloning:

# clone your forked repository and `cd` inside\ngit clone https://github.com/abhiTronix/vidgear.git && cd vidgear\n\n# checkout the latest testing branch\ngit checkout testing\n

Branch Features:

Cloning:

# clone your forked repository and `cd` inside\ngit clone https://github.com/abhiTronix/vidgear.git && cd vidgear\n\n# checkout the latest development branch\ngit checkout development\n

Workflow:

Typically any feature/improvement/bug-fix code flows as follows:

# clone your forked repository(change with your username) and get inside\ngit clone https://github.com/{YOUR USERNAME}/vidgear.git && cd vidgear\n\n# pull any recent updates\ngit pull\n\n# checkout the latest testing branch\ngit checkout testing\n\n# Now create your new branch with suitable name(such as \"subbranch_of_testing\")\ngit checkout -b subbranch_of_testing\n

Now after working with this newly created branch for your Pull Request, you can commit and push or merge it locally or remotely as usual.

"},{"location":"contribution/PR/#pr-submission-checklist","title":"PR Submission Checklist","text":"

There are some important checks you need to perform while submitting your Pull Request(s) for VidGear library:

"},{"location":"contribution/PR/#testing-formatting-linting","title":"Testing, Formatting & Linting","text":"

All Pull Request(s) must be tested, formatted & linted against our library standards as discussed below:

"},{"location":"contribution/PR/#requirements","title":"Requirements","text":"

Testing VidGear requires additional test dependencies and dataset, which can be handled manually as follows:

"},{"location":"contribution/PR/#running-tests","title":"Running Tests","text":"

All tests can be run with pytest(in VidGear's root folder) as follows:

 pytest -sv  #-sv for verbose output.\n
"},{"location":"contribution/PR/#formatting-linting","title":"Formatting & Linting","text":"

For formatting and linting, following libraries are used:

"},{"location":"contribution/PR/#frequently-asked-questions","title":"Frequently Asked Questions","text":"

Q1. Why do my changes taking so long to be Reviewed and/or Merged?

Submission Aftermaths

Pull requests will be reviewed by the maintainers and the rationale behind the maintainer\u2019s decision to accept or deny the changes will be posted in the pull request. Please wait for our code review and approval, possibly enhancing your change on request.

Q2. Would you accept a huge Pull Request with Lots of Changes?

First, make sure that the changes are somewhat related. Otherwise, please create separate pull requests. Anyway, before submitting a huge change, it's probably a good idea to open an issue in the VidGear Github repository to ask the maintainers if they agree with your proposed changes. Otherwise, they could refuse your proposal after you put all that hard work into making the changes. We definitely don't want you to waste your time!

"},{"location":"contribution/issue/","title":"Submitting an Issue Guidelines","text":""},{"location":"contribution/issue/#submitting-an-issue-guidelines","title":"Submitting an Issue Guidelines","text":"

If you've found a new bug or you've come up with some new feature which can improve the quality of the VidGear, then related issues are welcomed! But, Before you do, please read the following guidelines:

First Issue on GitHub?

You can easily learn about it from creating an issue wiki.

Info

Please note that your issue will be fixed much faster if you spend about half an hour preparing it, including the exact reproduction steps and a demo. If you're in a hurry or don't feel confident, it's fine to report issues with less details, but this makes it less likely they'll get fixed soon.

"},{"location":"contribution/issue/#search-the-docs-and-previous-issues","title":"Search the Docs and Previous Issues","text":""},{"location":"contribution/issue/#gather-required-information","title":"Gather Required Information","text":""},{"location":"contribution/issue/#follow-the-issue-template","title":"Follow the Issue Template","text":""},{"location":"contribution/issue/#raise-the-issue","title":"Raise the Issue","text":""},{"location":"gears/camgear/overview/","title":"Overview","text":""},{"location":"gears/camgear/overview/#camgear-api","title":"CamGear API","text":"CamGear API's generalized workflow"},{"location":"gears/camgear/overview/#overview","title":"Overview","text":"

CamGear supports a diverse range of video streams which can handle/control video stream almost any IP/USB Cameras, multimedia video file format (upto 4k tested), any network stream URL such as http(s), rtp, rtsp, rtmp, mms, etc. In addition to this, it also supports Gstreamer's RAW pipelines and various live video streaming sites like YouTube, Twitch, Dailymotion etc.

CamGear API provides a flexible, high-level multi-threaded wrapper around OpenCV's VideoCapture API with direct access to almost all of its available parameters. It relies on Threaded Queue mode for threaded, error-free and synchronized frame handling.

CamGear internally implements yt_dlp backend class for seamlessly pipelining live video-frames and metadata from various streaming services like YouTube, Twitch, and many more \u27b6

Helpful Tips

"},{"location":"gears/camgear/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through CamGear Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/camgear/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/camgear/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/camgear/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/camgear/params/","title":"Parameters","text":""},{"location":"gears/camgear/params/#camgear-api-parameters","title":"CamGear API Parameters","text":""},{"location":"gears/camgear/params/#source","title":"source","text":"

CamGear API will throw RuntimeError if source provided is invalid.

This parameter defines the source for the input stream.

Data-Type: Based on input.

Default Value: Its default value is 0.

Its valid input can be one of the following:

"},{"location":"gears/camgear/params/#stream_mode","title":"stream_mode","text":"

This parameter controls the Stream Mode, .i.e if enabled(stream_mode=True), the CamGear API will interpret the given source input as YouTube URL address.

Due to a FFmpeg bug that causes video to freeze frequently in OpenCV, It is advised to always use GStreamer backend for any livestream videos. Checkout this FAQ for compiling OpenCV with GStreamer support.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

Supported Streaming Websites

The complete list of all supported Streaming Websites URLs can be found here \u27b6

CamGear(source='https://youtu.be/bvetuLwJIkA', stream_mode=True)\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/camgear/params/#colorspace","title":"colorspace","text":"

This parameter selects the colorspace of the input stream.

Data-Type: String

Default Value: Its default value is None.

Usage:

All supported colorspace values are given here \u27b6

CamGear(source=0, colorspace=\"COLOR_BGR2HSV\")\n

Its complete usage example is given here \u27b6

"},{"location":"gears/camgear/params/#backend","title":"backend","text":"

This parameter manually selects the backend for OpenCV's VideoCapture class (only if specified).

Data-Type: Integer

Default Value: Its default value is 0

Usage:

All supported backends are listed here \u27b6

Its value can be for e.g. backend = cv2.CAP_DSHOW for selecting Direct Show as backend:

CamGear(source=0, backend = cv2.CAP_DSHOW)\n

"},{"location":"gears/camgear/params/#options","title":"options","text":"

This parameter provides the ability to alter various Source Tweak Parameters available within OpenCV's VideoCapture API properties.

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

All supported parameters are listed here \u27b6

The desired parameters can be passed to CamGear API by formatting them as this parameter's attributes, as follows:

# formatting parameters as dictionary attributes\noptions = {\"CAP_PROP_FRAME_WIDTH\":320, \"CAP_PROP_FRAME_HEIGHT\":240, \"CAP_PROP_FPS\":60}\n# assigning it\nCamGear(source=0, **options)\n

"},{"location":"gears/camgear/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

CamGear(source=0, logging=True)\n

"},{"location":"gears/camgear/params/#time_delay","title":"time_delay","text":"

This parameter set the time delay (in seconds) before the CamGear API start reading the frames. This delay is only required if the source required some warm-up delay before starting up.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

CamGear(source=0, time_delay=1) # set 1 seconds time delay\n

"},{"location":"gears/camgear/usage/","title":"Usage Examples","text":""},{"location":"gears/camgear/usage/#camgear-api-usage-examples","title":"CamGear API Usage Examples:","text":"

After going through following Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/camgear/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with CamGear API:

# import required libraries\nfrom vidgear.gears import CamGear\nimport cv2\n\n\n# open any valid video stream(for e.g `myvideo.avi` file)\nstream = CamGear(source=\"myvideo.avi\").start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/camgear/usage/#using-camgear-with-streaming-websites","title":"Using Camgear with Streaming Websites","text":"

CamGear internally implements yt_dlp backend class for seamlessly pipelining live video-frames and metadata from various streaming services like Twitch, Vimeo, Dailymotion, and many more \u27b6. All you have to do is to provide the desired Video's URL to its source parameter, and enable its stream_mode parameter.

The complete usage example for Dailymotion and Twitch URLs are as follows:

Bug in OpenCV's FFmpeg

To workaround a FFmpeg bug that causes video to freeze frequently in OpenCV, It is advised to always use GStreamer backend for Livestream videos.

Checkout this FAQ \u27b6 for compiling OpenCV with GStreamer support.

Not all resolutions are supported with GStreamer Backend. See issue #244

Exclusive CamGear Attributes for yt_dlp backend

CamGear also provides exclusive attributes:

with its options dictionary parameter. More information can be found here \u27b6

Supported Streaming Websites

The list of all supported Streaming Websites URLs can be found here \u27b6

Accessing Stream's Metadata

CamGear now provides ytv_metadata global parameter for accessing given Video's metadata as JSON Object. It can used as follows:

New in v0.2.4

ytv_metadata global parameter was added in v0.2.4.

# import required libraries\nfrom vidgear.gears import CamGear\n\n# Add YouTube Video URL as input source (for e.g https://www.dailymotion.com/video/x2yrnum)\n# and enable Stream Mode (`stream_mode = True`)\nstream = CamGear(\n    source=\"https://www.dailymotion.com/video/x2yrnum\", stream_mode=True, logging=True, **options\n).start()\n\n# get Video's metadata as JSON object\nvideo_metadata =  stream.ytv_metadata\n\n# print all available keys\nprint(video_metadata.keys())\n\n# get data like `title`\nprint(video_metadata[\"title\"])\n
Dailymotion Twitch
# import required libraries\nfrom vidgear.gears import CamGear\nimport cv2\n\n# set desired quality as 720p\noptions = {\"STREAM_RESOLUTION\": \"720p\"}\n\n# Add any desire Video URL as input source\n# for e.g https://vimeo.com/151666798\n# and enable Stream Mode (`stream_mode = True`)\nstream = CamGear(\n    source=\"https://www.dailymotion.com/video/x2yrnum\",\n    stream_mode=True,\n    logging=True,\n    **options\n).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

If Twitch user is offline, CamGear will throw ValueError.

# import required libraries\nfrom vidgear.gears import CamGear\nimport cv2\n\n# set desired quality as 720p\noptions = {\"STREAM_RESOLUTION\": \"720p\"}\n\n# Add any desire Video URL as input source\n# for e.g hhttps://www.twitch.tv/shroud\n# and enable Stream Mode (`stream_mode = True`)\nstream = CamGear(\n    source=\"https://www.twitch.tv/shroud\",\n    stream_mode=True,\n    logging=True,\n    **options\n).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/camgear/usage/#using-camgear-with-youtube-videos","title":"Using Camgear with Youtube Videos","text":"

CamGear API also provides out-of-the-box support for pipelining live video-frames and metadata from YouTube (Livestream + Normal) Videos.

YouTube Playlists are not supported yet.

The complete usage example is as follows:

Bug in OpenCV's FFmpeg

To workaround a FFmpeg bug that causes video to freeze frequently in OpenCV, It is advised to always use GStreamer backend for Livestream videos.

Checkout this FAQ \u27b6 for compiling OpenCV with GStreamer support.

Not all resolutions are supported with GStreamer Backend. See issue #244

Exclusive CamGear Attributes for yt_dlp backend

CamGear also provides exclusive attributes:

with its options dictionary parameter. More information can be found here \u27b6

Accessing Stream's Metadata

CamGear now provides ytv_metadata global parameter for accessing given Video's metadata as JSON Object. It can used as follows:

New in v0.2.4

ytv_metadata global parameter was added in v0.2.4.

# import required libraries\nfrom vidgear.gears import CamGear\n\n# Add YouTube Video URL as input source (for e.g https://youtu.be/uCy5OuSQnyA)\n# and enable Stream Mode (`stream_mode = True`)\nstream = CamGear(\n    source=\"https://youtu.be/uCy5OuSQnyA\", stream_mode=True, logging=True, **options\n).start()\n\n# get Video's metadata as JSON object\nvideo_metadata =  stream.ytv_metadata\n\n# print all available keys\nprint(video_metadata.keys())\n\n# get data like `title`\nprint(video_metadata[\"title\"])\n
# import required libraries\nfrom vidgear.gears import CamGear\nimport cv2\n\n# Add YouTube Video URL as input source (for e.g https://youtu.be/uCy5OuSQnyA)\n# and enable Stream Mode (`stream_mode = True`)\nstream = CamGear(\n    source=\"https://youtu.be/uCy5OuSQnyA\", \n    stream_mode=True,\n    logging=True\n).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/camgear/usage/#using-camgear-with-variable-camera-properties","title":"Using CamGear with Variable Camera Properties","text":"

CamGear API also flexibly support various Source Tweak Parameters available within OpenCV's VideoCapture API. These tweak parameters can be used to transform input source Camera-Device properties (such as its brightness, saturation, framerate, resolution, gain etc.) seamlessly, and can be easily applied in CamGear API through its options dictionary parameter by formatting them as its attributes.

The complete usage example is as follows:

All the supported Source Tweak Parameters can be found here \u27b6

# import required libraries\nfrom vidgear.gears import CamGear\nimport cv2\n\n\n# define suitable tweak parameters for your stream.\noptions = {\n    \"CAP_PROP_FRAME_WIDTH\": 320, # resolution 320x240\n    \"CAP_PROP_FRAME_HEIGHT\": 240,\n    \"CAP_PROP_FPS\": 60, # framerate 60fps\n}\n\n# To open live video stream on webcam at first index(i.e. 0) \n# device and apply source tweak parameters\nstream = CamGear(source=0, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/camgear/usage/#using-camgear-with-direct-colorspace-manipulation","title":"Using Camgear with Direct Colorspace Manipulation","text":"

CamGear API also supports Direct Colorspace Manipulation, which is ideal for changing source colorspace on the run.

A more detailed information on colorspace manipulation can be found here \u27b6

In following example code, we will start with HSV as source colorspace, and then we will switch to GRAY colorspace when W key is pressed, and then LAB colorspace when E key is pressed, finally default colorspace (i.e. BGR) when S key is pressed. Also, quit when Q key is pressed:

Any incorrect or None-type value, will immediately revert the colorspace to default i.e. BGR.

# import required libraries\nfrom vidgear.gears import CamGear\nimport cv2\n\n# Open any source of your choice, like Webcam first index(i.e. 0)\n# and change its colorspace to `HSV`\nstream = CamGear(source=0, colorspace=\"COLOR_BGR2HSV\", logging=True).start()\n\n# loop over\nwhile True:\n\n    # read HSV frames\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the HSV frame here}\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for key if pressed\n    key = cv2.waitKey(1) & 0xFF\n\n    # check if 'w' key is pressed\n    if key == ord(\"w\"):\n        # directly change colorspace at any instant\n        stream.color_space = cv2.COLOR_BGR2GRAY  # Now colorspace is GRAY\n\n    # check for 'e' key is pressed\n    if key == ord(\"e\"):\n        stream.color_space = cv2.COLOR_BGR2LAB  # Now colorspace is CieLAB\n\n    # check for 's' key is pressed\n    if key == ord(\"s\"):\n        stream.color_space = None  # Now colorspace is default(ie BGR)\n\n    # check for 'q' key is pressed\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/camgear/advanced/source_params/","title":"Source Tweak Parameters","text":""},{"location":"gears/camgear/advanced/source_params/#source-tweak-parameters-for-camgear-api","title":"Source Tweak Parameters for CamGear API","text":""},{"location":"gears/camgear/advanced/source_params/#overview","title":"Overview","text":"

With CamGear's options dictionary parameter, the user has the ability to alter various tweak parameters available within OpenCV's VideoCapture Class by formatting them as its attributes.

These tweak parameters can be used to transform input Camera-Source properties (such as its brightness, saturation, resolution, iso, gain etc.) seamlessly. All parameters supported by CamGear API are disscussed in this document.

"},{"location":"gears/camgear/advanced/source_params/#exclusive-camgear-attributes","title":"Exclusive CamGear Attributes","text":"CamGear's Exclusive Attributes

In addition to Source Tweak Parameters, CamGear also provides some exclusive attributes for its options dictionary parameters.

These attributes are as follows:

"},{"location":"gears/camgear/advanced/source_params/#supported-source-tweak-parameters","title":"Supported Source Tweak Parameters","text":"

All Source Tweak Parameters supported by CamGear API are as follows:

Remember, Not all parameters are supported by all cameras devices, which is one of the most troublesome thing with OpenCV library. Each camera type, from android cameras, to USB cameras , to professional ones, offers a different interface to modify its parameters. Therefore, there are many branches in OpenCV code to support as many of them, but of course, not all possible devices are covered, and thereby works. Furthermore, OpenCV does not return any helpful error messages regarding this problem, so it\u2019s pretty much based on trial and error.

You can easily check parameter values supported by your webcam, by hooking it to a Linux machine, and using the command v4l2-ctl -d 0 --list-formats-ext (where 0 is an index of the given camera) to list the supported video parameters and their values. If that doesn't works, refer to its datasheet (if available).

These parameters can be passed to CamGear's options dictionary parameter by formatting them as its string attributes. Its complete usage example is here \u27b6

Values Description CAP_PROP_POS_MSEC Current position of the video file in milliseconds. CAP_PROP_POS_FRAMES 0-based index of the frame to be decoded/captured next. CAP_PROP_POS_AVI_RATIO Relative position of the video file: 0=start of the film, 1=end of the film. CAP_PROP_FRAME_WIDTH Width of the frames in the video stream. CAP_PROP_FRAME_HEIGHT Height of the frames in the video stream. CAP_PROP_FPS Frame rate. CAP_PROP_FOURCC 4-character code of codec. see VideoWriter::fourcc. CAP_PROP_FRAME_COUNT Number of frames in the video file. CAP_PROP_FORMAT Format of the Mat objects returned by VideoCapture::retrieve(). CAP_PROP_MODE Backend-specific value indicating the current capture mode. CAP_PROP_BRIGHTNESS Brightness of the image (only for those cameras that support). CAP_PROP_CONTRAST Contrast of the image (only for cameras). CAP_PROP_SATURATION Saturation of the image (only for cameras). CAP_PROP_HUE Hue of the image (only for cameras). CAP_PROP_GAIN Gain of the image (only for those cameras that support). CAP_PROP_EXPOSURE Exposure (only for those cameras that support). CAP_PROP_CONVERT_RGB Boolean flags indicating whether images should be converted to RGB. CAP_PROP_WHITE_BALANCE_BLUE_U Currently unsupported. CAP_PROP_RECTIFICATION Rectification flag for stereo cameras (note: only supported by DC1394 v 2.x backend currently). CAP_PROP_MONOCHROME CAP_PROP_SHARPNESS CAP_PROP_AUTO_EXPOSURE DC1394: exposure control done by camera, user can adjust reference level using this feature. CAP_PROP_GAMMA CAP_PROP_TEMPERATURE CAP_PROP_TRIGGER CAP_PROP_TRIGGER_DELAY CAP_PROP_WHITE_BALANCE_RED_V CAP_PROP_ZOOM CAP_PROP_FOCUS CAP_PROP_GUID CAP_PROP_ISO_SPEED CAP_PROP_BACKLIGHT CAP_PROP_PAN CAP_PROP_TILT CAP_PROP_ROLL CAP_PROP_IRIS CAP_PROP_SETTINGS Pop up video/camera filter dialog (note: only supported by DSHOW backend currently. The property value is ignored) CAP_PROP_BUFFERSIZE CAP_PROP_AUTOFOCUS CAP_PROP_SAR_NUM Sample aspect ratio: num/den (num) CAP_PROP_SAR_DEN Sample aspect ratio: num/den (den) CAP_PROP_BACKEND Current backend (enum VideoCapture APIs). Read-only property. CAP_PROP_CHANNEL Video input or Channel Number (only for those cameras that support) CAP_PROP_AUTO_WB enable/ disable auto white-balance CAP_PROP_WB_TEMPERATURE white-balance color temperature

"},{"location":"gears/netgear/overview/","title":"Overview","text":""},{"location":"gears/netgear/overview/#netgear-api","title":"NetGear API","text":"NetGear API generalized"},{"location":"gears/netgear/overview/#overview","title":"Overview","text":"

NetGear is exclusively designed to transfer video frames synchronously and asynchronously between interconnecting systems over the network in real-time.

NetGear implements a high-level wrapper around PyZmQ python library that contains python bindings for ZeroMQ - a high-performance asynchronous distributed messaging library that provides a message queue, but unlike message-oriented middleware, its system can run without a dedicated message broker.

NetGear also enables real-time JPEG Frame Compression capabilities for boosting performance significantly while sending video-frames over the network in real-time.

Lazy Pirate pattern in NetGear API

NetGear API now internally implements robust Lazy Pirate pattern (auto-reconnection) for its synchronous messaging patterns(zmq.PAIR & zmq.REQ/zmq.REP) at both Server and Client ends, where its API instead of doing a blocking receive, will:

Netgear API also provides max_retries and request_timeout like attributes for controlling this polling.

NetGear as of now seamlessly supports three ZeroMQ messaging patterns:

whereas the supported protocol are: tcp and ipc.

"},{"location":"gears/netgear/overview/#modes-of-operation","title":"Modes of Operation","text":""},{"location":"gears/netgear/overview/#primary-modes","title":"Primary Modes","text":"

NetGear API primarily has two modes of operations:

"},{"location":"gears/netgear/overview/#exclusive-modes","title":"Exclusive Modes","text":"

In addition to the primary modes, NetGear API also offers application-specific Exclusive Modes:

Also, checkout this compatibility chart for these modes interoperability.

"},{"location":"gears/netgear/overview/#a-multi-servers-mode","title":"A. Multi-Servers Mode","text":""},{"location":"gears/netgear/overview/#b-multi-clients-mode","title":"B. Multi-Clients Mode","text":""},{"location":"gears/netgear/overview/#c-bidirectional-mode","title":"C. Bidirectional Mode","text":""},{"location":"gears/netgear/overview/#d-ssh-tunneling-mode","title":"D. SSH Tunneling Mode","text":""},{"location":"gears/netgear/overview/#e-secure-mode","title":"E. Secure Mode","text":"

Important Information

"},{"location":"gears/netgear/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through NetGear Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/netgear/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/netgear/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/netgear/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/netgear/params/","title":"Parameters","text":""},{"location":"gears/netgear/params/#netgear-api-parameters","title":"NetGear API Parameters","text":""},{"location":"gears/netgear/params/#address","title":"address","text":"

This parameter sets the valid Network IP address for Server/Client. Network addresses are unique identifiers across the network.

Data-Type: String

Default Value: Its default value is based on selected primary mode, i.e 'localhost' for Send Mode and '*' for Receive Mode on a local machine.

Usage:

NetGear(address=\"192.168.0.145\")\n

"},{"location":"gears/netgear/params/#port","title":"port","text":"

This parameter sets the valid Network Port for Server/Client. Network port is a number that identifies one side of a connection between two devices on the network and is used determine to which process or application a message should be delivered.

Exception for Exclusive Modes

In Multi-Servers Mode:

In Multi-Client Mode:

Data-Type: String or List/Tuple

Default Value: Its default value is '5555'

Usage:

NetGear(port=\"5575\")\n

"},{"location":"gears/netgear/params/#protocol","title":"protocol","text":"

This parameter sets the valid messaging protocol between server and client. A network protocol is a set of established rules that dictates how to format, transmit and receive data so computer network devices - from servers and routers to endpoints - can communicate regardless of the differences in their underlying infrastructures, designs or standards. Supported protocol are: 'tcp' and 'ipc'.

Data-Type: String

Default Value: Its default value is 'tcp'

Usage:

NetGear(protocol=\"ipc\")\n

"},{"location":"gears/netgear/params/#pattern","title":"pattern","text":"

This parameter sets the supported messaging pattern(flow of communication) between server and client. Messaging patterns are the network-oriented architectural pattern that describes the flow of communication between interconnecting systems. NetGear provides access to ZeroMQ's pre-optimized sockets which enables you to take advantage of these patterns.

Data-Type: Integer

Default Value: Its default value is 0 (i.e zmq.PAIR).

Supported ZMQ patterns

All supported ZMQ patterns for NetGear are:

Usage:

NetGear(pattern=1) # sets zmq.REQ/zmq.REP pattern\n

"},{"location":"gears/netgear/params/#receive_mode","title":"receive_mode","text":"

This parameter select the Netgear's Mode of operation. It basically activates Receive Mode(if True) and Send Mode(if False). Furthermore, recv() method will only work when this flag is enabled(i.e. Receive Mode), whereas send() method will only work when this flag is disabled(i.e.Send Mode).

Data-Type: Boolean

Default Value: Its default value is False(i.e. Send Mode is activated by default).

Usage:

NetGear(receive_mode=True) # activates Recieve Mode\n

"},{"location":"gears/netgear/params/#options","title":"options","text":"

This parameter provides the flexibility to alter various NetGear API's internal properties, modes, and some PyZMQ flags.

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

Supported dictionary attributes for NetGear API

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

The desired attributes can be passed to NetGear API as follows:

# formatting parameters as dictionary attributes\noptions = {\n    \"secure_mode\": 2,\n    \"custom_cert_location\": \"/home/foo/foo1/foo2\",\n    \"overwrite_cert\": True,\n    \"flag\": 0, \n    \"copy\": True, \n    \"track\": False\n}\n# assigning it\nNetGear(logging=True, **options)\n

"},{"location":"gears/netgear/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

NetGear_Async(logging=True)\n

"},{"location":"gears/netgear/usage/","title":"Usage Examples","text":""},{"location":"gears/netgear/usage/#netgear-api-usage-examples","title":"NetGear API Usage Examples:","text":"

Important Information

After going through following Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/netgear/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with NetGear API:

"},{"location":"gears/netgear/usage/#servers-end","title":"Server's End","text":"

Open your favorite terminal and execute the following python code:

You can terminate both sides anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\n\n# open any valid video stream(for e.g `test.mp4` file)\nstream = VideoGear(source=\"test.mp4\").start()\n\n# Define Netgear Server with default parameters\nserver = NetGear()\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n
"},{"location":"gears/netgear/usage/#clients-end","title":"Client's End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n\n# define Netgear Client with `receive_mode = True` and default parameter\nclient = NetGear(receive_mode=True)\n\n# loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/usage/#using-netgear-with-variable-parameters","title":"Using NetGear with Variable Parameters","text":""},{"location":"gears/netgear/usage/#clients-end_1","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from the Server) and execute the following python code:

Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# define various tweak flags\noptions = {\"flag\": 0, \"copy\": True, \"track\": False}\n\n# Define Netgear Client at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n
"},{"location":"gears/netgear/usage/#servers-end_1","title":"Server's End","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\n\n# define various tweak flags\noptions = {\"flag\": 0, \"copy\": True, \"track\": False}\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = VideoGear(source=0).start()\n\n# Define Netgear server at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/usage/#using-netgear-with-opencv","title":"Using NetGear with OpenCV","text":"

You can easily use NetGear directly with any Video Processing library such as OpenCV itself. The complete usage example is as follows:

"},{"location":"gears/netgear/usage/#clients-end_2","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from the Server) and execute the following python code:

Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# define tweak flags\noptions = {\"flag\": 0, \"copy\": True, \"track\": False}\n\n# Define Netgear Client at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=0,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the received frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n
"},{"location":"gears/netgear/usage/#servers-end_2","title":"Server's End","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = cv2.VideoCapture(0)\n\n# define tweak flags\noptions = {\"flag\": 0, \"copy\": True, \"track\": False}\n\n# Define Netgear Client at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=0,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        (grabbed, frame) = stream.read()\n\n        # check for frame if not grabbed\n        if not grabbed:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.release()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/usage/#using-netgear-with-other-videocapture-gears","title":"Using NetGear with Other VideoCapture Gears","text":"

You can use any VideoCapture Gear in the similar manner. Let's implement given usage example with ScreenGear:

"},{"location":"gears/netgear/usage/#clients-end_3","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from the Server) and execute the following python code:

Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# define various tweak flags\noptions = {\"flag\": 0, \"copy\": True, \"track\": False}\n\n# Define Netgear Client at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n
"},{"location":"gears/netgear/usage/#servers-end_3","title":"Server's End","text":"

Now, Open the terminal on another Server System (let's say you want to transmit Monitor Screen Frames from a Laptop), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import ScreenGear\nfrom vidgear.gears import NetGear\n\n# define various tweak flags\noptions = {\"flag\": 0, \"copy\": True, \"track\": False}\n\n# Start capturing live Monitor screen frames with default settings\nstream = ScreenGear().start()\n\n# Define Netgear server at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/bidirectional_mode/","title":"Bidirectional Mode","text":""},{"location":"gears/netgear/advanced/bidirectional_mode/#bidirectional-mode-for-netgear-api","title":"Bidirectional Mode for NetGear API","text":"NetGear's Bidirectional Mode"},{"location":"gears/netgear/advanced/bidirectional_mode/#overview","title":"Overview","text":"

Bidirectional Mode enables seamless support for Bidirectional data transmission between Client/Consumer and Sender/Publisher along with video-frames through its synchronous messaging patterns such as zmq.PAIR (ZMQ Pair Pattern) & zmq.REQ/zmq.REP (ZMQ Request/Reply Pattern).

In Bidirectional Mode, we utilizes the NetGear API's message parameter of send() method for sending data from Server-to-Client, and return_data parameter of recv() method to return data back from Client-to-Server all while transferring frames in real-time.

This mode can be easily activated in NetGear through bidirectional_mode attribute of its options dictionary parameter during initialization.

Important Information regarding Bidirectional Mode

"},{"location":"gears/netgear/advanced/bidirectional_mode/#features-of-bidirectional-mode","title":"Features of Bidirectional Mode","text":"

"},{"location":"gears/netgear/advanced/bidirectional_mode/#exclusive-parameters","title":"Exclusive Parameters","text":"

To send data bidirectionally, NetGear API provides two exclusive parameters for its methods:

"},{"location":"gears/netgear/advanced/bidirectional_mode/#usage-examples","title":"Usage Examples","text":""},{"location":"gears/netgear/advanced/bidirectional_mode/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with Bidirectional Mode in NetGear API:

"},{"location":"gears/netgear/advanced/bidirectional_mode/#server-end","title":"Server End","text":"

Open your favorite terminal and execute the following python code:

You can terminate both sides anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\n\n# open any valid video stream(for e.g `test.mp4` file)\nstream = VideoGear(source=\"test.mp4\").start()\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# Define NetGear Server with defined parameters\nserver = NetGear(logging=True, **options)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # send frame & data and also receive data from Client\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # print data just received from Client\n        if not (recv_data is None):\n            print(recv_data)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.
"},{"location":"gears/netgear/advanced/bidirectional_mode/#client-end","title":"Client End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# define NetGear Client with `receive_mode = True` and defined parameter\nclient = NetGear(receive_mode=True, logging=True, **options)\n\n# loop over\nwhile True:\n\n    # prepare data to be sent\n    target_data = \"Hi, I am a Client here.\"\n\n    # receive data from server and also send our data\n    data = client.recv(return_data=target_data)\n\n    # check for data if None\n    if data is None:\n        break\n\n    # extract server_data & frame from data\n    server_data, frame = data\n\n    # again check for frame if None\n    if frame is None:\n        break\n\n    # {do something with the extracted frame and data here}\n\n    # lets print extracted server data\n    if not (server_data is None):\n        print(server_data)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/bidirectional_mode/#using-bidirectional-mode-with-variable-parameters","title":"Using Bidirectional Mode with Variable Parameters","text":""},{"location":"gears/netgear/advanced/bidirectional_mode/#clients-end","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from the Server) and execute the following python code:

Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# Define NetGear Client at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# loop over\nwhile True:\n\n    # prepare data to be sent\n    target_data = \"Hi, I am a Client here.\"\n\n    # receive data from server and also send our data\n    data = client.recv(return_data=target_data)\n\n    # check for data if None\n    if data is None:\n        break\n\n    # extract server_data & frame from data\n    server_data, frame = data\n\n    # again check for frame if None\n    if frame is None:\n        break\n\n    # {do something with the extracted frame and data here}\n\n    # lets print received server data\n    if not (server_data is None):\n        print(server_data)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/bidirectional_mode/#server-end_1","title":"Server End","text":"

Now, Open the terminal on another Server System (a Raspberry Pi with Camera Module), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this backend, otherwise nothing will work.

New Picamera2 backendLegacy Picamera backend
# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import PiGear\nfrom libcamera import Transform\n\n# add various Picamera2 API tweaks\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# Define NetGear server at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # send frame & data and also receive data from Client\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # print data just received from Client\n        if not (recv_data is None):\n            print(recv_data)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import PiGear\n\n# add various Picamera tweak parameters to dictionary\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# Define NetGear server at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # send frame & data and also receive data from Client\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # print data just received from Client\n        if not (recv_data is None):\n            print(recv_data)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.

"},{"location":"gears/netgear/advanced/bidirectional_mode/#using-bidirectional-mode-for-video-frames-transfer","title":"Using Bidirectional Mode for Video-Frames Transfer","text":"

In this example we are going to implement a bare-minimum example, where we will be sending video-frames (3-Dimensional numpy arrays) of the same Video bidirectionally at the same time, for testing the real-time performance and synchronization between the Server and the Client using this(Bidirectional) Mode.

This example is useful for building applications like Real-Time Video Chat.

We're also using reducer() method for reducing frame-size on-the-go for additional performance.

Remember, Sending large HQ video-frames may required more network bandwidth and packet size which may lead to video latency!

"},{"location":"gears/netgear/advanced/bidirectional_mode/#server-end_2","title":"Server End","text":"

Open your favorite terminal and execute the following python code:

You can terminate both side anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears.helper import reducer\nimport numpy as np\nimport cv2\n\n# open any valid video stream(for e.g `test.mp4` file)\nstream = cv2.VideoCapture(\"test.mp4\")\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# Define NetGear Server with defined parameters\nserver = NetGear(pattern=1, logging=True, **options)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        (grabbed, frame) = stream.read()\n\n        # check for frame if not grabbed\n        if not grabbed:\n            break\n\n        # reducer frames size if you want more performance, otherwise comment this line\n        frame = reducer(frame, percentage=30)  # reduce frame by 30%\n\n        # {do something with the frame here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # send frame & data and also receive data from Client\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # check data just received from Client is of numpy datatype\n        if not (recv_data is None) and isinstance(recv_data, np.ndarray):\n\n            # {do something with received numpy array here}\n\n            # Let's show it on output window\n            cv2.imshow(\"Received Frame\", recv_data)\n            key = cv2.waitKey(1) & 0xFF\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.release()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.

"},{"location":"gears/netgear/advanced/bidirectional_mode/#client-end_1","title":"Client End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears.helper import reducer\nimport cv2\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# again open the same video stream\nstream = cv2.VideoCapture(\"test.mp4\")\n\n# define NetGear Client with `receive_mode = True` and defined parameter\nclient = NetGear(receive_mode=True, pattern=1, logging=True, **options)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n        break\n\n    # reducer frames size if you want more performance, otherwise comment this line\n    frame = reducer(frame, percentage=30)  # reduce frame by 30%\n\n    # receive data from server and also send our data\n    data = client.recv(return_data=frame)\n\n    # check for data if None\n    if data is None:\n        break\n\n    # extract server_data & frame from data\n    server_data, frame = data\n\n    # again check for frame if None\n    if frame is None:\n        break\n\n    # {do something with the extracted frame and data here}\n\n    # lets print extracted server data\n    if not (server_data is None):\n        print(server_data)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.release()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/bidirectional_mode/#using-bidirectional-mode-for-video-frames-transfer-with-frame-compression","title":"Using Bidirectional Mode for Video-Frames Transfer with Frame Compression","text":"

This usage examples can be found here \u27b6

  1. Additional data of numpy.ndarray data-type is ONLY SUPPORTED at Client's end with its return_data parameter.

    \u21a9\u21a9

"},{"location":"gears/netgear/advanced/compression/","title":"Frame Compression","text":""},{"location":"gears/netgear/advanced/compression/#frame-compression-for-netgear-api","title":"Frame Compression for NetGear API","text":""},{"location":"gears/netgear/advanced/compression/#overview","title":"Overview","text":"

NetGear API enables real-time JPEG Frame Compression capabilities for optimizing performance significantly while sending frames over the network.

For enabling Frame Compression, NetGear uses powerful simplejpeg library at its backend, which is based on recent versions of libjpeg-turbo JPEG image codec, to accelerate baseline JPEG compression and decompression on all modern systems. NetGear API employs its exposed decode_jpeg and encode_jpeg methods to encode video-frames to JFIF format before sending it at Server, and cleverly decode it at the Client(s) all in real-time, thereby leveraging performance at cost of minor loss in frame quality.

Frame Compression is enabled by default in NetGear, and can be easily controlled through jpeg_compression_quality, jpeg_compression_fastdct, jpeg_compression_fastupsample like attributes of its options dictionary parameter during initialization.

Useful Information about Frame Compression

Frame Compression is primarily controlled by Server end. That means, if Frame Compression is enabled at Server, then Client(s) will automatically enforce the Frame Compression with defined performance attributes. Otherwise if it is disabled, then Client(s) disables it too.

"},{"location":"gears/netgear/advanced/compression/#exclusive-attributes","title":"Exclusive Attributes","text":"

For implementing Frame Compression, NetGear API currently provide following exclusive attribute for its options dictionary parameter to leverage performance with Frame Compression:

"},{"location":"gears/netgear/advanced/compression/#performance-attributes","title":"Performance Attributes","text":""},{"location":"gears/netgear/advanced/compression/#usage-examples","title":"Usage Examples","text":""},{"location":"gears/netgear/advanced/compression/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with Frame Compression in NetGear API:

"},{"location":"gears/netgear/advanced/compression/#server-end","title":"Server End","text":"

Open your favorite terminal and execute the following python code:

You can terminate both sides anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\nimport cv2\n\n# open any valid video stream(for e.g `test.mp4` file)\nstream = VideoGear(source=\"test.mp4\").start()\n\n# activate jpeg encoding and specify other related parameters\noptions = {\n    \"jpeg_compression\": True,\n    \"jpeg_compression_quality\": 90,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# Define NetGear Server with defined parameters\nserver = NetGear(pattern=1, logging=True, **options)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if None-type\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/compression/#client-end","title":"Client End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

You can terminate client anytime by pressing Ctrl+C on your keyboard!

If compression is enabled at Server, then Client will automatically enforce Frame Compression with its performance attributes.

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# define NetGear Client with `receive_mode = True` and defined parameter\nclient = NetGear(receive_mode=True, pattern=1, logging=True)\n\n# loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/compression/#bare-minimum-usage-with-variable-colorspace","title":"Bare-Minimum Usage with Variable Colorspace","text":"

Frame Compression also supports specify incoming frames colorspace with compression. In following bare-minimum code, we will be sending GRAY frames from Server to Client:

New in v0.2.2

This example was added in v0.2.2.

This example works in conjunction with Source ColorSpace manipulation for VideoCapture Gears \u27b6

Supported colorspace values are RGB, BGR, RGBX, BGRX, XBGR, XRGB, GRAY, RGBA, BGRA, ABGR, ARGB, CMYK. More information can be found here \u27b6

"},{"location":"gears/netgear/advanced/compression/#server-end_1","title":"Server End","text":"

Open your favorite terminal and execute the following python code:

You can terminate both sides anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\nimport cv2\n\n# open any valid video stream(for e.g `test.mp4` file) and change its colorspace to grayscale\nstream = VideoGear(source=\"test.mp4\", colorspace=\"COLOR_BGR2GRAY\").start()\n\n# activate jpeg encoding and specify other related parameters\noptions = {\n    \"jpeg_compression\": \"GRAY\", # set grayscale\n    \"jpeg_compression_quality\": 90,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# Define NetGear Server with defined parameters\nserver = NetGear(pattern=1, logging=True, **options)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read grayscale frames from stream\n        frame = stream.read()\n\n        # check for frame if None-type\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send grayscale frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/compression/#client-end_1","title":"Client End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

You can terminate client anytime by pressing Ctrl+C on your keyboard!

If compression is enabled at Server, then Client will automatically enforce Frame Compression with its performance attributes.

Client's end also automatically enforces Server's colorspace, there's no need to define it again.

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# define NetGear Client with `receive_mode = True` and defined parameter\nclient = NetGear(receive_mode=True, pattern=1, logging=True)\n\n# loop over\nwhile True:\n\n    # receive grayscale frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the grayscale frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Grayscale Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/compression/#using-frame-compression-with-variable-parameters","title":"Using Frame Compression with Variable Parameters","text":""},{"location":"gears/netgear/advanced/compression/#clients-end","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from the Server) and execute the following python code:

Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

If compression is enabled at Server, then Client will automatically enforce Frame Compression with its performance attributes.

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n\n# Define NetGear Client at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n#  loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/compression/#server-end_2","title":"Server End","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate jpeg encoding and specify other related parameters\noptions = {\n    \"jpeg_compression\": True,\n    \"jpeg_compression_quality\": 90,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = VideoGear(source=0).start()\n\n# Define NetGear server at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/compression/#using-bidirectional-mode-for-video-frames-transfer-with-frame-compression","title":"Using Bidirectional Mode for Video-Frames Transfer with Frame Compression","text":"

NetGear now supports Dual Frame Compression for transferring video-frames with its exclusive Bidirectional Mode for achieving unmatchable performance bidirectionally. You can easily enable Frame Compression with its performance attributes at both ends to boost performance bidirectionally.

In this example we are going to implement a bare-minimum example, where we will be sending video-frames (3-Dimensional numpy arrays) of the same Video bidirectionally at the same time for testing the real-time performance and synchronization between the Server and Client using Bidirectional Mode. Furthermore, we're going to use optimal Dual Frame Compression Setting for Sending and Receiving frames at both Server and Client end.

This example is great for building applications like Real-time Video Chat System.

This Dual Frame Compression feature also available for Multi-Clients Mode.

We're also using reducer() Helper method for reducing frame-size on-the-go for additional performance.

Remember to define Frame Compression's performance attributes both on Server and Client ends in Dual Frame Compression to boost performance bidirectionally!

"},{"location":"gears/netgear/advanced/compression/#server-end_3","title":"Server End","text":"

Open your favorite terminal and execute the following python code:

You can terminate both side anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears.helper import reducer\nimport numpy as np\nimport cv2\n\n# open any valid video stream(for e.g `test.mp4` file)\nstream = cv2.VideoCapture(\"test.mp4\")\n\n# activate Bidirectional mode and Frame Compression\noptions = {\n    \"bidirectional_mode\": True,\n    \"jpeg_compression\": True,\n    \"jpeg_compression_quality\": 95,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# Define NetGear Server with defined parameters\nserver = NetGear(pattern=1, logging=True, **options)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        (grabbed, frame) = stream.read()\n\n        # check for frame if not grabbed\n        if not grabbed:\n            break\n\n        # reducer frames size if you want even more performance, otherwise comment this line\n        frame = reducer(frame, percentage=20)  # reduce frame by 20%\n\n        # {do something with the frame here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # send frame & data and also receive data from Client\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # check data just received from Client is of numpy datatype\n        if not (recv_data is None) and isinstance(recv_data, np.ndarray):\n\n            # {do something with received numpy array here}\n\n            # Let's show it on output window\n            cv2.imshow(\"Received Frame\", recv_data)\n            key = cv2.waitKey(1) & 0xFF\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.release()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.

"},{"location":"gears/netgear/advanced/compression/#client-end_2","title":"Client End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears.helper import reducer\nimport cv2\n\n# activate Bidirectional mode and Frame Compression\noptions = {\n    \"bidirectional_mode\": True,\n    \"jpeg_compression\": True,\n    \"jpeg_compression_quality\": 95,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# again open the same video stream\nstream = cv2.VideoCapture(\"test.mp4\")\n\n# define NetGear Client with `receive_mode = True` and defined parameter\nclient = NetGear(receive_mode=True, pattern=1, logging=True, **options)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n        break\n\n    # reducer frames size if you want even more performance, otherwise comment this line\n    frame = reducer(frame, percentage=20)  # reduce frame by 20%\n\n    # receive data from server and also send our data\n    data = client.recv(return_data=frame)\n\n    # check for data if None\n    if data is None:\n        break\n\n    # extract server_data & frame from data\n    server_data, frame = data\n\n    # again check for frame if None\n    if frame is None:\n        break\n\n    # {do something with the extracted frame and data here}\n\n    # lets print extracted server data\n    if not (server_data is None):\n        print(server_data)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.release()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/multi_client/","title":"Multi-Clients Mode","text":""},{"location":"gears/netgear/advanced/multi_client/#multi-clients-mode-for-netgear-api","title":"Multi-Clients Mode for NetGear API","text":"NetGear's Multi-Clients Mode"},{"location":"gears/netgear/advanced/multi_client/#overview","title":"Overview","text":"

In Multi-Clients Mode, NetGear robustly handles Multiple Clients at once thereby able to broadcast frames and data across multiple Clients/Consumers in the network at same time. This mode works contrary to Multi-Servers Mode such that every new Client that connects to single Server can be identified by its unique port address on the network.

The supported patterns for this mode are Publish/Subscribe (zmq.PUB/zmq.SUB) and Request/Reply(zmq.REQ/zmq.REP) and can be easily activated in NetGear API through multiclient_mode attribute of its options dictionary parameter during initialization.

Multi-Clients Mode is best for broadcasting Meta-Data with Video-frames to specific limited number of clients in real time. But if you're looking to scale broadcast to a very large pool of clients, then see our WebGear or WebGear_RTC APIs.

Important Information regarding Multi-Clients Mode

"},{"location":"gears/netgear/advanced/multi_client/#features-of-multi-clients-mode","title":"Features of Multi-Clients Mode","text":"

"},{"location":"gears/netgear/advanced/multi_client/#usage-examples","title":"Usage Examples","text":"

Important

"},{"location":"gears/netgear/advanced/multi_client/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

In this example, we will capturing live video-frames from a source (a.k.a Server) with a webcam connected to it. Afterwards, those captured frame will be sent over the network to two independent system (a.k.a Clients) using this Multi-Clients Mode in NetGear API. Finally, both Clients will be displaying received frames in Output Windows in real time.

This example is useful for building applications like Real-Time Video Broadcasting to multiple clients in local network.

"},{"location":"gears/netgear/advanced/multi_client/#servers-end","title":"Server's End","text":"

Now, Open the terminal on a Server System (with a webcam connected to it at index 0). Now execute the following python code:

Important Notes

Frame/Data transmission will NOT START untill all given Client(s) are connected to this Server.

You can terminate streaming anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import CamGear\n\n# Open suitable video stream (webcam on first index in our case)\nstream = CamGear(source=0).start()\n\n# activate multiclient_mode mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple of\n# all unique Server((5577,5578) in our case) and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.x.x\",\n    port=(5567, 5577),\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# Define received data dictionary\ndata_dict = {}\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if not None-type\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame and also receive data from Client(s)\n        recv_data = server.send(frame)\n\n        # check if valid data received\n        if not (recv_data is None):\n            # extract unique port address and its respective data\n            unique_address, data = recv_data\n            # update the extracted data in the data dictionary\n            data_dict[unique_address] = data\n\n        if data_dict:\n            # print data just received from Client(s)\n            for key, value in data_dict.items():\n                print(\"Client at port {} said: {}\".format(key, value))\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#client-1s-end","title":"Client-1's End","text":"

Now, Open a terminal on another Client System (where you want to display the input frames received from Server), let's name it Client-1. Execute the following python code:

Replace the IP address in the following code with Server's IP address you noted earlier and also assign a unique port address (required by Server to identify this system).

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate Multi-Clients mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at Server's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5567\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n) \n\n# loop over\nwhile True:\n    # receive data from server\n    frame = client.recv()\n\n    # check for frame if None\n    if frame is None:\n        break\n\n    # {do something with frame here}\n\n    # Show output window\n    cv2.imshow(\"Client 5567 Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#client-2s-end","title":"Client-2's End","text":"

Finally, Open a terminal on another Client System (where you want to display the input frames received from Server), let's name it Client-2. Execute the following python code:

Replace the IP address in the following code with Server's IP address you noted earlier and also assign a unique port address (required by Server to identify this system).

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate Multi-Clients mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at Server's IP address and assign a unique port address and other parameters\n # !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5577\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# loop over\nwhile True:\n\n    # receive data from server\n    frame = client.recv()\n\n    # check for frame if None\n    if frame is None:\n        break\n\n    # {do something with frame here}\n\n    # Show output window\n    cv2.imshow(\"Client 5577 Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#bare-minimum-usage-with-opencv","title":"Bare-Minimum Usage with OpenCV","text":"

In this example, we will be re-implementing previous bare-minimum example with OpenCV and NetGear API.

"},{"location":"gears/netgear/advanced/multi_client/#servers-end_1","title":"Server's End","text":"

Now, Open the terminal on a Server System (with a webcam connected to it at index 0). Now execute the following python code:

Important Notes

Frame/Data transmission will NOT START untill all given Client(s) are connected to this Server.

You can terminate streaming anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# Open suitable video stream (webcam on first index in our case)\nstream = cv2.VideoCapture(0)\n\n# activate multiclient_mode mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple of all unique Server((5577,5578) in our case) and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.x.x\",\n    port=(5567, 5577),\n    protocol=\"tcp\",\n    pattern=2,\n    logging=True,\n    **options\n)\n\n# Define received data dictionary\ndata_dict = {}\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        (grabbed, frame) = stream.read()\n\n        # check for frame if not grabbed\n        if not grabbed:\n            break\n\n        # {do something with the frame here}\n\n        # send frame and also receive data from Client(s)\n        recv_data = server.send(frame)\n\n        # check if valid data received\n        if not (recv_data is None):\n            # extract unique port address and its respective data\n            unique_address, data = recv_data\n            # update the extracted data in the data dictionary\n            data_dict[unique_address] = data\n\n        if data_dict:\n            # print data just received from Client(s)\n            for key, value in data_dict.items():\n                print(\"Client at port {} said: {}\".format(key, value))\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.release()\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#client-1s-end_1","title":"Client-1's End","text":"

Now, Open a terminal on another Client System (where you want to display the input frames received from Server), let's name it Client-1. Execute the following python code:

Replace the IP address in the following code with Server's IP address you noted earlier and also assign a unique port address (required by Server to identify this system).

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate Multi-Clients mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at Server's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5567\",\n    protocol=\"tcp\",\n    pattern=2,\n    receive_mode=True,\n    logging=True,\n    **options\n) \n\n# loop over\nwhile True:\n    # receive data from server\n    frame = client.recv()\n\n    # check for frame if None\n    if frame is None:\n        break\n\n    # {do something with frame here}\n\n    # Show output window\n    cv2.imshow(\"Client 5567 Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#client-2s-end_1","title":"Client-2's End","text":"

Finally, Open a terminal on another Client System (also, where you want to display the input frames received from Server), let's name it Client-2. Execute the following python code:

Replace the IP address in the following code with Server's IP address you noted earlier and also assign a unique port address (required by Server to identify this system).

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate Multi-Clients mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at Server's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5577\",\n    protocol=\"tcp\",\n    pattern=2,\n    receive_mode=True,\n    logging=True,\n    **options\n) \n\n# loop over\nwhile True:\n    # receive data from server\n    frame = client.recv()\n\n    # check for frame if None\n    if frame is None:\n        break\n\n    # {do something with frame here}\n\n    # Show output window\n    cv2.imshow(\"Client 5577 Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#using-multi-clients-mode-for-unidirectional-custom-data-transfer","title":"Using Multi-Clients Mode for Unidirectional Custom Data Transfer","text":"

Abstract

With Multi-Clients Mode, you can also send additional data of any data-type (such as list, tuple, string, int, ndarray etc.) along with frame, from all connected Clients(s) back to a Server unidirectionally.

In Multi-Clients Mode, unidirectional data transfer ONLY works with pattern 1 (i.e. Request/Reply zmq.REQ/zmq.REP), and NOT with pattern 2 (i.e. Publish/Subscribe zmq.PUB/zmq.SUB)!

In this example, We will be transferring video-frames from a single Server (consisting of Raspberry Pi with Camera Module) over the network to two independent Client for displaying them in real-time. At the same time, we will be sending data (a Text String, for the sake of simplicity) from both the Client(s) back to our Server, which will be printed onto the terminal.

"},{"location":"gears/netgear/advanced/multi_client/#servers-end_2","title":"Server's End","text":"

Now, Open the terminal on a Server System (with a webcam connected to it at index 0). Now execute the following python code:

Important Notes

Frame/Data transmission will NOT START untill all given Client(s) are connected to this Server.

You can terminate streaming anytime by pressing Ctrl+C on your keyboard!

Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this backend, otherwise nothing will work.

New Picamera2 backendLegacy Picamera backend
# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import NetGear\n\n# add various Picamera2 tweak parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate multiclient_mode mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple of all unique Server((5577,5578) in our case) and other parameters\nserver = NetGear(\n    address=\"192.168.x.x\",\n    port=(5577, 5578),\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)  # !!! change following IP address '192.168.x.xxx' with yours !!!\n\n# Define received data dictionary\ndata_dict = {}\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame and also receive data from Client(s)\n        recv_data = server.send(frame)\n\n        # check if valid data received\n        if not (recv_data is None):\n            # extract unique port address and its respective data\n            unique_address, data = recv_data\n            # update the extracted data in the data dictionary\n            data_dict[unique_address] = data\n\n        if data_dict:\n            # print data just received from Client(s)\n            for key, value in data_dict.items():\n                print(\"Client at port {} said: {}\".format(key, value))\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import NetGear\n\n# add various Picamera tweak parameters to dictionary\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate multiclient_mode mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple of all unique Server((5577,5578) in our case) and other parameters\nserver = NetGear(\n    address=\"192.168.x.x\",\n    port=(5577, 5578),\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)  # !!! change following IP address '192.168.x.xxx' with yours !!!\n\n# Define received data dictionary\ndata_dict = {}\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame and also receive data from Client(s)\n        recv_data = server.send(frame)\n\n        # check if valid data received\n        if not (recv_data is None):\n            # extract unique port address and its respective data\n            unique_address, data = recv_data\n            # update the extracted data in the data dictionary\n            data_dict[unique_address] = data\n\n        if data_dict:\n            # print data just received from Client(s)\n            for key, value in data_dict.items():\n                print(\"Client at port {} said: {}\".format(key, value))\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#client-1s-end_2","title":"Client-1's End","text":"

Now, Open a terminal on another Client System (where you want to display the input frames received from Server), let's name it Client-1. Execute the following python code:

Replace the IP address in the following code with Server's IP address you noted earlier and also assign a unique port address (required by Server to identify this system).

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate Multi-Clients mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at Server's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5577\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# loop over\nwhile True:\n\n    # prepare data to be sent\n    target_data = \"Hi, I am 5577 Client here.\"\n\n    # receive data from server and also send our data\n    frame = client.recv(return_data=target_data)\n\n    # check for frame if None\n    if frame is None:\n        break\n\n    # {do something with frame here}\n\n    # Show output window\n    cv2.imshow(\"Client 5577 Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#client-2s-end_2","title":"Client-2's End","text":"

Finally, Open a terminal on another Client System (also, where you want to display the input frames received from Server), let's name it Client-2. Execute the following python code:

Replace the IP address in the following code with Server's IP address you noted earlier and also assign a unique port address (required by Server to identify this system).

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate Multi-Clients mode\noptions = {\"multiclient_mode\": True}\n\n# Define NetGear Client at Server's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5578\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n) \n\n# loop over\nwhile True:\n\n    # prepare data to be sent\n    target_data = \"Hi, I am 5578 Client here.\"\n\n    # receive data from server and also send our data\n    frame = client.recv(return_data=target_data)\n\n    # check for frame if None\n    if frame is None:\n        break\n\n    # {do something with frame here}\n\n    # Show output window\n    cv2.imshow(\"Client 5578 Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#using-multi-clients-mode-with-bidirectional-mode","title":"Using Multi-Clients Mode with Bidirectional Mode","text":"

Abstract

Multi-Clients Mode now also compatible with Bidirectional Mode, which lets you send additional data of any datatype1 along with frame in real-time bidirectionally between a single Server and all connected Client(s).

Important Information

New in v0.2.5

This example was added in v0.2.5.

In this example, We will be transferring video-frames and data (a Text String, for the sake of simplicity) from a single Server (In this case, Raspberry Pi with Camera Module) over the network to two independent Clients for displaying them both in real-time. At the same time, we will be sending data (a Text String, for the sake of simplicity) back from both the Client(s) to our Server, which will be printed onto the terminal.

"},{"location":"gears/netgear/advanced/multi_client/#servers-end_3","title":"Server's End","text":"

Now, Open the terminal on a Server System (with a webcam connected to it at index 0). Now execute the following python code:

Important Notes

Frame/Data transmission will NOT START untill all given Client(s) are connected to this Server.

You can terminate streaming anytime by pressing Ctrl+C on your keyboard!

Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this backend, otherwise nothing will work.

New Picamera2 backendLegacy Picamera backend
# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import NetGear\nfrom libcamera import Transform\n\n# add various Picamera2 tweak parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate both multiclient and bidirectional modes\noptions = {\"multiclient_mode\": True, \"bidirectional_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple of \n# all unique Server((5577,5578) in our case) and other parameters\nserver = NetGear(\n    address=\"192.168.x.x\",\n    port=(5577, 5578),\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)  # !!! change following IP address '192.168.x.xxx' with yours !!!\n\n# Define received data dictionary\ndata_dict = {}\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # send frame & data and also receive data from Client(s)\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # check if valid data received\n        if not (recv_data is None):\n            # extract unique port address and its respective data\n            unique_address, data = recv_data\n            # update the extracted data in the data dictionary\n            data_dict[unique_address] = data\n\n        if data_dict:\n            # print data just received from Client(s)\n            for key, value in data_dict.items():\n                print(\"Client at port {} said: {}\".format(key, value))\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import NetGear\n\n# add various Picamera tweak parameters to dictionary\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate both multiclient and bidirectional modes\noptions = {\"multiclient_mode\": True, \"bidirectional_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple of \n# all unique Server((5577,5578) in our case) and other parameters\nserver = NetGear(\n    address=\"192.168.x.x\",\n    port=(5577, 5578),\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)  # !!! change following IP address '192.168.x.xxx' with yours !!!\n\n# Define received data dictionary\ndata_dict = {}\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # send frame & data and also receive data from Client(s)\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # check if valid data received\n        if not (recv_data is None):\n            # extract unique port address and its respective data\n            unique_address, data = recv_data\n            # update the extracted data in the data dictionary\n            data_dict[unique_address] = data\n\n        if data_dict:\n            # print data just received from Client(s)\n            for key, value in data_dict.items():\n                print(\"Client at port {} said: {}\".format(key, value))\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.

"},{"location":"gears/netgear/advanced/multi_client/#client-1s-end_3","title":"Client-1's End","text":"

Now, Open a terminal on another Client System (where you want to display the input frames received from Server), let's name it Client-1. Execute the following python code:

Replace the IP address in the following code with Server's IP address you noted earlier and also assign a unique port address (required by Server to identify this system).

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate both multiclient and bidirectional modes\noptions = {\"multiclient_mode\": True, \"bidirectional_mode\": True}\n\n# Define NetGear Client at Server's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5577\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# loop over\nwhile True:\n\n    # prepare data to be sent\n    target_data = \"Hi, I am 5577 Client here.\"\n\n    # receive data from server and also send our data\n    data = client.recv(return_data=target_data)\n\n    # check for data if None\n    if data is None:\n        break\n\n    # extract server_data & frame from data\n    server_data, frame = data\n\n    # again check for frame if None\n    if frame is None:\n        break\n\n    # {do something with the extracted frame and data here}\n\n    # lets print extracted server data\n    if not (server_data is None):\n        print(server_data)\n\n    # Show output window\n    cv2.imshow(\"Client 5577 Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/multi_client/#client-2s-end_3","title":"Client-2's End","text":"

Finally, Open a terminal on another Client System (also, where you want to display the input frames received from Server), let's name it Client-2. Execute the following python code:

Replace the IP address in the following code with Server's IP address you noted earlier and also assign a unique port address (required by Server to identify this system).

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate both multiclient and bidirectional modes\noptions = {\"multiclient_mode\": True, \"bidirectional_mode\": True}\n\n# Define NetGear Client at Server's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5578\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n) \n\n# loop over\nwhile True:\n\n    # prepare data to be sent\n    target_data = \"Hi, I am 5578 Client here.\"\n\n    # receive data from server and also send our data\n    data = client.recv(return_data=target_data)\n\n    # check for data if None\n    if data is None:\n        break\n\n    # extract server_data & frame from data\n    server_data, frame = data\n\n    # again check for frame if None\n    if frame is None:\n        break\n\n    # {do something with the extracted frame and data here}\n\n    # lets print extracted server data\n    if not (server_data is None):\n        print(server_data)\n\n    # Show output window\n    cv2.imshow(\"Client 5578 Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

  1. Additional data of numpy.ndarray data-type is NOT SUPPORTED at Server's end with its message parameter.

    \u21a9

"},{"location":"gears/netgear/advanced/multi_server/","title":"Multi-Servers Mode","text":""},{"location":"gears/netgear/advanced/multi_server/#multi-servers-mode-for-netgear-api","title":"Multi-Servers Mode for NetGear API","text":"NetGear's Multi-Servers Mode"},{"location":"gears/netgear/advanced/multi_server/#overview","title":"Overview","text":"

In Multi-Servers Mode, NetGear API robustly handles Multiple Servers at once, thereby providing seamless access to frames and unidirectional data transfer across multiple Publishers/Servers in the network at the same time. Each new server connects to a single client can be identified by its unique port address on the network.

The supported patterns for this mode are Publish/Subscribe (zmq.PUB/zmq.SUB) and Request/Reply(zmq.REQ/zmq.REP) and can be easily activated in NetGear API through multiserver_mode attribute of its options dictionary parameter during initialization.

Important Information regarding Multi-Servers Mode

"},{"location":"gears/netgear/advanced/multi_server/#key-features","title":"Key Features","text":"

"},{"location":"gears/netgear/advanced/multi_server/#usage-examples","title":"Usage Examples","text":"

Example Assumptions

"},{"location":"gears/netgear/advanced/multi_server/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

In this example, we will capturing live video-frames on two independent sources (a.k.a Servers), each with a webcam connected to it. Afterwards, these frames will be sent over the network to a single system (a.k.a Client) using this Multi-Servers Mode in NetGear API in real time, and will be displayed as a live montage.

This example is useful for building applications like Real-Time Security System with multiple cameras.

"},{"location":"gears/netgear/advanced/multi_server/#clients-end","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from Multiple Servers) and execute the following python code:

Important Notes

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom imutils import build_montages # (1)\nimport cv2\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple \n# of all unique Server((5566,5567) in our case) and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=(5566, 5567),\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    **options\n)\n\n# Define received frame dictionary\nframe_dict = {}\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # receive data from network\n        data = client.recv()\n\n        # check if data received isn't None\n        if data is None:\n            break\n\n        # extract unique port address and its respective frame\n        unique_address, frame = data\n\n        # {do something with the extracted frame here}\n\n        # get extracted frame's shape\n        (h, w) = frame.shape[:2]\n\n        # update the extracted frame in the received frame dictionary\n        frame_dict[unique_address] = frame\n\n        # build a montage using data dictionary\n        montages = build_montages(frame_dict.values(), (w, h), (2, 1))\n\n        # display the montage(s) on the screen\n        for (i, montage) in enumerate(montages):\n\n            cv2.imshow(\"Montage Footage {}\".format(i), montage)\n\n        # check for 'q' key if pressed\n        key = cv2.waitKey(1) & 0xFF\n        if key == ord(\"q\"):\n            break\n\n    except KeyboardInterrupt:\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n
  1. For building Frames Montage you'll need imutils python library. Install it with pip install imutils command.

"},{"location":"gears/netgear/advanced/multi_server/#server-1s-end","title":"Server-1's End","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and let's called it Server-1. Now execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier and also assign a unique port address (required by Client to identify this system).

You can terminate stream anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import CamGear\n\n# Open suitable video stream (webcam on first index in our case)\nstream = CamGear(source=0).start()\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.x.x\", port=\"5566\", protocol=\"tcp\", pattern=1, **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if not None-type\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/multi_server/#server-2s-end","title":"Server-2's End","text":"

Finally, Open the terminal on another Server System (also with a webcam connected to it at index 0), and let's called it Server-2. Now execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier and also assign a unique port address (required by Client to identify this system).

You can terminate stream anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import CamGear\n\n# Open suitable video stream (webcam on first index in our case)\nstream = CamGear(source=0).start()\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.x.x\", port=\"5567\", protocol=\"tcp\", pattern=1, **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if not None-type\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/multi_server/#bare-minimum-usage-with-opencv","title":"Bare-Minimum Usage with OpenCV","text":"

In this example, we will be re-implementing previous bare-minimum example with OpenCV and NetGear API.

"},{"location":"gears/netgear/advanced/multi_server/#clients-end_1","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from Mutiple Servers) and execute the following python code:

Important Notes

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom imutils import build_montages # (1)\nimport cv2\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple of all \n# unique Server((5566,5567) in our case) and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=(5566, 5567),\n    protocol=\"tcp\",\n    pattern=2,\n    receive_mode=True,\n    **options\n)\n\n# Define received frame dictionary\nframe_dict = {}\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # receive data from network\n        data = client.recv()\n\n        # check if data received isn't None\n        if data is None:\n            break\n\n        # extract unique port address and its respective frame\n        unique_address, frame = data\n\n        # {do something with the extracted frame here}\n\n        # get extracted frame's shape\n        (h, w) = frame.shape[:2]\n\n        # update the extracted frame in the received frame dictionary\n        frame_dict[unique_address] = frame\n\n        # build a montage using data dictionary\n        montages = build_montages(frame_dict.values(), (w, h), (2, 1))\n\n        # display the montage(s) on the screen\n        for (i, montage) in enumerate(montages):\n\n            cv2.imshow(\"Montage Footage {}\".format(i), montage)\n\n        # check for 'q' key if pressed\n        key = cv2.waitKey(1) & 0xFF\n        if key == ord(\"q\"):\n            break\n\n    except KeyboardInterrupt:\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n
  1. For building Frames Montage you'll need imutils python library. Install it with pip install imutils command.

"},{"location":"gears/netgear/advanced/multi_server/#server-1s-end_1","title":"Server-1's End","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and let's called it Server-1. Now execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier and also assign a unique port address (required by Client to identify this system).

You can terminate stream anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# Open suitable video stream (webcam on first index in our case)\nstream = cv2.VideoCapture(0)\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameter\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.x.x\", port=\"5566\", protocol=\"tcp\", pattern=2, **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        (grabbed, frame) = stream.read()\n\n        # check for frame if not grabbed\n        if not grabbed:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.release()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/multi_server/#server-2s-end_1","title":"Server-2's End","text":"

Finally, Open the terminal on another Server System (also with a webcam connected to it at index 0), and let's called it Server-2. Now execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier and also assign a unique port address (required by Client to identify this system).

You can terminate stream anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# Open suitable video stream (webcam on first index in our case)\nstream = cv2.VideoCapture(0)\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.x.x\", port=\"5567\", protocol=\"tcp\", pattern=2, **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        (grabbed, frame) = stream.read()\n\n        # check for frame if not grabbed\n        if not grabbed:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.release()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/multi_server/#using-multi-servers-mode-for-unidirectional-custom-data-transfer","title":"Using Multi-Servers Mode for Unidirectional Custom Data Transfer","text":"

Abstract

With Multi-Servers Mode, you can send additional data of any datatype1 along with frame with frame in real-time, from all connected Server(s) to a single Client unidirectionally.

But numpy.ndarray data-type is NOT supported as data.

In this example, We will be transferring video-frames and data (a Text String, for the sake of simplicity) from two Servers (consisting of a Raspberry Pi with Camera Module & a Laptop with webcam) to a single Client over the network in real-time. The received video-frames at Client's end will displayed as a live montage, whereas the received data will be printed to the terminal.

"},{"location":"gears/netgear/advanced/multi_server/#clients-end_2","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from Mutiple Servers) and execute the following python code:

Important Notes

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom imutils import build_montages # (1)\nimport cv2\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple of all unique Server((5577,5578) in our case) and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=(5577, 5578),\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)  \n# Define received frame dictionary\nframe_dict = {}\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # receive data from network\n        data = client.recv()\n\n        # check if data received isn't None\n        if data is None:\n            break\n\n        # extract unique port address and its respective frame and received data\n        unique_address, extracted_data, frame = data\n\n        # {do something with the extracted frame and data here}\n        # let's display extracted data on our extracted frame\n        cv2.putText(\n            frame,\n            extracted_data,\n            (10, frame.shape[0] - 10),\n            cv2.FONT_HERSHEY_SIMPLEX,\n            0.6,\n            (0, 255, 0),\n            2,\n        )\n\n        # get extracted frame's shape\n        (h, w) = frame.shape[:2]\n\n        # update the extracted frame in the frame dictionary\n        frame_dict[unique_address] = frame\n\n        # build a montage using data dictionary\n        montages = build_montages(frame_dict.values(), (w, h), (2, 1))\n\n        # display the montage(s) on the screen\n        for (i, montage) in enumerate(montages):\n\n            cv2.imshow(\"Montage Footage {}\".format(i), montage)\n\n        # check for 'q' key if pressed\n        key = cv2.waitKey(1) & 0xFF\n        if key == ord(\"q\"):\n            break\n\n    except KeyboardInterrupt:\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n
  1. For building Frames Montage you'll need imutils python library. Install it with pip install imutils command.

"},{"location":"gears/netgear/advanced/multi_server/#server-1s-end_2","title":"Server-1's End","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and let's called it Server-1. Now execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier and also assign a unique port address (required by Client to identify this system).

You can terminate stream anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import VideoGear\nimport cv2\n\n# Open suitable video stream (webcam on first index in our case)\nstream = VideoGear(source=0).start()\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5577\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with frame and data(to be sent) here}\n\n        # let's prepare a text string as data\n        target_data = \"I'm Server-1 at Port: 5577\"\n\n        # send frame and data through server\n        server.send(frame, message=target_data) # (1)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.

"},{"location":"gears/netgear/advanced/multi_server/#server-2s-end_2","title":"Server-2's End","text":"

Finally, Open the terminal on another Server System (this time a Raspberry Pi with Camera Module connected to it), and let's called it Server-2. Now execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier and also assign a unique port address (required by Client to identify this system).

You can terminate stream anytime by pressing Ctrl+C on your keyboard!

Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this backend, otherwise nothing will work.

New Picamera2 backendLegacy Picamera backend
# import libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import PiGear\nfrom libcamera import Transform\nimport cv2\n\n# add various Picamera tweak parameters to dictionary\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.1.xxx\",\n    port=\"5578\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with frame and data(to be sent) here}\n\n        # let's prepare a text string as data\n        text = \"I'm Server-2 at Port: 5578\"\n\n        # send frame and data through server\n        server.send(frame, message=text)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream.\nstream.stop()\n\n# safely close server\nserver.close()\n
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import PiGear\nimport cv2\n\n# add various Picamera tweak parameters to dictionary\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate multiserver_mode\noptions = {\"multiserver_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.1.xxx\",\n    port=\"5578\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with frame and data(to be sent) here}\n\n        # let's prepare a text string as data\n        text = \"I'm Server-2 at Port: 5578\"\n\n        # send frame and data through server\n        server.send(frame, message=text)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream.\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/multi_server/#using-multi-servers-mode-with-bidirectional-mode","title":"Using Multi-Servers Mode with Bidirectional Mode","text":"

Abstract

Multi-Servers Mode now also compatible with Bidirectional Mode, which lets you send additional data of any datatype1 along with frame in real-time bidirectionally between a single Client and all connected Server(s).

Important Information

New in v0.2.5

This example was added in v0.2.5.

In this example, We will be transferring video-frames and data (a Text String, for the sake of simplicity) from two Servers (consisting of a Raspberry Pi with Camera Module & a Laptop with webcam) to a single Client, and at same time sending back data (a Text String, for the sake of simplicity) to them over the network all in real-time. The received video-frames at Client's end will displayed as a live montage, whereas the received data will be printed to the terminal.

"},{"location":"gears/netgear/advanced/multi_server/#clients-end_3","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from Mutiple Servers) and execute the following python code:

Important Notes

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom imutils import build_montages # (1)\nimport cv2\n\n# activate both multiserver and bidirectional modes\noptions = {\"multiserver_mode\": True, \"bidirectional_mode\": True}\n\n# Define NetGear Client at given IP address and assign list/tuple of all unique Server((5577,5578) in our case) and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.x\",\n    port=(5577, 5578),\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)  \n# Define received frame dictionary\nframe_dict = {}\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # prepare data to be sent\n        target_data = \"Hi, I am a Client here.\"\n\n        # receive data from server(s) and also send our data\n        data = client.recv(return_data=target_data)\n\n        # check if data received isn't None\n        if data is None:\n            break\n\n        # extract unique port address and its respective frame and received data\n        unique_address, extracted_data, frame = recv_data\n\n        # {do something with the extracted frame and data here}\n        # let's display extracted data on our extracted frame\n        cv2.putText(\n            frame,\n            extracted_data,\n            (10, frame.shape[0] - 10),\n            cv2.FONT_HERSHEY_SIMPLEX,\n            0.6,\n            (0, 255, 0),\n            2,\n        )\n\n        # get extracted frame's shape\n        (h, w) = frame.shape[:2]\n\n        # update the extracted frame in the frame dictionary\n        frame_dict[unique_address] = frame\n\n        # build a montage using data dictionary\n        montages = build_montages(frame_dict.values(), (w, h), (2, 1))\n\n        # display the montage(s) on the screen\n        for (i, montage) in enumerate(montages):\n\n            cv2.imshow(\"Montage Footage {}\".format(i), montage)\n\n        # check for 'q' key if pressed\n        key = cv2.waitKey(1) & 0xFF\n        if key == ord(\"q\"):\n            break\n\n    except KeyboardInterrupt:\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n
  1. For building Frames Montage you'll need imutils python library. Install it with pip install imutils command.

"},{"location":"gears/netgear/advanced/multi_server/#server-1s-end_3","title":"Server-1's End","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and let's called it Server-1. Now execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier and also assign a unique port address (required by Client to identify this system).

You can terminate stream anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import VideoGear\nimport cv2\n\n# Open suitable video stream (webcam on first index in our case)\nstream = VideoGear(source=0).start()\n\n# activate both multiserver and bidirectional modes\noptions = {\"multiserver_mode\": True, \"bidirectional_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.x.x\",\n    port=\"5577\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with frame and data(to be sent) here}\n\n        # let's prepare a text string as data\n        target_data = \"I'm Server-1 at Port: 5577\"\n\n        # send frame & data and also receive data from Client\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # print data just received from Client\n        if not (recv_data is None):\n            print(recv_data)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.

"},{"location":"gears/netgear/advanced/multi_server/#server-2s-end_3","title":"Server-2's End","text":"

Finally, Open the terminal on another Server System (this time a Raspberry Pi with Camera Module connected to it), and let's called it Server-2. Now execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier and also assign a unique port address (required by Client to identify this system).

You can terminate stream anytime by pressing Ctrl+C on your keyboard!

Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this backend, otherwise nothing will work.

New Picamera2 backendLegacy Picamera backend
# import libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import PiGear\nfrom libcamera import Transform\nimport cv2\n\n# add various Picamera2 tweak parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate both multiserver and bidirectional modes\noptions = {\"multiserver_mode\": True, \"bidirectional_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.1.xxx\",\n    port=\"5578\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with frame and data(to be sent) here}\n\n        # let's prepare a text string as data\n        target_data = \"I'm Server-2 at Port: 5578\"\n\n        # send frame & data and also receive data from Client\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # print data just received from Client\n        if not (recv_data is None):\n            print(recv_data)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream.\nstream.stop()\n\n# safely close server\nserver.close()\n
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import PiGear\nimport cv2\n\n# add various Picamera tweak parameters to dictionary\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# activate both multiserver and bidirectional modes\noptions = {\"multiserver_mode\": True, \"bidirectional_mode\": True}\n\n# Define NetGear Server at Client's IP address and assign a unique port address and other parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nserver = NetGear(\n    address=\"192.168.1.xxx\",\n    port=\"5578\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until Keyboard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with frame and data(to be sent) here}\n\n        # let's prepare a text string as data\n        target_data = \"I'm Server-2 at Port: 5578\"\n\n        # send frame & data and also receive data from Client\n        recv_data = server.send(frame, message=target_data) # (1)\n\n        # print data just received from Client\n        if not (recv_data is None):\n            print(recv_data)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream.\nstream.stop()\n\n# safely close server\nserver.close()\n
  1. Everything except numpy.ndarray datatype data is accepted as target_data in message parameter.

  1. Additional data of numpy.ndarray data-type is NOT SUPPORTED at Server(s) with their message parameter.

    \u21a9\u21a9\u21a9

"},{"location":"gears/netgear/advanced/secure_mode/","title":"Secure Mode","text":""},{"location":"gears/netgear/advanced/secure_mode/#secure-mode-for-netgear-api","title":"Secure Mode for NetGear API","text":""},{"location":"gears/netgear/advanced/secure_mode/#overview","title":"Overview","text":"

Secure Mode provides easy access to powerful, smart & secure ZeroMQ's Security Layers in NetGear API that enables strong encryption on data, and unbreakable authentication between the Server and the Client with the help of custom Certificates/keys and brings cheap, standardized privacy and authentication for distributed systems over the network.

Secure Mode uses a new wire protocol, ZMTP 3.0 that adds a security handshake to all ZeroMQ connections and a new security protocol, CurveZMQ, that implements \"perfect forward security\" between two ZeroMQ peers over a TCP connection.

Secure Mode can be easily activated in NetGear API through secure_mode attribute of its options dictionary parameter, during initialization. Furthermore, for managing this mode, NetGear API provides additional custom_cert_location & overwrite_cert like attribute too.

"},{"location":"gears/netgear/advanced/secure_mode/#supported-zmq-security-layers","title":"Supported ZMQ Security Layers","text":"

Secure mode supports the two most powerful ZMQ security layers:

Important Information regarding Secure Mode

"},{"location":"gears/netgear/advanced/secure_mode/#features","title":"Features","text":"

"},{"location":"gears/netgear/advanced/secure_mode/#exclusive-attributes","title":"Exclusive Attributes","text":"

For implementing Secure Mode, NetGear API currently provide following exclusive attribute for its options dictionary parameter:

"},{"location":"gears/netgear/advanced/secure_mode/#usage-examples","title":"Usage Examples","text":""},{"location":"gears/netgear/advanced/secure_mode/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with Secure Mode in NetGear API:

In Secure Mode, Client's end MUST run before the Server's end to establish a secure connection!

"},{"location":"gears/netgear/advanced/secure_mode/#clients-end","title":"Client's End","text":"

Open your favorite terminal and execute the following python code:

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate StoneHouse security mechanism\noptions = {\"secure_mode\": 1}\n\n# define NetGear Client with `receive_mode = True` and defined parameter\nclient = NetGear(pattern=1, receive_mode=True, logging=True, **options)\n\n# loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n
"},{"location":"gears/netgear/advanced/secure_mode/#servers-end","title":"Server's End","text":"

Then open another terminal on the same system and execute the following python code to send the frames to our client:

You can terminate both sides anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\n\n# open any valid video stream(for e.g `test.mp4` file)\nstream = VideoGear(source=\"test.mp4\").start()\n\n# activate StoneHouse security mechanism\noptions = {\"secure_mode\": 1}\n\n# Define NetGear Server with defined parameters\nserver = NetGear(pattern=1, logging=True, **options)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/secure_mode/#using-secure-mode-with-variable-parameters","title":"Using Secure Mode with Variable Parameters","text":""},{"location":"gears/netgear/advanced/secure_mode/#clients-end_1","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from the Server) and execute the following python code:

In Secure Mode, Client's end MUST run before the Server's end to establish a secure connection!

Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

You need to paste the Public+Secret Keypairs (generated at the Server End) at the $HOME/.vidgear/keys directory of your Client machine for a successful authentication!

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate IronHouse security mechanism\noptions = {\"secure_mode\": 2}\n\n# Define NetGear Client at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=2,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/secure_mode/#servers-end_1","title":"Server's End","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

You also need to copy the Public+Secret Keypairs (generated on running this example code) present in the $HOME/.vidgear/keys directory, and make available at Client's end for a successful authentication.

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\n\n# activate IronHouse security mechanism, and \n# [BEWARE!!!] generating new Keypairs for this example !!!\noptions = {\"secure_mode\": 2, \"overwrite_cert\": True}\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = VideoGear(source=0).start()\n\n# Define NetGear server at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=2,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear/advanced/ssh_tunnel/","title":"SSH Tunneling Mode","text":""},{"location":"gears/netgear/advanced/ssh_tunnel/#ssh-tunneling-mode-for-netgear-api","title":"SSH Tunneling Mode for NetGear API","text":"NetGear's Bidirectional Mode"},{"location":"gears/netgear/advanced/ssh_tunnel/#overview","title":"Overview","text":"New in v0.2.2

This document was added in v0.2.2.

SSH Tunneling Mode allows you to connect NetGear client and server via secure SSH connection over the untrusted network and access its intranet services across firewalls. This mode works with pyzmq's zmq.ssh module for tunneling ZeroMQ connections over ssh.

This mode implements SSH Remote Port Forwarding which enables accessing Host(client) machine outside the network by exposing port to the public Internet. Thereby, once you have established the tunnel, connections to local machine will actually be connections to remote machine as seen from the server.

Beware \u2620\ufe0f

Cybercriminals or malware could exploit SSH tunnels to hide their unauthorized communications, or to exfiltrate stolen data from the network. More information can be found here \u27b6

All patterns are valid for this mode and it can be easily activated in NetGear API at server end through ssh_tunnel_mode string attribute of its options dictionary parameter during initialization.

Important

Useful Tips

"},{"location":"gears/netgear/advanced/ssh_tunnel/#prerequisites","title":"Prerequisites","text":"

SSH Tunnel Mode requires pexpect or paramiko as an additional dependency which is not part of standard VidGear package. It can be easily installed via pypi as follows:

PramikoPexpect

paramiko is compatible with all platforms.

paramiko support is automatically enabled in ZeroMQ if installed.

# install paramiko\npip install paramiko\n

pexpect is NOT compatible with Windows Machines.

# install pexpect\npip install pexpect\n

"},{"location":"gears/netgear/advanced/ssh_tunnel/#exclusive-attributes","title":"Exclusive Attributes","text":"

All these attributes will work on Server end only whereas Client end will simply discard them.

For implementing SSH Tunneling Mode, NetGear API currently provide following exclusive attribute for its options dictionary parameter:

"},{"location":"gears/netgear/advanced/ssh_tunnel/#usage-example","title":"Usage Example","text":"Assumptions for this Example

In this particular example, we assume that:

"},{"location":"gears/netgear/advanced/ssh_tunnel/#clients-end","title":"Client's End","text":"

Open a terminal on Client System (A Regular PC where you want to display the input frames received from the Server) and execute the following python code:

Requirements for Client's End

To ensure a successful Remote NetGear Connection with Server:

Finding Public IP Address

Only IPv4 IP-addresses are supported

Enabling Dynamic DNS

SSH tunneling requires public IP address to able to access host on public Internet. Thereby, if it's troublesome to remember Public IP address or your IP address change constantly, then you can use dynamic DNS services like https://www.noip.com/

How to TCP Port Forward in your Router

For more information on Forwarding Port in Popular Home Routers. See this document \u27b6

Secsh channel X open FAILED: open failed: Administratively prohibited

Error: This error means that installed OpenSSH is preventing connections to forwarded ports from outside your Client Machine.

Solution: You need to change GatewayPorts no option to GatewayPorts yes in the OpenSSH server configuration file sshd_config to allows anyone to connect to the forwarded ports on Client Machine.

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nimport cv2\n\n# Define NetGear Client at given IP address and define parameters \nclient = NetGear(\n    address=\"127.0.0.1\", # don't change this\n    port=\"5454\",\n    pattern=2,\n    receive_mode=True,\n    logging=True,\n)\n\n# loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n

"},{"location":"gears/netgear/advanced/ssh_tunnel/#servers-end","title":"Server's End","text":"

Now, Open the terminal on Remote Server System (A Raspberry Pi with a webcam connected to it at index 0), and execute the following python code:

Make sure to replace the Client's Public IP Address and Forwarded TCP port(default is 22) in SSH URL with yours in the following example.

On Server end, NetGear automatically validates if the port is open at specified Client's Public IP Address or not, and if it fails (i.e. port is closed), NetGear will throw AssertionError!

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\n\n# activate SSH tunneling with SSH URL, and\n# [BEWARE!!!] Change SSH URL and SSH password with yours for this example !!!\noptions = {\n    \"ssh_tunnel_mode\": \"test@52.155.1.89\", # defaults to port 22\n    \"ssh_tunnel_pwd\": \"pas$wd\",\n}\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = VideoGear(source=0).start()\n\n# Define NetGear server at given IP address and define parameters\nserver = NetGear(\n    address=\"127.0.0.1\", # don't change this\n    port=\"5454\",\n    pattern=2, \n    logging=True, \n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"gears/netgear_async/overview/","title":"Overview","text":""},{"location":"gears/netgear_async/overview/#netgear_async-api","title":"NetGear_Async API","text":""},{"location":"gears/netgear_async/overview/#overview","title":"Overview","text":"

NetGear_Async can generate the same performance as NetGear API at about one-third the memory consumption, and also provide complete server-client handling with various options to use variable protocols/patterns similar to NetGear, but lacks in term of flexibility as it supports only a few NetGear's Exclusive Modes.

NetGear_Async is built on zmq.asyncio, and powered by a high-performance asyncio event loop called uvloop to achieve unmatchable high-speed and lag-free video streaming over the network with minimal resource constraints. NetGear_Async can transfer thousands of frames in just a few seconds without causing any significant load on your system.

NetGear_Async provides complete server-client handling and options to use variable protocols/patterns similar to NetGear API. Furthermore, NetGear_Async allows us to define our custom Server as source to transform frames easily before sending them across the network(see this doc example).

NetGear_Async now supports additional bidirectional data transmission between receiver(client) and sender(server) while transferring frames. Users can easily build complex applications such as like Real-Time Video Chat in just few lines of code.

In addition to all this, NetGear_Async API also provides internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs, thereby granting it exclusive power for transferring frames incoming from any source to the network.

NetGear_Async as of now supports four ZeroMQ messaging patterns:

Whereas supported protocol are: tcp and ipc.

Helpful Tips

"},{"location":"gears/netgear_async/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through NetGear_Async Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/netgear_async/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/netgear_async/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/netgear_async/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/netgear_async/params/","title":"Parameters","text":""},{"location":"gears/netgear_async/params/#netgear_async-api-parameters","title":"NetGear_Async API Parameters","text":"

NetGear_Async provides a special internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs and their parameters.

"},{"location":"gears/netgear_async/params/#enablepicamera","title":"enablePiCamera","text":"

This parameter provide access to PiGear or CamGear APIs respectively. This means the if enablePiCamera flag is True, the PiGear API will be accessed, and if False, the CamGear API will be accessed.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

NetGear_Async(enablePiCamera=True) # enable access to PiGear API\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/netgear_async/params/#address","title":"address","text":"

This parameter sets the valid network address of the Server/Client. Network addresses unique identifiers across the network.

Data-Type: String

Default Value: Its default value is based on selected primary mode, i.e 'localhost' for Send Mode and '*' for Receive Mode.

Usage:

NetGear_Async(address=\"192.168.0.145\")\n

"},{"location":"gears/netgear_async/params/#port","title":"port","text":"

This parameter sets the valid Network Port of the Server/Client. A network port is a number that identifies one side of a connection between two devices on the network and is used determine to which process or application a message should be delivered.

Data-Type: String

Default Value: Its default value is '5555'

Usage:

NetGear_Async(port=\"5575\")\n

"},{"location":"gears/netgear_async/params/#protocol","title":"protocol","text":"

This parameter sets the valid messaging protocol between Server/Client. A network protocol is a set of established rules that dictates how to format, transmit and receive data so computer network devices - from servers and routers to endpoints - can communicate regardless of the differences in their underlying infrastructures, designs or standards. Supported protocol are: 'tcp' and 'ipc'.

Data-Type: String

Default Value: Its default value is 'tcp'

Usage:

NetGear_Async(protocol=\"ipc\")\n

"},{"location":"gears/netgear_async/params/#pattern","title":"pattern","text":"

This parameter sets the supported messaging pattern(flow of communication) between Server/Client. Messaging patterns are the network-oriented architectural pattern that describes the flow of communication between interconnecting systems. NetGear provides access to ZeroMQ's pre-optimized sockets which enables you to take advantage of these patterns.

Data-Type: Integer

Default Value: Its default value is 0 (i.e zmq.PAIR).

All supported ZMQ patterns for NetGear_Async are:

Usage:

NetGear_Async(pattern=1) # sets zmq.REQ/zmq.REP pattern\n

"},{"location":"gears/netgear_async/params/#receive_mode","title":"receive_mode","text":"

This parameter select the Netgear's Mode of operation. It basically activates Receive Mode(if True) and Send Mode(if False). Furthermore, recv() method will only work when this flag is enabled(i.e. Receive Mode), whereas send() method will only work when this flag is disabled(i.e.Send Mode).

Data-Type: Boolean

Default Value: Its default value is False(i.e. Send Mode is activated by default).

Usage:

NetGear_Async(receive_mode=True) # activates Recieve Mode\n

"},{"location":"gears/netgear_async/params/#timeout","title":"timeout","text":"

In NetGear_Async, the Receiver-end keeps tracks if frames are received from Server-end within this specified timeout value (in seconds), Otherwise TimeoutError will be raised, which helps to close the Receiver-end safely if the Server has lost connection prematurely. This parameter controls that timeout value (i.e. the maximum waiting time (in seconds)) after which Client exit itself with a TimeoutError to save resources. Its minimum value is 0.0 but no max limit.

Data-Type: Float/Integer

Default Value: Its default value is 10.0.

Usage:

NetGear_Async(timeout=5.0) # sets 5secs timeout\n
"},{"location":"gears/netgear_async/params/#options","title":"options","text":"

This parameter provides the flexibility to alter various NetGear_Async API's internal properties and modes.

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

Supported dictionary attributes for NetGear_Async API

"},{"location":"gears/netgear_async/params/#parameters-for-stabilizer-backend","title":"Parameters for Stabilizer Backend","text":"

Enable this backend with stabilize=True in NetGear_Async.

"},{"location":"gears/netgear_async/params/#stabilize","title":"stabilize","text":"

This parameter enable access to Stabilizer Class for stabilizing frames, i.e. can be set to True(to enable) or unset to False(to disable).

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

NetGear_Async(stabilize=True) # enable stablization\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/netgear_async/params/#options_1","title":"options","text":"

This parameter can be used in addition, to pass user-defined parameters supported by Stabilizer Class. These parameters can be formatted as this parameter's attribute.

Supported dictionary attributes for Stabilizer Class are:

"},{"location":"gears/netgear_async/params/#parameters-for-camgear-backend","title":"Parameters for CamGear backend","text":"

Enable this backend with enablePiCamera=False in NetGear_Async. Default is also False.

"},{"location":"gears/netgear_async/params/#source","title":"source","text":"

NetGear_Async API will throw RuntimeError if source provided is invalid.

This parameter defines the source for the input stream.

Data-Type: Based on input.

Default Value: Its default value is 0.

Its valid input can be one of the following:

"},{"location":"gears/netgear_async/params/#stream_mode","title":"stream_mode","text":"

This parameter controls the Stream Mode, .i.e if enabled(stream_mode=True), the CamGear API will interpret the given source input as YouTube URL address.

Due to a FFmpeg bug that causes video to freeze frequently in OpenCV, It is advised to always use GStreamer backend for any livestream videos. Checkout this FAQ for compiling OpenCV with GStreamer support.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

Supported Streaming Websites

The complete list of all supported Streaming Websites URLs can be found here \u27b6

NetGear_Async(source='https://youtu.be/bvetuLwJIkA', stream_mode=True)\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/netgear_async/params/#backend","title":"backend","text":"

This parameter manually selects the backend for OpenCV's VideoCapture class (only if specified).

Data-Type: Integer

Default Value: Its default value is 0

Usage:

All supported backends are listed here \u27b6

Its value can be for e.g. backend = cv2.CAP_DSHOW for selecting Direct Show as backend:

NetGear_Async(source=0, backend = cv2.CAP_DSHOW)\n

"},{"location":"gears/netgear_async/params/#options_2","title":"options","text":"

This parameter provides the ability to alter various Source Tweak Parameters available within OpenCV's VideoCapture API properties.

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

All supported parameters are listed here \u27b6

The desired parameters can be passed to NetGear_Async API by formatting them as this parameter's attributes, as follows:

# formatting parameters as dictionary attributes\noptions = {\"CAP_PROP_FRAME_WIDTH\":320, \"CAP_PROP_FRAME_HEIGHT\":240, \"CAP_PROP_FPS\":60}\n# assigning it\nNetGear_Async(source=0, **options)\n

"},{"location":"gears/netgear_async/params/#parameters-for-pigear-backend","title":"Parameters for PiGear backend","text":"

Enable this backend with enablePiCamera=True in NetGear_Async.

"},{"location":"gears/netgear_async/params/#camera_num","title":"camera_num","text":"

This parameter selects the camera index to be used as the source, allowing you to drive these multiple cameras simultaneously from within a single Python session. Its value can only be zero or greater, otherwise, NetGear_Async API will throw ValueError for any negative value.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

# select Camera Module at index `1`\nNetGear_Async(enablePiCamera=True, camera_num=1)\n

The complete usage example demonstrating the usage of the camera_num parameter is available here \u27b6.

"},{"location":"gears/netgear_async/params/#resolution","title":"resolution","text":"

This parameter controls the resolution - a tuple (i.e. (width,height)) of two values giving the width and height of the output frames.

Make sure both width and height values should be at least 64.

When using the Picamera2 backend, the resolution parameter will be OVERRIDDEN, if the user explicitly defines the output_size property of the sensor configurational parameter.

Data-Type: Tuple

Default Value: Its default value is (640,480).

Usage:

NetGear_Async(enablePiCamera=True, resolution=(1280,720)) # sets 1280x720 resolution\n

"},{"location":"gears/netgear_async/params/#framerate","title":"framerate","text":"

This parameter controls the framerate of the source.

Data-Type: integer/float

Default Value: Its default value is 30.

Usage:

NetGear_Async(enablePiCamera=True, framerate=60) # sets 60fps framerate\n

"},{"location":"gears/netgear_async/params/#options_3","title":"options","text":"

This dictionary parameter in the internal PiGear API backend allows you to control various camera settings for both the picamera2 and legacy picamera backends and some internal API tasks. These settings include:

"},{"location":"gears/netgear_async/params/#a-configurational-camera-parameters","title":"A. Configurational Camera Parameters","text":""},{"location":"gears/netgear_async/params/#b-user-defined-parameters","title":"B. User-defined Parameters","text":""},{"location":"gears/netgear_async/params/#common-parameters","title":"Common Parameters","text":"

These are common parameters that works with every backend in NetGear_Async.

"},{"location":"gears/netgear_async/params/#colorspace","title":"colorspace","text":"

This parameter selects the colorspace of the source stream.

Data-Type: String

Default Value: Its default value is None.

Usage:

All supported colorspace values are given here \u27b6

NetGear_Async(colorspace=\"COLOR_BGR2HSV\")\n

Its complete usage example is given here \u27b6

"},{"location":"gears/netgear_async/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

NetGear_Async(logging=True)\n

"},{"location":"gears/netgear_async/params/#time_delay","title":"time_delay","text":"

This parameter set the time delay (in seconds) before the NetGear_Async API start reading the frames. This delay is only required if the source required some warm-up delay before starting up.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

NetGear_Async(time_delay=1)  # set 1 seconds time delay\n

"},{"location":"gears/netgear_async/usage/","title":"Usage Examples","text":""},{"location":"gears/netgear_async/usage/#netgear_async-api-usage-examples","title":"NetGear_Async API Usage Examples:","text":"

Helpful Tips

After going through following Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/netgear_async/usage/#requirement","title":"Requirement","text":"

NetGear_Async API is the part of asyncio package of VidGear, thereby you need to install VidGear with asyncio support as follows:

pip install vidgear[asyncio]\n

"},{"location":"gears/netgear_async/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with NetGear_Async API:

"},{"location":"gears/netgear_async/usage/#servers-end","title":"Server's End","text":"

Open your favorite terminal and execute the following python code:

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nimport asyncio\n\n# initialize Server with suitable source\nserver = NetGear_Async(source=\"/home/foo/foo1.mp4\").launch()\n\nif __name__ == \"__main__\":\n    # set event loop\n    asyncio.set_event_loop(server.loop)\n    try:\n        # run your main function task until it is complete\n        server.loop.run_until_complete(server.task)\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    finally:\n        # finally close the server\n        server.close()\n
"},{"location":"gears/netgear_async/usage/#clients-end","title":"Client's End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

Client will throw TimeoutError if it fails to connect to the Server in given timeout value!

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nimport cv2, asyncio\n\n# define and launch Client with `receive_mode=True`\nclient = NetGear_Async(receive_mode=True).launch()\n\n# Create a async function where you want to show/manipulate your received frames\nasync def main():\n    # loop over Client's Asynchronous Frame Generator\n    async for frame in client.recv_generator():\n\n        # do something with received frames here\n\n        # Show output window\n        cv2.imshow(\"Output Frame\", frame)\n        key = cv2.waitKey(1) & 0xFF\n\n        # await before continuing\n        await asyncio.sleep(0)\n\nif __name__ == \"__main__\":\n    # Set event loop to client's\n    asyncio.set_event_loop(client.loop)\n    try:\n        # run your main function task until it is complete\n        client.loop.run_until_complete(main())\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n\n    # close all output window\n    cv2.destroyAllWindows()\n    # safely close client\n    client.close()\n

"},{"location":"gears/netgear_async/usage/#using-netgear_async-with-variable-parameters","title":"Using NetGear_Async with Variable Parameters","text":""},{"location":"gears/netgear_async/usage/#clients-end_1","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from the Server) and execute the following python code:

Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

Client will throw TimeoutError if it fails to connect to the Server in given timeout value!

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nimport cv2, asyncio\n\n# define and launch Client with `receive_mode=True`. #change following IP address '192.168.x.xxx' with yours\nclient = NetGear_Async(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=2,\n    receive_mode=True,\n    logging=True,\n).launch()\n\n\n# Create a async function where you want to show/manipulate your received frames\nasync def main():\n    # loop over Client's Asynchronous Frame Generator\n    async for frame in client.recv_generator():\n\n        # do something with received frames here\n\n        # Show output window\n        cv2.imshow(\"Output Frame\", frame)\n        key = cv2.waitKey(1) & 0xFF\n\n        # await before continuing\n        await asyncio.sleep(0)\n\n\nif __name__ == \"__main__\":\n    # Set event loop to client's\n    asyncio.set_event_loop(client.loop)\n    try:\n        # run your main function task until it is complete\n        client.loop.run_until_complete(main())\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n\n    # close all output window\n    cv2.destroyAllWindows()\n    # safely close client\n    client.close()\n
"},{"location":"gears/netgear_async/usage/#servers-end_1","title":"Server's End","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nimport asyncio\n\n# initialize Server with suitable source\nserver = NetGear_Async(\n    source=0,\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=2,\n    logging=True,\n).launch()\n\nif __name__ == \"__main__\":\n    # set event loop\n    asyncio.set_event_loop(server.loop)\n    try:\n        # run your main function task until it is complete\n        server.loop.run_until_complete(server.task)\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    finally:\n        # finally close the server\n        server.close()\n

"},{"location":"gears/netgear_async/usage/#using-netgear_async-with-a-custom-sourceopencv","title":"Using NetGear_Async with a Custom Source(OpenCV)","text":"

NetGear_Async allows you to easily define your own custom Source at Server-end that you want to use to transform your frames before sending them onto the network.

Let's implement a bare-minimum example with a Custom Source using NetGear_Async API and OpenCV:

"},{"location":"gears/netgear_async/usage/#servers-end_2","title":"Server's End","text":"

Open your favorite terminal and execute the following python code:

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import library\nfrom vidgear.gears.asyncio import NetGear_Async\nimport cv2, asyncio\n\n# initialize Server without any source\nserver = NetGear_Async(source=None, logging=True)\n\n# !!! define your own video source here !!!\n# Open any video stream such as live webcam\n# video stream on first index(i.e. 0) device\nstream = cv2.VideoCapture(0)\n\n# Create a async frame generator as custom source\nasync def my_frame_generator():\n\n    # loop over stream until its terminated\n    while True:\n\n        # read frames\n        (grabbed, frame) = stream.read()\n\n        # check if frame empty\n        if not grabbed:\n            break\n\n        # do something with the frame to be sent here\n\n        # yield frame\n        yield frame\n        # sleep for sometime\n        await asyncio.sleep(0)\n\n\nif __name__ == \"__main__\":\n    # set event loop\n    asyncio.set_event_loop(server.loop)\n    # Add your custom source generator to Server configuration\n    server.config[\"generator\"] = my_frame_generator()\n    # Launch the Server\n    server.launch()\n    try:\n        # run your main function task until it is complete\n        server.loop.run_until_complete(server.task)\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    finally:\n        # close stream\n        stream.release()\n        # finally close the server\n        server.close()\n
"},{"location":"gears/netgear_async/usage/#clients-end_2","title":"Client's End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

Client will throw TimeoutError if it fails to connect to the Server in given timeout value!

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nimport cv2, asyncio\n\n# define and launch Client with `receive_mode=True`\nclient = NetGear_Async(receive_mode=True, logging=True).launch()\n\n\n# Create a async function where you want to show/manipulate your received frames\nasync def main():\n    # loop over Client's Asynchronous Frame Generator\n    async for frame in client.recv_generator():\n\n        # {do something with received frames here}\n\n        # Show output window\n        cv2.imshow(\"Output Frame\", frame)\n        key = cv2.waitKey(1) & 0xFF\n\n        # await before continuing\n        await asyncio.sleep(0)\n\n\nif __name__ == \"__main__\":\n    # Set event loop to client's\n    asyncio.set_event_loop(client.loop)\n    try:\n        # run your main function task until it is complete\n        client.loop.run_until_complete(main())\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n\n    # close all output window\n    cv2.destroyAllWindows()\n    # safely close client\n    client.close()\n

"},{"location":"gears/netgear_async/usage/#using-netgear_async-with-other-gears","title":"Using NetGear_Async with Other Gears","text":"

NetGear_Async can be used with any other Gears without any compatibility issues.

Let's implement a bare-minimum example where we are sending Stabilized frames from Server-end and saving them at Client's end with WriteGear as follows:

"},{"location":"gears/netgear_async/usage/#servers-end_3","title":"Server's End","text":"

Open your favorite terminal and execute the following python code:

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nimport asyncio\n\n# initialize Server with suitable source and enable stabilization\nserver = NetGear_Async(\n    source=\"/home/foo/foo1.mp4\", stabilize=True, logging=True\n).launch()\n\nif __name__ == \"__main__\":\n    # set event loop\n    asyncio.set_event_loop(server.loop)\n    try:\n        # run your main function task until it is complete\n        server.loop.run_until_complete(server.task)\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    finally:\n        # finally close the server\n        server.close()\n
"},{"location":"gears/netgear_async/usage/#clients-end_3","title":"Client's End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

Client will throw TimeoutError if it fails to connect to the Server in given timeout value!

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nfrom vidgear.gears import WriteGear\nimport cv2, asyncio\n\n# define and launch Client with `receive_mode=True`\nclient = NetGear_Async(receive_mode=True).launch()\n\n# Define writer with output filename 'Output.mp4'\nwriter = WriteGear(output=\"Output.mp4\", logging=True)\n\n# Create a async function where you want to show/manipulate your received frames\nasync def main():\n    # loop over Client's Asynchronous Frame Generator\n    async for frame in client.recv_generator():\n\n        # {do something with received frames here}\n\n        # write a modified frame to writer\n        writer.write(frame)\n\n        # Show output window\n        cv2.imshow(\"Output Frame\", frame)\n        key = cv2.waitKey(1) & 0xFF\n\n        # await before continuing\n        await asyncio.sleep(0)\n\n\nif __name__ == \"__main__\":\n    # Set event loop to client's\n    asyncio.set_event_loop(client.loop)\n    try:\n        # run your main function task until it is complete\n        client.loop.run_until_complete(main())\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n\n    # close all output window\n    cv2.destroyAllWindows()\n    # safely close client\n    client.close()\n    # safely close writer\n    writer.close()\n

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/","title":"Bidirectional Mode","text":""},{"location":"gears/netgear_async/advanced/bidirectional_mode/#bidirectional-mode-for-netgear_async-api","title":"Bidirectional Mode for NetGear_Async API","text":"NetGear_Async's Bidirectional Mode"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#overview","title":"Overview","text":"New in v0.2.2

This document was added in v0.2.2.

Bidirectional Mode enables seamless support for Bidirectional data transmission between Client and Sender along with video-frames through its synchronous messaging patterns such as zmq.PAIR (ZMQ Pair Pattern) & zmq.REQ/zmq.REP (ZMQ Request/Reply Pattern) in NetGear_Async API.

In Bidirectional Mode, we utilizes the NetGear_Async API's transceive_data method for transmitting data (at Client's end) and receiving data (in Server's end) all while transferring frames in real-time.

This mode can be easily activated in NetGear_Async through bidirectional_mode attribute of its options dictionary parameter during initialization.

Important

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#exclusive-method-and-parameter","title":"Exclusive Method and Parameter","text":"

To send data bidirectionally, NetGear_Async API provides following exclusive method and parameter:

transceive_data only works when Bidirectional Mode is enabled.

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#usage-examples","title":"Usage Examples","text":"

For Bidirectional Mode, NetGear_Async must need User-defined Custom Source at its Server end otherwise it will throw ValueError.

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#bare-minimum-usage-with-opencv","title":"Bare-Minimum Usage with OpenCV","text":"

Following is the bare-minimum code you need to get started with Bidirectional Mode over Custom Source Server built using OpenCV and NetGear_Async API:

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#server-end","title":"Server End","text":"

Open your favorite terminal and execute the following python code:

You can terminate both sides anytime by pressing Ctrl+C on your keyboard!

# import library\nfrom vidgear.gears.asyncio import NetGear_Async\nimport cv2, asyncio\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# initialize Server without any source\nserver = NetGear_Async(source=None, logging=True, **options)\n\n# Create a async frame generator as custom source\nasync def my_frame_generator():\n\n    # !!! define your own video source here !!!\n    # Open any valid video stream(for e.g `foo.mp4` file)\n    stream = cv2.VideoCapture(\"foo.mp4\")\n\n    # loop over stream until its terminated\n    while True:\n        # read frames\n        (grabbed, frame) = stream.read()\n\n        # check for empty frame\n        if not grabbed:\n            break\n\n        # {do something with the frame to be sent here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # receive data from Client\n        recv_data = await server.transceive_data()\n\n        # print data just received from Client\n        if not (recv_data is None):\n            print(recv_data)\n\n        # send our frame & data\n        yield (target_data, frame) # (1)\n\n        # sleep for sometime\n        await asyncio.sleep(0)\n\n    # safely close video stream\n    stream.release()\n\n\nif __name__ == \"__main__\":\n    # set event loop\n    asyncio.set_event_loop(server.loop)\n    # Add your custom source generator to Server configuration\n    server.config[\"generator\"] = my_frame_generator()\n    # Launch the Server\n    server.launch()\n    try:\n        # run your main function task until it is complete\n        server.loop.run_until_complete(server.task)\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    finally:\n        # finally close the server\n        server.close()\n
  1. Everything except numpy.ndarray datatype data is accepted in target_data.
"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#client-end","title":"Client End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nimport cv2, asyncio\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# define and launch Client with `receive_mode=True`\nclient = NetGear_Async(receive_mode=True, logging=True, **options).launch()\n\n\n# Create a async function where you want to show/manipulate your received frames\nasync def main():\n    # loop over Client's Asynchronous Frame Generator\n    async for (data, frame) in client.recv_generator():\n\n        # do something with receive data from server\n        if not (data is None):\n            # let's print it\n            print(data)\n\n        # {do something with received frames here}\n\n        # Show output window(comment these lines if not required)\n        cv2.imshow(\"Output Frame\", frame)\n        cv2.waitKey(1) & 0xFF\n\n        # prepare data to be sent\n        target_data = \"Hi, I am a Client here.\"\n        # send our data to server\n        await client.transceive_data(data=target_data)\n\n        # await before continuing\n        await asyncio.sleep(0)\n\n\nif __name__ == \"__main__\":\n    # Set event loop to client's\n    asyncio.set_event_loop(client.loop)\n    try:\n        # run your main function task until it is complete\n        client.loop.run_until_complete(main())\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n\n    # close all output window\n    cv2.destroyAllWindows()\n\n    # safely close client\n    client.close()\n

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#using-bidirectional-mode-with-variable-parameters","title":"Using Bidirectional Mode with Variable Parameters","text":""},{"location":"gears/netgear_async/advanced/bidirectional_mode/#clients-end","title":"Client's End","text":"

Open a terminal on Client System (where you want to display the input frames received from the Server) and execute the following python code:

Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nimport cv2, asyncio\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# Define NetGear_Async Client at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear_Async(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# Create a async function where you want to show/manipulate your received frames\nasync def main():\n    # loop over Client's Asynchronous Frame Generator\n    async for (data, frame) in client.recv_generator():\n\n        # do something with receive data from server\n        if not (data is None):\n            # let's print it\n            print(data)\n\n        # {do something with received frames here}\n\n        # Show output window(comment these lines if not required)\n        cv2.imshow(\"Output Frame\", frame)\n        cv2.waitKey(1) & 0xFF\n\n        # prepare data to be sent\n        target_data = \"Hi, I am a Client here.\"\n        # send our data to server\n        await client.transceive_data(data=target_data)\n\n        # await before continuing\n        await asyncio.sleep(0)\n\n\nif __name__ == \"__main__\":\n    # Set event loop to client's\n    asyncio.set_event_loop(client.loop)\n    try:\n        # run your main function task until it is complete\n        client.loop.run_until_complete(main())\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n\n    # close all output window\n    cv2.destroyAllWindows()\n\n    # safely close client\n    client.close()\n

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#server-end_1","title":"Server End","text":"

Now, Open the terminal on another Server System (a Raspberry Pi with Camera Module), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this backend, otherwise nothing will work.

New Picamera2 backendLegacy Picamera backend
# import libs\nfrom vidgear.gears.asyncio import NetGear_Async\nfrom vidgear.gears import VideoGear\nfrom libcamera import Transform\nimport cv2, asyncio\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# initialize Server without any source at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear_Async(\n    source=None,\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# Create a async frame generator as custom source\nasync def my_frame_generator():\n\n    # !!! define your own video source below !!!\n\n    # define various Picamera2 tweak parameters\n    options = {\n        \"queue\": True,\n        \"buffer_count\": 4,\n        \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n        \"transform\": Transform(hflip=1),\n        \"auto_align_output_config\": True,  # auto-align camera configuration\n    }\n\n    # open pi video stream with defined parameters\n    stream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n    # loop over stream until its terminated\n    while True:\n        # read frames\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame to be sent here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # receive data from Client\n        recv_data = await server.transceive_data()\n\n        # print data just received from Client\n        if not (recv_data is None):\n            print(recv_data)\n\n        # send our frame & data\n        yield (target_data, frame) # (1)\n\n        # sleep for sometime\n        await asyncio.sleep(0)\n\n    # safely close video stream\n    stream.stop()\n\n\nif __name__ == \"__main__\":\n    # set event loop\n    asyncio.set_event_loop(server.loop)\n    # Add your custom source generator to Server configuration\n    server.config[\"generator\"] = my_frame_generator()\n    # Launch the Server\n    server.launch()\n    try:\n        # run your main function task until it is complete\n        server.loop.run_until_complete(server.task)\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    finally:\n        # finally close the server\n        server.close()\n
  1. Everything except numpy.ndarray datatype data is accepted in target_data.
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import library\nfrom vidgear.gears.asyncio import NetGear_Async\nfrom vidgear.gears import VideoGear\nimport cv2, asyncio\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# initialize Server without any source at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear_Async(\n    source=None,\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# Create a async frame generator as custom source\nasync def my_frame_generator():\n\n    # !!! define your own video source below !!!\n\n    # define various Picamera tweak parameters\n    options = {\n        \"hflip\": True,\n        \"exposure_mode\": \"auto\",\n        \"iso\": 800,\n        \"exposure_compensation\": 15,\n        \"awb_mode\": \"horizon\",\n        \"sensor_mode\": 0,\n    }\n\n    # open pi video stream with defined parameters\n    stream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n    # loop over stream until its terminated\n    while True:\n        # read frames\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame to be sent here}\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # receive data from Client\n        recv_data = await server.transceive_data()\n\n        # print data just received from Client\n        if not (recv_data is None):\n            print(recv_data)\n\n        # send our frame & data\n        yield (target_data, frame) # (1)\n\n        # sleep for sometime\n        await asyncio.sleep(0)\n\n    # safely close video stream\n    stream.stop()\n\n\nif __name__ == \"__main__\":\n    # set event loop\n    asyncio.set_event_loop(server.loop)\n    # Add your custom source generator to Server configuration\n    server.config[\"generator\"] = my_frame_generator()\n    # Launch the Server\n    server.launch()\n    try:\n        # run your main function task until it is complete\n        server.loop.run_until_complete(server.task)\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    finally:\n        # finally close the server\n        server.close()\n
  1. Everything except numpy.ndarray datatype data is accepted in target_data.

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#using-bidirectional-mode-for-video-frames-transfer","title":"Using Bidirectional Mode for Video-Frames Transfer","text":"

In this example we are going to implement a bare-minimum example, where we will be sending video-frames (3-Dimensional numpy arrays) of the same Video bidirectionally at the same time, for testing the real-time performance and synchronization between the Server and the Client using this(Bidirectional) Mode.

This feature is great for building applications like Real-Time Video Chat.

We're also using reducer() method for reducing frame-size on-the-go for additional performance.

Remember, Sending large HQ video-frames may required more network bandwidth and packet size which may lead to video latency!

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#server-end_2","title":"Server End","text":"

Open your favorite terminal and execute the following python code:

You can terminate both side anytime by pressing Ctrl+C on your keyboard!

Server end can only send numpy.ndarray datatype as frame but not as data.

# import library\nfrom vidgear.gears.asyncio import NetGear_Async\nfrom vidgear.gears.asyncio.helper import reducer\nimport cv2, asyncio\nimport numpy as np\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# Define NetGear Server without any source and with defined parameters\nserver = NetGear_Async(source=None, pattern=1, logging=True, **options)\n\n# Create a async frame generator as custom source\nasync def my_frame_generator():\n    # !!! define your own video source here !!!\n    # Open any valid video stream(for e.g `foo.mp4` file)\n    stream = cv2.VideoCapture(\"foo.mp4\")\n    # loop over stream until its terminated\n    while True:\n\n        # read frames\n        (grabbed, frame) = stream.read()\n\n        # check for empty frame\n        if not grabbed:\n            break\n\n        # reducer frames size if you want more performance, otherwise comment this line\n        frame = await reducer(frame, percentage=30)  # reduce frame by 30%\n\n        # {do something with the frame to be sent here}\n\n        # send frame & data and also receive data from Client\n        recv_data = await server.transceive_data()\n\n        # receive data from Client\n        if not (recv_data is None):\n            # check data is a numpy frame\n            if isinstance(recv_data, np.ndarray):\n\n                # {do something with received numpy frame here}\n\n                # Let's show it on output window\n                cv2.imshow(\"Received Frame\", recv_data)\n                cv2.waitKey(1) & 0xFF\n            else:\n                # otherwise just print data\n                print(recv_data)\n\n        # prepare data to be sent(a simple text in our case)\n        target_data = \"Hello, I am a Server.\"\n\n        # send our frame & data to client\n        yield (target_data, frame) # (1)\n\n        # sleep for sometime\n        await asyncio.sleep(0)\n\n    # safely close video stream\n    stream.release()\n\n\nif __name__ == \"__main__\":\n    # set event loop\n    asyncio.set_event_loop(server.loop)\n    # Add your custom source generator to Server configuration\n    server.config[\"generator\"] = my_frame_generator()\n    # Launch the Server\n    server.launch()\n    try:\n        # run your main function task until it is complete\n        server.loop.run_until_complete(server.task)\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    finally:\n        # finally close the server\n        server.close()\n
  1. Everything except numpy.ndarray datatype data is accepted in target_data.

"},{"location":"gears/netgear_async/advanced/bidirectional_mode/#client-end_1","title":"Client End","text":"

Then open another terminal on the same system and execute the following python code and see the output:

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nfrom vidgear.gears.asyncio.helper import reducer\nimport cv2, asyncio\n\n# activate Bidirectional mode\noptions = {\"bidirectional_mode\": True}\n\n# define and launch Client with `receive_mode=True`\nclient = NetGear_Async(pattern=1, receive_mode=True, logging=True, **options).launch()\n\n# Create a async function where you want to show/manipulate your received frames\nasync def main():\n    # !!! define your own video source here !!!\n    # again open the same video stream for comparison\n    stream = cv2.VideoCapture(\"foo.mp4\")\n    # loop over Client's Asynchronous Frame Generator\n    async for (server_data, frame) in client.recv_generator():\n\n        # check for server data\n        if not (server_data is None):\n\n            # {do something with the server data here}\n\n            # lets print extracted server data\n            print(server_data)\n\n        # {do something with received frames here}\n\n        # Show output window\n        cv2.imshow(\"Output Frame\", frame)\n        key = cv2.waitKey(1) & 0xFF\n\n        # read frame target data from stream to be sent to server\n        (grabbed, target_data) = stream.read()\n        # check for frame\n        if grabbed:\n            # reducer frames size if you want more performance, otherwise comment this line\n            target_data = await reducer(\n                target_data, percentage=30\n            )  # reduce frame by 30%\n            # send our frame data\n            await client.transceive_data(data=target_data)\n\n        # await before continuing\n        await asyncio.sleep(0)\n\n    # safely close video stream\n    stream.release()\n\n\nif __name__ == \"__main__\":\n    # Set event loop to client's\n    asyncio.set_event_loop(client.loop)\n    try:\n        # run your main function task until it is complete\n        client.loop.run_until_complete(main())\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    # close all output window\n    cv2.destroyAllWindows()\n    # safely close client\n    client.close()\n

  1. Additional data of numpy.ndarray datatype is ONLY SUPPORTED at Client's end with transceive_data method using its data parameter. Whereas Server end can only send numpy.ndarray datatype as frame but not as data.

    \u21a9\u21a9

"},{"location":"gears/pigear/overview/","title":"Overview","text":""},{"location":"gears/pigear/overview/#pigear-api","title":"PiGear API","text":"Raspberry Pi Camera Module"},{"location":"gears/pigear/overview/#overview","title":"Overview","text":"

PiGear is a specialized API similar to the CamGear API but optimized for Raspberry Pi Boards, offering comprehensive support for camera modules (e.g., OmniVision OV5647, Sony IMX219), along with limited compatibility for USB cameras.

PiGear implements a seamless and robust wrapper around the picamera2 python library, simplifying integration with minimal code changes and ensuring a smooth transition for developers already familiar with the Picamera2 API. PiGear leverages the libcamera API under the hood with multi-threading, providing high-performance , enhanced control and functionality for Raspberry Pi camera modules.

PiGear handles common configuration parameters and non-standard settings for various camera types, simplifying the integration process. PiGear currently supports PiCamera2 API parameters such as sensor, controls, transform, and format etc., with internal type and sanity checks for robust performance.

While primarily focused on Raspberry Pi camera modules, PiGear also provides basic functionality for USB webcams only with Picamera2 API, along with the ability to accurately differentiate between USB and Raspberry Pi cameras using metadata.

Backward compatibility with picamera library

PiGear seamlessly switches to the legacy picamera library if the picamera2 library is unavailable, ensuring seamless backward compatibility. For this, PiGear also provides a flexible multi-threaded framework around complete picamera API, allowing developers to effortlessly exploit a wide range of parameters, such as brightness, saturation, sensor_mode, iso, exposure, and more.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

Furthermore, PiGear supports the use of multiple camera modules, including those found on Raspberry Pi Compute Module IO boards and USB cameras (only with Picamera2 API).

Threaded Internal Timer

PiGear ensures proper resource release during the termination of the API, preventing potential issues or resource leaks. PiGear API internally implements a Threaded Internal Timer that silently keeps active track of any frozen-threads or hardware-failures and exits safely if any do occur. This means that if you're running the PiGear API in your script and someone accidentally pulls the Camera-Module cable out, instead of going into a possible kernel panic, the API will exit safely to save resources.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this API, otherwise nothing will work.

Helpful Tips

"},{"location":"gears/pigear/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through PiGear Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/pigear/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/pigear/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/pigear/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/pigear/params/","title":"Parameters","text":""},{"location":"gears/pigear/params/#pigear-api-parameters","title":"PiGear API Parameters","text":""},{"location":"gears/pigear/params/#camera_num","title":"camera_num","text":"

This parameter selects the camera index to be used as the source, allowing you to drive these multiple cameras simultaneously from within a single Python session. Its value can only be zero or greater, otherwise, PiGear API will throw ValueError for any negative value.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

# select Camera Module at index `1`\nPiGear(camera_num=1)\n

The complete usage example demonstrating the usage of the camera_num parameter is available here \u27b6.

"},{"location":"gears/pigear/params/#resolution","title":"resolution","text":"

This parameter controls the resolution - a tuple (i.e. (width,height)) of two values giving the width and height of the output frames.

Make sure both width and height values should be at least 64.

When using the Picamera2 backend, the resolution parameter will be OVERRIDDEN, if the user explicitly defines the output_size property of the sensor configurational parameter in PiGear API.

Data-Type: Tuple

Default Value: Its default value is (640,480).

Usage:

PiGear(resolution=(1280,720)) # sets 1280x720 resolution\n

"},{"location":"gears/pigear/params/#framerate","title":"framerate","text":"

This parameter controls the framerate of the source.

Data-Type: integer/float

Default Value: Its default value is 30.

Usage:

PiGear(framerate=60) # sets 60fps framerate\n

"},{"location":"gears/pigear/params/#colorspace","title":"colorspace","text":"

This parameter controls the colorspace of the output frames.

With the Picamera2 backend, you can also define a custom format (format of output frame pixels) in PiGear API. Checkout this bonus example \u27b6

Data-Type: String

Default Value: Its default value is None (i.e. Default BGR colorspace).

Usage:

All supported colorspace values are described here \u27b6

PiGear(colorspace=\"COLOR_BGR2HSV\")\n

Its complete usage example is given here \u27b6

"},{"location":"gears/pigear/params/#options","title":"options","text":"

This dictionary parameter in the PiGear API allows you to control various camera settings for both the picamera2 and legacy picamera backends and some internal API tasks. These settings include:

"},{"location":"gears/pigear/params/#a-configurational-camera-parameters","title":"A. Configurational Camera Parameters","text":""},{"location":"gears/pigear/params/#b-user-defined-parameters","title":"B. User-defined Parameters","text":"

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

The complete usage example demonstrating the usage of the options parameter is available here \u27b6.

You can format these user-defined and configurational parameters as attributes of this options dictionary parameter as follows:

New Picamera2 backendLegacy Picamera backend
# formulate various Picamera2 API parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"exposure_compensation\": 15,\n    \"sensor\": {\"output_size\": (480, 320)},  # !!! will override `resolution` !!!\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n
# formulate various Picamera API parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n

"},{"location":"gears/pigear/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

PiGear(logging=True)\n

"},{"location":"gears/pigear/params/#time_delay","title":"time_delay","text":"

This parameter set the time delay (in seconds) before the PiGear API start reading the frames. This delay is only required if the source required some warm-up delay before starting up.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

PiGear(time_delay=1)  # set 1 seconds time delay\n

"},{"location":"gears/pigear/usage/","title":"Usage Examples","text":""},{"location":"gears/pigear/usage/#pigear-api-usage-examples","title":"PiGear API Usage Examples:","text":"

PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this API, otherwise nothing will work.

After going through following Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/pigear/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with PiGear API:

Under the hood, PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, PiGear API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

Disabling common libcamera API messages in silent mode.

The picamera2 backend can be a bit verbose with logging messages from the underlying libcamera library, even when logging is disabled (logging=False) in the PiGear API.

Linux Windows (Powershell) MacOS
export LIBCAMERA_LOG_LEVELS=2\n
$Env:LIBCAMERA_LOG_LEVELS=2\n
export LIBCAMERA_LOG_LEVELS=2\n
# import required libraries\nfrom vidgear.gears import PiGear\nimport cv2\n\n# open stream with default parameters\nstream = PiGear().start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/pigear/usage/#using-pigear-with-variable-camera-properties","title":"Using PiGear with Variable Camera Properties","text":"New Picamera2 backendLegacy Picamera backend

PiGear provides a user-friendly interface for the underlying picamera2 library, offering access to almost all of its important configurational parameters. It simplifies configuration for developers with even basic knowledge of Raspberry Pi camera modules, allowing them to easily configure and control the camera functionality with just a few lines of code.

This example doc showcases the capabilities of PiGear and demonstrates how it simplifies camera configuration with Picamera2 API backend.

All supported Picamera2 Library Configurational Parameters [IMPORTANT]

Following are the list of Picamera2 parameters, i.e. if supported, can be applied to the source stream in PiGear API through its options dictionary parameter by formatting them as its attributes.

Few Important points Parameters Datatype Description Supported Supported on USB Cameras Remarks buffer_count int, >=1 number of sets of buffers to allocate for the camera system Read Docs here \u27b6 queue bool whether the system is allowed to queue up a frame ready for a capture request Read Docs here \u27b6 controls dict specify a set of runtime controls that can be regarded as part of the camera configuration Read Docs here \u27b6 sensor dict allow to select a particular mode of operation for the sensor Read Docs here \u27b6 format str Pixel formats Read Docs here \u27b6 and see Bonus example \u27b6 transform Transform1 The 2D plane transform that is applied to all images from all the configured streams. Read Docs here \u27b6 colour_space colour space of the output images Handled by colorspace parameter of PiGear API size A tuple of two values giving the width and height of the output image. (Both numbers should be no less than 64) Handled by resolution parameter of PiGear API display name of the stream that will be displayed in the preview window. Not-Required encode name of the stream that will be used for video recording. Not-Required Limited support for USB Cameras

This example also works with USB Cameras, However:

Enabling verbose logs for backend PiCamera2 Library

The PiGear API allows you to enable more detailed logging from the picamera2 backend library using the enable_verbose_logs user-defined optional parameter attribute. This can be used in conjunction with enabling general logging (logging=True) in the PiGear API for even more granular control over logging output.

PiGear also support changing parameter at runtime. Checkout this bonus example here \u27b6

# import required libraries\nfrom vidgear.gears import PiGear\nfrom libcamera import Transform\nimport cv2\n\n# formulate various Picamera2 API \n# configurational parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"sensor\": {\"output_size\": (480, 320)},  # !!! will override `resolution` !!!\n    \"auto_align_output_size\": True,  # auto-align output size\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

PiGear API switches to the legacy picamerabackend if the picamera2 library is unavailable.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

PiGear also supports almost every parameter available within picamera python library. These parameters can be easily applied to the source stream in PiGear API through its options dictionary parameter by formatting them as its attributes. The complete usage example is as follows:

All supported parameters are listed in PiCamera Docs \u27b6

PiGear also support changing parameter at runtime. Checkout this bonus example here \u27b6

# import required libraries\nfrom vidgear.gears import PiGear\nimport cv2\n\n# formulate various Picamera API \n# configurational parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/pigear/usage/#using-pigear-with-direct-colorspace-manipulation","title":"Using PiGear with Direct Colorspace Manipulation","text":"

PiGear API also supports Direct Colorspace Manipulation, which is ideal for changing source colorspace on the run.

A more detailed information on colorspace manipulation can be found here \u27b6

In following example code, we will start with HSV as source colorspace, and then we will switch to GRAY colorspace when W key is pressed, and then LAB colorspace when E key is pressed, finally default colorspace (i.e. BGR) when S key is pressed. Also, quit when Q key is pressed:

Any incorrect or None-Type value will immediately revert the colorspace to default (i.e. BGR).

# import required libraries\nfrom vidgear.gears import PiGear\nimport cv2\n\n# open pi video stream with defined parameters and change colorspace to `HSV`\nstream = PiGear(\n    resolution=(640, 480),\n    framerate=60,\n    colorspace=\"COLOR_BGR2HSV\",\n    logging=True\n).start()\n\n\n# loop over\nwhile True:\n\n    # read HSV frames\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the HSV frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for key if pressed\n    key = cv2.waitKey(1) & 0xFF\n\n    # check if 'w' key is pressed\n    if key == ord(\"w\"):\n        # directly change colorspace at any instant\n        stream.color_space = cv2.COLOR_BGR2GRAY  # Now colorspace is GRAY\n\n    # check for 'e' key is pressed\n    if key == ord(\"e\"):\n        stream.color_space = cv2.COLOR_BGR2LAB  # Now colorspace is CieLAB\n\n    # check for 's' key is pressed\n    if key == ord(\"s\"):\n        stream.color_space = None  # Now colorspace is default(ie BGR)\n\n    # check for 'q' key is pressed\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/pigear/usage/#using-pigear-with-writegear-api","title":"Using PiGear with WriteGear API","text":"

PiGear can be easily used with WriteGear API directly without any compatibility issues. The suitable example is as follows:

New Picamera2 backendLegacy Picamera backend
# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import WriteGear\nfrom libcamera import Transform\nimport cv2\n\n# formulate various Picamera2 API \n# configurational parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"sensor\": {\"output_size\": (480, 320)},  # will override `resolution`\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# define suitable (Codec,CRF,preset) FFmpeg parameters for writer\noutput_params = {\"-vcodec\": \"libx264\", \"-crf\": 0, \"-preset\": \"fast\"}\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n    # lets convert frame to gray for this example\n    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n\n    # write gray frame to writer\n    writer.write(gray)\n\n    # Show output window\n    cv2.imshow(\"Output Gray Frame\", gray)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n
PiGear API switches to the legacy picamerabackend if the picamera2 library is unavailable.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# formulate various Picamera API \n# configurational parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# define suitable (Codec,CRF,preset) FFmpeg parameters for writer\noutput_params = {\"-vcodec\": \"libx264\", \"-crf\": 0, \"-preset\": \"fast\"}\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n    # lets convert frame to gray for this example\n    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n\n    # write gray frame to writer\n    writer.write(gray)\n\n   # Show output window\n    cv2.imshow(\"Output Gray Frame\", gray)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

  1. A custom libcamera API class. Must be imported as from libcamera import Transform.\u00a0\u21a9

"},{"location":"gears/screengear/overview/","title":"Overview","text":""},{"location":"gears/screengear/overview/#screengear-api","title":"ScreenGear API","text":"ScreenGear API in action"},{"location":"gears/screengear/overview/#overview","title":"Overview","text":"

ScreenGear is designed exclusively for targeting rapid Screencasting Capabilities, which means it can grab frames from your monitor in real-time, either by defining an area on the computer screen or full-screen, at the expense of inconsiderable latency. ScreenGear also seamlessly support frame capturing from multiple monitors as well as supports multiple backends.

ScreenGear API implements a multi-threaded wrapper around dxcam, pyscreenshot & python-mss python library, and also flexibly supports its internal parameter.

Helpful Tips

"},{"location":"gears/screengear/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through ScreenGear Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/screengear/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/screengear/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/screengear/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/screengear/params/","title":"Parameters","text":""},{"location":"gears/screengear/params/#screengear-api-parameters","title":"ScreenGear API Parameters","text":""},{"location":"gears/screengear/params/#monitor","title":"monitor","text":"

This parameter enforces dxcam (if installed) and mss (otherwise) usage, and it is suitable for selecting index of a specific screen/monitor device (from where you want retrieve frames) in multi-monitor setup. For example, its value can be assign to 2, to fetch frames from a secondary monitor screen.

Implication of using monitor parameter

Any value on monitor parameter other than None in ScreenGear API:

Data-Type: Integer, Tuple (only if dxcam backend on Windows)

Default Value: Its default value is None (i.e. disabled by default).

Usage:

With dxcam on Windows With mss backend Using GPU acceleration on Windows

With dxcam library backend, you can also assign which GPU devices ids to use along with monitor device ids as tuple (monitor_idx, gpu_idx), as follows:

# open video stream with defined parameters with \n# monitor at index `1` and GPU at index `0`.\nstream = ScreenGear(monitor=(1,0), logging=True).start()\n

Getting a complete list of monitor devices and GPUs

To get a complete list of monitor devices and outputs(GPUs), you can use dxcam library itself:

>>> import dxcam\n>>> dxcam.device_info()\n'Device[0]:<Device Name:NVIDIA GeForce RTX 3090 Dedicated VRAM:24348Mb VendorId:4318>\\n'\n>>> dxcam.output_info()\n'Device[0] Output[0]: Res:(1920, 1080) Rot:0 Primary:True\\nDevice[0] Output[1]: Res:(1920, 1080) Rot:0 Primary:False\\n'\n

# open video stream with defined parameters \n# with monitor at index `1` selected\nScreenGear(monitor=1)\n

With mss library backend, You can also assign monitor value to -1 to fetch frames from all connected multiple monitor screens with mss backend.

With mss library backend, API will output BGRA colorspace frames instead of default BGR.

# open video stream with defined parameters \n# with monitor at index `1` selected\nScreenGear(monitor=1)\n

"},{"location":"gears/screengear/params/#backend","title":"backend","text":"

This parameter enables pyscreenshot usage and select suitable backend for extracting frames in ScreenGear. The user have the authority of selecting suitable backend which generates best performance as well as the most compatible with their machines. The possible values are: dxcam (Windows only), pil, mss, scrot, maim, imagemagick, pyqt5, pyqt, pyside2, pyside, wx, pygdk3, mac_screencapture, mac_quartz, gnome_dbus, gnome-screenshot, kwin_dbus.

Performance Benchmarking of all backend can be found here \u27b6 and here \u27b6

Remember to install backend library and all of its dependencies you're planning to use with ScreenGear API.

Any value on monitor parameter will disable the backend parameter. You cannot use both parameters at same time.

Backend defaults to dxcam library on Windows (if installed), and pyscreenshot otherwise.

Data-Type: String

Default Value: Its default value is \"\" (i.e. default backend).

Usage:

ScreenGear(backend=\"pil\") # to enforce `pil` as backend for extracting frames.\n

"},{"location":"gears/screengear/params/#colorspace","title":"colorspace","text":"

This parameter selects the colorspace of the source stream.

Data-Type: String

Default Value: Its default value is None.

Usage:

All supported colorspace values are given here \u27b6.

ScreenGear(colorspace=\"COLOR_BGR2HSV\")\n

Its complete usage example is given here \u27b6

"},{"location":"gears/screengear/params/#options","title":"options","text":"

This parameter provides the flexibility to manually set the dimensions of capture screen area.

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, defined below:

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

The desired dimensional coordinates parameters can be passed to ScreenGear API by formatting them as attributes, as follows:

# formatting dimensional parameters as dictionary attributes\noptions = {'top': 40, 'left': 0, 'width': 100, 'height': 100}\n# assigning it\nScreenGear(**options)\n

"},{"location":"gears/screengear/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

ScreenGear(logging=True)\n

"},{"location":"gears/screengear/usage/","title":"Usage Examples","text":""},{"location":"gears/screengear/usage/#screengear-api-usage-examples","title":"ScreenGear API Usage Examples:","text":"

After going through ScreenGear Usage Examples, Checkout more of its advanced configurations here \u27b6

Recommended: Install DXcam library on Windows Machines

On Windows Machines, if installed, ScreenGear API uses dxcam backend machines for higher FPS performance. Thereby, it is highly recommended to install it via pip as follows:

pip install dxcam\n

"},{"location":"gears/screengear/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with ScreenGear API:

# import required libraries\nfrom vidgear.gears import ScreenGear\nimport cv2\n\n# open video stream with default parameters\nstream = ScreenGear().start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/screengear/usage/#using-screengear-with-variable-screen-dimensions","title":"Using ScreenGear with Variable Screen Dimensions","text":"

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 dictionary parameter by formatting them as its attributes.

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, defined below:

The complete usage example is as follows:

# import required libraries\nfrom vidgear.gears import ScreenGear\nimport cv2\n\n# define dimensions of screen w.r.t to given monitor to be captured\noptions = {\"top\": 40, \"left\": 0, \"width\": 100, \"height\": 100}\n\n# open video stream with defined parameters\nstream = ScreenGear(logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/screengear/usage/#using-screengear-with-multiple-screens","title":"Using ScreenGear with Multiple Screens","text":"

ScreenGear API provides us the flexibility to select any connected display for fetching frames, with its monitor parameter:

Implication of using monitor parameter

Any value on monitor parameter other than None in ScreenGear API:

With dxcam on Windows With mss backend Using GPU acceleration on Windows

With dxcam library backend, you can also assign which GPU devices ids to use along with monitor device ids as tuple (monitor_idx, gpu_idx), as follows:

# open video stream with defined parameters with \n# monitor at index `1` and GPU at index `0`.\nstream = ScreenGear(monitor=(1,0), logging=True).start()\n

Getting a complete list of monitor devices and GPUs

To get a complete list of monitor devices and outputs(GPUs), you can use dxcam library itself:

>>> import dxcam\n>>> dxcam.device_info()\n'Device[0]:<Device Name:NVIDIA GeForce RTX 3090 Dedicated VRAM:24348Mb VendorId:4318>\\n'\n>>> dxcam.output_info()\n'Device[0] Output[0]: Res:(1920, 1080) Rot:0 Primary:True\\nDevice[0] Output[1]: Res:(1920, 1080) Rot:0 Primary:False\\n'\n

# import required libraries\nfrom vidgear.gears import ScreenGear\nimport cv2\n\n# open video stream with defined parameters with monitor at index `1` selected\nstream = ScreenGear(monitor=1, logging=True).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

With mss library backend, You can also assign monitor value to -1 to fetch frames from all connected multiple monitor screens with mss backend.

With mss library backend, API will output BGRA colorspace frames instead of default BGR.

# import required libraries\nfrom vidgear.gears import ScreenGear\nimport cv2\n\n# open video stream with defined parameters with monitor at index `1` selected\nstream = ScreenGear(monitor=1, logging=True).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/screengear/usage/#using-screengear-with-variable-backend","title":"Using ScreenGear with Variable Backend","text":"

With ScreenGear API, you can select from many different backends that generates best performance as well as the most compatible with our machine by employing its backend parameter that supports many different backends:

Supported backend values

Its possible values are: dxcam (Windows only), pil, mss, scrot, maim, imagemagick, pyqt5, pyqt, pyside2, pyside, wx, pygdk3, mac_screencapture, mac_quartz, gnome_dbus, gnome-screenshot, kwin_dbus.

Remember to install backend library and all of its dependencies you're planning to use with ScreenGear API. More information on all these backends (except dxcam) can be found here \u27b6

Backend defaults to dxcam library on Windows (if installed), and pyscreenshot otherwise.

Any value on monitor parameter will disable the backend parameter. You cannot use them simultaneously.

# import required libraries\nfrom vidgear.gears import ScreenGear\nimport cv2\n\n# open video stream with defined parameters and `mss` backend \n# for extracting frames.\nstream = ScreenGear(backend=\"mss\", logging=True).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/screengear/usage/#using-screengear-with-direct-colorspace-manipulation","title":"Using ScreenGear with Direct Colorspace Manipulation","text":"

ScreenGear API also supports Direct Colorspace Manipulation, which is ideal for changing source colorspace on the run.

A more detailed information on colorspace manipulation can be found here \u27b6

In following example code, we will start with HSV as source colorspace, and then we will switch to GRAY colorspace when W key is pressed, and then LAB colorspace when E key is pressed, finally default colorspace (i.e. BGR) when S key is pressed. Also, quit when Q key is pressed:

Any incorrect or None-type value, will immediately revert the colorspace to default i.e. BGR.

# import required libraries\nfrom vidgear.gears import ScreenGear\nimport cv2\n\n# Change colorspace to `HSV`\nstream = ScreenGear(colorspace=\"COLOR_BGR2HSV\", logging=True).start()\n\n# loop over\nwhile True:\n\n    # read HSV frames\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the HSV frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for key if pressed\n    key = cv2.waitKey(1) & 0xFF\n\n    # check if 'w' key is pressed\n    if key == ord(\"w\"):\n        # directly change colorspace at any instant\n        stream.color_space = cv2.COLOR_BGR2GRAY  # Now colorspace is GRAY\n\n    # check for 'e' key is pressed\n    if key == ord(\"e\"):\n        stream.color_space = cv2.COLOR_BGR2LAB  # Now colorspace is CieLAB\n\n    # check for 's' key is pressed\n    if key == ord(\"s\"):\n        stream.color_space = None  # Now colorspace is default(ie BGR)\n\n    # check for 'q' key is pressed\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/screengear/usage/#using-screengear-with-writegear-api","title":"Using ScreenGear with WriteGear API","text":"

ScreenGear can be used in conjunction with WriteGear API directly without any compatibility issues. The suitable example is as follows:

# import required libraries\nfrom vidgear.gears import ScreenGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n\n# define dimensions of screen w.r.t to given monitor to be captured\noptions = {\"top\": 40, \"left\": 0, \"width\": 100, \"height\": 100}\n\n# define suitable (Codec,CRF,preset) FFmpeg parameters for writer\noutput_params = {\"-vcodec\": \"libx264\", \"-crf\": 0, \"-preset\": \"fast\"}\n\n# open video stream with defined parameters\nstream = ScreenGear(monitor=1, logging=True, **options).start()\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n    # lets convert frame to gray for this example\n    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n\n    # write gray frame to writer\n    writer.write(gray)\n\n    # Show output window\n    cv2.imshow(\"Output Gray Frame\", gray)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/stabilizer/overview/","title":"Overview","text":""},{"location":"gears/stabilizer/overview/#stabilizer-class","title":"Stabilizer Class","text":"

VidGear's Stabilizer in Action(Video Credits @SIGGRAPH2013)

This video is transcoded with StreamGear API and hosted on GitHub Repository and served with raw.githack.com

"},{"location":"gears/stabilizer/overview/#overview","title":"Overview","text":"

Stabilizer is an auxiliary class that enables Video Stabilization for vidgear with minimalistic latency, and at the expense of little to no additional computational requirements.

The basic idea behind it is to tracks and save the salient feature array for the given number of frames and then uses these anchor point to cancel out all perturbations relative to it for the incoming frames in the queue. This class relies on Fixed-Size Python Queues for error-free & ultra-fast frame handling.

For more detailed information on Stabilizer working, See this blogpost \u27b6

"},{"location":"gears/stabilizer/overview/#features","title":"Features","text":"

Important

"},{"location":"gears/stabilizer/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through Stabilizer Class Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/stabilizer/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/stabilizer/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/stabilizer/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/stabilizer/params/","title":"Parameters","text":""},{"location":"gears/stabilizer/params/#stabilizer-class-parameters","title":"Stabilizer Class Parameters","text":""},{"location":"gears/stabilizer/params/#smoothing_radius","title":"smoothing_radius","text":"

This parameter can be used to alter averaging window size. It basically handles the quality of stabilization at the expense of latency and sudden panning. Larger its value, less will be panning, more will be latency and vice-versa.

Data-Type: Integer

Default Value: Its default value is 25.

Usage:

You can easily pass this parameter as follows:

Stabilizer(smoothing_radius=30)\n

"},{"location":"gears/stabilizer/params/#border_size","title":"border_size","text":"

This parameter enables and set the value for extended border size that compensates for reduction of black borders during stabilization.

Data-Type: Integer

Default Value: Its default value is 0(no borders).

Usage:

You can easily pass this parameter as follows:

Stabilizer(border_size=10)\n

"},{"location":"gears/stabilizer/params/#crop_n_zoom","title":"crop_n_zoom","text":"

This parameter enables cropping and zooming of frames (to original size) to reduce the black borders from being too noticeable (similar to the Stabilized, cropped and Auto-Scaled feature available in Adobe AfterEffects) during stabilization. It simply works in conjunction with the border_size parameter, i.e. when this parameter is enabled, border_size will be used for cropping border instead of extending them.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

You can easily pass this parameter as follows:

Stabilizer(border_size=10, crop_n_zoom=True)\n

"},{"location":"gears/stabilizer/params/#border_type","title":"border_type","text":"

This parameter can be used to change the extended border type. Valid border types are 'black', 'reflect', 'reflect_101', 'replicate' and 'wrap', learn more about it here.

Altering border_type parameter is DISABLED when crop_n_zoom is enabled!

Data-Type: String

Default Value: Its default value is 'black'.

Usage:

You can easily pass this parameter as follows:

Stabilizer(border_type='reflect')\n

"},{"location":"gears/stabilizer/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

Stabilizer(logging=True)\n

"},{"location":"gears/stabilizer/usage/","title":"Usage Examples","text":""},{"location":"gears/stabilizer/usage/#stabilizer-class-usage-examples","title":"Stabilizer Class Usage Examples:","text":"

The stabilizer may not perform well against High-frequency jitter in video. Use at your own risk!

The stabilizer might be slower for High-Quality/Resolution videos-frames.

It is advised to enable logging on the first run for easily identifying any runtime errors.

After going through Stabilizer Class Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/stabilizer/usage/#bare-minimum-usage-with-videocapture-gears","title":"Bare-Minimum Usage with VideoCapture Gears","text":"

Following is the bare-minimum code you need to get started with Stabilizer Class and various VideoCapture Gears:

You can use any VideoCapture Gear instead of CamGear in the similar manner, as shown in this usage example.

# import required libraries\nfrom vidgear.gears.stabilizer import Stabilizer\nfrom vidgear.gears import CamGear\nimport cv2\n\n# To open live video stream on webcam at first index(i.e. 0) device\nstream = CamGear(source=0).start()\n\n# initiate stabilizer object with default parameters\nstab = Stabilizer()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # send current frame to stabilizer for processing\n    stabilized_frame = stab.stabilize(frame)\n\n    # wait for stabilizer which still be initializing\n    if stabilized_frame is None:\n        continue\n\n    # {do something with the stabilized frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Stabilized Frame\", stabilized_frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# clear stabilizer resources\nstab.clean()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/stabilizer/usage/#bare-minimum-usage-with-opencv","title":"Bare-Minimum Usage with OpenCV","text":"

The VidGear's stabilizer class can also work standalone easily with any Computer Vision library such as OpenCV itself. Following is the bare-minimum code you need to get started with Stabilizer Class and OpenCV:

# import required libraries\nfrom vidgear.gears.stabilizer import Stabilizer\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = cv2.VideoCapture(0)\n\n# initiate stabilizer object with default parameters\nstab = Stabilizer()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n        break\n\n    # send current frame to stabilizer for processing\n    stabilized_frame = stab.stabilize(frame)\n\n    # wait for stabilizer which still be initializing\n    if stabilized_frame is None:\n        continue\n\n    # {do something with the stabilized frame here}\n\n    # Show output window\n    cv2.imshow(\"Stabilized Frame\", stabilized_frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# clear stabilizer resources\nstab.clean()\n\n# safely close video stream\nstream.release()\n

"},{"location":"gears/stabilizer/usage/#using-stabilizer-with-variable-parameters","title":"Using Stabilizer with Variable Parameters","text":"

Stabilizer class provide certain parameters which you can use to tweak its internal properties. The complete usage example is as follows:

# import required libraries\nfrom vidgear.gears.stabilizer import Stabilizer\nfrom vidgear.gears import CamGear\nimport cv2\n\n# To open live video stream on webcam at first index(i.e. 0) device\nstream = CamGear(source=0).start()\n\n# initiate stabilizer object with defined parameters\nstab = Stabilizer(smoothing_radius=30, crop_n_zoom=True, border_size=5, logging=True)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # send current frame to stabilizer for processing\n    stabilized_frame = stab.stabilize(frame)\n\n    # wait for stabilizer which still be initializing\n    if stabilized_frame is None:\n        continue\n\n    # {do something with the stabilized frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Stabilized Frame\", stabilized_frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# clear stabilizer resources\nstab.clean()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/stabilizer/usage/#using-stabilizer-with-writegear","title":"Using Stabilizer with WriteGear","text":"

VideoGear's stabilizer can be used in conjunction with WriteGear API directly without any compatibility issues. The complete usage example is as follows:

You can also add live audio input to WriteGear pipeline. See this bonus example \u27b6

# import required libraries\nfrom vidgear.gears.stabilizer import Stabilizer\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# Open suitable video stream\nstream = CamGear(source=\"unstabilized_stream.mp4\").start()\n\n# initiate stabilizer object with default parameters\nstab = Stabilizer()\n\n# Define writer with default parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\")\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if not None-type\n    if frame is None:\n        break\n\n    # send current frame to stabilizer for processing\n    stabilized_frame = stab.stabilize(frame)\n\n    # wait for stabilizer which still be initializing\n    if stabilized_frame is None:\n        continue\n\n    # {do something with the stabilized frame here}\n\n    # write stabilized frame to writer\n    writer.write(stabilized_frame)\n\n    # Show output window\n    cv2.imshow(\"Stabilized Frame\", stabilized_frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# clear stabilizer resources\nstab.clean()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/stabilizer/usage/#using-videogear-with-stabilizer-backend","title":"Using VideoGear with Stabilizer backend","text":"

VideoGear API provides a special internal wrapper around Stabilizer class that enables easy stabilization for various video-streams (real-time or not) with minimum effort and writing way fewer lines of code.

The complete usage example can be found here \u27b6

"},{"location":"gears/streamgear/ffmpeg_install/","title":"FFmpeg Installation","text":""},{"location":"gears/streamgear/ffmpeg_install/#ffmpeg-installation-instructions","title":"FFmpeg Installation Instructions","text":"

StreamGear must requires FFmpeg executables for transcoding Media Chunks. You can following machine-specific instructions for its installation:

StreamGear API will throw RuntimeError, if it fails to detect valid FFmpeg executables on your system.

Enable logging (logging=True) for debugging FFmpeg validation process.

"},{"location":"gears/streamgear/ffmpeg_install/#linux-ffmpeg-installation","title":"Linux FFmpeg Installation","text":"

The StreamGear API supports Auto-Detection and Manual Configuration methods on a Linux machine:

"},{"location":"gears/streamgear/ffmpeg_install/#a-auto-detection","title":"A. Auto-Detection","text":"

This is a recommended approach on Linux Machines

If StreamGear API not receives any input from the user on custom_ffmpeg parameter, then on Linux system, it tries to auto-detects the required FFmpeg installed binaries through validation test that employs subprocess python module.

Installation: You can install easily install official FFmpeg according to your Linux Distro by following this post \u27b6

"},{"location":"gears/streamgear/ffmpeg_install/#b-manual-configuration","title":"B. Manual Configuration","text":"

"},{"location":"gears/streamgear/ffmpeg_install/#windows-ffmpeg-installation","title":"Windows FFmpeg Installation","text":"

The StreamGear API supports Auto-Installation and Manual Configuration methods on Windows systems.

"},{"location":"gears/streamgear/ffmpeg_install/#a-auto-installation","title":"A. Auto-Installation","text":"

This is a recommended approach on Windows Machines

If StreamGear API not receives any input from the user on custom_ffmpeg parameter, then on Windows system StreamGear API auto-generates the required FFmpeg Static Binaries from a dedicated Github Server into the temporary directory (for e.g. C:\\Temp) of your machine.

Warning

"},{"location":"gears/streamgear/ffmpeg_install/#b-manual-configuration_1","title":"B. Manual Configuration","text":"

"},{"location":"gears/streamgear/ffmpeg_install/#macos-ffmpeg-installation","title":"MacOS FFmpeg Installation","text":"

The StreamGear API supports Auto-Detection and Manual Configuration methods on a macOS machine.

"},{"location":"gears/streamgear/ffmpeg_install/#a-auto-detection_1","title":"A. Auto-Detection","text":"

This is a recommended approach on MacOS Machines

If StreamGear API not receives any input from the user on custom_ffmpeg parameter, then on macOS system, it tries to auto-detects the required FFmpeg installed binaries through validation test that employs subprocess python module.

Installation: You can easily install FFmpeg on your macOS machine by following this tutorial \u27b6

"},{"location":"gears/streamgear/ffmpeg_install/#b-manual-configuration_2","title":"B. Manual Configuration","text":"

"},{"location":"gears/streamgear/introduction/","title":"Introduction","text":""},{"location":"gears/streamgear/introduction/#streamgear-api","title":"StreamGear API","text":"StreamGear API's generalized workflow"},{"location":"gears/streamgear/introduction/#overview","title":"Overview","text":"

StreamGear streamlines and simplifies the transcoding workflow to generate Ultra-Low Latency, High-Quality, Dynamic & Adaptive Streaming Formats like MPEG-DASH and Apple HLS with just a few lines of Python code, allowing developers to focus on their application logic rather than dealing with the complexities of transcoding and chunking media files.

StreamGear API provides a standalone, highly extensible, and flexible wrapper around the FFmpeg multimedia framework for generating chunk-encoded media segments from your multimedia content effortlessly.

With StreamGear, you can transcode source video/audio files and real-time video frames into a sequence of multiple smaller chunks/segments of suitable lengths. These segments facilitate streaming at different quality levels (bitrates or spatial resolutions) and allow for seamless switching between quality levels during playback based on available bandwidth. You can serve these segments on a web server, making them easily accessible via standard HTTP GET requests.

SteamGear currently supports both MPEG-DASH (Dynamic Adaptive Streaming over HTTP, ISO/IEC 23009-1) and Apple HLS (HTTP Live Streaming).

Additionally, StreamGear generates a manifest file (such as MPD for DASH) or a master playlist (such as M3U8 for Apple HLS) alongside the segments. These files contain essential segment information, including timing, URLs, and media characteristics like video resolution and adaptive bitrate. They are provided to the client before the streaming session begins.

For streaming with older traditional protocols such as RTMP, RTSP/RTP you could use WriteGear API instead.

New in v0.2.2

Apple HLS support was added in v0.2.2.

Important

Useful Links

"},{"location":"gears/streamgear/introduction/#mode-of-operations","title":"Mode of Operations","text":"

StreamGear primarily operates in following independent modes for transcoding:

Real-time Frames Mode itself is NOT Live-Streaming

To enable live-streaming in Real-time Frames Mode, use the exclusive -livestream attribute of the stream_params dictionary parameter in the StreamGear API. Checkout this usage example \u27b6 for more information.

"},{"location":"gears/streamgear/introduction/#watch-demo","title":"Watch Demo","text":"Watch MPEG-DASH StreamWatch APPLE HLS Stream

Watch StreamGear transcoded MPEG-DASH Stream:

Powered by clappr & shaka-player

This video assets (Manifest and segments) are hosted on GitHub Repository and served with raw.githack.com

Video Credits: \"Tears of Steel\" - Project Mango Teaser

Watch StreamGear transcoded APPLE HLS Stream:

Powered by clappr & HlsjsPlayback

This video assets (Playlist and segments) are hosted on GitHub Repository and served with raw.githack.com

Video Credits: \"Sintel\" - Project Durian Teaser

"},{"location":"gears/streamgear/introduction/#recommended-players","title":"Recommended Players","text":"GUI PlayersCommand-Line PlayersOnline Players

To run Online players locally, you'll need a HTTP server. For creating one yourself, See this well-curated list \u27b6

"},{"location":"gears/streamgear/introduction/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/streamgear/introduction/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/streamgear/introduction/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/streamgear/params/","title":"Parameters","text":""},{"location":"gears/streamgear/params/#streamgear-api-parameters","title":"StreamGear API Parameters","text":""},{"location":"gears/streamgear/params/#output","title":"output","text":"

This parameter sets the valid filename/path for storing the StreamGear assets, including Manifest file (such as MPD in case of DASH) or a Master Playlist (such as M3U8 in case of Apple HLS) and generated sequence of chunks/segments.

StreamGear API will throw ValueError if the provided output is empty or invalid.

Make sure to provide a valid filename with a valid file extension for the selected format value (such as .mpd for MPEG-DASH and .m3u8 for APPLE-HLS), otherwise StreamGear will throw AssertionError.

You can easily delete all previous assets at the output location by using the -clear_prev_assets attribute of the stream_params dictionary parameter.

Data-Type: String

Usage:

Its valid input can be one of the following:

"},{"location":"gears/streamgear/params/#format","title":"format","text":"

This parameter enables the adaptive HTTP streaming format. This parameter currently supported these formats: dash (i.e MPEG-DASH) and hls (i.e Apple HLS).

Make sure to provide a valid filename with a valid file extension in the output parameter for the selected format value (i.e., .mpd for MPEG-DASH and .m3u8 for APPLE-HLS), otherwise StreamGear will throw an AssertionError.

Any improper value assigned to format parameter will result in a ValueError!

Data-Type: String

Default Value: Its default value is dash

Usage:

DASHHLS
# Define streamer with DASH format\nStreamGear(output = \"output_dash.mpd\", format=\"dash\")\n
# Define streamer with HLS format\nStreamGear(output = \"output_hls.m3u8\", format=\"hls\")\n

"},{"location":"gears/streamgear/params/#custom_ffmpeg","title":"custom_ffmpeg","text":"

This parameter assigns the custom path/directory where the custom/downloaded FFmpeg executables are located.

Behavior on Windows Systems

On Windows, if a custom FFmpeg executable's path/directory is not provided through this custom_ffmpeg parameter, the StreamGear API will automatically attempt to download and extract suitable Static FFmpeg binaries at a suitable location on your Windows machine. More information can be found here \u27b6.

Data-Type: String

Default Value: Its default value is None.

Usage:

# Define streamer with custom ffmpeg binary\nStreamGear(output = 'output_foo.mpd', custom_ffmpeg=\"C://foo//bar//ffmpeg.exe\")\n

"},{"location":"gears/streamgear/params/#stream_params","title":"stream_params","text":"

This parameter allows developers to leverage nearly all FFmpeg options, providing effortless and flexible control over its internal settings for transcoding and generating high-quality streams. All supported parameters can be formatted as attributes within this dictionary parameter.

Please read the FFmpeg Documentation carefully before passing any additional values to the stream_params parameter. Incorrect values may cause errors or result in no output.

Data-Type: Dictionary

Default Value: Its default value is {}.

"},{"location":"gears/streamgear/params/#supported-parameters","title":"Supported Parameters","text":""},{"location":"gears/streamgear/params/#a-exclusive-parameters","title":"A. Exclusive Parameters","text":"

StreamGear API provides some exclusive internal parameters to easily generate Streaming Assets and effortlessly tweak its internal properties. These parameters are discussed below:

"},{"location":"gears/streamgear/params/#b-ffmpeg-parameters","title":"B. FFmpeg Parameters","text":"

Almost all FFmpeg parameters can be passed as dictionary attributes in stream_params. For example, to use the libx264 encoder to produce a lossless output video, you can pass the required FFmpeg parameters as dictionary attributes as follows:

Please check the H.264 documentation \u27b6 and FFmpeg Documentation \u27b6 for more information on following parameters.

All FFmpeg parameters are case-sensitive. Double-check each parameter if any errors occur.

In addition to these parameters, almost any FFmpeg parameter (supported by the installed FFmpeg) is also supported. Be sure to read the FFmpeg Documentation carefully first.

# libx264 encoder and its supported parameters\nstream_params = {\"-vcodec\":\"libx264\", \"-crf\": 0, \"-preset\": \"fast\", \"-tune\": \"zerolatency\"} \n

"},{"location":"gears/streamgear/params/#supported-encoders-and-decoders","title":"Supported Encoders and Decoders","text":"

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:

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 for creating HLS/DASH chunks of the primary stream efficiently. However, consider the following points:

# for checking encoder\nffmpeg -encoders           # use `ffmpeg.exe -encoders` on windows\n# for checking decoders\nffmpeg -decoders           # use `ffmpeg.exe -decoders` on windows\n

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

"},{"location":"gears/streamgear/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

StreamGear(logging=True)\n

  1. In Real-time Frames Mode, the Primary Stream's framerate defaults to -input_framerate attribute value, if defined, else it will be 25fps.\u00a0\u21a9

"},{"location":"gears/streamgear/rtfm/overview/","title":"Overview","text":""},{"location":"gears/streamgear/rtfm/overview/#streamgear-api-real-time-frames-mode","title":"StreamGear API: Real-time Frames Mode","text":"Real-time Frames Mode generalized workflow"},{"location":"gears/streamgear/rtfm/overview/#overview","title":"Overview","text":"

When no valid input is received on -video_source attribute of stream_params dictionary parameter, StreamGear API activates this mode where it directly transcodes real-time numpy.ndarray video-frames (as opposed to a entire video file) into a sequence of multiple smaller chunks/segments for adaptive streaming.

This mode works exceptionally well when you desire to flexibility manipulate or transform video-frames in real-time before sending them onto FFmpeg Pipeline for processing. But on the downside, StreamGear DOES NOT automatically maps video-source's audio to generated streams with this mode. You need to manually assign separate audio-source through -audio attribute of stream_params dictionary parameter.

SteamGear supports both MPEG-DASH (Dynamic Adaptive Streaming over HTTP, ISO/IEC 23009-1) and Apple HLS (HTTP Live Streaming) with this mode.

For this mode, StreamGear API provides exclusive stream() method for directly trancoding video-frames into streamable chunks.

New in v0.2.2

Apple HLS support was added in v0.2.2.

Real-time Frames Mode itself is NOT Live-Streaming

To enable live-streaming in Real-time Frames Mode, use the exclusive -livestream attribute of the stream_params dictionary parameter in the StreamGear API. Checkout this usage example \u27b6 for more information.

Please Remember

"},{"location":"gears/streamgear/rtfm/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through StreamGear Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/streamgear/rtfm/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/streamgear/rtfm/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/streamgear/rtfm/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/streamgear/rtfm/usage/","title":"Usage Examples","text":""},{"location":"gears/streamgear/rtfm/usage/#streamgear-api-usage-examples-real-time-frames-mode","title":"StreamGear API Usage Examples: Real-time Frames Mode","text":"

Real-time Frames Mode itself is NOT Live-Streaming

To enable live-streaming in Real-time Frames Mode, use the exclusive -livestream attribute of the stream_params dictionary parameter in the StreamGear API. Checkout following usage example \u27b6 for more information.

Important Information

DEPRECATION NOTICES for v0.3.3 and above

After going through following Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/streamgear/rtfm/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with StreamGear API in Real-time Frames Mode:

We are using CamGear in this Bare-Minimum example, but any VideoCapture Gear will work in the similar manner.

In this mode, StreamGear DOES NOT automatically maps video-source audio to generated streams. You need to manually assign separate audio-source through -audio attribute of stream_params dictionary parameter.

DASHHLS
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# open any valid video stream(for e.g `foo1.mp4` file)\nstream = CamGear(source='foo1.mp4').start() \n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"dash_out.mpd\")\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# open any valid video stream(for e.g `foo1.mp4` file)\nstream = CamGear(source='foo1.mp4').start() \n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\")\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n

After running this bare-minimum example, StreamGear will produce a Manifest file (dash.mpd) with streamable chunks that contains information about a Primary Stream of same resolution and framerate1 as input (without any audio).

"},{"location":"gears/streamgear/rtfm/usage/#bare-minimum-usage-with-controlled-input-framerate","title":"Bare-Minimum Usage with controlled Input-framerate","text":"

In Real-time Frames Mode, StreamGear API provides the exclusive -input_framerate attribute for the stream_params dictionary parameter, which allows you to set the assumed constant framerate for incoming frames.

In this example, we will retrieve the framerate from a webcam video stream and set it as the value for the -input_framerate attribute in StreamGear.

Remember, the input framerate defaults to 25.0 fps if the -input_framerate attribute value is not defined in Real-time Frames mode.

DASHHLS
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = CamGear(source=0).start()\n\n# retrieve framerate from CamGear Stream and pass it as `-input_framerate` value\nstream_params = {\"-input_framerate\":stream.framerate}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = CamGear(source=0).start()\n\n# retrieve framerate from CamGear Stream and pass it as `-input_framerate` value\nstream_params = {\"-input_framerate\":stream.framerate}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n

"},{"location":"gears/streamgear/rtfm/usage/#bare-minimum-usage-with-live-streaming","title":"Bare-Minimum Usage with Live-Streaming","text":"

You can easily activate Low-latency Live-Streaming in Real-time Frames Mode, where chunks will contain information for new frames only and forget previous ones, using the exclusive -livestream attribute of the stream_params dictionary parameter. The complete example is as follows:

In this mode, StreamGear DOES NOT automatically maps video-source audio to generated streams. You need to manually assign separate audio-source through -audio attribute of stream_params dictionary parameter.

DASHHLS

Controlling chunk size in DASH

To control the number of frames kept in Chunks for the DASH stream (controlling latency), you can use the -window_size and -extra_window_size FFmpeg parameters. Lower values for these parameters will result in lower latency.

After every few chunks (equal to the sum of -window_size and -extra_window_size values), all chunks will be overwritten while Live-Streaming. This means that newer chunks in the manifest will contain NO information from older chunks, and the resulting DASH stream will only play the most recent frames, reducing latency.

# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# open any valid video stream(from web-camera attached at index `0`)\nstream = CamGear(source=0).start()\n\n# enable livestreaming and retrieve framerate from CamGear Stream and\n# pass it as `-input_framerate` parameter for controlled framerate\nstream_params = {\"-input_framerate\": stream.framerate, \"-livestream\": True}\n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n

Controlling chunk size in HLS

To control the number of frames kept in Chunks for the HLS stream (controlling latency), you can use the -hls_init_time & -hls_time FFmpeg parameters. Lower values for these parameters will result in lower latency.

After every few chunks (equal to the sum of -hls_init_time & -hls_time values), all chunks will be overwritten while Live-Streaming. This means that newer chunks in the master playlist will contain NO information from older chunks, and the resulting HLS stream will only play the most recent frames, reducing latency.

# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# open any valid video stream(from web-camera attached at index `0`)\nstream = CamGear(source=0).start()\n\n# enable livestreaming and retrieve framerate from CamGear Stream and\n# pass it as `-input_framerate` parameter for controlled framerate\nstream_params = {\"-input_framerate\": stream.framerate, \"-livestream\": True}\n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n

"},{"location":"gears/streamgear/rtfm/usage/#bare-minimum-usage-with-opencv","title":"Bare-Minimum Usage with OpenCV","text":"

You can easily use the StreamGear API directly with any other Video Processing library (for e.g. OpenCV) in Real-time Frames Mode.

The following is a complete StreamGear API usage example with OpenCV:

This is a bare-minimum example with OpenCV, but any other Real-time Frames Mode feature or example will work in a similar manner.

DASHHLS
# import required libraries\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = cv2.VideoCapture(0) \n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"dash_out.mpd\")\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n      break\n\n    # {do something with the frame here}\n    # lets convert frame to gray for this example\n    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n\n    # send frame to streamer\n    streamer.stream(gray)\n\n    # Show output window\n    cv2.imshow(\"Output Gray Frame\", gray)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.release()\n\n# safely close streamer\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = cv2.VideoCapture(0) \n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\")\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n      break\n\n    # {do something with the frame here}\n    # lets convert frame to gray for this example\n    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n\n    # send frame to streamer\n    streamer.stream(gray)\n\n    # Show output window\n    cv2.imshow(\"Output Gray Frame\", gray)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.release()\n\n# safely close streamer\nstreamer.close()\n

"},{"location":"gears/streamgear/rtfm/usage/#usage-with-additional-streams","title":"Usage with Additional Streams","text":"

Similar to Single-Source Mode, in addition to the Primary Stream, you can easily generate any number of additional Secondary Streams with variable bitrate or spatial resolution, using the exclusive -streams attribute of the stream_params dictionary parameter.

To generate Secondary Streams, add each desired resolution and bitrate/framerate as a list of dictionaries to the -streams attribute. StreamGear will handle the rest automatically. The complete example is as follows:

A more detailed information on -streams attribute can be found here \u27b6

In this mode, StreamGear DOES NOT automatically maps video-source audio to generated streams. You need to manually assign separate audio-source through -audio attribute of stream_params dictionary parameter.

Important Information about -streams attribute

Always use the -streams attribute to define additional streams safely. Duplicate or incorrect definitions can break the transcoding pipeline and corrupt the output chunks.

DASHHLS
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = CamGear(source=0).start() \n\n# define various streams\nstream_params = {\n    \"-streams\": [\n        {\"-resolution\": \"1280x720\", \"-framerate\": 30.0},  # Stream1: 1280x720 at 30fps framerate\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream2: 640x360 at 60fps framerate\n        {\"-resolution\": \"320x240\", \"-video_bitrate\": \"500k\"},  # Stream3: 320x240 at 500kbs bitrate\n    ],\n}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"dash_out.mpd\")\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = CamGear(source=0).start() \n\n# define various streams\nstream_params = {\n    \"-streams\": [\n        {\"-resolution\": \"1280x720\", \"-framerate\": 30.0},  # Stream1: 1280x720 at 30fps framerate\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream2: 640x360 at 60fps framerate\n        {\"-resolution\": \"320x240\", \"-video_bitrate\": \"500k\"},  # Stream3: 320x240 at 500kbs bitrate\n    ],\n}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\")\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n

"},{"location":"gears/streamgear/rtfm/usage/#usage-with-file-audio-input","title":"Usage with File Audio-Input","text":"

In Real-time Frames Mode, if you want to add audio to your streams, you need to use the exclusive -audio attribute of the stream_params dictionary parameter.

To add a audio source, provide the path to your audio file as a string to the -audio attribute. The API will automatically validate and map the audio to all generated streams. The complete example is as follows:

Ensure the provided -audio audio source is compatible with the input video source. Incompatibility can cause multiple errors or result in no output at all.

You MUST use -input_framerate attribute to set exact value of input framerate when using external audio in Real-time Frames mode, otherwise audio delay will occur in output streams.

You can also assign a valid audio URL as input instead of a file path. More details can be found here \u27b6

DASHHLS
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# open any valid video stream(for e.g `foo1.mp4` file)\nstream = CamGear(source='foo1.mp4').start() \n\n# add various streams, along with custom audio\nstream_params = {\n    \"-streams\": [\n        {\"-resolution\": \"1920x1080\", \"-video_bitrate\": \"4000k\"},  # Stream1: 1920x1080 at 4000kbs bitrate\n        {\"-resolution\": \"1280x720\", \"-framerate\": 30.0},  # Stream2: 1280x720 at 30fps\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream3: 640x360 at 60fps\n    ],\n    \"-input_framerate\": stream.framerate, # controlled framerate for audio-video sync !!! don't forget this line !!!\n    \"-audio\": \"/home/foo/foo1.aac\" # assign external audio-source\n}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# open any valid video stream(for e.g `foo1.mp4` file)\nstream = CamGear(source='foo1.mp4').start() \n\n# add various streams, along with custom audio\nstream_params = {\n    \"-streams\": [\n        {\"-resolution\": \"1920x1080\", \"-video_bitrate\": \"4000k\"},  # Stream1: 1920x1080 at 4000kbs bitrate\n        {\"-resolution\": \"1280x720\", \"-framerate\": 30.0},  # Stream2: 1280x720 at 30fps\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream3: 640x360 at 60fps\n    ],\n    \"-input_framerate\": stream.framerate, # controlled framerate for audio-video sync !!! don't forget this line !!!\n    \"-audio\": \"/home/foo/foo1.aac\" # assign external audio-source\n}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n

"},{"location":"gears/streamgear/rtfm/usage/#usage-with-device-audio-input","title":"Usage with Device Audio-Input","text":"

In Real-time Frames Mode, you can also use the exclusive -audio attribute of the stream_params dictionary parameter for streaming live audio from an external device.

To stream live audio, format your audio device name followed by a suitable demuxer as a list, and assign it to the -audio attribute. The API will automatically validate and map the audio to all generated streams. The complete example is as follows:

Example Assumptions

Using devices sources with -audio attribute on different OS platforms

To use device sources with the -audio attribute on different OS platforms, follow these instructions:

Windows Linux MacOS

Windows OS users can use the dshow (DirectShow) to list audio input device which is the preferred option for Windows users. You can refer following steps to identify and specify your sound card:

If audio still doesn't work then checkout this troubleshooting guide \u27b6 or reach us out on Gitter \u27b6 Community channel

Linux OS users can use the alsa to list input device to capture live audio input such as from a webcam. You can refer following steps to identify and specify your sound card:

If audio still doesn't work then reach us out on Gitter \u27b6 Community channel

MAC OS users can use the avfoundation to list input devices for grabbing audio from integrated iSight cameras as well as cameras connected via USB or FireWire. You can refer following steps to identify and specify your sound card on MacOS/OSX machines:

If audio still doesn't work then reach us out on Gitter \u27b6 Community channel

It is advised to use this example with live-streaming enabled(True) by using StreamGear API's exclusive -livestream attribute of stream_params dictionary parameter.

Ensure the provided -audio audio source is compatible with the video source device. Incompatibility can cause multiple errors or result in no output at all.

You MUST use -input_framerate attribute to set exact value of input framerate when using external audio in Real-time Frames mode, otherwise audio delay will occur in output streams.

DASHHLS
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# open any valid DEVICE video stream\nstream = CamGear(source=0).start()\n\n# add various streams, along with custom audio\nstream_params = {\n    \"-streams\": [\n        {\n            \"-resolution\": \"640x360\",\n            \"-video_bitrate\": \"4000k\",\n        },  # Stream1: 640x360 at 4000kbs bitrate\n        {\"-resolution\": \"320x240\", \"-framerate\": 30.0},  # Stream2: 320x240 at 30fps\n    ],\n    \"-input_framerate\": stream.framerate,  # controlled framerate for audio-video sync !!! don't forget this line !!!\n    \"-livestream\": True,\n    \"-audio\": [\n        \"-f\",\n        \"dshow\",\n        \"-i\",\n        \"audio=Microphone (USB2.0 Camera)\",\n    ],  # assign appropriate input audio-source device(compatible with video source) and its demuxer\n}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# open any valid DEVICE video stream\nstream = CamGear(source=0).start()\n\n# add various streams, along with custom audio\nstream_params = {\n    \"-streams\": [\n        {\n            \"-resolution\": \"640x360\",\n            \"-video_bitrate\": \"4000k\",\n        },  # Stream1: 640x360 at 4000kbs bitrate\n        {\"-resolution\": \"320x240\", \"-framerate\": 30.0},  # Stream2: 320x240 at 30fps\n    ],\n    \"-input_framerate\": stream.framerate,  # controlled framerate for audio-video sync !!! don't forget this line !!!\n    \"-livestream\": True,\n    \"-audio\": [\n        \"-f\",\n        \"dshow\",\n        \"-i\",\n        \"audio=Microphone (USB2.0 Camera)\",\n    ],  # assign appropriate input audio-source device(compatible with video source) and its demuxer\n}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"hls_out.m3u8\", format=\"hls\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n

"},{"location":"gears/streamgear/rtfm/usage/#usage-with-hardware-video-encoder","title":"Usage with Hardware Video-Encoder","text":"

In Real-time Frames Mode, you can easily change the video encoder according to your requirements by passing the -vcodec FFmpeg parameter as an attribute in the stream_params dictionary parameter. Additionally, you can specify additional properties, features, and optimizations for your system's GPU.

In this example, we will be using h264_vaapi as our Hardware Encoder and specifying the device hardware's location and compatible video filters by formatting them as attributes in the stream_params dictionary parameter.

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.

Checking VAAPI Support for Hardware Encoding

To use VAAPI (Video Acceleration API) as a hardware encoder in this example, follow these steps to ensure your FFmpeg supports VAAPI:

ffmpeg  -hide_banner -encoders | grep vaapi \n\n V..... h264_vaapi           H.264/AVC (VAAPI) (codec h264)\n V..... hevc_vaapi           H.265/HEVC (VAAPI) (codec hevc)\n V..... mjpeg_vaapi          MJPEG (VAAPI) (codec mjpeg)\n V..... mpeg2_vaapi          MPEG-2 (VAAPI) (codec mpeg2video)\n V..... vp8_vaapi            VP8 (VAAPI) (codec vp8)\n

Please read the FFmpeg Documentation carefully before passing any additional values to the stream_params parameter. Incorrect values may cause errors or result in no output.

DASHHLS
# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = VideoGear(source=0).start() \n\n# add various streams with custom Video Encoder and optimizations\nstream_params = {\n    \"-streams\": [\n        {\"-resolution\": \"1920x1080\", \"-video_bitrate\": \"4000k\"},  # Stream1: 1920x1080 at 4000kbs bitrate\n        {\"-resolution\": \"1280x720\", \"-framerate\": 30.0},  # Stream2: 1280x720 at 30fps\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream3: 640x360 at 60fps\n    ],\n    \"-vcodec\": \"h264_vaapi\", # define custom Video encoder\n    \"-vaapi_device\": \"/dev/dri/renderD128\", # define device location\n    \"-vf\": \"format=nv12,hwupload\",  # define video filters\n}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = VideoGear(source=0).start() \n\n# add various streams with custom Video Encoder and optimizations\nstream_params = {\n    \"-streams\": [\n        {\"-resolution\": \"1920x1080\", \"-video_bitrate\": \"4000k\"},  # Stream1: 1920x1080 at 4000kbs bitrate\n        {\"-resolution\": \"1280x720\", \"-framerate\": 30.0},  # Stream2: 1280x720 at 30fps\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream3: 640x360 at 60fps\n    ],\n    \"-vcodec\": \"h264_vaapi\", # define custom Video encoder\n    \"-vaapi_device\": \"/dev/dri/renderD128\", # define device location\n    \"-vf\": \"format=nv12,hwupload\",  # define video pixformat\n}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n

  1. In Real-time Frames Mode, the Primary Stream's framerate defaults to the value of the -input_framerate attribute, if defined. Otherwise, it will be set to 25 fps.\u00a0\u21a9\u21a9\u21a9

"},{"location":"gears/streamgear/ssm/overview/","title":"Overview","text":""},{"location":"gears/streamgear/ssm/overview/#streamgear-api-single-source-mode","title":"StreamGear API: Single-Source Mode","text":"Single-Source Mode generalized workflow"},{"location":"gears/streamgear/ssm/overview/#overview","title":"Overview","text":"

In this mode, StreamGear transcodes entire audio-video file (as opposed to frames-by-frame) into a sequence of multiple smaller chunks/segments for adaptive streaming.

This mode works exceptionally well when you're transcoding long-duration lossless videos(with audio) files for streaming that requires no interruptions. But on the downside, the provided source cannot be flexibly manipulated or transformed before sending onto FFmpeg Pipeline for processing.

SteamGear supports both MPEG-DASH (Dynamic Adaptive Streaming over HTTP, ISO/IEC 23009-1) and Apple HLS (HTTP Live Streaming) with this mode.

For this mode, StreamGear API provides exclusive transcode_source() method to easily process audio-video files into streamable chunks.

This mode can be easily activated by assigning suitable video path as input to -video_source attribute of stream_params dictionary parameter, during StreamGear initialization.

New in v0.2.2

Apple HLS support was added in v0.2.2.

Please Remember

"},{"location":"gears/streamgear/ssm/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through StreamGear Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/streamgear/ssm/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/streamgear/ssm/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/streamgear/ssm/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/streamgear/ssm/usage/","title":"Usage Examples","text":""},{"location":"gears/streamgear/ssm/usage/#streamgear-api-usage-examples-single-source-mode","title":"StreamGear API Usage Examples: Single-Source Mode","text":"

Important Information

DEPRECATION NOTICES for v0.3.3 and above 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:

After going through following Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/streamgear/ssm/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with StreamGear API in Single-Source Mode:

If input video-source (i.e. -video_source) contains any audio stream/channel, then it automatically gets mapped to all generated streams.

DASHHLS
# import required libraries\nfrom vidgear.gears import StreamGear\n\n# activate Single-Source Mode with valid video input\nstream_params = {\"-video_source\": \"foo.mp4\"}\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n# transcode source\nstreamer.transcode_source()\n# close\nstreamer.close()\n

After running this bare-minimum example, StreamGear will produce a Manifest file (dash_out.mpd) with streamable chunks, containing information about a Primary Stream with the same resolution and framerate as the input.

# import required libraries\nfrom vidgear.gears import StreamGear\n\n# activate Single-Source Mode with valid video input\nstream_params = {\"-video_source\": \"foo.mp4\"}\n# describe a suitable master playlist location/name and assign params\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", **stream_params)\n# transcode source\nstreamer.transcode_source()\n# close\nstreamer.close()\n

After running this bare-minimum example, StreamGear will produce a Master Playlist file (hls_out.mpd) with streamable chunks, containing information about a Primary Stream with the same resolution and framerate as the input.

"},{"location":"gears/streamgear/ssm/usage/#usage-with-additional-streams","title":"Usage with Additional Streams","text":"

In addition to the Primary Stream, you can easily generate any number of additional Secondary Streams with variable bitrate or spatial resolutions, using the exclusive -streams attribute of the stream_params dictionary parameter.

To generate Secondary Streams, add each desired resolution and bitrate/framerate as a list of dictionaries to the -streams attribute. StreamGear will handle the rest automatically. The complete example is as follows:

A more detailed information on -streams attribute can be found here \u27b6

If input video-source (i.e. -video_source) contains any audio stream/channel, then it automatically gets mapped to all generated streams without any extra efforts.

Important Information about -streams attribute

Always use the -streams attribute to define additional streams safely. Duplicate or incorrect definitions can break the transcoding pipeline and corrupt the output chunks.

DASHHLS
# import required libraries\nfrom vidgear.gears import StreamGear\n\n# activate Single-Source Mode and also define various streams\nstream_params = {\n    \"-video_source\": \"foo.mp4\",\n    \"-streams\": [\n        {\"-resolution\": \"1920x1080\", \"-video_bitrate\": \"4000k\"},  # Stream1: 1920x1080 at 4000kbs bitrate\n        {\"-resolution\": \"1280x720\", \"-framerate\": 30.0},  # Stream2: 1280x720 at 30fps framerate\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream3: 640x360 at 60fps framerate\n        {\"-resolution\": \"320x240\", \"-video_bitrate\": \"500k\"},  # Stream3: 320x240 at 500kbs bitrate\n    ],\n}\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n# transcode source\nstreamer.transcode_source()\n# close\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import StreamGear\n\n# activate Single-Source Mode and also define various streams\nstream_params = {\n    \"-video_source\": \"foo.mp4\",\n    \"-streams\": [\n        {\"-resolution\": \"1920x1080\", \"-video_bitrate\": \"4000k\"},  # Stream1: 1920x1080 at 4000kbs bitrate\n        {\"-resolution\": \"1280x720\", \"-framerate\": 30.0},  # Stream2: 1280x720 at 30fps framerate\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream3: 640x360 at 60fps framerate\n        {\"-resolution\": \"320x240\", \"-video_bitrate\": \"500k\"},  # Stream3: 320x240 at 500kbs bitrate\n    ],\n}\n# describe a suitable master playlist location/name and assign params\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", **stream_params)\n# transcode source\nstreamer.transcode_source()\n# close\nstreamer.close()\n

"},{"location":"gears/streamgear/ssm/usage/#usage-with-custom-audio-input","title":"Usage with Custom Audio-Input","text":"

In single source mode, by default, if the input video source (i.e., -video_source) contains audio, it gets automatically mapped to all generated streams. However, if you want to add a custom audio source, you can use the exclusive -audio attribute of the stream_params dictionary parameter.

To add a custom audio source, provide the path to your audio file as a string to the -audio attribute. The API will automatically validate and map the audio to all generated streams. The complete example is as follows:

Ensure the provided -audio audio source is compatible with the input video source (-video_source). Incompatibility can cause multiple errors or result in no output at all.

You can also assign a valid audio URL as input instead of a file path. More details can be found here \u27b6

DASHHLS
# import required libraries\nfrom vidgear.gears import StreamGear\n\n# activate Single-Source Mode and various streams, along with custom audio\nstream_params = {\n    \"-video_source\": \"foo.mp4\",\n    \"-streams\": [\n        {\"-resolution\": \"1280x720\", \"-video_bitrate\": \"4000k\"},  # Stream1: 1280x720 at 4000kbs bitrate\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream2: 640x360 at 60fps\n    ],\n    \"-audio\": \"/home/foo/foo1.aac\", # define custom audio-source\n    \"-acodec\": \"copy\", # define copy audio encoder\n}\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n# transcode source\nstreamer.transcode_source()\n# close\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import StreamGear\n\n# activate Single-Source Mode and various streams, along with custom audio\nstream_params = {\n    \"-video_source\": \"foo.mp4\",\n    \"-streams\": [\n        {\"-resolution\": \"1280x720\", \"-video_bitrate\": \"4000k\"},  # Stream1: 1280x720 at 4000kbs bitrate\n        {\"-resolution\": \"640x360\", \"-framerate\": 60.0},  # Stream2: 640x360 at 60fps\n    ],\n    \"-audio\": \"/home/foo/foo1.aac\",  # define custom audio-source\n    \"-acodec\": \"copy\", # define copy audio encoder\n}\n# describe a suitable master playlist location/name and assign params\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", **stream_params)\n# transcode source\nstreamer.transcode_source()\n# close\nstreamer.close()\n

"},{"location":"gears/streamgear/ssm/usage/#usage-with-variable-ffmpeg-parameters","title":"Usage with Variable FFmpeg Parameters","text":"

For fine-grained control over the transcoding process, StreamGear provides a highly extensible and flexible wrapper around FFmpeg library and access to almost all of its configurational parameter.

In this example, we'll use the H.265/HEVC video encoder and AAC audio encoder, apply various optimal FFmpeg configurational parameters.

This example assumes that the given input video source (-video_source) contains at least one audio stream.

This example is just conveying the idea on how to use FFmpeg's internal encoders/parameters with StreamGear API. You can use any FFmpeg parameter in the similar manner.

Please read the FFmpeg Documentation carefully before passing any additional values to the stream_params parameter. Incorrect values may cause errors or result in no output.

DASHHLS
# import required libraries\nfrom vidgear.gears import StreamGear\n\n# activate Single-Source Mode and various other parameters\nstream_params = {\n    \"-video_source\": \"foo.mp4\", # define Video-Source\n    \"-vcodec\": \"libx265\", # specify H.265/HEVC video encoder\n    \"-x265-params\": \"lossless=1\", # enables Lossless encoding\n    \"-bpp\": 0.15, # Bits-Per-Pixel(BPP), an Internal StreamGear parameter to ensure good quality of high motion scenes\n    \"-streams\": [\n        {\"-resolution\": \"640x360\", \"-video_bitrate\": \"4000k\"}, # Stream1: 1280x720 at 4000kbs bitrate\n        {\"-resolution\": \"320x240\", \"-framerate\": 60.0},  # Stream2: 640x360 at 60fps\n    ],\n    \"-acodec\": \"aac\", # specify AAC audio encoder\n}\n\n# describe a suitable manifest-file location/name and assign params\nstreamer = StreamGear(output=\"dash_out.mpd\", logging=True, **stream_params)\n# transcode source\nstreamer.transcode_source()\n# close\nstreamer.close()\n
# import required libraries\nfrom vidgear.gears import StreamGear\n\nstream_params = {\n    \"-video_source\": \"foo.mp4\", # define Video-Source\n    \"-vcodec\": \"libx265\", # specify H.265/HEVC video encoder\n    \"-x265-params\": \"lossless=1\", # enables Lossless encoding\n    \"-bpp\": 0.15, # Bits-Per-Pixel(BPP), an Internal StreamGear parameter to ensure good quality of high motion scenes\n    \"-streams\": [\n        {\"-resolution\": \"640x360\", \"-video_bitrate\": \"4000k\"}, # Stream1: 1280x720 at 4000kbs bitrate\n        {\"-resolution\": \"320x240\", \"-framerate\": 60.0},  # Stream2: 640x360 at 60fps\n    ],\n    \"-acodec\": \"aac\", # specify AAC audio encoder\n}\n\n# describe a suitable master playlist file location/name and assign params\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", logging=True, **stream_params)\n# transcode source\nstreamer.transcode_source()\n# close\nstreamer.close()\n

  1. In Real-time Frames Mode, the Primary Stream's framerate defaults to -input_framerate attribute value, if defined, else it will be 25fps.\u00a0\u21a9

"},{"location":"gears/videogear/overview/","title":"Overview","text":""},{"location":"gears/videogear/overview/#videogear-api","title":"VideoGear API","text":"VideoGear API's generalized workflow"},{"location":"gears/videogear/overview/#overview","title":"Overview","text":"

VideoGear API provides a special internal wrapper around VidGear's exclusive Video Stabilizer class.

VideoGear also acts as a Common Video-Capture API that provides internal access for both CamGear and PiGear APIs and their parameters with an exclusive enablePiCamera boolean flag.

VideoGear is ideal when you need to switch to different video sources without changing your code much. Also, it enables easy stabilization for various video-streams (real-time or not) with minimum effort and writing way fewer lines of code.

Helpful Tips

"},{"location":"gears/videogear/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through VideoGear Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/videogear/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/videogear/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/videogear/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/videogear/params/","title":"Parameters","text":""},{"location":"gears/videogear/params/#videogear-api-parameters","title":"VideoGear API Parameters","text":"

VideoGear acts as a Common Video-Capture API that provides internal access for both CamGear and PiGear APIs and their parameters.

"},{"location":"gears/videogear/params/#enablepicamera","title":"enablePiCamera","text":"

This parameter provide direct access to PiGear or CamGear APIs respectively in VideoGear. This means the if enablePiCamera flag is True, the PiGear API will be accessed, and if False, the CamGear API will be accessed.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

VideoGear(enablePiCamera=True) # enable access to PiGear API\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/videogear/params/#parameters-for-stabilizer-backend","title":"Parameters for Stabilizer Backend","text":"

Enable this backend with stabilize=True in VideoGear.

"},{"location":"gears/videogear/params/#stabilize","title":"stabilize","text":"

This parameter enable access to Stabilizer Class for stabilizing frames, i.e. can be set to True(to enable) or unset to False(to disable).

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

VideoGear(stabilize=True) # enable stablization\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/videogear/params/#options","title":"options","text":"

This parameter can be used in addition, to pass user-defined parameters supported by Stabilizer Class. These parameters can be formatted as this parameter's attribute.

Supported dictionary attributes for Stabilizer Class are:

"},{"location":"gears/videogear/params/#parameters-for-camgear-backend","title":"Parameters for CamGear backend","text":"

Enable this backend with enablePiCamera=False in VideoGear. Default is also False.

"},{"location":"gears/videogear/params/#source","title":"source","text":"

VideoGear API will throw RuntimeError if source provided is invalid.

This parameter defines the source for the input stream.

Data-Type: Based on input.

Default Value: Its default value is 0.

Its valid input can be one of the following:

"},{"location":"gears/videogear/params/#stream_mode","title":"stream_mode","text":"

This parameter controls the Stream Mode, .i.e if enabled(stream_mode=True), the VideoGear API will interpret the given source input as YouTube URL address.

Due to a FFmpeg bug that causes video to freeze frequently in OpenCV, It is advised to always use GStreamer backend (backend=cv2.CAP_GSTREAMER) for any livestreams (such as Twitch).

VideoGear automatically enforce GStreamer backend (backend=cv2.CAP_GSTREAMER) for YouTube-livestreams!

VideoGear will exit with RuntimeError for YouTube livestreams, if OpenCV is not compiled with GStreamer(>=v1.0.0) support. Checkout this FAQ for compiling OpenCV with GStreamer support.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

VideoGear(source='https://youtu.be/bvetuLwJIkA', stream_mode=True)\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/videogear/params/#backend","title":"backend","text":"

This parameter manually selects the backend for OpenCV's VideoCapture class (only if specified).

Data-Type: Integer

Default Value: Its default value is 0

Usage:

All supported backends are listed here \u27b6

Its value can be for e.g. backend = cv2.CAP_DSHOW for selecting Direct Show as backend:

VideoGear(source=0, backend = cv2.CAP_DSHOW)\n

"},{"location":"gears/videogear/params/#options_1","title":"options","text":"

This parameter provides the ability to alter various Source Tweak Parameters available within OpenCV's VideoCapture API properties.

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

All supported parameters are listed here \u27b6

The desired parameters can be passed to VideoGear API by formatting them as this parameter's attributes, as follows:

# formatting parameters as dictionary attributes\noptions = {\"CAP_PROP_FRAME_WIDTH\":320, \"CAP_PROP_FRAME_HEIGHT\":240, \"CAP_PROP_FPS\":60}\n# assigning it\nVideoGear(source=0, **options)\n

"},{"location":"gears/videogear/params/#parameters-for-pigear-backend","title":"Parameters for PiGear backend","text":"

Enable this backend with enablePiCamera=True in VideoGear.

"},{"location":"gears/videogear/params/#camera_num","title":"camera_num","text":"

This parameter selects the camera index to be used as the source, allowing you to drive these multiple cameras simultaneously from within a single Python session. Its value can only be zero or greater, otherwise, VideoGear API will throw ValueError for any negative value.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

# select Camera Module at index `1`\nVideoGear(enablePiCamera=True, camera_num=1)\n

The complete usage example demonstrating the usage of the camera_num parameter is available here \u27b6.

"},{"location":"gears/videogear/params/#resolution","title":"resolution","text":"

This parameter controls the resolution - a tuple (i.e. (width,height)) of two values giving the width and height of the output frames.

Make sure both width and height values should be at least 64.

When using the Picamera2 backend, the resolution parameter will be OVERRIDDEN, if the user explicitly defines the output_size property of the sensor configurational parameter.

Data-Type: Tuple

Default Value: Its default value is (640,480).

Usage:

VideoGear(enablePiCamera=True, resolution=(1280,720)) # sets 1280x720 resolution\n

"},{"location":"gears/videogear/params/#framerate","title":"framerate","text":"

This parameter controls the framerate of the source.

Data-Type: integer/float

Default Value: Its default value is 30.

Usage:

VideoGear(enablePiCamera=True, framerate=60) # sets 60fps framerate\n

"},{"location":"gears/videogear/params/#options_2","title":"options","text":"

This dictionary parameter in the internal PiGear API backend allows you to control various camera settings for both the picamera2 and legacy picamera backends and some internal API tasks. These settings include:

"},{"location":"gears/videogear/params/#a-configurational-camera-parameters","title":"A. Configurational Camera Parameters","text":""},{"location":"gears/videogear/params/#b-user-defined-parameters","title":"B. User-defined Parameters","text":"

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

The complete usage example demonstrating the usage of the options parameter is available here \u27b6.

You can format these user-defined and configurational parameters as attributes of this options dictionary parameter as follows:

New Picamera2 backendLegacy Picamera backend
# formulate various Picamera2 API parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"exposure_compensation\": 15,\n    \"sensor\": {\"output_size\": (480, 320)},  # !!! will override `resolution` !!!\n}\n\n# open pi video stream with defined parameters\nstream = VideoGear(enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options).start()\n
# formulate various Picamera API parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = VideoGear(enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options).start()\n

"},{"location":"gears/videogear/params/#common-parameters","title":"Common Parameters","text":"

These are common parameters that works with every backend in VideoGear.

"},{"location":"gears/videogear/params/#colorspace","title":"colorspace","text":"

This parameter selects the colorspace of the source stream.

Data-Type: String

Default Value: Its default value is None.

Usage:

All supported colorspace values are given here \u27b6

VideoGear(colorspace=\"COLOR_BGR2HSV\")\n

Its complete usage example is given here \u27b6

"},{"location":"gears/videogear/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

VideoGear(logging=True)\n

"},{"location":"gears/videogear/params/#time_delay","title":"time_delay","text":"

This parameter set the time delay (in seconds) before the VideoGear API start reading the frames. This delay is only required if the source required some warm-up delay before starting up.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

VideoGear(time_delay=1)  # set 1 seconds time delay\n

"},{"location":"gears/videogear/usage/","title":"Usage Examples","text":""},{"location":"gears/videogear/usage/#videogear-api-usage-examples","title":"VideoGear API Usage Examples:","text":"

After going through following Usage Examples, Checkout more of its advanced configurations here \u27b6

"},{"location":"gears/videogear/usage/#bare-minimum-usage-with-camgear-backend","title":"Bare-Minimum Usage with CamGear backend","text":"

VideoGear by default provides direct internal access to CamGear API.

Following is the bare-minimum code you need to access CamGear API with VideoGear:

# import required libraries\nfrom vidgear.gears import VideoGear\nimport cv2\n\n\n# open any valid video stream(for e.g `myvideo.avi` file)\nstream = VideoGear(source=\"myvideo.avi\").start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/videogear/usage/#bare-minimum-usage-with-pigear-backend","title":"Bare-Minimum Usage with PiGear backend","text":"

VideoGear contains a special enablePiCamera flag that when True provides internal access to PiGear API.

Following is the bare-minimum code you need to access PiGear API with VideoGear:

Under the hood, PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, PiGear API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this API, otherwise nothing will work.

# import required libraries\nfrom vidgear.gears import VideoGear\nimport cv2\n\n# enable enablePiCamera boolean flag to access PiGear API backend\nstream = VideoGear(enablePiCamera=True).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/videogear/usage/#using-videogear-with-video-stabilizer-backend","title":"Using VideoGear with Video Stabilizer backend","text":"

VideoGear API provides a special internal wrapper around VidGear's Exclusive Video Stabilizer class and provides easy way of activating stabilization for various video-streams (real-time or not) with its stabilize boolean parameter during initialization.

The usage example is as follows:

For a more detailed information on Video-Stabilizer Class, Read here \u27b6

The stabilizer might be slower for High-Quality/Resolution videos-frames.

# import required libraries\nfrom vidgear.gears import VideoGear\nimport numpy as np\nimport cv2\n\n# open any valid video stream with stabilization enabled(`stabilize = True`)\nstream_stab = VideoGear(source=\"test.mp4\", stabilize=True).start()\n\n# loop over\nwhile True:\n\n    # read stabilized frames\n    frame_stab = stream_stab.read()\n\n    # check for stabilized frame if None-type\n    if frame_stab is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Stabilized Output\", frame_stab)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close streams\nstream_stab.stop()\n

"},{"location":"gears/videogear/usage/#advanced-videogear-usage-with-camgear-backend","title":"Advanced VideoGear usage with CamGear Backend","text":"

VideoGear provides internal access to both CamGear and PiGear APIs, and thereby all additional parameters of PiGear API or CamGear API are also easily accessible within VideoGear API.

The usage example of VideoGear API with Variable Camera Properties is as follows:

This example demonstrates how to use the VideoGear API in a similar manner to the CamGear's example for controlling variable source properties. Any CamGear usage example can be implemented using the VideoGear API in a similar way.

All the supported Source Tweak Parameters can be found here \u27b6

# import required libraries\nfrom vidgear.gears import VideoGear\nimport cv2\n\n\n# define suitable tweak parameters for your stream.\noptions = {\n    \"CAP_PROP_FRAME_WIDTH\": 320, # resolution 320x240\n    \"CAP_PROP_FRAME_HEIGHT\": 240,\n    \"CAP_PROP_FPS\": 60, # framerate 60fps\n}\n\n# To open live video stream on webcam at first index(i.e. 0) \n# device and apply source tweak parameters\nstream = VideoGear(source=0, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/videogear/usage/#advanced-videogear-usage-with-pigear-backend","title":"Advanced VideoGear usage with PiGear Backend","text":"

VideoGear provides internal access to both CamGear and PiGear APIs, and thereby all additional parameters of PiGear API or CamGear API are also easily accessible within VideoGear API.

The usage example of VideoGear API with Variable Camera Properties is as follows:

This example demonstrates how to use the VideoGear API in a similar manner to the PiGear's example for using variable camera properties. Any PiGear usage example can be implemented using the VideoGear API in a similar way.

Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this backend, otherwise nothing will work.

New Picamera2 backendLegacy Picamera backend
# import required libraries\nfrom vidgear.gears import VideoGear\nfrom libcamera import Transform\nimport cv2\n\n# formulate various Picamera2 API \n# configurational parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream with defined parameters\nstream = VideoGear(enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import required libraries\nfrom vidgear.gears import VideoGear\nimport cv2\n\n# formulate various Picamera API \n# configurational parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = VideoGear(enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/videogear/usage/#using-videogear-with-colorspace-manipulation","title":"Using VideoGear with Colorspace Manipulation","text":"

VideoGear API also supports Colorspace Manipulation but NOT Direct like other VideoCapture Gears.

Important: color_space global variable is NOT Supported in VideoGear API

In following example code, we will convert source colorspace to HSV on initialization:

# import required libraries\nfrom vidgear.gears import VideoGear\nimport cv2\n\n# Open any source of your choice, like Webcam first index(i.e. 0) and change its colorspace to `HSV`\nstream = VideoGear(source=0, colorspace=\"COLOR_BGR2HSV\", logging=True).start()\n\n# loop over\nwhile True:\n\n    # read HSV frames\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the HSV frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for key if pressed\n    key = cv2.waitKey(1) & 0xFF\n\n    # check for 'q' key is pressed\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"gears/videogear/usage/#bonus-examples","title":"Bonus Examples","text":"

Checkout more advanced VideoGear examples with unusual configuration here \u27b6

"},{"location":"gears/webgear/advanced/","title":"Advanced Usages","text":""},{"location":"gears/webgear/advanced/#webgear-api-advanced-usage","title":"WebGear API Advanced Usage:","text":"

This is a continuation of the WebGear doc \u27b6. Thereby, It's advised to first get familiarize with this API, and its requirements.

After going through following Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/webgear/advanced/#using-webgear-with-variable-colorspace","title":"Using WebGear with Variable Colorspace","text":"

WebGear by default only supports \"BGR\" colorspace frames as input, but you can use jpeg_compression_colorspace string attribute through its options dictionary parameter to specify incoming frames colorspace.

Let's implement a bare-minimum example using WebGear, where we will be sending GRAY frames to client browser:

New in v0.2.2

This example was added in v0.2.2.

This example works in conjunction with Source ColorSpace manipulation for VideoCapture Gears \u27b6

Supported jpeg_compression_colorspace colorspace values are RGB, BGR, RGBX, BGRX, XBGR, XRGB, GRAY, RGBA, BGRA, ABGR, ARGB, CMYK. More information can be found here \u27b6

# import required libraries\nimport uvicorn\nfrom vidgear.gears.asyncio import WebGear\n\n# various performance tweaks and enable grayscale input\noptions = {\n    \"frame_size_reduction\": 25,\n    \"jpeg_compression_colorspace\": \"GRAY\",  # set grayscale\n    \"jpeg_compression_quality\": 90,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# initialize WebGear app and change its colorspace to grayscale\nweb = WebGear(\n    source=\"foo.mp4\", colorspace=\"COLOR_BGR2GRAY\", logging=True, **options\n)\n\n# run this app on Uvicorn server at address http://0.0.0.0:8000/\nuvicorn.run(web(), host=\"0.0.0.0\", port=8000)\n\n# close app safely\nweb.shutdown()\n

And that's all, Now you can see output at http://localhost:8000/ address on your local machine.

"},{"location":"gears/webgear/advanced/#using-webgear-with-a-custom-sourceopencv","title":"Using WebGear with a Custom Source(OpenCV)","text":"New in v0.2.1

This example was added in v0.2.1.

WebGear allows you to easily define your own custom Source that you want to use to transform your frames before sending them onto the browser.

JPEG Frame-Compression and all of its performance enhancing attributes are disabled with a Custom Source!

Let's implement a bare-minimum example with a Custom Source using WebGear API and OpenCV:

# import necessary libs\nimport uvicorn, asyncio, cv2\nfrom vidgear.gears.asyncio import WebGear\nfrom vidgear.gears.asyncio.helper import reducer\n\n# initialize WebGear app without any source\nweb = WebGear(logging=True)\n\n# create your own custom frame producer\nasync def my_frame_producer():\n\n    # !!! define your own video source here !!!\n    # Open any video stream such as live webcam \n    # video stream on first index(i.e. 0) device\n    stream = cv2.VideoCapture(0)\n    # loop over frames\n    while True:\n        # read frame from provided source\n        (grabbed, frame) = stream.read()\n        # break if NoneType\n        if not grabbed:\n            break\n\n        # do something with your OpenCV frame here\n\n        # reducer frames size if you want more performance otherwise comment this line\n        frame = await reducer(frame, percentage=30, interpolation=cv2.INTER_AREA)  # reduce frame by 30%\n        # handle JPEG encoding\n        encodedImage = cv2.imencode(\".jpg\", frame)[1].tobytes()\n        # yield frame in byte format\n        yield (b\"--frame\\r\\nContent-Type:image/jpeg\\r\\n\\r\\n\" + encodedImage + b\"\\r\\n\")\n        await asyncio.sleep(0)\n    # close stream\n    stream.release()\n\n\n# add your custom frame producer to config\nweb.config[\"generator\"] = my_frame_producer\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

And that's all, Now you can see output at http://localhost:8000/ address.

"},{"location":"gears/webgear/advanced/#using-webgear-with-custom-mounting-points","title":"Using WebGear with Custom Mounting Points","text":"

With our highly extensible WebGear API, you can add your own mounting points, where additional files located, as follows:

# import libs\nimport uvicorn\nfrom starlette.routing import Mount\nfrom starlette.staticfiles import StaticFiles\nfrom vidgear.gears.asyncio import WebGear\n\n# various performance tweaks\noptions = {\n    \"frame_size_reduction\": 40,\n    \"jpeg_compression_quality\": 80,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": False,\n}\n\n# initialize WebGear app\nweb = WebGear(\n    source=\"foo.mp4\", logging=True, **options\n)  # enable source i.e. `test.mp4` and enable `logging` for debugging\n\n# append new route i.e. mount another folder called `test` located at `/home/foo/.vidgear/test` directory\nweb.routes.append(\n    Mount(\"/test\", app=StaticFiles(directory=\"/home/foo/.vidgear/test\"), name=\"test\")\n)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

Then you can use this folder in your HTML page, to host data-files. For example, if we have jQuery script jquery-3.3.1.slim.min.js in this folder and want to integrate it, then, we can do something like this:

<script src=\"{{ url_for('test', path='jquery-3.3.1.slim.min.js') }}\"></script>\n

"},{"location":"gears/webgear/advanced/#using-webgear-with-custom-webpage-routes","title":"Using WebGear with Custom Webpage Routes","text":"

With Webgear's flexible API, you can even add your additional HTML Static webpages without any extra efforts.

Suppose we want to add a simple hello world webpage to our WebGear server. So let's create a bare-minimum hello.html file with HTML code as follows:

<html>\n   <header>\n      <title>This is Hello world page</title>\n   </header>\n   <body>\n      <h1>Hello World</h1>\n      <p>how ya doing?</p>\n   </body>\n</html>\n

Then in our application code, we can integrate this webpage route, as follows:

# import libs\nimport uvicorn, asyncio\nfrom starlette.templating import Jinja2Templates\nfrom starlette.routing import Route\nfrom vidgear.gears.asyncio import WebGear\n\n# Build out Jinja2 template render at `/home/foo/.vidgear/custom_template` path in which our `hello.html` file is located\ntemplate = Jinja2Templates(directory=\"/home/foo/.vidgear/custom_template\")\n\n# render and return our webpage template\nasync def hello_world(request):\n    page = \"hello.html\"\n    context = {\"request\": request}\n    return template.TemplateResponse(page, context)\n\n\n# add various performance tweaks as usual\noptions = {\n    \"frame_size_reduction\": 40,\n    \"jpeg_compression_quality\": 80,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": False,\n}\n\n# initialize WebGear app with a valid source\nweb = WebGear(\n    source=\"/home/foo/foo1.mp4\", logging=True, **options\n)  # enable source i.e. `test.mp4` and enable `logging` for debugging\n\n# append new route to point our rendered webpage\nweb.routes.append(Route(\"/hello\", endpoint=hello_world))\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n
And that's all, Now you can see output at http://localhost:8000/hello address.

"},{"location":"gears/webgear/advanced/#using-webgear-with-middlewares","title":"Using WebGear with MiddleWares","text":"

WebGear natively supports ASGI middleware classes with Starlette for implementing behavior that is applied across your entire ASGI application easily.

New in v0.2.2

This example was added in v0.2.2.

All supported middlewares can be found here \u27b6

For this example, let's use CORSMiddleware for implementing appropriate CORS headers to outgoing responses in our application in order to allow cross-origin requests from browsers, as follows:

The default parameters used by the CORSMiddleware implementation are restrictive by default, so you'll need to explicitly enable particular origins, methods, or headers, in order for browsers to be permitted to use them in a Cross-Domain context.

Starlette provides several arguments for enabling origins, methods, or headers for CORSMiddleware API. More information can be found here \u27b6

# import libs\nimport uvicorn, asyncio\nfrom starlette.middleware import Middleware\nfrom starlette.middleware.cors import CORSMiddleware\nfrom vidgear.gears.asyncio import WebGear\n\n# add various performance tweaks as usual\noptions = {\n    \"frame_size_reduction\": 40,\n    \"jpeg_compression_quality\": 80,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": False,\n}\n\n# initialize WebGear app with a valid source\nweb = WebGear(\n    source=\"/home/foo/foo1.mp4\", logging=True, **options\n)  # enable source i.e. `test.mp4` and enable `logging` for debugging\n\n# define and assign suitable cors middlewares\nweb.middleware = [\n    Middleware(\n        CORSMiddleware,\n        allow_origins=[\"*\"],\n        allow_credentials=True,\n        allow_methods=[\"*\"],\n        allow_headers=[\"*\"],\n    )\n]\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n
And that's all, Now you can see output at http://localhost:8000 address.

"},{"location":"gears/webgear/advanced/#rules-for-altering-webgear-files-and-folders","title":"Rules for Altering WebGear Files and Folders","text":"

WebGear gives us complete freedom of altering data files generated in Auto-Generation Process, But you've to keep the following rules in mind:

"},{"location":"gears/webgear/advanced/#rules-for-altering-data-files","title":"Rules for Altering Data Files","text":""},{"location":"gears/webgear/advanced/#rules-for-altering-data-folders","title":"Rules for Altering Data Folders","text":""},{"location":"gears/webgear/advanced/#bonus-examples","title":"Bonus Examples","text":"

Checkout more advanced WebGear examples with unusual configuration here \u27b6

"},{"location":"gears/webgear/overview/","title":"Overview","text":""},{"location":"gears/webgear/overview/#webgear-api","title":"WebGear API","text":"WebGear API's Video Server running at http://localhost:8000/ address."},{"location":"gears/webgear/overview/#overview","title":"Overview","text":"

WebGear is a powerful ASGI Video-Broadcaster API ideal for transmitting Motion-JPEG-frames from a single source to multiple recipients via the browser.

WebGear API works on Starlette's ASGI application and provides a highly extensible and flexible async wrapper around its complete framework. WebGear can flexibly interact with Starlette's ecosystem of shared middleware, mountable applications, Response classes, Routing tables, Static Files, Templating engine(with Jinja2), etc.

WebGear API uses an intraframe-only compression scheme under the hood where the sequence of video-frames are first encoded as JPEG-DIB (JPEG with Device-Independent Bit compression) and then streamed over HTTP using Starlette's Multipart Streaming Response and a Uvicorn ASGI Server. This method imposes lower processing and memory requirements, but the quality is not the best, since JPEG compression is not very efficient for motion video.

In layman's terms, WebGear acts as a powerful Video Broadcaster that transmits live video-frames to any web-browser in the network. Additionally, WebGear API also provides internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs, thereby granting it exclusive power for transferring frames incoming from any source to the network.

"},{"location":"gears/webgear/overview/#data-files-auto-generation-workflow-for-webgear","title":"Data-Files Auto-Generation WorkFlow for WebGear","text":"Disabling Auto-Generation process in WebGear

Starting with vidgear v0.3.0, you can now completely disable Auto-Generation process in WebGear API using skip_generate_webdata optional boolean attribute. When {skip_generate_webdata:True}, no default data files will be downloaded or validated during initialization.

Only /video route is available when {skip_generate_webdata:True} in WebGear API. All other default routes will be JSONResponses with 404/500 status codes.

Customizing default video endpoint path

Starting with vidgear v0.3.1, you can change default /video video endpoint path to any alphanumeric string value, using custom_video_endpoint optional string attribute. For example:

Only alphanumeric string with no space in between are allowed as custom_video_endpoint value. Any other value will be discarded.

WebGear's Default Theme which expects only default /video video endpoint path, will fail to work, if it is customized to any other value using this custom_video_endpoint attribute.

# custom alphanumeric video endpoint string\noptions = {\"custom_video_endpoint\": \"xyz\"}\n\n# initialize WebGear app\nweb = WebGear(source=\"foo.mp4\", logging=True, **options)\n
Hence, default video endpoint will now be available at /xyz path.

On initializing WebGear API, it automatically checks for three critical data files(i.e index.html, 404.html & 500.html) inside the templates folder of the webgear directory at the default location which gives rise to the following two possible scenario:

"},{"location":"gears/webgear/overview/#default-location","title":"Default Location","text":"

Identifying Default location

You can set logging=True during initialization, for easily identifying the selected default location, which will be something like this on a Linux machine:

WebGear :: DEBUG :: `/home/foo/.vidgear` is the default location for saving WebGear data-files.\n
"},{"location":"gears/webgear/overview/#auto-generation-process","title":"Auto-Generation process","text":"

Info

"},{"location":"gears/webgear/overview/#webgears-default-template","title":"WebGear's Default Template","text":"New in v0.2.1

New Standalone WebGear's Default Theme was added in v0.2.1.

The WebGear API by default uses simple & elegant WebGear's Default Theme which looks like something as follows:

"},{"location":"gears/webgear/overview/#indexhtml","title":"Index.html","text":"

Can be accessed by visiting WebGear app server, running at http://localhost:8000/:

"},{"location":"gears/webgear/overview/#404html","title":"404.html","text":"

Appears when respective URL is not found, for example http://localhost:8000/ok:

"},{"location":"gears/webgear/overview/#500html","title":"500.html","text":"

Appears when an API Error is encountered:

If logging is enabled and an error occurs, then instead of displaying this 500 handler, WebGear will respond with a traceback response.

"},{"location":"gears/webgear/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through WebGear Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/webgear/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/webgear/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/webgear/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/webgear/params/","title":"Parameters","text":""},{"location":"gears/webgear/params/#webgear-api-parameters","title":"WebGear API Parameters","text":"

WebGear provides a special internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs and their parameters.

"},{"location":"gears/webgear/params/#enablepicamera","title":"enablePiCamera","text":"

This parameter provide direct access to PiGear or CamGear APIs respectively in WebGear. This means the if enablePiCamera flag is True, the PiGear API will be accessed, and if False, the CamGear API will be accessed.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

WebGear(enablePiCamera=True) # enable access to PiGear API\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/webgear/params/#options","title":"options","text":"

This parameter can be used to pass user-defined parameter to WebGear API by formatting them as this parameter's attribute.

Data-Type: Dictionary

Default Value: Its default value is {}

"},{"location":"gears/webgear/params/#webgear-specific-attributes","title":"WebGear Specific attributes","text":"

"},{"location":"gears/webgear/params/#parameters-for-stabilizer-backend","title":"Parameters for Stabilizer Backend","text":"

Enable this backend with stabilize=True in WebGear.

"},{"location":"gears/webgear/params/#stabilize","title":"stabilize","text":"

This parameter enable access to Stabilizer Class for stabilizing frames, i.e. can be set to True(to enable) or unset to False(to disable).

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

WebGear(stabilize=True) # enable stablization\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/webgear/params/#options_1","title":"options","text":"

This parameter can be used in addition, to pass user-defined parameters supported by Stabilizer Class. These parameters can be formatted as this parameter's attribute.

Supported dictionary attributes for Stabilizer Class are:

"},{"location":"gears/webgear/params/#parameters-for-camgear-backend","title":"Parameters for CamGear backend","text":"

Enable this backend with enablePiCamera=False in WebGear. Default is also False.

"},{"location":"gears/webgear/params/#source","title":"source","text":"

WebGear API will throw RuntimeError if source provided is invalid.

This parameter defines the source for the input stream.

Data-Type: Based on input.

Default Value: Its default value is 0.

Its valid input can be one of the following:

"},{"location":"gears/webgear/params/#stream_mode","title":"stream_mode","text":"

This parameter controls the Stream Mode, .i.e if enabled(stream_mode=True), the CamGear API will interpret the given source input as YouTube URL address.

Due to a FFmpeg bug that causes video to freeze frequently in OpenCV, It is advised to always use GStreamer backend for any livestream videos. Checkout this FAQ for compiling OpenCV with GStreamer support.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

Supported Streaming Websites

The complete list of all supported Streaming Websites URLs can be found here \u27b6

WebGear(source='https://youtu.be/bvetuLwJIkA', stream_mode=True)\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/webgear/params/#backend","title":"backend","text":"

This parameter manually selects the backend for OpenCV's VideoCapture class (only if specified).

Data-Type: Integer

Default Value: Its default value is 0

Usage:

All supported backends are listed here \u27b6

Its value can be for e.g. backend = cv2.CAP_DSHOW for selecting Direct Show as backend:

WebGear(source=0, backend = cv2.CAP_DSHOW)\n

"},{"location":"gears/webgear/params/#options_2","title":"options","text":"

This parameter provides the ability to alter various Source Tweak Parameters available within OpenCV's VideoCapture API properties.

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

All supported parameters are listed here \u27b6

The desired parameters can be passed to WebGear API by formatting them as this parameter's attributes, as follows:

# formatting parameters as dictionary attributes\noptions = {\"CAP_PROP_FRAME_WIDTH\":320, \"CAP_PROP_FRAME_HEIGHT\":240, \"CAP_PROP_FPS\":60}\n# assigning it\nWebGear(source=0, **options)\n

"},{"location":"gears/webgear/params/#parameters-for-pigear-backend","title":"Parameters for PiGear backend","text":"

Enable this backend with enablePiCamera=True in WebGear.

"},{"location":"gears/webgear/params/#camera_num","title":"camera_num","text":"

This parameter selects the camera index to be used as the source, allowing you to drive these multiple cameras simultaneously from within a single Python session. Its value can only be zero or greater, otherwise, WebGear API will throw ValueError for any negative value.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

# select Camera Module at index `1`\nWebGear(enablePiCamera=True, camera_num=1)\n

The complete usage example demonstrating the usage of the camera_num parameter is available here \u27b6.

"},{"location":"gears/webgear/params/#resolution","title":"resolution","text":"

This parameter controls the resolution - a tuple (i.e. (width,height)) of two values giving the width and height of the output frames.

Make sure both width and height values should be at least 64.

When using the Picamera2 backend, the resolution parameter will be OVERRIDDEN, if the user explicitly defines the output_size property of the sensor configurational parameter.

Data-Type: Tuple

Default Value: Its default value is (640,480).

Usage:

WebGear(enablePiCamera=True, resolution=(1280,720)) # sets 1280x720 resolution\n

"},{"location":"gears/webgear/params/#framerate","title":"framerate","text":"

This parameter controls the framerate of the source.

Data-Type: integer/float

Default Value: Its default value is 30.

Usage:

WebGear(enablePiCamera=True, framerate=60) # sets 60fps framerate\n

"},{"location":"gears/webgear/params/#options_3","title":"options","text":"

This dictionary parameter in the internal PiGear API backend allows you to control various camera settings for both the picamera2 and legacy picamera backends and some internal API tasks. These settings include:

"},{"location":"gears/webgear/params/#a-configurational-camera-parameters","title":"A. Configurational Camera Parameters","text":""},{"location":"gears/webgear/params/#b-user-defined-parameters","title":"B. User-defined Parameters","text":"

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

The complete usage example demonstrating the usage of the options parameter is available here \u27b6.

You can format these user-defined and configurational parameters as attributes of this options dictionary parameter as follows:

New Picamera2 backendLegacy Picamera backend
# formulate various Picamera2 API parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"exposure_compensation\": 15,\n    \"sensor\": {\"output_size\": (480, 320)},  # !!! will override `resolution` !!!\n}\n\n# open pi video stream with defined parameters\nstream = WebGear(enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options).start()\n
# formulate various Picamera API parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = WebGear(enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options).start()\n

"},{"location":"gears/webgear/params/#common-parameters","title":"Common Parameters","text":"

These are common parameters that works with every backend in WebGear.

"},{"location":"gears/webgear/params/#colorspace","title":"colorspace","text":"

This parameter selects the colorspace of the source stream.

Data-Type: String

Default Value: Its default value is None.

Usage:

All supported colorspace values are given here \u27b6

WebGear(colorspace=\"COLOR_BGR2HSV\")\n

Its complete usage example is given here \u27b6

"},{"location":"gears/webgear/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

WebGear(logging=True)\n

"},{"location":"gears/webgear/params/#time_delay","title":"time_delay","text":"

This parameter set the time delay (in seconds) before the WebGear API start reading the frames. This delay is only required if the source required some warm-up delay before starting up.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

WebGear(time_delay=1)  # set 1 seconds time delay\n

"},{"location":"gears/webgear/usage/","title":"Usage Examples","text":""},{"location":"gears/webgear/usage/#webgear-api-usage-examples","title":"WebGear API Usage Examples:","text":""},{"location":"gears/webgear/usage/#requirements","title":"Requirements","text":""},{"location":"gears/webgear/usage/#installation-with-asyncio-support","title":"Installation with Asyncio Support","text":"

WebGear API is the part of asyncio package of VidGear, thereby you need to install VidGear with asyncio support as follows:

pip install vidgear[asyncio]\n
"},{"location":"gears/webgear/usage/#asgi-server","title":"ASGI Server","text":"

You'll also need to install an ASGI Server to run following WebGear usage examples, and by default WebGear ships the state-of-the-art uvicorn Server. But you can also use other ASGI server such as daphne, or hypercorn with it.

"},{"location":"gears/webgear/usage/#performance-enhancements","title":"Performance Enhancements","text":"

WebGear provides certain performance enhancing attributes for its options dictionary parameter to cope with performance-throttling.

Performance Enhancing Attributes

"},{"location":"gears/webgear/usage/#bare-minimum-usage-with-performance-enhancements","title":"Bare-Minimum Usage with Performance Enhancements","text":"

Let's implement our Bare-Minimum usage example with these Performance Enhancing Attributes \u27b6 for speeding up the output.

"},{"location":"gears/webgear/usage/#running-programmatically","title":"Running Programmatically","text":"

You can access and run WebGear VideoStreamer Server programmatically in your python script in just a few lines of code, as follows:

For accessing WebGear on different Client Devices on the network, use \"0.0.0.0\" as host value instead of \"localhost\" on Host Machine. More information can be found here \u27b6

# import required libraries\nimport uvicorn\nfrom vidgear.gears.asyncio import WebGear\n\n# various performance tweaks\noptions = {\n    \"frame_size_reduction\": 40,\n    \"jpeg_compression_quality\": 80,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": False,\n}\n\n# initialize WebGear app\nweb = WebGear(source=\"foo.mp4\", logging=True, **options)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

which can be accessed on any browser on your machine at http://localhost:8000/.

"},{"location":"gears/webgear/usage/#running-from-terminal","title":"Running from Terminal","text":"

You can also access and run WebGear Server directly from the terminal commandline. The following command will run a WebGear VideoStreamer server at http://localhost:8000/:

Make sure your PYTHON_PATH is set to python 3.7+ versions only.

If you're using --options/-op flag, then kindly wrap your dictionary value in single '' quotes.

python3 -m vidgear.gears.asyncio --source test.avi --logging True --options '{\"frame_size_reduction\": 50, \"jpeg_compression_quality\": 80, \"jpeg_compression_fastdct\": True, \"jpeg_compression_fastupsample\": False}'\n

which can also be accessed on any browser on the network at http://localhost:8000/.

Advanced Usage from Terminal

You can run python3 -m vidgear.gears.asyncio -h help command to see all the advanced settings, as follows:

usage: python -m vidgear.gears.asyncio [-h] [-m MODE] [-s SOURCE] [-ep ENABLEPICAMERA] [-S STABILIZE]\n            [-cn CAMERA_NUM] [-yt stream_mode] [-b BACKEND] [-cs COLORSPACE]\n            [-r RESOLUTION] [-f FRAMERATE] [-td TIME_DELAY]\n            [-ip IPADDRESS] [-pt PORT] [-l LOGGING] [-op OPTIONS]\n\nRuns WebGear/WebGear_RTC Video Server through terminal.\n\noptional arguments:\n  -h, --help            show this help message and exit\n  -m {mjpeg,webrtc}, --mode {mjpeg,webrtc}\n                        Whether to use \"MJPEG\" or \"WebRTC\" mode for streaming.\n  -s SOURCE, --source SOURCE\n                        Path to input source for CamGear API.\n  -ep ENABLEPICAMERA, --enablePiCamera ENABLEPICAMERA\n                        Sets the flag to access PiGear(if True) or otherwise\n                        CamGear API respectively.\n  -S STABILIZE, --stabilize STABILIZE\n                        Enables/disables real-time video stabilization.\n  -cn CAMERA_NUM, --camera_num CAMERA_NUM\n                        Sets the camera module index that will be used by\n                        PiGear API.\n  -yt STREAM_MODE, --stream_mode STREAM_MODE\n                        Enables YouTube Mode in CamGear API.\n  -b BACKEND, --backend BACKEND\n                        Sets the backend of the video source in CamGear API.\n  -cs COLORSPACE, --colorspace COLORSPACE\n                        Sets the colorspace of the output video stream.\n  -r RESOLUTION, --resolution RESOLUTION\n                        Sets the resolution (width,height) for camera module\n                        in PiGear API.\n  -f FRAMERATE, --framerate FRAMERATE\n                        Sets the framerate for camera module in PiGear API.\n  -td TIME_DELAY, --time_delay TIME_DELAY\n                        Sets the time delay(in seconds) before start reading\n                        the frames.\n  -ip IPADDRESS, --ipaddress IPADDRESS\n                        Uvicorn binds the socket to this ipaddress.\n  -pt PORT, --port PORT\n                        Uvicorn binds the socket to this port.\n  -l LOGGING, --logging LOGGING\n                        Enables/disables error logging, essential for\n                        debugging.\n  -op OPTIONS, --options OPTIONS\n                        Sets the parameters supported by APIs(whichever being\n                        accessed) to the input videostream, But make sure to\n                        wrap your dict value in single or double quotes.\n

"},{"location":"gears/webgear_rtc/advanced/","title":"Advanced Usages","text":""},{"location":"gears/webgear_rtc/advanced/#webgear_rtc-api-advanced-usage","title":"WebGear_RTC API Advanced Usage:","text":"

This is a continuation of the WebGear_RTC doc \u27b6. Thereby, It's advised to first get familiarize with this API, and its requirements.

After going through following Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/webgear_rtc/advanced/#using-webgear_rtc-as-real-time-broadcaster","title":"Using WebGear_RTC as Real-time Broadcaster","text":"

WebGear_RTC by default only supports one-to-one peer connection with a single consumer or client. But you can use enable_live_broadcast boolean attribute through its options dictionary parameter to easily enable live broadcast/stream to multiple peer consumers/clients at the same time.

Let's implement a bare-minimum example using WebGear_RTC as Real-time Broadcaster:

enable_infinite_frames is enforced by default with this(enable_live_broadcast) attribute.

For accessing WebGear_RTC on different Client Devices on the network, we use \"0.0.0.0\" as host value instead of \"localhost\" on Host Machine. More information can be found here \u27b6

# import required libraries\nimport uvicorn\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# various performance tweaks and enable live broadcasting\noptions = {\n    \"frame_size_reduction\": 25,\n    \"enable_live_broadcast\": True,\n}\n\n# initialize WebGear_RTC app\nweb = WebGear_RTC(source=\"foo.mp4\", logging=True, **options)\n\n# run this app on Uvicorn server at address http://0.0.0.0:8000/\nuvicorn.run(web(), host=\"0.0.0.0\", port=8000)\n\n# close app safely\nweb.shutdown()\n

And that's all, Now you can see output at http://localhost:8000/ address on your local machine.

"},{"location":"gears/webgear_rtc/advanced/#using-webgear_rtc-with-a-custom-sourceopencv","title":"Using WebGear_RTC with a Custom Source(OpenCV)","text":"

WebGear_RTC provides custom_stream attribute with its options parameter that allows you to easily define your own Custom Streaming Class with suitable source that you want to use to transform your frames before sending them onto the browser.

Let's implement a bare-minimum example with a Custom Source using WebGear_RTC API and OpenCV:

New in v0.2.4

This implementation was added in v0.2.4.

Auto-Reconnection or Auto-Refresh works out-of-the-box with this implementation.

Make sure your Custom Streaming Class at-least implements read() and stop() methods as shown in following example, otherwise WebGear_RTC will throw ValueError!

Using Vidgear's VideoCapture APIs instead of OpenCV

You can directly replace Custom Streaming Class(Custom_Stream_Class in following example) with any VideoCapture APIs. These APIs implements read() and stop() methods by-default, so they're also supported out-of-the-box.

See this example \u27b6 for more information.

# import necessary libs\nimport uvicorn, cv2\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# create your own custom streaming class\nclass Custom_Stream_Class:\n    \"\"\"\n    Custom Streaming using OpenCV\n    \"\"\"\n\n    def __init__(self, source=0):\n\n        # !!! define your own video source here !!!\n        self.source = cv2.VideoCapture(source)\n\n        # define running flag\n        self.running = True\n\n    def read(self):\n\n        # don't forget this function!!!\n\n        # check if source was initialized or not\n        if self.source is None:\n            return None\n        # check if we're still running\n        if self.running:\n            # read frame from provided source\n            (grabbed, frame) = self.source.read()\n            # check if frame is available\n            if grabbed:\n\n                # do something with your OpenCV frame here\n\n                # lets convert frame to gray for this example\n                gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n\n                # return our gray frame\n                return gray\n            else:\n                # signal we're not running now\n                self.running = False\n        # return None-type\n        return None\n\n    def stop(self):\n\n        # don't forget this function!!!\n\n        # flag that we're not running\n        self.running = False\n        # close stream\n        if not self.source is None:\n            self.source.release()\n\n# assign your Custom Streaming Class with adequate source (for e.g. foo.mp4) \n# to `custom_stream` attribute in options parameter\noptions = {\"custom_stream\": Custom_Stream_Class(source=\"foo.mp4\")}\n\n# initialize WebGear_RTC app without any source\nweb = WebGear_RTC(logging=True, **options)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

And that's all, Now you can see output at http://localhost:8000/ address.

"},{"location":"gears/webgear_rtc/advanced/#using-webgear_rtc-with-custom-mounting-points","title":"Using WebGear_RTC with Custom Mounting Points","text":"

With our highly extensible WebGear_RTC API, you can add your own mounting points, where additional files located, as follows:

# import libs\nimport uvicorn\nfrom starlette.routing import Mount\nfrom starlette.staticfiles import StaticFiles\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# various performance tweaks\noptions = {\n    \"frame_size_reduction\": 25,\n}\n\n# initialize WebGear_RTC app\nweb = WebGear_RTC(\n    source=\"foo.mp4\", logging=True, **options\n)  # enable source i.e. `test.mp4` and enable `logging` for debugging\n\n# append new route i.e. mount another folder called `test` located at `/home/foo/.vidgear/test` directory\nweb.routes.append(\n    Mount(\"/test\", app=StaticFiles(directory=\"/home/foo/.vidgear/test\"), name=\"test\")\n)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

Then you can use this folder in your HTML page, to host data-files. For example, if we have jQuery script jquery-3.3.1.slim.min.js in this folder and want to integrate it, then, we can do something like this:

<script src=\"{{ url_for('test', path='jquery-3.3.1.slim.min.js') }}\"></script>\n

"},{"location":"gears/webgear_rtc/advanced/#using-webgear_rtc-with-custom-webpage-routes","title":"Using WebGear_RTC with Custom Webpage Routes","text":"

With Webgear_RTC's flexible API, you can even add your additional HTML Static webpages without any extra efforts.

Suppose we want to add a simple hello world webpage to our WebGear_RTC server. So let's create a bare-minimum hello.html file with HTML code as follows:

<html>\n   <header>\n      <title>This is Hello world page</title>\n   </header>\n   <body>\n      <h1>Hello World</h1>\n      <p>how ya doing?</p>\n   </body>\n</html>\n

Then in our application code, we can integrate this webpage route, as follows:

# import libs\nimport uvicorn, asyncio\nfrom starlette.templating import Jinja2Templates\nfrom starlette.routing import Route\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# Build out Jinja2 template render at `/home/foo/.vidgear/custom_template` path in which our `hello.html` file is located\ntemplate = Jinja2Templates(directory=\"/home/foo/.vidgear/custom_template\")\n\n# render and return our webpage template\nasync def hello_world(request):\n    page = \"hello.html\"\n    context = {\"request\": request}\n    return template.TemplateResponse(page, context)\n\n\n# add various performance tweaks as usual\noptions = {\n    \"frame_size_reduction\": 25,\n}\n\n# initialize WebGear_RTC app with a valid source\nweb = WebGear_RTC(\n    source=\"/home/foo/foo1.mp4\", logging=True, **options\n)  # enable source i.e. `test.mp4` and enable `logging` for debugging\n\n# append new route to point our rendered webpage\nweb.routes.append(Route(\"/hello\", endpoint=hello_world))\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n
And that's all, Now you can see output at http://localhost:8000/hello address.

"},{"location":"gears/webgear_rtc/advanced/#using-webgear_rtc-with-middlewares","title":"Using WebGear_RTC with MiddleWares","text":"

WebGear_RTC also natively supports ASGI middleware classes with Starlette for implementing behavior that is applied across your entire ASGI application easily.

New in v0.2.2

This example was added in v0.2.2.

All supported middlewares can be found here \u27b6

For this example, let's use CORSMiddleware for implementing appropriate CORS headers to outgoing responses in our application in order to allow cross-origin requests from browsers, as follows:

The default parameters used by the CORSMiddleware implementation are restrictive by default, so you'll need to explicitly enable particular origins, methods, or headers, in order for browsers to be permitted to use them in a Cross-Domain context.

Starlette provides several arguments for enabling origins, methods, or headers for CORSMiddleware API. More information can be found here \u27b6

# import libs\nimport uvicorn, asyncio\nfrom starlette.middleware import Middleware\nfrom starlette.middleware.cors import CORSMiddleware\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# add various performance tweaks as usual\noptions = {\n    \"frame_size_reduction\": 25,\n}\n\n# initialize WebGear_RTC app with a valid source\nweb = WebGear_RTC(\n    source=\"/home/foo/foo1.mp4\", logging=True, **options\n)  # enable source i.e. `test.mp4` and enable `logging` for debugging\n\n# define and assign suitable cors middlewares\nweb.middleware = [\n    Middleware(\n        CORSMiddleware,\n        allow_origins=[\"*\"],\n        allow_credentials=True,\n        allow_methods=[\"*\"],\n        allow_headers=[\"*\"],\n    )\n]\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

And that's all, Now you can see output at http://localhost:8000 address.

"},{"location":"gears/webgear_rtc/advanced/#rules-for-altering-webgear_rtc-files-and-folders","title":"Rules for Altering WebGear_RTC Files and Folders","text":"

WebGear_RTC gives us complete freedom of altering data files generated in Auto-Generation Process, But you've to keep the following rules in mind:

"},{"location":"gears/webgear_rtc/advanced/#rules-for-altering-data-files","title":"Rules for Altering Data Files","text":""},{"location":"gears/webgear_rtc/advanced/#rules-for-altering-data-folders","title":"Rules for Altering Data Folders","text":""},{"location":"gears/webgear_rtc/advanced/#bonus-examples","title":"Bonus Examples","text":"

Checkout more advanced WebGear_RTC examples with unusual configuration here \u27b6

"},{"location":"gears/webgear_rtc/overview/","title":"Overview","text":""},{"location":"gears/webgear_rtc/overview/#webgear_rtc-api","title":"WebGear_RTC API","text":"WebGear_RTC API's Video Server running at http://localhost:8000/ address."},{"location":"gears/webgear_rtc/overview/#overview","title":"Overview","text":"

WebGear_RTC is similar to WeGear API in many aspects but utilizes WebRTC technology under the hood instead of Motion JPEG, which makes it suitable for building powerful video-streaming solutions for all modern browsers as well as native clients available on all major platforms.

New in v0.2.1

WebGear_RTC API was added in v0.2.1.

WebGear_RTC is implemented with the help of aiortc library which is built on top of asynchronous I/O framework for Web Real-Time Communication (WebRTC) and Object Real-Time Communication (ORTC) and supports many features like SDP generation/parsing, Interactive Connectivity Establishment with half-trickle and mDNS support, DTLS key and certificate generation, DTLS handshake, etc.

WebGear_RTC can handle multiple consumers seamlessly and provides native support for ICE (Interactive Connectivity Establishment) protocol, STUN (Session Traversal Utilities for NAT), and TURN (Traversal Using Relays around NAT) servers that help us to seamlessly establish direct media connection with the remote peers for uninterrupted data flow. It also allows us to define our custom streaming class with suitable source to transform frames easily before sending them across the network(see this doc example).

WebGear_RTC API works in conjunction with Starlette ASGI application and can also flexibly interact with Starlette's ecosystem of shared middleware, mountable applications, Response classes, Routing tables, Static Files, Templating engine(with Jinja2), etc.

Additionally, WebGear_RTC API also provides internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs.

"},{"location":"gears/webgear_rtc/overview/#data-files-auto-generation-workflow-for-webgear_rtc","title":"Data-Files Auto-Generation WorkFlow for WebGear_RTC","text":"

Same as WebGear, WebGear_RTC API automatically checks for three critical data files(i.e index.html, 404.html & 500.html) on initialization inside the templates folder of the webgear_rtc directory at the default location which gives rise to the following two possible scenario:

"},{"location":"gears/webgear_rtc/overview/#default-location","title":"Default Location","text":"

Identifying Default location

You can set logging=True during initialization, for easily identifying the selected default location, which will be something like this on a Linux machine:

WebGear_RTC :: DEBUG :: `/home/foo/.vidgear` is the default location for saving WebGear_RTC data-files.\n
"},{"location":"gears/webgear_rtc/overview/#auto-generation-process","title":"Auto-Generation process","text":"

Info

"},{"location":"gears/webgear_rtc/overview/#webgear_rtcs-default-template","title":"WebGear_RTC's Default Template","text":"

The WebGear_RTC API by default uses simple & elegant WebGear_RTC's Default Theme which looks like something as follows:

"},{"location":"gears/webgear_rtc/overview/#indexhtml","title":"Index.html","text":"

Can be accessed by visiting WebGear_RTC app server, running at http://localhost:8000/:

"},{"location":"gears/webgear_rtc/overview/#404html","title":"404.html","text":"

Appears when respective URL is not found, for example http://localhost:8000/ok:

"},{"location":"gears/webgear_rtc/overview/#500html","title":"500.html","text":"

Appears when an API Error is encountered:

If logging is enabled and an error occurs, then instead of displaying this 500 handler, WebGear_RTC will respond with a traceback response.

"},{"location":"gears/webgear_rtc/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through WebGear_RTC Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/webgear_rtc/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/webgear_rtc/overview/#references","title":"References","text":"See here \ud83d\ude80"},{"location":"gears/webgear_rtc/overview/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/webgear_rtc/params/","title":"Parameters","text":""},{"location":"gears/webgear_rtc/params/#webgear_rtc-api-parameters","title":"WebGear_RTC API Parameters","text":"

WebGear_RTC provides a special internal wrapper around VideoGear, which itself provides internal access to both CamGear and PiGear APIs and their parameters.

"},{"location":"gears/webgear_rtc/params/#enablepicamera","title":"enablePiCamera","text":"

This parameter provide direct access to PiGear or CamGear APIs respectively in WebGear_RTC. This means the if enablePiCamera flag is True, the PiGear API will be accessed, and if False, the CamGear API will be accessed.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

WebGear_RTC(enablePiCamera=True) # enable access to PiGear API\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/webgear_rtc/params/#options","title":"options","text":"

This parameter can be used to pass user-defined parameter to WebGear_RTC API by formatting them as this parameter's attribute.

Data-Type: Dictionary

Default Value: Its default value is {}

"},{"location":"gears/webgear_rtc/params/#webgear_rtc-specific-attributes","title":"WebGear_RTC Specific attributes","text":"

"},{"location":"gears/webgear_rtc/params/#parameters-for-stabilizer-backend","title":"Parameters for Stabilizer Backend","text":"

Enable this backend with stabilize=True in WebGear_RTC. Default is also False.

"},{"location":"gears/webgear_rtc/params/#stabilize","title":"stabilize","text":"

This parameter enable access to Stabilizer Class for stabilizing frames, i.e. can be set to True(to enable) or unset to False(to disable).

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

WebGear_RTC(stabilize=True) # enable stablization\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/webgear_rtc/params/#options_1","title":"options","text":"

This parameter can be used in addition, to pass user-defined parameters supported by Stabilizer Class. These parameters can be formatted as this parameter's attribute.

Supported dictionary attributes for Stabilizer Class are:

"},{"location":"gears/webgear_rtc/params/#parameters-for-camgear-backend","title":"Parameters for CamGear backend","text":"

Enable this backend with enablePiCamera=False in WebGear_RTC.

"},{"location":"gears/webgear_rtc/params/#source","title":"source","text":"

WebGear_RTC API will throw RuntimeError if source provided is invalid.

This parameter defines the source for the input stream.

Data-Type: Based on input.

Default Value: Its default value is 0.

Its valid input can be one of the following:

"},{"location":"gears/webgear_rtc/params/#stream_mode","title":"stream_mode","text":"

This parameter controls the Stream Mode, .i.e if enabled(stream_mode=True), the CamGear API will interpret the given source input as YouTube URL address.

Due to a FFmpeg bug that causes video to freeze frequently in OpenCV, It is advised to always use GStreamer backend for any livestream videos. Checkout this FAQ for compiling OpenCV with GStreamer support.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

Supported Streaming Websites

The complete list of all supported Streaming Websites URLs can be found here \u27b6

WebGear_RTC(source='https://youtu.be/bvetuLwJIkA', stream_mode=True)\n

Its complete usage example is given here \u27b6.

"},{"location":"gears/webgear_rtc/params/#backend","title":"backend","text":"

This parameter manually selects the backend for OpenCV's VideoCapture class (only if specified).

Data-Type: Integer

Default Value: Its default value is 0

Usage:

All supported backends are listed here \u27b6

Its value can be for e.g. backend = cv2.CAP_DSHOW for selecting Direct Show as backend:

WebGear_RTC(source=0, backend = cv2.CAP_DSHOW)\n

"},{"location":"gears/webgear_rtc/params/#options_2","title":"options","text":"

This parameter provides the ability to alter various Source Tweak Parameters available within OpenCV's VideoCapture API properties.

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

All supported parameters are listed here \u27b6

The desired parameters can be passed to WebGear_RTC API by formatting them as this parameter's attributes, as follows:

# formatting parameters as dictionary attributes\noptions = {\"CAP_PROP_FRAME_WIDTH\":320, \"CAP_PROP_FRAME_HEIGHT\":240, \"CAP_PROP_FPS\":60}\n# assigning it\nWebGear_RTC(source=0, **options)\n

"},{"location":"gears/webgear_rtc/params/#parameters-for-pigear-backend","title":"Parameters for PiGear backend","text":"

Enable this backend with enablePiCamera=True in WebGear_RTC.

"},{"location":"gears/webgear_rtc/params/#camera_num","title":"camera_num","text":"

This parameter selects the camera index to be used as the source, allowing you to drive these multiple cameras simultaneously from within a single Python session. Its value can only be zero or greater, otherwise, WebGear_RTC API will throw ValueError for any negative value.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

# select Camera Module at index `1`\nWebGear_RTC(enablePiCamera=True, camera_num=1)\n

The complete usage example demonstrating the usage of the camera_num parameter is available here \u27b6.

"},{"location":"gears/webgear_rtc/params/#resolution","title":"resolution","text":"

This parameter controls the resolution - a tuple (i.e. (width,height)) of two values giving the width and height of the output frames.

Make sure both width and height values should be at least 64.

When using the Picamera2 backend, the resolution parameter will be OVERRIDDEN, if the user explicitly defines the output_size property of the sensor configurational parameter.

Data-Type: Tuple

Default Value: Its default value is (640,480).

Usage:

WebGear_RTC(enablePiCamera=True, resolution=(1280,720)) # sets 1280x720 resolution\n

"},{"location":"gears/webgear_rtc/params/#framerate","title":"framerate","text":"

This parameter controls the framerate of the source.

Data-Type: integer/float

Default Value: Its default value is 30.

Usage:

WebGear_RTC(enablePiCamera=True, framerate=60) # sets 60fps framerate\n

"},{"location":"gears/webgear_rtc/params/#options_3","title":"options","text":"

This dictionary parameter in the internal PiGear API backend allows you to control various camera settings for both the picamera2 and legacy picamera backends and some internal API tasks. These settings include:

"},{"location":"gears/webgear_rtc/params/#a-configurational-camera-parameters","title":"A. Configurational Camera Parameters","text":""},{"location":"gears/webgear_rtc/params/#b-user-defined-parameters","title":"B. User-defined Parameters","text":"

Data-Type: Dictionary

Default Value: Its default value is {}

Usage:

The complete usage example demonstrating the usage of the options parameter is available here \u27b6.

You can format these user-defined and configurational parameters as attributes of this options dictionary parameter as follows:

New Picamera2 backendLegacy Picamera backend
# formulate various Picamera2 API parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"exposure_compensation\": 15,\n    \"sensor\": {\"output_size\": (480, 320)},  # !!! will override `resolution` !!!\n}\n\n# open pi video stream with defined parameters\nstream = WebGear_RTC(enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options).start()\n
# formulate various Picamera API parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = WebGear_RTC(enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options).start()\n

"},{"location":"gears/webgear_rtc/params/#common-parameters","title":"Common Parameters","text":"

These are common parameters that works with every backend in WebGear_RTC.

"},{"location":"gears/webgear_rtc/params/#colorspace","title":"colorspace","text":"

This parameter selects the colorspace of the source stream.

Data-Type: String

Default Value: Its default value is None.

Usage:

All supported colorspace values are given here \u27b6

WebGear_RTC(colorspace=\"COLOR_BGR2HSV\")\n

Its complete usage example is given here \u27b6

"},{"location":"gears/webgear_rtc/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

WebGear_RTC(logging=True)\n

"},{"location":"gears/webgear_rtc/params/#time_delay","title":"time_delay","text":"

This parameter set the time delay (in seconds) before the WebGear_RTC API start reading the frames. This delay is only required if the source required some warm-up delay before starting up.

Data-Type: Integer

Default Value: Its default value is 0.

Usage:

WebGear_RTC(time_delay=1)  # set 1 seconds time delay\n

"},{"location":"gears/webgear_rtc/usage/","title":"Usage Examples","text":""},{"location":"gears/webgear_rtc/usage/#webgear_rtc-api-usage-examples","title":"WebGear_RTC API Usage Examples:","text":""},{"location":"gears/webgear_rtc/usage/#requirements","title":"Requirements","text":""},{"location":"gears/webgear_rtc/usage/#installation-with-asyncio-support","title":"Installation with Asyncio Support","text":"

WebGear_RTC API is the part of asyncio package of VidGear, thereby you need to install VidGear with asyncio support as follows:

pip install vidgear[asyncio]\n
"},{"location":"gears/webgear_rtc/usage/#aiortc","title":"Aiortc","text":"

Must Required with WebGear_RTC API. You can easily install it via pip:

Microsoft Visual C++ 14.0 is required.

Installing aiortc on windows requires Microsoft Build Tools for Visual C++ libraries installed. You can easily fix this error by installing any ONE of these choices:

While the error is calling for VC++ 14.0 - but newer versions of Visual C++ libraries works as well.

Afterwards, Select: Workloads \u2192 Desktop development with C++, then for Individual Components, select only:

Finally, proceed installing aiortc via pip.

  pip install aiortc\n
"},{"location":"gears/webgear_rtc/usage/#asgi-server","title":"ASGI Server","text":"

You'll also need to install an ASGI Server to run following WebGear_RTC usage examples, and by default WebGear_RTC ships the state-of-the-art uvicorn Server. But you can also use other ASGI server such as daphne, or hypercorn with it.

"},{"location":"gears/webgear_rtc/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Let's implement a Bare-Minimum usage example:

"},{"location":"gears/webgear_rtc/usage/#running-programmatically","title":"Running Programmatically","text":"

You can access and run WebGear_RTC VideoStreamer Server programmatically in your python script in just a few lines of code, as follows:

For accessing WebGear_RTC on different Client Devices on the network, use \"0.0.0.0\" as host value instead of \"localhost\" on Host Machine. More information can be found here \u27b6

We are using frame_size_reduction attribute for frame size reduction (in percentage) to be streamed with its options dictionary parameter to cope with performance-throttling in this example.

# import required libraries\nimport uvicorn\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# various performance tweaks\noptions = {\n    \"frame_size_reduction\": 25,\n}\n\n# initialize WebGear_RTC app\nweb = WebGear_RTC(source=\"foo.mp4\", logging=True, **options)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

which can be accessed on any browser on your machine at http://localhost:8000/.

"},{"location":"gears/webgear_rtc/usage/#running-from-terminal","title":"Running from Terminal","text":"

You can also access and run WebGear_RTC Server directly from the terminal commandline. The following command will run a WebGear_RTC VideoStreamer server at http://localhost:8000/:

Make sure your PYTHON_PATH is set to python 3.7+ versions only.

If you're using --options/-op flag, then kindly wrap your dictionary value in single '' quotes.

python3 -m vidgear.gears.asyncio --mode webrtc --source test.avi --logging True --options '{\"frame_size_reduction\": 50, \"frame_jpeg_quality\": 80, \"frame_jpeg_optimize\": True, \"frame_jpeg_progressive\": False}'\n

which can also be accessed on any browser on the network at http://localhost:8000/.

Advanced Usage from Terminal

You can run python3 -m vidgear.gears.asyncio -h help command to see all the advanced settings, as follows:

usage: python -m vidgear.gears.asyncio [-h] [-m MODE] [-s SOURCE] [-ep ENABLEPICAMERA] [-S STABILIZE]\n            [-cn CAMERA_NUM] [-yt stream_mode] [-b BACKEND] [-cs COLORSPACE]\n            [-r RESOLUTION] [-f FRAMERATE] [-td TIME_DELAY]\n            [-ip IPADDRESS] [-pt PORT] [-l LOGGING] [-op OPTIONS]\n\nRuns WebGear/WebGear_RTC Video Server through terminal.\n\noptional arguments:\n  -h, --help            show this help message and exit\n  -m {mjpeg,webrtc}, --mode {mjpeg,webrtc}\n                        Whether to use \"MJPEG\" or \"WebRTC\" mode for streaming.\n  -s SOURCE, --source SOURCE\n                        Path to input source for CamGear API.\n  -ep ENABLEPICAMERA, --enablePiCamera ENABLEPICAMERA\n                        Sets the flag to access PiGear(if True) or otherwise\n                        CamGear API respectively.\n  -S STABILIZE, --stabilize STABILIZE\n                        Enables/disables real-time video stabilization.\n  -cn CAMERA_NUM, --camera_num CAMERA_NUM\n                        Sets the camera module index that will be used by\n                        PiGear API.\n  -yt STREAM_MODE, --stream_mode STREAM_MODE\n                        Enables YouTube Mode in CamGear API.\n  -b BACKEND, --backend BACKEND\n                        Sets the backend of the video source in CamGear API.\n  -cs COLORSPACE, --colorspace COLORSPACE\n                        Sets the colorspace of the output video stream.\n  -r RESOLUTION, --resolution RESOLUTION\n                        Sets the resolution (width,height) for camera module\n                        in PiGear API.\n  -f FRAMERATE, --framerate FRAMERATE\n                        Sets the framerate for camera module in PiGear API.\n  -td TIME_DELAY, --time_delay TIME_DELAY\n                        Sets the time delay(in seconds) before start reading\n                        the frames.\n  -ip IPADDRESS, --ipaddress IPADDRESS\n                        Uvicorn binds the socket to this ipaddress.\n  -pt PORT, --port PORT\n                        Uvicorn binds the socket to this port.\n  -l LOGGING, --logging LOGGING\n                        Enables/disables error logging, essential for\n                        debugging.\n  -op OPTIONS, --options OPTIONS\n                        Sets the parameters supported by APIs(whichever being\n                        accessed) to the input videostream, But make sure to\n                        wrap your dict value in single or double quotes.\n

"},{"location":"gears/writegear/introduction/","title":"Introduction","text":""},{"location":"gears/writegear/introduction/#writegear-api","title":"WriteGear API","text":"WriteGear API generalized workflow"},{"location":"gears/writegear/introduction/#overview","title":"Overview","text":"

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 specifications (such asbitrate, codec, framerate, resolution, subtitles, etc.).

WriteGear also supports streaming with traditional protocols such as RTSP/RTP, RTMP. It is powerful enough to perform complex tasks such as Live-Streaming (such as for Twitch, YouTube etc.) and Multiplexing Video-Audio with real-time frames in just few lines of code.

Best of all, WriteGear grants users the complete freedom to play with any FFmpeg parameter with its exclusive Custom Commands function (see this doc) without relying on any third-party API.

In addition to this, WriteGear also provides flexible access to OpenCV's VideoWriter API tools for video-frames encoding without compression.

"},{"location":"gears/writegear/introduction/#modes-of-operation","title":"Modes of Operation","text":"

WriteGear primarily operates in following modes:

Helpful Tips

"},{"location":"gears/writegear/introduction/#faqs","title":"FAQs","text":"See here \ud83d\ude80"},{"location":"gears/writegear/compression/overview/","title":"Overview","text":""},{"location":"gears/writegear/compression/overview/#writegear-api-compression-mode","title":"WriteGear API: Compression Mode","text":"WriteGear API's Compression Mode generalized workflow"},{"location":"gears/writegear/compression/overview/#overview","title":"Overview","text":"

When compression_mode parameter is enabled (.i.e compression_mode = True), WriteGear API provides a complete, flexible & robust wrapper around FFmpeg to encode lossless & compressed multimedia files.

This mode can process real-time video frames into a lossless compressed format with any suitable setting video/audio properties such as bitrate, codec, framerate, resolution, subtitles, and much more in just a few easy lines of code. It can also perform complex tasks such as Live-Streaming (such as for Twitch), multiplexing video with audio in real-time (see this usage example) while handling all errors robustly.

Important Information

You can speed up the execution time by disabling logging (.i.e logging = False) for production use, and by tweaking FFmpeg parameters in output_params values. Look into FFmpeg docs \u27b6 for such hacks.

"},{"location":"gears/writegear/compression/overview/#custom-ffmpeg-commands-in-writegear-api","title":"Custom FFmpeg Commands in WriteGear API","text":"

WriteGear API now provides the execute_ffmpeg_cmd Function in Compression Mode, that enables the user to pass any custom CLI commands as an input to its internal FFmpeg Pipeline by formating it as a list.

This function opens endless possibilities of exploiting any FFmpeg supported parameter within WriteGear, without relying on a third-party library/API to do the same, and while doing that it robustly handles all errors/warnings quietly.

A complete guide on execute_ffmpeg_cmd Function can be found here \u27b6

"},{"location":"gears/writegear/compression/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through WriteGear Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/writegear/compression/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/writegear/compression/params/","title":"Parameters","text":""},{"location":"gears/writegear/compression/params/#writegear-api-parameters-compression-mode","title":"WriteGear API Parameters: Compression Mode","text":""},{"location":"gears/writegear/compression/params/#output","title":"output","text":"

This parameter sets the valid filename/path/URL for the video output.

Warning

WriteGear API will throw ValueError if output provided is empty or invalid.

Data-Type: String

Usage:

Its valid input can be one of the following:

"},{"location":"gears/writegear/compression/params/#compression_mode","title":"compression_mode","text":"

This parameter selects the WriteGear's Primary Mode of Operation, i.e. if this parameter is enabled (.i.e compression_mode = True) WriteGear will use FFmpeg to encode output video, and if disabled (.i.e compression_mode = False), the OpenCV's VideoWriter API will be used for encoding files/streams.

Data-Type: Boolean

Default Value: Its default value is True.

Usage:

WriteGear(output = 'output.mp4', compression_mode=True)\n

"},{"location":"gears/writegear/compression/params/#custom_ffmpeg","title":"custom_ffmpeg","text":"

This parameter assigns the custom path/directory where the custom FFmpeg executables are located in Compression Mode only.

Compression Mode Behavior on Windows

In Compression Mode, if a custom FFmpeg executable's path | directory is not provided through custom_ffmpeg parameter on Windows machine, then WriteGear API will automatically attempt to download and extract suitable Static FFmpeg binaries at suitable location on your windows machine. More information can be found here \u27b6.

Data-Type: String

Default Value: Its default value is None.

Usage:

# if ffmpeg executables are located at \"/foo/foo1/FFmpeg\"\nWriteGear(output = 'output.mp4', custom_ffmpeg=\"/foo/foo1/FFmpeg\")\n

"},{"location":"gears/writegear/compression/params/#output_params","title":"output_params","text":"

This parameter allows us to exploit almost all FFmpeg supported parameters effortlessly and flexibly for encoding in Compression Mode, by formatting desired FFmpeg Parameters as this parameter's attributes. All supported parameters and encoders for compression mode discussed below:

Kindly read FFmpeg Docs carefully, before passing any values to output_param dictionary parameter. Wrong values may result in undesired Errors or no output at all.

Data-Type: Dictionary

Default Value: Its default value is {}.

"},{"location":"gears/writegear/compression/params/#supported-parameters","title":"Supported Parameters","text":""},{"location":"gears/writegear/compression/params/#supported-encoders","title":"Supported Encoders","text":"

All the encoders that are compiled with FFmpeg in use, are supported by WriteGear API. You can easily check the compiled encoders by running following command in your terminal:

Similarily, supported demuxers and filters depends upons compiled FFmpeg in use.

ffmpeg -encoders           # use `ffmpeg.exe -encoders` on windows\n

"},{"location":"gears/writegear/compression/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

WriteGear(output = 'output.mp4', logging=True)\n

"},{"location":"gears/writegear/compression/usage/","title":"Usage Examples","text":""},{"location":"gears/writegear/compression/usage/#writegear-api-usage-examples-compression-mode","title":"WriteGear API Usage Examples: Compression Mode","text":"

Important Information

After going through WriteGear Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/writegear/compression/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with WriteGear API in Compression Mode:

# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# open any valid video stream(for e.g `myvideo.avi` file)\nstream = CamGear(source=\"myvideo.avi\").start()\n\n# Define writer with default parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\")\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/compression/usage/#using-compression-mode-in-rgb-mode","title":"Using Compression Mode in RGB Mode","text":"

In Compression Mode, WriteGear API contains rgb_mode boolean parameter for RGB Mode, which when enabled (i.e. rgb_mode=True), specifies that incoming frames are of RGB format (instead of default BGR format). This mode makes WriteGear directly compatible with libraries that only supports RGB format.

The complete usage example is as follows:

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = VideoGear(source=0).start()\n\n# Define writer with default parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\")\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # simulating RGB frame for example\n    frame_rgb = frame[:, :, ::-1]\n\n    # writing RGB frame to writer\n    writer.write(frame_rgb, rgb_mode=True)  # activate RGB Mode\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/compression/usage/#using-compression-mode-with-controlled-framerate","title":"Using Compression Mode with controlled FrameRate","text":"

WriteGear API provides -input_framerate attribute for its options dictionary parameter in Compression Mode, which allow us to control/set the constant framerate of the output video.

Advanced Tip for setting constant framerate

If -input_framerate attribute doesn't works for you, then define it in conjunction with another -r FFmpeg parameter as attribute:

# set output constant framerate to (say 60 fps)\noutput_params = {\"-input_framerate\":60, \"-r\":60}\n# assign that to WriteGear\nwriter = WriteGear(output=\"out.mp4\", logging =True, **output_params)\n

But make sure you MUST set value of -r and -input_framerate parameter less than or equal to your input source framerate.

In this code we will retrieve framerate from video stream, and set it as -input_framerate attribute for option parameter in WriteGear API:

# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = CamGear(source=0).start()\n\n# retrieve framerate from CamGear Stream and pass it as `-input_framerate` parameter\noutput_params = {\"-input_framerate\": stream.framerate}\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\", **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if None-type\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/compression/usage/#using-compression-mode-for-live-streaming","title":"Using Compression Mode for live streaming","text":"

In Compression Mode, WriteGear also allows URL strings (as output) for live streaming realtime frames with its output parameter.

In this example, we will stream live camera frames directly to Twitch :

For streaming with traditional protocols such as RTSP/RTP, Checkout this WriteGear's Bonus Examples \u27b6.

YouTube-Live Streaming example code also available in WriteGear's Bonus Examples \u27b6

This example assume you already have a Twitch Account for publishing video.

Make sure to change Twitch Stream Key with yours in following code before running!

# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# Open live webcam video stream on first index(i.e. 0) device\nstream = CamGear(source=0, logging=True).start()\n\n# define required FFmpeg optimizing parameters for your writer\noutput_params = {\n    \"-preset:v\": \"veryfast\",\n    \"-g\": 60,\n    \"-keyint_min\": 60,\n    \"-sc_threshold\": 0,\n    \"-bufsize\": \"2500k\",\n    \"-f\": \"flv\",\n}\n\n# [WARNING] Change your Twitch Stream Key here:\nTWITCH_KEY = \"live_XXXXXXXXXX~XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\"\n\n# Define writer with defined parameters and\nwriter = WriteGear(\n    output=\"rtmp://live.twitch.tv/app/{}\".format(TWITCH_KEY),\n    logging=True,\n    **output_params\n)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/compression/usage/#using-compression-mode-with-hardware-encoders","title":"Using Compression Mode with Hardware encoders","text":"

By default, WriteGear API uses libx264 encoder for encoding output files in Compression Mode. But you can easily change encoder to your suitable supported encoder by passing -vcodec FFmpeg parameter as an attribute with its output_param dictionary parameter. In addition to this, you can also specify the additional properties/features of your system's GPU easily.

User Discretion Advised

This example is just conveying the idea on how to use FFmpeg's hardware encoders with WriteGear API in Compression mode, which MAY/MAY NOT suit your system. Kindly use suitable parameters based your system hardware settings only.

In this example, we will be using h264_vaapi as our hardware encoder and also optionally be specifying our device hardware's location (i.e. '-vaapi_device':'/dev/dri/renderD128') and other features such as '-vf':'format=nv12,hwupload':

Remember to 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:

ffmpeg  -hide_banner -encoders | grep vaapi \n\n V..... h264_vaapi           H.264/AVC (VAAPI) (codec h264)\n V..... hevc_vaapi           H.265/HEVC (VAAPI) (codec hevc)\n V..... mjpeg_vaapi          MJPEG (VAAPI) (codec mjpeg)\n V..... mpeg2_vaapi          MPEG-2 (VAAPI) (codec mpeg2video)\n V..... vp8_vaapi            VP8 (VAAPI) (codec vp8)\n
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# Open live webcam video stream on first index(i.e. 0) device\nstream = CamGear(source=0, logging=True).start()\n\n# define required FFmpeg parameters for your writer\noutput_params = {\n    \"-vcodec\": \"h264_vaapi\",\n    \"-vaapi_device\": \"/dev/dri/renderD128\",\n    \"-vf\": \"format=nv12,hwupload\",\n}\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\", **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/compression/usage/#using-compression-mode-with-opencv","title":"Using Compression Mode with OpenCV","text":"

You can easily use WriterGear API directly with any Video Processing library(For e.g OpenCV itself) in Compression Mode. The complete usage example is as follows:

# import required libraries\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# define suitable (Codec,CRF,preset) FFmpeg parameters for writer\noutput_params = {\"-vcodec\": \"libx264\", \"-crf\": 0, \"-preset\": \"fast\"}\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = cv2.VideoCapture(0)\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n        break\n\n    # {do something with the frame here}\n    # lets convert frame to gray for this example\n    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n\n    # write gray frame to writer\n    writer.write(gray)\n\n    # Show output window\n    cv2.imshow(\"Output Gray Frame\", gray)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.release()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/compression/usage/#using-compression-mode-with-live-audio-input","title":"Using Compression Mode with Live Audio Input","text":"

In Compression Mode, WriteGear API allows us to exploit almost all FFmpeg supported parameters that you can think of in its Compression Mode. Hence, combining audio with live video frames is pretty easy.

In this example code, we will merging the audio from a Audio Device (for e.g. Webcam inbuilt mic) to live frames incoming from the Video Source (for e.g external webcam), and save the output as a compressed video file, all in real time:

Example Assumptions

Identifying and Specifying sound card on different OS platforms Windows Linux MacOS

Windows OS users can use the dshow (DirectShow) to list audio input device which is the preferred option for Windows users. You can refer following steps to identify and specify your sound card:

If audio still doesn't work then checkout this troubleshooting guide \u27b6 or reach us out on Gitter \u27b6 Community channel

Linux OS users can use the alsa to list input device to capture live audio input such as from a webcam. You can refer following steps to identify and specify your sound card:

If audio still doesn't work then reach us out on Gitter \u27b6 Community channel

MAC OS users can use the avfoundation to list input devices for grabbing audio from integrated iSight cameras as well as cameras connected via USB or FireWire. You can refer following steps to identify and specify your sound card on MacOS/OSX machines:

If audio still doesn't work then reach us out on Gitter \u27b6 Community channel

Make sure this -i audio-source it compatible with provided video-source, otherwise you could encounter multiple errors or no output at all.

You MUST use -input_framerate attribute to set exact value of input framerate when using external audio in Real-time Frames mode, otherwise audio delay will occur in output streams.

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = VideoGear(source=0).start()\n\n# change with your webcam soundcard, plus add additional required FFmpeg parameters for your writer\noutput_params = {\n    \"-input_framerate\": stream.framerate,\n    \"-thread_queue_size\": \"512\",\n    \"-ac\": \"2\",\n    \"-ar\": \"48000\",\n    \"-f\": \"alsa\", # !!! warning: always keep this line above \"-i\" parameter !!!\n    \"-i\": \"hw:1\",\n}\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4\nwriter = WriteGear(output=\"Output.mp4\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/compression/advanced/cciw/","title":"Custom FFmpeg Commands","text":""},{"location":"gears/writegear/compression/advanced/cciw/#custom-ffmpeg-commands-in-writegear-api","title":"Custom FFmpeg Commands in WriteGear API","text":"

WriteGear API now provides the execute_ffmpeg_cmd Method in Compression Mode that enables the user to pass any custom FFmpeg CLI (Command Line Interface) commands as input to its internal FFmpeg Pipeline by formating it as a list.

This opens endless possibilities of exploiting every FFmpeg params within WriteGear without relying on a third-party API to do the same and while doing that it robustly handles all errors/warnings quietly.

Important Information

"},{"location":"gears/writegear/compression/advanced/cciw/#features","title":"Features","text":"

"},{"location":"gears/writegear/compression/advanced/cciw/#methods","title":"Methods","text":""},{"location":"gears/writegear/compression/advanced/cciw/#execute_ffmpeg_cmd","title":"execute_ffmpeg_cmd","text":"

This method allows the users to pass the custom FFmpeg terminal commands as a formatted list directly to WriteGear API's FFmpeg pipeline for processing/execution. Its usage is as follows:

# format FFmpeg terminal command `ffmpeg -y -i source_video -acodec copy input_audio.aac` as a list\nffmpeg_command = [\"-y\", \"-i\", source_video, \"-acodec\", \"copy\", \"input_audio.aac\"]\n\n# execute this list using this function\nexecute_ffmpeg_cmd(ffmpeg_command)\n

"},{"location":"gears/writegear/compression/advanced/cciw/#usage-examples","title":"Usage Examples","text":"

Following usage examples is just an idea of what can be done with this powerful function. So just Tinker with various FFmpeg parameters/commands yourself and see it working. Also, if you're unable to run any terminal FFmpeg command, then report an issue.

"},{"location":"gears/writegear/compression/advanced/cciw/#using-writegear-to-separate-audio-from-video","title":"Using WriteGear to separate Audio from Video","text":"

In this example, we will extract and save audio from a URL stream:

# import required libraries\nfrom vidgear.gears import WriteGear\n\n# define a valid url\nurl_to_stream = (\n    \"http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4\"\n)\n\n# Define writer with default parameters\nwriter = WriteGear(output=\"Output.mp4\", logging=True)\n\n# format command to convert stream audio as 'output_audio.aac' as list\nffmpeg_command_to_save_audio = [\n    \"-y\",\n    \"-i\",\n    url_to_stream,\n    \"output_audio.aac\",\n]  # `-y` parameter is to overwrite outputfile if exists\n\n# execute FFmpeg command\nwriter.execute_ffmpeg_cmd(ffmpeg_command_to_save_audio)\n\n# safely close writer\nwriter.close()\n

After running this script, You will get the final 'output_audio.aac' audio file.

"},{"location":"gears/writegear/compression/advanced/cciw/#using-writegear-to-merge-audio-with-video","title":"Using WriteGear to merge Audio with Video","text":"

In this example, we will merge audio with video:

You can also directly add external audio input to video-frames in WriteGear. For more information, See this FAQ example \u27b6

Example Assumptions

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import WriteGear\nimport cv2\nimport time\n\n# Open input video stream\nstream = VideoGear(source=\"input-video.mp4\").start()\n\n# set input audio stream path\ninput_audio = \"input-audio.aac\"\n\n# define your parameters\noutput_params = {\n    \"-input_framerate\": stream.framerate\n}  # output framerate must match source framerate\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\", **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n\n\n# sleep 1 sec as the above video might still be rendering\ntime.sleep(1)\n\n\n# format FFmpeg command to generate `Output_with_audio.mp4` by merging input_audio in above rendered `Output.mp4`\nffmpeg_command = [\n    \"-y\",\n    \"-i\",\n    \"Output.mp4\",\n    \"-i\",\n    input_audio,\n    \"-c:v\",\n    \"copy\",\n    \"-c:a\",\n    \"copy\",\n    \"-map\",\n    \"0:v:0\",\n    \"-map\",\n    \"1:a:0\",\n    \"-shortest\",\n    \"Output_with_audio.mp4\",\n]  # `-y` parameter is to overwrite outputfile if exists\n\n# execute FFmpeg command\nwriter.execute_ffmpeg_cmd(ffmpeg_command)\n

After running this script, You will get the final 'Output_with_audio.mp4' file with both video and audio merged.

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/","title":"FFmpeg Installation","text":""},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#ffmpeg-installation-instructions","title":"FFmpeg Installation Instructions","text":"

WriteGear must requires FFmpeg executables for its Compression capabilities in Compression Mode. You can following machine-specific instructions for its installation:

In case WriteGear API fails to detect valid FFmpeg executables on your system (even if Compression Mode is enabled), it automatically fallbacks to Non-Compression Mode.

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#linux-ffmpeg-installation","title":"Linux FFmpeg Installation","text":"

The WriteGear API supports Auto-Detection and Manual Configuration methods on a Linux machine:

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#a-auto-detection","title":"A. Auto-Detection","text":"

This is a recommended approach on Linux Machines

If WriteGear API not receives any input from the user on custom_ffmpeg parameter, then on Linux system, it tries to auto-detects the required FFmpeg installed binaries through validation test that employs subprocess python module.

Installation: You can install easily install official FFmpeg according to your Linux Distro by following this post \u27b6

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#b-manual-configuration","title":"B. Manual Configuration","text":"

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#windows-ffmpeg-installation","title":"Windows FFmpeg Installation","text":"

The WriteGear API supports Auto-Installation and Manual Configuration methods on Windows systems.

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#a-auto-installation","title":"A. Auto-Installation","text":"

This is a recommended approach on Windows Machines

If WriteGear API not receives any input from the user on custom_ffmpeg parameter, then on Windows system WriteGear API auto-generates the required FFmpeg Static Binaries from a dedicated Github Server into the temporary directory (for e.g. C:\\Temp) of your machine.

Warning

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#b-manual-configuration_1","title":"B. Manual Configuration","text":"

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#macos-ffmpeg-installation","title":"MacOS FFmpeg Installation","text":"

The WriteGear API supports Auto-Detection and Manual Configuration methods on a macOS machine.

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#a-auto-detection_1","title":"A. Auto-Detection","text":"

This is a recommended approach on MacOS Machines

If WriteGear API not receives any input from the user on custom_ffmpeg parameter, then on macOS system, it tries to auto-detects the required FFmpeg installed binaries through validation test that employs subprocess python module.

Installation: You can easily install FFmpeg on your macOS machine by following this tutorial \u27b6

"},{"location":"gears/writegear/compression/advanced/ffmpeg_install/#b-manual-configuration_2","title":"B. Manual Configuration","text":"

"},{"location":"gears/writegear/non_compression/overview/","title":"Overview","text":""},{"location":"gears/writegear/non_compression/overview/#writegear-api-non-compression-mode","title":"WriteGear API: Non-Compression Mode","text":"WriteGear API's Non-Compression Mode generalized workflow"},{"location":"gears/writegear/non_compression/overview/#overview","title":"Overview","text":"

When compression_mode parameter is disabled (.i.e compression_mode = False), WriteGear API uses basic OpenCV's inbuilt VideoWriter API tools for encoding multimedia files but without compression.

This mode provides flexible access to OpenCV's VideoWriter API,and also supports various parameters available within this API, but lacks the ability to control output quality, compression, and other important features like lossless video compression, audio encoding, etc. which are only available in Compression Mode. Thereby, the resultant output video-file size will be many times larger as compared to Compression Mode.

Important Information

"},{"location":"gears/writegear/non_compression/overview/#usage-examples","title":"Usage Examples","text":"See here \ud83d\ude80

After going through WriteGear Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/writegear/non_compression/overview/#parameters","title":"Parameters","text":"See here \ud83d\ude80"},{"location":"gears/writegear/non_compression/params/","title":"Parameters","text":""},{"location":"gears/writegear/non_compression/params/#writegear-api-parameters-non-compression-mode","title":"WriteGear API Parameters: Non-Compression Mode","text":""},{"location":"gears/writegear/non_compression/params/#output","title":"output","text":"

This parameter sets the valid output Video filename/path for the output video.

WriteGear API will throw RuntimeError if output provided is empty or invalid.

Data-Type: String

Default Value: Its default value is 0.

Usage:

Make sure to provide valid filename with valid file-extension based on the encoder in use (default is .mp4).

Its valid input can be one of the following:

"},{"location":"gears/writegear/non_compression/params/#compression_mode","title":"compression_mode","text":"

This parameter selects the WriteGear's Primary Mode of Operation, i.e. if this parameter is enabled (.i.e compression_mode = True) WriteGear will use FFmpeg to encode output video, and if disabled (.i.e compression_mode = False), the OpenCV's VideoWriter API will be used for encoding files/streams.

Data-Type: Boolean

Default Value: Its default value is True.

Usage:

WriteGear(output = 'output.mp4', compression_mode=False)\n

"},{"location":"gears/writegear/non_compression/params/#custom_ffmpeg","title":"custom_ffmpeg","text":"

Not supported in Non-Compression Mode!

"},{"location":"gears/writegear/non_compression/params/#output_params","title":"output_params","text":"

This parameter allows us to exploit almost all OpenCV's VideoWriter API supported parameters effortlessly and flexibly for video-encoding in Non-Compression Mode, by formatting desired FFmpeg Parameters as this parameter's attributes. All supported parameters and FOURCC codecs for compression mode discussed below:

Remember, Non-Compression mode lacks the ability to control output quality and other important features like lossless video compression, audio encoding, etc., which are available with WriteGear's Compression Mode only.

Data-Type: Dictionary

Default Value: Its default value is {}.

"},{"location":"gears/writegear/non_compression/params/#supported-attributes","title":"Supported Attributes","text":"

Non-Compression Mode only gives access to a limited number of Parameters through its output_params parameter's attributes, which are as follows:

"},{"location":"gears/writegear/non_compression/params/#a-opencv-parameters","title":"A. OpenCV Parameters","text":"

WriteGear provides access to all available OpenCV's VideoWriter API parameters in Non-Compression Mode.

Parameters Description -fourcc 4-character code of codec used to encode frames -fps controls the framerate of output video(Default value: 25) -backend (optional) In case of multiple backends, this parameter allows us to specify VideoWriter API's backends to use. Its valid values are CAP_FFMPEG or CAP_GSTREAMER(if enabled) -color (optional) If it is not zero(0), the encoder will expect and encode color frames, otherwise it will work with grayscale frames (the flag is currently supported on Windows only)

-height and -width parameter are no longer supported and are automatically derived from the input frames.

"},{"location":"gears/writegear/non_compression/params/#b-exclusive-parameters","title":"B. Exclusive Parameters","text":"

In addition to OpenCV Parameters, WriteGear API also provides few exclusive attribute, which are as follows:

Usage:

To assign desired parameters in Non-Compression Mode, you can format it as dictionary attribute and pass through this(output_params) parameter as follows:

# format parameter as dictionary attribute\noutput_params = {\"-fps\":30} \n# and then, assign it\nWriteGear(output = 'output.mp4', compression_mode=False, **output_params)\n

Its usage example can be found here \u27b6.

"},{"location":"gears/writegear/non_compression/params/#supported-fourcc-codecs","title":"Supported FOURCC Codecs","text":"

FOURCC is a 4-character code of the codec used to encode video in Non-Compression Mode(OpenCV's VideoWriter API) without compression.

List of all supported FOURCC codecs can found here \u27b6

Usage:

To select desired FOURCC codec in Non-Compression Mode, you can format it as dictionary attribute and pass through this(output_params) parameter. For example, using MJPG as codec, we can:

# format codec as dictionary attribute\noutput_params = {\"-fourcc\":\"MJPG\"} \n# and then, assign it\nWriteGear(output = 'output.mp4', compression_mode=False, **output_params)\n

Its usage example can be found here \u27b6.

"},{"location":"gears/writegear/non_compression/params/#logging","title":"logging","text":"

This parameter enables logging (if True), essential for debugging.

Data-Type: Boolean

Default Value: Its default value is False.

Usage:

WriteGear(output = 'output.mp4', compression_mode=False, logging=True)\n

"},{"location":"gears/writegear/non_compression/usage/","title":"Usage Examples","text":""},{"location":"gears/writegear/non_compression/usage/#writegear-api-usage-examples-non-compression-mode","title":"WriteGear API Usage Examples: Non-Compression Mode","text":"

Important Information

After going through WriteGear Usage Examples, Checkout more bonus examples here \u27b6

"},{"location":"gears/writegear/non_compression/usage/#bare-minimum-usage","title":"Bare-Minimum Usage","text":"

Following is the bare-minimum code you need to get started with WriteGear API in Non-Compression Mode:

# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# open any valid video stream(for e.g `myvideo.avi` file)\nstream = CamGear(source=\"myvideo.avi\").start()\n\n# Define writer with Non-compression mode and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\", compression_mode=False)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/non_compression/usage/#using-non-compression-mode-with-videocapture-gears","title":"Using Non-Compression Mode with VideoCapture Gears","text":"

In Non-Compression mode, WriteGear API provides flexible control over OpenCV's VideoWriter API parameters through its output_param dictionary parameter by formating them as dictionary attributes. Moreover, WriteGear API can be used in conjunction with any other Gears/APIs effortlessly.

All supported attributes for output_param can be found here \u27b6

The complete usage example is as follows:

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# define suitable tweak parameters for writer\noutput_params = {\"-fourcc\": \"MJPG\", \"-fps\": 30}\n\n# open live video stream on webcam at first index(i.e. 0) device\nstream = VideoGear(source=0, logging=True).start()\n\n# Define writer with defined parameters and suitable output filename \n# for e.g. `Output.mp4`\nwriter = WriteGear(\n    output=\"Output.mp4\", compression_mode=False, logging=True, **output_params\n)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n    # lets convert frame to gray for this example\n    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n\n    # write gray frame to writer\n    writer.write(gray)\n\n    # Show output window\n    cv2.imshow(\"Output Gray Frame\", gray)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/non_compression/usage/#using-non-compression-mode-with-opencv","title":"Using Non-Compression Mode with OpenCV","text":"

You can easily use WriterGear API directly with any Video Processing library(For e.g OpenCV itself) in Non-Compression Mode. The complete usage example is as follows:

# import required libraries\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# define suitable tweak parameters for writer\noutput_params = {\"-fourcc\": \"MJPG\", \"-fps\": 30}\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = cv2.VideoCapture(0)\n\n# Define writer with defined parameters and suitable output filename \n# for e.g. `Output.mp4`\nwriter = WriteGear(\n    output=\"Output.mp4\", compression_mode=False, logging=True, **output_params\n)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n        break\n\n    # {do something with the frame here}\n    # lets convert frame to gray for this example\n    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n\n    # write gray frame to writer\n    writer.write(gray)\n\n    # Show output window\n    cv2.imshow(\"Output Gray Frame\", gray)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.release()\n\n# safely close writer\nwriter.close()\n

"},{"location":"gears/writegear/non_compression/usage/#using-non-compression-mode-with-gstreamer-pipeline","title":"Using Non-Compression Mode with GStreamer Pipeline","text":"

WriteGear API's Non-Compression Mode also supports GStreamer Pipeline as input to its output parameter, when GStreamer Pipeline Mode is enabled. This provides flexible way to write video frames to file or network stream with controlled framerate and bitrate. The complete usage example is as follows:

Requirement for GStreamer Pipelining

GStreamer Pipelining in WriteGear requires your OpenCV to be built with GStreamer support. Checkout this FAQ for compiling OpenCV with GStreamer support.

New in v0.2.5

This example was added in v0.2.5.

In this example we will be constructing GStreamer pipeline to write video-frames into a file(foo.mp4) at 1M video-bitrate.

# import required libraries\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# enable GStreamer Pipeline Mode for writer\noutput_params = {\"-gst_pipeline_mode\": True}\n\n# open live video stream on webcam at first index(i.e. 0) device\nstream = cv2.VideoCapture(0)\n\n# gst pipeline to write to a file `foo.mp4` at 1M video-bitrate\nGSTPipeline = \"appsrc ! videoconvert ! avenc_mpeg4 bitrate=100000 ! mp4mux ! filesink location={}\".format(\n    \"foo.mp4\"\n)\n\n# Define writer with defined parameters and with our Gstreamer pipeline\nwriter = WriteGear(\n    output=GSTPipeline, compression_mode=False, logging=True, **output_params\n)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.release()\n\n# safely close writer\nwriter.close()\n

"},{"location":"help/camgear_ex/","title":"Bonus Examples","text":""},{"location":"help/camgear_ex/#camgear-examples","title":"CamGear Examples","text":""},{"location":"help/camgear_ex/#synchronizing-two-sources-in-camgear","title":"Synchronizing Two Sources in CamGear","text":"

In this example both streams and corresponding frames will be processed synchronously i.e. with no delay:

Using same source with more than one instances of CamGear can lead to Global Interpreter Lock (GIL) that degrades performance even when it is not a bottleneck.

# import required libraries\nfrom vidgear.gears import CamGear\nimport cv2\nimport time\n\n# define and start the stream on first source ( For e.g #0 index device)\nstream1 = CamGear(source=0, logging=True).start() \n\n# define and start the stream on second source ( For e.g #1 index device)\nstream2 = CamGear(source=1, logging=True).start() \n\n# infinite loop\nwhile True:\n\n    frameA = stream1.read()\n    # read frames from stream1\n\n    frameB = stream2.read()\n    # read frames from stream2\n\n    # check if any of two frame is None\n    if frameA is None or frameB is None:\n        #if True break the infinite loop\n        break\n\n    # do something with both frameA and frameB here\n    cv2.imshow(\"Output Frame1\", frameA)\n    cv2.imshow(\"Output Frame2\", frameB)\n    # Show output window of stream1 and stream 2 separately\n\n    key = cv2.waitKey(1) & 0xFF\n    # check for 'q' key-press\n    if key == ord(\"q\"):\n        #if 'q' key-pressed break out\n        break\n\n    if key == ord(\"w\"):\n        #if 'w' key-pressed save both frameA and frameB at same time\n        cv2.imwrite(\"Image-1.jpg\", frameA)\n        cv2.imwrite(\"Image-2.jpg\", frameB)\n        #break   #uncomment this line to break out after taking images\n\ncv2.destroyAllWindows()\n# close output window\n\n# safely close both video streams\nstream1.stop()\nstream2.stop()\n

"},{"location":"help/camgear_ex/#using-variable-yt_dlp-parameters-in-camgear","title":"Using variable yt_dlp parameters in CamGear","text":"

CamGear provides exclusive attributes STREAM_RESOLUTION (for specifying stream resolution) & STREAM_PARAMS (for specifying underlying API(e.g. yt_dlp) parameters) with its options dictionary parameter.

The complete usage example is as follows:

More information on STREAM_RESOLUTION & STREAM_PARAMS attributes can be found here \u27b6

# import required libraries\nfrom vidgear.gears import CamGear\nimport cv2\n\n# specify attributes\noptions = {\"STREAM_RESOLUTION\": \"720p\", \"STREAM_PARAMS\": {\"nocheckcertificate\": True}}\n\n# Add YouTube Video URL as input source (for e.g https://youtu.be/bvetuLwJIkA)\n# and enable Stream Mode (`stream_mode = True`)\nstream = CamGear(\n    source=\"https://youtu.be/bvetuLwJIkA\", stream_mode=True, logging=True, **options\n).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"help/camgear_ex/#using-camgear-for-capturing-rtsprtmp-urls","title":"Using CamGear for capturing RTSP/RTMP URLs","text":"

You can open any network stream (such as RTSP/RTMP) just by providing its URL directly to CamGear's source parameter.

Here's a high-level wrapper code around CamGear API to enable auto-reconnection during capturing:

New in v0.2.2

This example was added in v0.2.2.

Enforcing UDP stream

You can easily enforce UDP for RTSP streams inplace of default TCP, by putting following lines of code on the top of your existing code:

# import required libraries\nimport os\n\n# enforce UDP\nos.environ[\"OPENCV_FFMPEG_CAPTURE_OPTIONS\"] = \"rtsp_transport;udp\"\n

Finally, use backend parameter value as backend=cv2.CAP_FFMPEG in CamGear.

from vidgear.gears import CamGear\nimport cv2\nimport datetime\nimport time\n\n\nclass Reconnecting_CamGear:\n    def __init__(self, cam_address, reset_attempts=50, reset_delay=5):\n        self.cam_address = cam_address\n        self.reset_attempts = reset_attempts\n        self.reset_delay = reset_delay\n        self.source = CamGear(source=self.cam_address).start()\n        self.running = True\n\n    def read(self):\n        if self.source is None:\n            return None\n        if self.running and self.reset_attempts > 0:\n            frame = self.source.read()\n            if frame is None:\n                self.source.stop()\n                self.reset_attempts -= 1\n                print(\n                    \"Re-connection Attempt-{} occured at time:{}\".format(\n                        str(self.reset_attempts),\n                        datetime.datetime.now().strftime(\"%m-%d-%Y %I:%M:%S%p\"),\n                    )\n                )\n                time.sleep(self.reset_delay)\n                self.source = CamGear(source=self.cam_address).start()\n                # return previous frame\n                return self.frame\n            else:\n                self.frame = frame\n                return frame\n        else:\n            return None\n\n    def stop(self):\n        self.running = False\n        self.reset_attempts = 0\n        self.frame = None\n        if not self.source is None:\n            self.source.stop()\n\n\nif __name__ == \"__main__\":\n    # open any valid video stream\n    stream = Reconnecting_CamGear(\n        cam_address=\"rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov\",\n        reset_attempts=20,\n        reset_delay=5,\n    )\n\n    # loop over\n    while True:\n\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if None-type\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # Show output window\n        cv2.imshow(\"Output\", frame)\n\n        # check for 'q' key if pressed\n        key = cv2.waitKey(1) & 0xFF\n        if key == ord(\"q\"):\n            break\n\n    # close output window\n    cv2.destroyAllWindows()\n\n    # safely close video stream\n    stream.stop()\n

"},{"location":"help/camgear_faqs/","title":"FAQs","text":""},{"location":"help/camgear_faqs/#camgear-faqs","title":"CamGear FAQs","text":""},{"location":"help/camgear_faqs/#what-is-camgear-api-and-what-does-it-do","title":"What is CamGear API and what does it do?","text":"

Answer: CamGear supports a diverse range of video streams which can handle/control video stream almost any IP/USB Cameras, multimedia video file format (upto 4k tested), any network stream URL such as http(s), rtp, rtsp, rtmp, mms, etc. In addition to this, it also supports live Gstreamer's RAW pipelines and YouTube video/livestreams URLs. For more info. see CamGear doc \u27b6.

"},{"location":"help/camgear_faqs/#im-only-familiar-with-opencv-how-to-get-started-with-camgear-api","title":"I'm only familiar with OpenCV, how to get started with CamGear API?","text":"

Answer: Answer: First, refer to the Switching from OpenCV guide, then go through CamGear documentation \u27b6. If you still have doubts, ask us on Gitter \u27b6 Community channel.

"},{"location":"help/camgear_faqs/#how-to-change-opencv-source-backend-in-camgear-api","title":"How to change OpenCV source backend in CamGear API?","text":"

Answer: See its Parameters \u27b6. Its, backend(int) parameter sets the backend of the source. Its value can be for e.g. backend = cv2.CAP_DSHOW in case of Direct Show.

"},{"location":"help/camgear_faqs/#how-to-get-framerate-of-the-source-in-camgear-api","title":"How to get framerate of the source in CamGear API?","text":"

Answer: CamGear's framerate global variable can be used to retrieve framerate of the input video stream. See this example \u27b6.

"},{"location":"help/camgear_faqs/#how-to-compile-opencv-with-gstreamer-support","title":"How to compile OpenCV with GStreamer support?","text":"

Answer: For compiling OpenCV with GSstreamer(>=v1.0.0) support:

Linux Windows MacOS

"},{"location":"help/camgear_faqs/#how-to-change-quality-and-parameters-of-youtube-streams-with-camgear","title":"How to change quality and parameters of YouTube Streams with CamGear?","text":"

Answer: CamGear provides exclusive attributes STREAM_RESOLUTION (for specifying stream resolution) & STREAM_PARAMS (for specifying underlying API(e.g. yt_dlp) parameters) with its options dictionary parameter. See this bonus example \u27b6.

"},{"location":"help/camgear_faqs/#how-to-open-rtsp-network-streams-with-camgear","title":"How to open RTSP network streams with CamGear?","text":"

Answer: You can open any local network stream (such as RTSP) just by providing its URL directly to CamGear's source parameter. See this bonus example \u27b6.

"},{"location":"help/camgear_faqs/#how-to-set-camera-settings-with-camgear","title":"How to set Camera Settings with CamGear?","text":"

Answer: See this usage example \u27b6.

"},{"location":"help/camgear_faqs/#can-i-play-4k8k-video-with-camgear-api","title":"Can I play 4K/8k video with CamGear API?","text":"

Answer: Yes, you can if your System Hardware supports it.

"},{"location":"help/camgear_faqs/#how-to-synchronize-between-two-cameras","title":"How to synchronize between two cameras?","text":"

Answer: See this bonus example \u27b6.

"},{"location":"help/camgear_faqs/#can-i-use-gpu-to-decode-the-video-source","title":"Can I use GPU to decode the video source?","text":"

Answer: See this issue comment \u27b6.

"},{"location":"help/camgear_faqs/#why-camgear-is-throwing-warning-that-threaded-queue-mode-is-disabled","title":"Why CamGear is throwing warning that Threaded Queue Mode is disabled?","text":"

Answer: That's a normal behavior. Please read about Threaded Queue Mode \u27b6

"},{"location":"help/general_faqs/","title":"General FAQs","text":""},{"location":"help/general_faqs/#general-faqs","title":"General FAQs","text":""},{"location":"help/general_faqs/#im-new-to-python-programming-or-its-usage-in-opencv-library-how-to-use-vidgear-in-my-projects","title":"\"I'm new to Python Programming or its usage in OpenCV Library\", How to use vidgear in my projects?","text":"

Answer: Before using vidgear, It's recommended to first go through the following dedicated blog sites and learn how OpenCV-Python syntax works (with examples):

Once done, visit Switching from OpenCV \u27b6 to easily replace OpenCV APIs with suitable Gears \u27b6 in your project. All the best!

If you run into any trouble or have any questions, then refer our Help section.

"},{"location":"help/general_faqs/#vidgear-is-using-multi-threading-but-python-is-notorious-for-its-poor-performance-in-multithreading","title":"\"VidGear is using Multi-threading, but Python is notorious for its poor performance in multithreading?\"","text":"

Answer: Refer vidgear's Threaded-Queue-Mode \u27b6

"},{"location":"help/general_faqs/#modulenotfounderror-no-module-named-vidgeargears-vidgear-is-not-a-package","title":"ModuleNotFoundError: No module named 'vidgear.gears'. 'vidgear' is not a package?","text":"

Answer: This error means you either have a file named vidgear.py in your python path or you've named your python script vidgear.py. Replace vidgear name with anything else to fix this error.

"},{"location":"help/general_faqs/#how-to-log-to-a-file-in-vidgear","title":"How to log to a file in VidGear?","text":"

Answer: VidGear provides exclusive VIDGEAR_LOGFILE environment variable to enable logging to a file while logging is enabled (i.e. logging=True) on respective Gear. You just have to set directory pathname (automatically creates vidgear.log file) or a log file pathname itself as value for this environment variable. This can be done on various Operating Systems as follows:

Remember enabling this logging to a file will completely disable any output on the terminal.

Linux Windows (Powershell) MacOS
# path to file\nexport VIDGEAR_LOGFILE=\"$HOME/foo.log\"\n\n# or just directory path \n# !!! Make sure `foo` path already exists !!!\nexport VIDGEAR_LOGFILE=\"$HOME/foo\"\n\n# to remove\nunset VIDGEAR_LOGFILE\n
# path to file\n$Env:VIDGEAR_LOGFILE = \"D:\\foo.log\"\n\n# or just directory path \n# !!! Make sure `foo` path already exists !!!\n$Env:VIDGEAR_LOGFILE = \"D:\\foo\"\n\n# to remove\n$Env:VIDGEAR_LOGFILE = \"\"\n
# path to file\nexport VIDGEAR_LOGFILE=\"$HOME/foo.log\"\n\n# or just directory path \n# !!! Make sure `foo` path already exists !!!\nexport VIDGEAR_LOGFILE=\"$HOME/foo\"\n\n# to remove\nunset VIDGEAR_LOGFILE\n

"},{"location":"help/general_faqs/#can-i-perform-deep-learning-task-with-vidgear","title":"Can I perform Deep Learning task with VidGear?","text":"

Answer: VidGear is a powerful Video Processing library (similar to OpenCV, FFmpeg, etc.) that can read, write, process, send & receive a sequence of video-frames in an optimized manner. But for Deep Learning or Machine Learning tasks, you have to use a third-party library. That being said, all VidGear's APIs can be used with any third-party Library(such as PyTorch, Tensorflow, etc.) that can leverage the overall performance if you're processing video/audio streams/frames in your application with Deep Learning tasks. Also, it eases the workflow since you have to write way fewer lines of code to read/store/process output videos.

"},{"location":"help/general_faqs/#can-i-ask-my-question-directly-without-raising-an-issue","title":"Can I ask my question directly without raising an issue?","text":"

Answer: Yes, please join our Gitter \u27b6 Community channel.

"},{"location":"help/general_faqs/#how-to-contribute-to-vidgear-development","title":"How to contribute to VidGear development?","text":"

Answer: See our Contribution Guidelines \u27b6

"},{"location":"help/general_faqs/#what-oses-are-supported-by-vidgear","title":"What OSes are supported by VidGear?","text":"

Answer: See Supported Systems \u27b6

"},{"location":"help/general_faqs/#what-python-versions-are-supported-by-vidgear","title":"What Python versions are supported by VidGear?","text":"

Answer: See Supported Python legacies \u27b6

"},{"location":"help/general_faqs/#can-i-include-vidgear-in-my-project-commercially-or-not","title":"Can I include VidGear in my project commercially or not?","text":"

Answer: Yes, you can, but strictly under the Terms and Conditions given in VidGear License \u27b6

"},{"location":"help/general_faqs/#i-love-using-vidgear-for-my-projects-how-can-i-support-it","title":"\"I Love using VidGear for my projects\", How can I support it?","text":"

Answer: See Helping VidGear \u27b6

"},{"location":"help/get_help/","title":"Getting Help","text":""},{"location":"help/get_help/#getting-help","title":"Getting Help","text":"Courtesy - Pinterest

Would you like to get help with VidGear?

There are several ways such as:

"},{"location":"help/get_help/#frequently-asked-questions","title":"Frequently Asked Questions","text":"

Got a question related to VidGear Working?

Checkout the Frequently Asked Questions - a curated list of all the questions with adequate answer that we commonly receive for quickly troubleshooting your problems:

"},{"location":"help/get_help/#bonus-examples","title":"Bonus Examples","text":"

How we do this with that API?

Checkout the Bonus Examples - a curated list of all experimental examples with unusual configuration that aren't included in general usage examples:

"},{"location":"help/get_help/#join-our-gitter-community-channel","title":"Join our Gitter Community channel","text":"

Have you come up with some new idea \ud83d\udca1 or looking for the fastest way troubleshoot your problems

Join and chat on our Gitter Community channel:

There you can ask quick questions, swiftly troubleshoot your problems, help others, share ideas & information, etc.

"},{"location":"help/get_help/#this-is-what-you-do-when","title":"This is what you do when...","text":""},{"location":"help/get_help/#reporting-an-issues","title":"Reporting an issues","text":"

Want to report a bug? Suggest a new feature?

Before you do, please read our guidelines \u27b6

"},{"location":"help/get_help/#preparing-a-pull-request","title":"Preparing a Pull Request","text":"

Interested in contributing to VidGear?

Before you do, please read our guidelines \u27b6

"},{"location":"help/netgear_async_ex/","title":"Bonus Examples","text":""},{"location":"help/netgear_async_ex/#netgear_async-examples","title":"NetGear_Async Examples","text":""},{"location":"help/netgear_async_ex/#using-netgear_async-with-webgear","title":"Using NetGear_Async with WebGear","text":"

The complete usage example is as follows:

New in v0.2.2

This example was added in v0.2.2.

"},{"location":"help/netgear_async_ex/#client-webgear-server","title":"Client + WebGear Server","text":"

Open a terminal on Client System where you want to display the input frames (and setup WebGear server) received from the Server and execute the following python code:

After running this code, Make sure to open Browser immediately otherwise NetGear_Async will soon exit with TimeoutError. You can also try setting timeout parameter to a higher value to extend this timeout.

Make sure you use different port value for NetGear_Async and WebGear API.

High CPU utilization may occur on Client's end. User discretion is advised.

Note down the IP-address of this system (required at Server's end) by executing the hostname -I command and also replace it in the following code.\"

# import libraries\nfrom vidgear.gears.asyncio import NetGear_Async\nfrom vidgear.gears.asyncio import WebGear\nfrom vidgear.gears.asyncio.helper import reducer\nimport uvicorn, asyncio, cv2\n\n# Define NetGear_Async Client at given IP address and define parameters\n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear_Async(\n    receive_mode=True,\n    pattern=1,\n    logging=True,\n).launch()\n\n# create your own custom frame producer\nasync def my_frame_producer():\n\n    # loop over Client's Asynchronous Frame Generator\n    async for frame in client.recv_generator():\n\n        # {do something with received frames here}\n\n        # reducer frames size if you want more performance otherwise comment this line\n        frame = await reducer(\n            frame, percentage=30, interpolation=cv2.INTER_AREA\n        )  # reduce frame by 30%\n\n        # handle JPEG encoding\n        encodedImage = cv2.imencode(\".jpg\", frame)[1].tobytes()\n        # yield frame in byte format\n        yield (b\"--frame\\r\\nContent-Type:image/jpeg\\r\\n\\r\\n\" + encodedImage + b\"\\r\\n\")\n        await asyncio.sleep(0)\n\n\nif __name__ == \"__main__\":\n    # Set event loop to client's\n    asyncio.set_event_loop(client.loop)\n\n    # initialize WebGear app without any source\n    web = WebGear(logging=True)\n\n    # add your custom frame producer to config with adequate IP address\n    web.config[\"generator\"] = my_frame_producer\n\n    # run this app on Uvicorn server at address http://localhost:8000/\n    uvicorn.run(web(), host=\"localhost\", port=8000)\n\n    # safely close client\n    client.close()\n\n    # close app safely\n    web.shutdown()\n

On successfully running this code, the output stream will be displayed at address http://localhost:8000/ in your Client's Browser.

"},{"location":"help/netgear_async_ex/#server","title":"Server","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

# import library\nfrom vidgear.gears.asyncio import NetGear_Async\nimport cv2, asyncio\n\n# initialize Server without any source\nserver = NetGear_Async(\n    source=None,\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n)\n\n# Create a async frame generator as custom source\nasync def my_frame_generator():\n\n    # !!! define your own video source here !!!\n    # Open any video stream such as live webcam\n    # video stream on first index(i.e. 0) device\n    stream = cv2.VideoCapture(0)\n\n    # loop over stream until its terminated\n    while True:\n\n        # read frames\n        (grabbed, frame) = stream.read()\n\n        # check if frame empty\n        if not grabbed:\n            break\n\n        # do something with the frame to be sent here\n\n        # yield frame\n        yield frame\n        # sleep for sometime\n        await asyncio.sleep(0)\n\n    # close stream\n    stream.release()\n\n\nif __name__ == \"__main__\":\n    # set event loop\n    asyncio.set_event_loop(server.loop)\n    # Add your custom source generator to Server configuration\n    server.config[\"generator\"] = my_frame_generator()\n    # Launch the Server\n    server.launch()\n    try:\n        # run your main function task until it is complete\n        server.loop.run_until_complete(server.task)\n    except (KeyboardInterrupt, SystemExit):\n        # wait for interrupts\n        pass\n    finally:\n        # finally close the server\n        server.close()\n

"},{"location":"help/netgear_async_faqs/","title":"FAQs","text":""},{"location":"help/netgear_async_faqs/#netgear_async-faqs","title":"NetGear_Async FAQs","text":""},{"location":"help/netgear_async_faqs/#what-is-netgear_async-api-and-what-does-it-do","title":"What is NetGear_Async API and what does it do?","text":"

Answer: NetGear_Async is an asyncio videoframe messaging framework, built on zmq.asyncio, and powered by high-performance asyncio event loop called uvloop to achieve unmatchable high-speed and lag-free video streaming over the network with minimal resource constraints. Basically, this API is able to transfer thousands of frames in just a few seconds without causing any significant load on your system. For more info. see NetGear_Async doc \u27b6

"},{"location":"help/netgear_async_faqs/#how-to-get-started-with-netgear_async-api","title":"How to get started with NetGear_Async API?","text":"

Answer: Answer: Answer: First, refer to the Switching from OpenCV guide, then go through NetGear_Async documentation \u27b6. If you still have doubts, ask us on Gitter \u27b6 Community channel.

See NetGear_Async doc \u27b6. Still in doubt, then ask us on Gitter \u27b6 Community channel.

"},{"location":"help/netgear_async_faqs/#netgear_async-is-throwing-modulenotfounderror-on-importing-why","title":"\"NetGear_Async is throwing ModuleNotFoundError on importing\", Why?","text":"

Answer: This error means, VidGear is installed WITHOUT asyncio package support on your machine. For this support, see Requirements \u27b6.

"},{"location":"help/netgear_async_faqs/#what-is-the-key-difference-between-netgear_async-and-netgear-apis","title":"What is the key difference between NetGear_Async and NetGear APIs?","text":"

Answer:

Key Difference: NetGear_Async is highly memory efficient, but has less features as compared to NetGear API which is marginally faster too.

"},{"location":"help/netgear_async_faqs/#can-i-use-multi-server-bi-directional-like-modes-in-netgear_async","title":"Can I use Multi-Server, Bi-Directional like modes in NetGear_Async?","text":"

Answer: No, NetGear_Async does NOT provide support for any NetGear's Exclusive modes yet.

"},{"location":"help/netgear_async_faqs/#how-to-use-netgear_async-with-custom-server-source-from-opencv","title":"How to use NetGear_Async with custom Server Source from OpenCV?","text":"

Answer: See this usage example \u27b6.

"},{"location":"help/netgear_async_faqs/#why-netgear_async-is-running-slow","title":"Why NetGear_Async is running slow?","text":"

Answer: Checkout tips suggested in this answer \u27b6

"},{"location":"help/netgear_ex/","title":"Bonus Examples","text":""},{"location":"help/netgear_ex/#netgear-examples","title":"NetGear Examples","text":""},{"location":"help/netgear_ex/#using-netgear-with-webgear","title":"Using NetGear with WebGear","text":"

The complete usage example is as follows:

New in v0.2.2

This example was added in v0.2.2.

"},{"location":"help/netgear_ex/#client-webgear-server","title":"Client + WebGear Server","text":"

Open a terminal on Client System where you want to display the input frames (and setup WebGear server) received from the Server and execute the following python code:

After running this code, Make sure to open Browser immediately otherwise NetGear will soon exit with RuntimeError. You can also try setting max_retries and request_timeout like attributes to a higher value to avoid this.

Make sure you use different port value for NetGear and WebGear API.

High CPU utilization may occur on Client's end. User discretion is advised.

Note down the local IP-address of this system (required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

# import necessary libs\nimport uvicorn, asyncio, cv2\nfrom vidgear.gears import NetGear\nfrom vidgear.gears.asyncio import WebGear\nfrom vidgear.gears.asyncio.helper import reducer\n\n# initialize WebGear app without any source\nweb = WebGear(logging=True)\n\n\n# activate jpeg encoding and specify other related parameters\noptions = {\n    \"jpeg_compression\": True,\n    \"jpeg_compression_quality\": 90,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# create your own custom frame producer\nasync def my_frame_producer():\n    # initialize global params\n    # Define NetGear Client at given IP address and define parameters\n    # !!! change following IP address '192.168.x.xxx' with yours !!!\n    client = NetGear(\n        receive_mode=True,\n        address=\"192.168.x.xxx\",\n        port=\"5454\",\n        protocol=\"tcp\",\n        pattern=1,\n        logging=True,\n        **options,\n    )\n\n    # loop over frames\n    while True:\n        # receive frames from network\n        frame = client.recv()\n\n        # if NoneType\n        if frame is None:\n            break\n\n        # do something with your OpenCV frame here\n\n        # reducer frames size if you want more performance otherwise comment this line\n        frame = await reducer(\n            frame, percentage=30, interpolation=cv2.INTER_AREA\n        )  # reduce frame by 30%\n\n        # handle JPEG encoding\n        encodedImage = cv2.imencode(\".jpg\", frame)[1].tobytes()\n        # yield frame in byte format\n        yield (b\"--frame\\r\\nContent-Type:image/jpeg\\r\\n\\r\\n\" + encodedImage + b\"\\r\\n\")\n        await asyncio.sleep(0)\n    # close stream\n    client.close()\n\n\n# add your custom frame producer to config with adequate IP address\nweb.config[\"generator\"] = my_frame_producer\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

On successfully running this code, the output stream will be displayed at address http://localhost:8000/ in your Client's Browser.

"},{"location":"help/netgear_ex/#server","title":"Server","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate jpeg encoding and specify other related parameters\noptions = {\n    \"jpeg_compression\": True,\n    \"jpeg_compression_quality\": 90,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = VideoGear(source=0).start()\n\n# Define NetGear server at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if None-type\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"help/netgear_ex/#using-netgear-with-webgear_rtc","title":"Using NetGear with WebGear_RTC","text":"

The complete usage example is as follows:

New in v0.2.4

This example was added in v0.2.4.

"},{"location":"help/netgear_ex/#client-webgear_rtc-server","title":"Client + WebGear_RTC Server","text":"

Open a terminal on Client System where you want to display the input frames (and setup WebGear_RTC server) received from the Server and execute the following python code:

After running this code, Make sure to open Browser immediately otherwise NetGear will soon exit with RuntimeError. You can also try setting max_retries and request_timeout like attributes to a higher value to avoid this.

Make sure you use different port value for NetGear and WebGear_RTC API.

High CPU utilization may occur on Client's end. User discretion is advised.

Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.

For VideoCapture APIs you also need to implement start() in addition to read() and stop() methods in your Custom Streaming Class as shown in following example, otherwise WebGear_RTC will fail to work!

# import necessary libs\nimport uvicorn, cv2\nfrom vidgear.gears import NetGear\nfrom vidgear.gears.helper import reducer\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# create your own custom streaming class\nclass Custom_Stream_Class:\n    \"\"\"\n    Custom Streaming using NetGear Receiver\n    \"\"\"\n\n    def __init__(\n        self,\n        address=None,\n        port=\"5454\",\n        protocol=\"tcp\",\n        pattern=1,\n        logging=True,\n        **options,\n    ):\n        # initialize global params\n        # Define NetGear Client at given IP address and define parameters\n        self.client = NetGear(\n            receive_mode=True,\n            address=address,\n            port=port,\n            protocol=protocol,\n            pattern=pattern,\n            logging=logging,\n            **options\n        )\n        self.running = False\n\n    def start(self):\n\n        # don't forget this function!!!\n        # This function is specific to VideoCapture APIs only\n\n        if not self.source is None:\n            self.source.start()\n\n    def read(self):\n\n        # don't forget this function!!!\n\n        # check if source was initialized or not\n        if self.source is None:\n            return None\n        # check if we're still running\n        if self.running:\n            # receive frames from network\n            frame = self.client.recv()\n            # check if frame is available\n            if not (frame is None):\n\n                # do something with your OpenCV frame here\n\n                # reducer frames size if you want more performance otherwise comment this line\n                frame = reducer(frame, percentage=20)  # reduce frame by 20%\n\n                # return our gray frame\n                return frame\n            else:\n                # signal we're not running now\n                self.running = False\n        # return None-type\n        return None\n\n    def stop(self):\n\n        # don't forget this function!!!\n\n        # flag that we're not running\n        self.running = False\n        # close stream\n        if not (self.client is None):\n            self.client.close()\n            self.client = None\n\n\n# activate jpeg encoding and specify NetGear related parameters\noptions = {\n    \"jpeg_compression\": True,\n    \"jpeg_compression_quality\": 90,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# assign your Custom Streaming Class with adequate NetGear parameters\n# to `custom_stream` attribute in options parameter of WebGear_RTC.\noptions = {\n    \"custom_stream\": Custom_Stream_Class(\n        address=\"192.168.x.xxx\",\n        port=\"5454\",\n        protocol=\"tcp\",\n        pattern=1,\n        logging=True,\n        **options\n    )\n}\n\n# initialize WebGear_RTC app without any source\nweb = WebGear_RTC(logging=True, **options)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

On successfully running this code, the output stream will be displayed at address http://localhost:8000/ in your Client's Browser.

"},{"location":"help/netgear_ex/#server_1","title":"Server","text":"

Now, Open the terminal on another Server System (with a webcam connected to it at index 0), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import NetGear\nimport cv2\n\n# activate jpeg encoding and specify other related parameters\noptions = {\n    \"jpeg_compression\": True,\n    \"jpeg_compression_quality\": 90,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": True,\n}\n\n# Open live video stream on webcam at first index(i.e. 0) device\nstream = VideoGear(source=0).start()\n\n# Define NetGear server at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"help/netgear_faqs/","title":"FAQs","text":""},{"location":"help/netgear_faqs/#netgear-faqs","title":"NetGear FAQs","text":""},{"location":"help/netgear_faqs/#what-is-netgear-api-and-what-does-it-do","title":"What is NetGear API and what does it do?","text":"

Answer: NetGear is exclusively designed to transfer video frames & data synchronously (Pair & Request/Reply) as well as asynchronously (Publish/Subscribe) between various interconnecting systems over the network in real-time. For more info. see NetGear doc \u27b6

"},{"location":"help/netgear_faqs/#how-to-get-started-with-netgear-api","title":"How to get started with NetGear API?","text":"

Answer: First, refer to the Switching from OpenCV guide, then go through NetGear documentation \u27b6. If you still have doubts, ask us on Gitter \u27b6 Community channel.

"},{"location":"help/netgear_faqs/#what-exclusive-modes-are-compatible-with-each-other-in-netgear-api","title":"What Exclusive Modes are compatible with each other in NetGear API?","text":"

Here's the compatibility chart for NetGear's Exclusive Modes:

Exclusive Modes Multi-Servers Multi-Clients Secure Bidirectional SSH Tunneling Multi-Servers - No (throws error) Yes Yes No (throws error) Multi-Clients No (throws error) - Yes Yes No (throws error) Secure Yes Yes - Yes Yes Bidirectional Yes Yes Yes - Yes SSH Tunneling No (throws error) No (throws error) Yes Yes -

"},{"location":"help/netgear_faqs/#why-netgear-is-running-slow","title":"Why NetGear is running slow?","text":"

Answer: Here are few tips to troubleshoot performance on your machine:

"},{"location":"help/netgear_faqs/#how-to-find-local-ip-address-on-different-os-platforms","title":"How to find local IP-address on different OS platforms?","text":"

Answer: For finding local IP-address of your machine:

On Linux OSOn Windows OSOn MAC OS

"},{"location":"help/netgear_faqs/#how-to-send-data-along-with-frames-in-multi-servers-and-multi-clients-modes","title":"How to send data along with frames in Multi-Servers and Multi-Clients Modes?","text":"

Answer: See Multi-Servers usage example \u27b6 and Multi-Clients usage example \u27b6

"},{"location":"help/netgear_faqs/#how-to-use-enable-encryption-and-authentication-in-netgear-api","title":"How to use enable Encryption and Authentication in NetGear API?","text":"

Answer: See its Secure Mode doc \u27b6.

"},{"location":"help/netgear_faqs/#how-to-send-custom-data-along-with-frames-bidirectionally-in-netgear-api","title":"How to send custom data along with frames bidirectionally in NetGear API?","text":"

Answer: See its Bidirectional Mode doc \u27b6.

"},{"location":"help/netgear_faqs/#how-to-access-netgear-api-outside-network-or-remotely","title":"How to access NetGear API outside network or remotely?","text":"

Answer: See its SSH Tunneling Mode doc \u27b6.

"},{"location":"help/netgear_faqs/#are-there-any-side-effect-of-sending-data-with-frames","title":"Are there any side-effect of sending data with frames?","text":"

Answer: Yes, it may lead to additional LATENCY depending upon the size/amount of the data being transferred. User discretion is advised.

"},{"location":"help/netgear_faqs/#why-netgear-api-not-working-correctly","title":"Why NetGear API not working correctly?","text":"

Answer: First, carefully go through NetGear doc \u27b6 that contains detailed information. Also, checkout PyZmq Docs \u27b6 for its various settings/parameters. If still it doesn't work for you, then let us know on Gitter \u27b6

"},{"location":"help/netgear_faqs/#how-to-solve-zmqerrorzmqerror-errors","title":"How to solve zmq.error.ZMQError errors?","text":"

Answer: For those used to the idea that a \"server\" provides their address to a client, then you should recheck your preconceptions! Please read the Netgear instructions carefully, and you will note that it is the client device that defines the IP that is provided to the server config. If you get this the wrong way (using the server IP on the client), then you will get a zmq.error.ZMQError error. Make sure it is the client's IP shared across the two systems.

"},{"location":"help/pigear_ex/","title":"Bonus Examples","text":""},{"location":"help/pigear_ex/#pigear-examples","title":"PiGear Examples","text":""},{"location":"help/pigear_ex/#changing-output-pixel-format-in-pigear-api-with-picamera2-backend","title":"Changing Output Pixel Format in PiGear API with Picamera2 Backend","text":"

With the Picamera2 backend, you can also define a custom format (format of output frame pixels) in PiGear API.

Handling output frames with a custom pixel format correctly

While defining custom format as an optional parameter, it is advised to also define the colorspace parameter in the PiGear API. This is required only under TWO conditions:

Otherwise, output frames might NOT be compatible with OpenCV functions, and you need to handle these frames manually!

Picamera2 library has an unconventional naming convention for its pixel formats.

Please note that, Picamera2 takes its pixel format naming from libcamera, which in turn takes them from certain underlying Linux components. The results are not always the most intuitive. For example, OpenCV users will typically want each pixel to be a (B, G, R) triple for which the RGB888 format should be chosen, and not BGR888. Similarly, OpenCV users wanting an alpha channel should select XRGB8888.

For more information, refer Picamera2 docs \u27b6

YUV420/YVU420YUYV

For reducing the size of frames in memory it is advised to use the YUV420 pixels format.

In this example we will be defining custom YUV420 (or YVU420) pixels format of output frame, and converting it back to BGR to be able to display with OpenCV.

You could also instead define colorspace=\"COLOR_YUV420p2RGB\" parameter in PiGear API for converting it back to BGR similarly.

# import required libraries\nfrom vidgear.gears import PiGear\nimport cv2\n\n# formulate `format` Picamera2 API \n# configurational parameters\noptions = {\n    \"format\": \"YUV420\" # or use `YVU420`\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    yuv420_frame = stream.read()\n\n    # check for frame if Nonetype\n    if yuv420_frame is None:\n        break\n\n    # {do something with the `YUV420` frame here}\n\n    # convert `YUV420` to `BGR`\n    bgr = cv2.cvtColor(yuv420_frame, cv2.COLOR_YUV420p2BGR)\n\n    # {do something with the `BGR` frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", bgr)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

YUYV is a one packed 4:2:2 YUV format that is popularly used by USB cameras.

Make sure YUYV pixel format is supported by your USB camera.

In this example we will be defining custom YUYV pixels format of output frame, and converting it back to BGR to be able to display with OpenCV.

You could also instead define colorspace=\"COLOR_YUV2BGR_YUYV\" parameter in PiGear API for converting it back to BGR similarly.

# import required libraries\nfrom vidgear.gears import PiGear\nimport cv2\n\n# formulate `format` Picamera2 API \n# configurational parameters\noptions = {\n    \"format\": \"YUYV\"\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    yuv420_frame = stream.read()\n\n    # check for frame if Nonetype\n    if yuv420_frame is None:\n        break\n\n    # {do something with the `YUV420` frame here}\n\n    # convert `YUV420` to `BGR`\n    bgr = cv2.cvtColor(yuv420_frame, cv2.COLOR_YUV2BGR_YUYV)\n\n    # {do something with the `BGR` frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", bgr)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"help/pigear_ex/#dynamically-adjusting-raspberry-pi-camera-parameters-at-runtime-in-pigear-api","title":"Dynamically Adjusting Raspberry Pi Camera Parameters at Runtime in PiGear API","text":"New Picamera2 backendLegacy Picamera backend

With the picamera2 backend, using stream global parameter in the PiGear API, you can change all camera controls (except output resolution and format) at runtime after the camera has started.

Accessing all available camera controls

A complete list of all the available camera controls can be found in the picamera2 docs \u27b6, and also by inspecting the camera_controls property of the Picamera2 object available with stream global parameter in PiGear API:

# import required libraries\nfrom vidgear.gears import PiGear\n\n# open any pi video stream\nstream = PiGear()\n\n#display all available camera controls\nprint(stream.stream.camera_controls)\n\n# safely close video stream\nstream.stop()\n

This returns a dictionary with the control names as keys, and each value being a tuple of (min, max, default) values for that control. The default value should be interpreted with some caution as in many cases libcamera's default value will be overwritten by the camera tuning as soon as the camera is started.

In this example, we will set the initial Camera Module's brightness value to -0.5 (dark), and will change it to 0.5 (bright) when the Z key is pressed at runtime:

Delay in setting runtime controls

There will be a delay of several frames before the controls take effect. This is because there is perhaps quite a large number of requests for camera frames already in flight, and for some controls (exposure time and analogue gain specifically), the camera may actually take several frames to apply the updates.

Using with construct for Guaranteed Camera Control Updates at Runtime

While directly modifying using set_controls method might seem convenient, it doesn't guarantee that all camera control settings are applied within the same frame at runtime. The with construct provides a structured approach to managing camera control updates in real-time. Here's how to use it:

# import required libraries\nfrom vidgear.gears import PiGear\n\n# formulate initial configurational parameters\noptions = \"controls\": {\"ExposureTime\": 5000, \"AnalogueGain\": 0.5}\n\n# open pi video stream with these parameters\nstream = PiGear(logging=True, **options).start() \n\n# Enter context manager and set runtime controls\n# Within this block, the controls are guaranteed to be applied atomically\nwith stream.stream.controls as controls:  \n    controls.ExposureTime = 10000  # Set new exposure time\n    controls.AnalogueGain = 1.0     # Set new analogue gain\n\n# ...rest of code goes here...\n\n# safely close video stream\nstream.stop()\n
# import required libraries\nfrom vidgear.gears import PiGear\nimport cv2\n\n# formulate initial configurational parameters\n# set brightness to -0.5 (dark)\noptions = {\"controls\": {\"Brightness\": -0.5}}\n\n# open pi video stream with these parameters\nstream = PiGear(logging=True, **options).start() \n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n    # check for 'z' key if pressed\n    if key == ord(\"z\"):\n        # change brightness to 0.5 (bright)\n        stream.stream.set_controls({\"Brightness\": 0.5})\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

You can also use the stream global parameter in PiGear with thepicamera backend to feed any picamera parameters at runtime after the camera has started.

PiGear API switches to the legacy picamerabackend if the picamera2 library is unavailable.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera optional parameter boolean attribute.

In this example we will set initial Camera Module's brightness value 80 (brighter), and will change it 30 (darker) when Z key is pressed at runtime:

# import required libraries\nfrom vidgear.gears import PiGear\nimport cv2\n\n# formulate initial configurational parameters \n# set brightness to `80` (bright)\noptions = {\"brightness\": 80} \n\n# open pi video stream with these parameters\nstream = PiGear(logging=True, **options).start() \n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n\n    # {do something with the frame here}\n\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n    # check for 'z' key if pressed\n    if key == ord(\"z\"):\n        # change brightness to `30` (darker)\n        stream.stream.brightness = 30\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n
"},{"location":"help/pigear_ex/#accessing-multiple-camera-through-its-index-in-pigear-api","title":"Accessing Multiple Camera through its Index in PiGear API","text":"

With the camera_num parameter in the PiGear API, you can easily select the camera index to be used as the source, allowing you to drive these multiple cameras simultaneously from within a single Python session.

The camera_num value can only be zero or greater, otherwise, PiGear API will throw ValueError for any negative value.

New Picamera2 backendLegacy Picamera backend

With the picamera2 backend, you can use the camera_num parameter in PiGear to select the camera index to be used as the source if you have multiple Raspberry Pi camera modules (such as CM4) and/or USB cameras connected simultaneously to your Raspberry Pi.

Accessing metadata about connected cameras.

You can call the global_camera_info() method of the Picamera2 object available with stream global parameter in PiGear API to find out what cameras are attached. This returns a list containing one dictionary for each camera, ordered according the camera number you would pass to the camera_num parameter in PiGear API to open that device. The dictionary contains:

You should always check this list to discover which camera is which as the order can change when the system boots or USB cameras are re-connected as follows:

# import required libraries\nfrom vidgear.gears import PiGear\n\n# open any pi video stream\nstream = PiGear()\n\n#display all available cameras metadata\nprint(stream.stream.global_camera_info())\n\n# safely close video stream\nstream.stop()\n

The PiGear API can accurately differentiate between USB and Raspberry Pi camera modules by utilizing the camera's metadata.

In this example, we will select the USB Camera connected at index 1 on the Raspberry Pi as the primary source for extracting frames in PiGear API:

Limited support for USB Cameras

This example also works with USB Cameras, However:

This example assumes a USB Camera is connected at index 1, and some other camera connected at index 0 on your Raspberry Pi.

# import required libraries\nfrom vidgear.gears import PiGear\nfrom libcamera import Transform\nimport cv2\n\n# formulate various Picamera2 API \n# configurational parameters for USB camera\noptions = {\n    \"sensor\": {\"output_size\": (480, 320)},  # will override `resolution`\n    \"format\": \"RGB888\" # BGR format for this example\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream at index `1` with defined parameters\nstream = PiGear(camera_num=1, resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

With the Picamera backend, you should not change the camera_num parameter unless you are using the Raspberry Pi 3/3+/4 Compute Module IO Boards or third party Arducam Camarray Multiple Camera Solutions, which supports attaching multiple camera modules to the same Raspberry Pi board using appropriate I/O connections.

You can use the camera_num parameter in PiGear with the picamera backend to select the camera index to be used as the source if you have multiple Raspberry Pi camera modules connected.

PiGear API switches to the legacy picamerabackend if the picamera2 library is unavailable.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera optional parameter boolean attribute.

In this example, we will select the Camera Module connected at index 1 on the Raspberry Pi as the primary source for extracting frames in PiGear API:

This example assumes a Camera Module is connected at index 1 on your Raspberry Pi.

# import required libraries\nfrom vidgear.gears import PiGear\nimport cv2\n\n# formulate various Picamera API \n# configurational parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream at index `1` with defined parameters\nstream = PiGear(camera_num=1, resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n

"},{"location":"help/pigear_faqs/","title":"FAQs","text":""},{"location":"help/pigear_faqs/#pigear-faqs","title":"PiGear FAQs","text":""},{"location":"help/pigear_faqs/#what-is-pigear-api-and-what-does-it-do","title":"What is PiGear API and what does it do?","text":"

Answer: PiGear is a specialized API similar to the CamGear API but optimized for Raspberry Pi Boards, offering comprehensive support for camera modules (e.g., OmniVision OV5647, Sony IMX219), along with limited compatibility for USB cameras. For more info. see PiGear doc \u27b6

"},{"location":"help/pigear_faqs/#im-only-familiar-with-opencv-how-to-get-started-with-pigear-api","title":"I'm only familiar with OpenCV, how to get started with PiGear API?","text":"

Answer: First, refer to the Switching from OpenCV guide, then go through PiGear documentation. If you still have doubts, ask us on Gitter \u27b6 Community channel.

"},{"location":"help/pigear_faqs/#why-my-camera-module-is-not-detected-by-pigear","title":"Why my camera module is not detected by PiGear?","text":"

Answer: Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using PiGear API. Also, recheck/change your Camera Module's ribbon-cable and Camera Module itself, if it damaged or got broken somehow.

"},{"location":"help/pigear_faqs/#how-to-select-camera-index-on-pi-compute-io-board-with-two-cameras-attached","title":"How to select camera index on Pi Compute IO board with two Cameras attached?","text":"

Answer: Refer this bonus example \u27b6

"},{"location":"help/pigear_faqs/#why-pigear-is-throwing-systemerror","title":"Why PiGear is throwing SystemError?","text":"

Answer: This means your Raspberry Pi CSI ribbon-cable is not connected properly to your Camera Module, or damaged, or even both.

"},{"location":"help/pigear_faqs/#how-to-assign-various-configurational-settings-for-camera-module-with-pigear","title":"How to assign various configurational settings for Camera Module with PiGear?","text":"

Answer: See this usage example \u27b6

"},{"location":"help/pigear_faqs/#video-output-is-too-dark-with-pigear-why","title":"\"Video output is too dark with PiGear\", Why?","text":"

Answer: The camera configuration settings might be incorrect. Check this usage example \u27b6 and try tinkering parameters like sensor_mode, shutter_speed, and exposure_mode. Additionally, if your framerate parameter value is too high, try lowering it.

"},{"location":"help/pigear_faqs/#how-to-dynamically-adjust-raspberry-pi-camera-parameters-at-runtime-with-pigear","title":"How to dynamically adjust Raspberry Pi Camera Parameters at runtime with PiGear?","text":"

Answer: See this bonus example \u27b6

"},{"location":"help/pigear_faqs/#is-it-possible-to-change-output-frames-pixel-format-in-pigear-api","title":"Is it possible to change output frames Pixel Format in PiGear API?","text":"

Answer: Yes it is possible with Picamera2 Backend. See this bonus example \u27b6

"},{"location":"help/screengear_ex/","title":"Bonus Examples","text":""},{"location":"help/screengear_ex/#screengear-examples","title":"ScreenGear Examples","text":""},{"location":"help/screengear_ex/#using-screengear-with-netgear-and-writegear","title":"Using ScreenGear with NetGear and WriteGear","text":"

The complete usage example is as follows:

New in v0.2.2

This example was added in v0.2.2.

"},{"location":"help/screengear_ex/#client-writegear","title":"Client + WriteGear","text":"

Open a terminal on Client System (where you want to save the input frames received from the Server) and execute the following python code:

Note down the IP-address of this system(required at Server's end) by executing the command: hostname -I and also replace it in the following code.

You can terminate client anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import NetGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# define various tweak flags\noptions = {\"flag\": 0, \"copy\": True, \"track\": False}\n\n# Define Netgear Client at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with yours !!!\nclient = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    receive_mode=True,\n    logging=True,\n    **options\n)\n\n# Define writer with default parameters and suitable output filename for e.g. `Output.mp4`\nwriter = WriteGear(output=\"Output.mp4\")\n\n# loop over\nwhile True:\n\n    # receive frames from network\n    frame = client.recv()\n\n    # check for received frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close client\nclient.close()\n\n# safely close writer\nwriter.close()\n
"},{"location":"help/screengear_ex/#server-screengear","title":"Server + ScreenGear","text":"

Now, Open the terminal on another Server System (with a montior/display attached to it), and execute the following python code:

Replace the IP address in the following code with Client's IP address you noted earlier.

You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!

# import required libraries\nfrom vidgear.gears import ScreenGear\nfrom vidgear.gears import NetGear\n\n# define dimensions of screen w.r.t to given monitor to be captured\noptions = {\"top\": 40, \"left\": 0, \"width\": 100, \"height\": 100}\n\n# open stream with defined parameters\nstream = ScreenGear(logging=True, **options).start()\n\n# define various netgear tweak flags\noptions = {\"flag\": 0, \"copy\": True, \"track\": False}\n\n# Define Netgear server at given IP address and define parameters \n# !!! change following IP address '192.168.x.xxx' with client's IP address !!!\nserver = NetGear(\n    address=\"192.168.x.xxx\",\n    port=\"5454\",\n    protocol=\"tcp\",\n    pattern=1,\n    logging=True,\n    **options\n)\n\n# loop over until KeyBoard Interrupted\nwhile True:\n\n    try:\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if Nonetype\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # send frame to server\n        server.send(frame)\n\n    except KeyboardInterrupt:\n        break\n\n# safely close video stream\nstream.stop()\n\n# safely close server\nserver.close()\n

"},{"location":"help/screengear_ex/#using-screengear-with-webgear_rtc","title":"Using ScreenGear with WebGear_RTC","text":"

The complete usage example is as follows:

New in v0.2.4

This example was added in v0.2.4.

Bare-MinimumAdvanced
# import necessary libs\nimport uvicorn, cv2\nfrom vidgear.gears import ScreenGear\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# assign your ScreenGear class with adequate parameters \n# to `custom_stream` attribute in options parameter\noptions = {\"custom_stream\": ScreenGear(logging=True)}\n\n# initialize WebGear_RTC app without any source\nweb = WebGear_RTC(logging=True, **options)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

For VideoCapture APIs you also need to implement start() in addition to read() and stop() methods in your Custom Streaming Class as shown in following example, otherwise WebGear_RTC will fail to work!

# import necessary libs\nimport uvicorn, cv2\nfrom vidgear.gears import ScreenGear\nfrom vidgear.gears.helper import reducer\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# create your own custom streaming class\nclass Custom_Stream_Class:\n    \"\"\"\n    Custom Streaming using ScreenGear\n    \"\"\"\n\n    def __init__(self, backend=\"mss\", logging=False):\n\n        # !!! define your own video source here !!!\n        self.source = ScreenGear(backend=backend, logging=logging)\n\n        # define running flag\n        self.running = True\n\n    def start(self):\n\n        # don't forget this function!!!\n        # This function is specific to VideoCapture APIs only\n\n        if not self.source is None:\n            self.source.start()\n\n    def read(self):\n\n        # don't forget this function!!!\n\n        # check if source was initialized or not\n        if self.source is None:\n            return None\n        # check if we're still running\n        if self.running:\n            # read frame from provided source\n            frame = self.source.read()\n            # check if frame is available\n            if not(frame is None):\n\n                # do something with your OpenCV frame here\n\n                # reducer frames size if you want more performance otherwise comment this line\n                frame = reducer(frame, percentage=20)  # reduce frame by 20%\n\n                # return our gray frame\n                return frame\n            else:\n                # signal we're not running now\n                self.running = False\n        # return None-type\n        return None\n\n    def stop(self):\n\n        # don't forget this function!!!\n\n        # flag that we're not running\n        self.running = False\n        # close stream\n        if not self.source is None:\n            self.source.stop()\n\n\n# assign your Custom Streaming Class with adequate ScreenGear parameters\n# to `custom_stream` attribute in options parameter\noptions = {\"custom_stream\": Custom_Stream_Class(backend=\"pil\", logging=True)}\n\n# initialize WebGear_RTC app without any source\nweb = WebGear_RTC(logging=True, **options)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

"},{"location":"help/screengear_faqs/","title":"FAQs","text":""},{"location":"help/screengear_faqs/#screengear-faqs","title":"ScreenGear FAQs","text":""},{"location":"help/screengear_faqs/#what-is-screengear-api-and-what-does-it-do","title":"What is ScreenGear API and what does it do?","text":"

Answer: ScreenGear is designed exclusively for targeting rapid Screencasting Capabilities, which means it can grab frames from your monitor in real-time, either by defining an area on the computer screen or full-screen, at the expense of inconsiderable latency. ScreenGear also seamlessly support frame capturing from multiple monitors as well as supports multiple backends. For more info. see ScreenGear doc \u27b6

"},{"location":"help/screengear_faqs/#im-only-familiar-with-opencv-how-to-get-started-with-screengear-api","title":"I'm only familiar with OpenCV, how to get started with ScreenGear API?","text":"

Answer: First, refer to the Switching from OpenCV guide, then go through ScreenGear documentation. If you still have doubts, ask us on Gitter \u27b6 Community channel.

"},{"location":"help/screengear_faqs/#screengear-is-slow","title":"ScreenGear is Slow?","text":"

Answer: This maybe due to selected backend for ScreenGear API is not compatible with your machine. See this usage example to change backend \u27b6. Try different backends, and select which works the best for your machine.

"},{"location":"help/screengear_faqs/#how-to-define-area-on-screen-to-record-with-screengear","title":"How to define area on screen to record with ScreenGear?","text":"

Answer: See this usage example \u27b6

"},{"location":"help/screengear_faqs/#how-to-record-video-from-all-connected-screens","title":"How to record video from all connected screens?","text":"

Answer: With mss backend, see ScreenGear's monitor parameter that sets the index of the monitor to grab a frame from. If its value is -1, it will record from all monitors. More information can be found here \u27b6

"},{"location":"help/screengear_faqs/#im-getting-attributeerror-dxcamera-object-has-no-attribute-is_capturing-error","title":"I'm getting \"AttributeError: 'DXCamera' object has no attribute 'is_capturing'\" Error?","text":"

Answer: This is a well-known error in backend dxcam library which occurs when you've multiple GPUs on your Windows machine. To workaround this, you need select Internal GPU in settings as follows:

On Windows 11On Windows 10

In Settings, go to System > Display > Graphics and add your Python.exe as \"Desktop App\", then select \"Power saving\" as follows:

And finally press Save button.

In Settings, go to Graphics Settings and add your Python.exe as \"Desktop App\", then select \"Power saving\" as follows:

And finally press Save button.

"},{"location":"help/stabilizer_ex/","title":"Bonus Examples","text":""},{"location":"help/stabilizer_ex/#stabilizer-class-examples","title":"Stabilizer Class Examples","text":""},{"location":"help/stabilizer_ex/#saving-stabilizer-class-output-with-live-audio-input","title":"Saving Stabilizer Class output with Live Audio Input","text":"

In this example code, we will merging the audio from a Audio Device (for e.g. Webcam inbuilt mic input) with Stabilized frames incoming from the Stabilizer Class (which is also using same Webcam video input through OpenCV), and save the final output as a compressed video file, all in real time:

New in v0.2.2

This example was added in v0.2.2.

Example Assumptions

Identifying and Specifying sound card on different OS platforms Windows Linux MacOS

Windows OS users can use the dshow (DirectShow) to list audio input device which is the preferred option for Windows users. You can refer following steps to identify and specify your sound card:

If audio still doesn't work then checkout this troubleshooting guide \u27b6 or reach us out on Gitter \u27b6 Community channel

Linux OS users can use the alsa to list input device to capture live audio input such as from a webcam. You can refer following steps to identify and specify your sound card:

If audio still doesn't work then reach us out on Gitter \u27b6 Community channel

MAC OS users can use the avfoundation to list input devices for grabbing audio from integrated iSight cameras as well as cameras connected via USB or FireWire. You can refer following steps to identify and specify your sound card on MacOS/OSX machines:

If audio still doesn't work then reach us out on Gitter \u27b6 Community channel

Make sure this -i audio-source it compatible with provided video-source, otherwise you could encounter multiple errors or no output at all.

You MUST use -input_framerate attribute to set exact value of input framerate when using external audio in Real-time Frames mode, otherwise audio delay will occur in output streams.

# import required libraries\nfrom vidgear.gears import WriteGear\nfrom vidgear.gears.stabilizer import Stabilizer\nimport cv2\n\n# Open suitable video stream, such as webcam on first index(i.e. 0)\nstream = cv2.VideoCapture(0)\n\n# initiate stabilizer object with defined parameters\nstab = Stabilizer(smoothing_radius=30, crop_n_zoom=True, border_size=5, logging=True)\n\n# change with your webcam soundcard, plus add additional required FFmpeg parameters for your writer\noutput_params = {\n    \"-input_framerate\": stream.get(cv2.CAP_PROP_FPS),\n    \"-thread_queue_size\": \"512\",\n    \"-ac\": \"2\",\n    \"-ar\": \"48000\",\n    \"-f\": \"alsa\", # (1)\n    \"-i\": \"hw:1\",\n}\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4\nwriter = WriteGear(output=\"Output.mp4\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n        break\n\n    # send current frame to stabilizer for processing\n    stabilized_frame = stab.stabilize(frame)\n\n    # wait for stabilizer which still be initializing\n    if stabilized_frame is None:\n        continue\n\n    # {do something with the stabilized frame here}\n\n    # write stabilized frame to writer\n    writer.write(stabilized_frame)\n\n\n# clear stabilizer resources\nstab.clean()\n\n# safely close video stream\nstream.release()\n\n# safely close writer\nwriter.close()\n
  1. Always keep this line above -i parameter!

"},{"location":"help/stabilizer_ex/#saving-stabilizer-class-output-with-file-audio-input","title":"Saving Stabilizer Class output with File Audio Input","text":"

In this example code, we will be directly merging the audio from a Video-File (to be stabilized) with its processed stabilized frames into a compressed video output in real time:

New in v0.2.4

This example was added in v0.2.4.

Make sure this input video-file (to be stabilized) contains valid audio source, otherwise you could encounter multiple errors or no output at all.

You MUST use -input_framerate attribute to set exact value of input framerate when using external audio in Real-time Frames mode, otherwise audio delay will occur in output streams.

Use -disable_force_termination flag when video duration is too short(<60sec), otherwise WriteGear will not produce any valid output.

# import required libraries\nfrom vidgear.gears import WriteGear\nfrom vidgear.gears.stabilizer import Stabilizer\nimport cv2\n\n# Give suitable video file path to be stabilized\nunstabilized_videofile = \"test.mp4\"\n\n# open stream on given path\nstream = cv2.VideoCapture(unstabilized_videofile)\n\n# initiate stabilizer object with defined parameters\nstab = Stabilizer(smoothing_radius=30, crop_n_zoom=True, border_size=5, logging=True)\n\n# define required FFmpeg optimizing parameters for your writer\noutput_params = {\n    \"-i\": unstabilized_videofile,\n    \"-c:a\": \"aac\",\n    \"-input_framerate\": stream.get(cv2.CAP_PROP_FPS),\n    \"-clones\": [\"-shortest\"],\n    # !!! Uncomment following line if video duration is too short(<60sec). !!!\n    #\"-disable_force_termination\": True,\n}\n\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4\nwriter = WriteGear(output=\"Output.mp4\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    (grabbed, frame) = stream.read()\n\n    # check for frame if not grabbed\n    if not grabbed:\n        break\n\n    # send current frame to stabilizer for processing\n    stabilized_frame = stab.stabilize(frame)\n\n    # wait for stabilizer which still be initializing\n    if stabilized_frame is None:\n        continue\n\n    # {do something with the stabilized frame here}\n\n    # write stabilized frame to writer\n    writer.write(stabilized_frame)\n\n\n# clear stabilizer resources\nstab.clean()\n\n# safely close video stream\nstream.release()\n\n# safely close writer\nwriter.close()\n

"},{"location":"help/stabilizer_faqs/","title":"FAQs","text":""},{"location":"help/stabilizer_faqs/#stabilizer-class-faqs","title":"Stabilizer Class FAQs","text":""},{"location":"help/stabilizer_faqs/#what-is-stabilizer-class-and-what-does-it-do","title":"What is Stabilizer Class and what does it do?","text":"

Answer: Stabilizer Class is an auxiliary class that enables Video Stabilization for vidgear with minimalistic latency, and at the expense of little to no additional computational requirements. For more info. see Stabilizer Class doc \u27b6

"},{"location":"help/stabilizer_faqs/#how-much-latency-you-would-typically-expect-with-stabilizer-class","title":"How much latency you would typically expect with Stabilizer Class?","text":"

Answer: The stabilizer will be Slower for High-Quality videos-frames. Try reducing frames size (Use reducer() method) before feeding them for reducing latency. Also, see smoothing_radius parameter of Stabilizer class that handles the quality of stabilization at the expense of latency and sudden panning. The larger its value, the less will be panning, more will be latency, and vice-versa.

"},{"location":"help/stabilizer_faqs/#how-to-remove-black-borders-in-output-video-after-stabilizing-it","title":"How to remove black borders in output video after stabilizing it?","text":"

Answer: See crop_n_zoom parameter of Stabilizer class, that enables the feature, where it crops and zooms frames(to original size) to reduce the black borders from stabilization being too noticeable (similar to the feature available in Adobe AfterEffects). It works in conjunction with the border_size parameter, i.e. when this parameter is enabled border_size will be used for cropping border instead of making them. Its default value is False.

"},{"location":"help/stabilizer_faqs/#can-i-use-stabilizer-directly-with-opencv","title":"Can I use Stabilizer directly with OpenCV?","text":"

Answer: Yes, see this usage example \u27b6.

"},{"location":"help/stabilizer_faqs/#why-stabilization-is-not-working-properly-for-my-video","title":"Why stabilization is not working properly for my video?","text":"

Answer: The Stabilizer may not perform well against High-frequency jitter in video. But,you can check if increasing smoothing_radius parameter value helps but it will add latency too.

"},{"location":"help/streamgear_ex/","title":"Bonus Examples","text":""},{"location":"help/streamgear_ex/#streamgear-examples","title":"StreamGear Examples","text":""},{"location":"help/streamgear_ex/#streamgear-live-streaming-usage-with-pigear","title":"StreamGear Live-Streaming Usage with PiGear","text":"

In this example, we will be Live-Streaming video-frames from Raspberry Pi (with Camera Module connected) using PiGear API and StreamGear API's Real-time Frames Mode:

Use -window_size & -extra_window_size FFmpeg parameters for controlling number of frames to be kept in Chunks. Less these value, less will be latency.

After every few chunks (equal to the sum of -window_size & -extra_window_size values), all chunks will be overwritten in Live-Streaming. Thereby, since newer chunks in manifest/playlist will contain NO information of any older ones, and therefore resultant DASH/HLS stream will play only the most recent frames.

In this mode, StreamGear DOES NOT automatically maps video-source audio to generated streams. You need to manually assign separate audio-source through -audio attribute of stream_params dictionary parameter.

PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using the PiGear API, otherwise nothing will work.

DASHHLS New Picamera2 backendLegacy Picamera backend
# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import StreamGear\nfrom libcamera import Transform\nimport cv2\n\n# formulate various Picamera2 API \n# configurational parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# enable livestreaming and retrieve framerate from CamGear Stream and\n# pass it as `-input_framerate` parameter for controlled framerate\nstream_params = {\"-input_framerate\": stream.framerate, \"-livestream\": True}\n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n
Under the hood, PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, PiGear API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# formulate various Picamera API \n# configurational parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# enable livestreaming and retrieve framerate from CamGear Stream and\n# pass it as `-input_framerate` parameter for controlled framerate\nstream_params = {\"-input_framerate\": stream.framerate, \"-livestream\": True}\n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"dash_out.mpd\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n
New Picamera2 backendLegacy Picamera backend
# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import StreamGear\nfrom libcamera import Transform\nimport cv2\n\n# formulate various Picamera2 API \n# configurational parameters\noptions = {\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# enable livestreaming and retrieve framerate from CamGear Stream and\n# pass it as `-input_framerate` parameter for controlled framerate\nstream_params = {\"-input_framerate\": stream.framerate, \"-livestream\": True}\n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n
Under the hood, PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, PiGear API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import required libraries\nfrom vidgear.gears import PiGear\nfrom vidgear.gears import StreamGear\nimport cv2\n\n# formulate various Picamera API \n# configurational parameters\noptions = {\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# open pi video stream with defined parameters\nstream = PiGear(resolution=(640, 480), framerate=60, logging=True, **options).start()\n\n# enable livestreaming and retrieve framerate from CamGear Stream and\n# pass it as `-input_framerate` parameter for controlled framerate\nstream_params = {\"-input_framerate\": stream.framerate, \"-livestream\": True}\n\n# describe a suitable manifest-file location/name\nstreamer = StreamGear(output=\"hls_out.m3u8\", format = \"hls\", **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # send frame to streamer\n    streamer.stream(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close streamer\nstreamer.close()\n

"},{"location":"help/streamgear_faqs/","title":"FAQs","text":""},{"location":"help/streamgear_faqs/#streamgear-faqs","title":"StreamGear FAQs","text":""},{"location":"help/streamgear_faqs/#what-is-streamgear-api-and-what-does-it-do","title":"What is StreamGear API and what does it do?","text":"

Answer: StreamGear automates transcoding workflow for generating Ultra-Low Latency, High-Quality, Dynamic & Adaptive Streaming Formats (such as MPEG-DASH) in just few lines of python code. For more info. see StreamGear doc \u27b6

"},{"location":"help/streamgear_faqs/#how-to-get-started-with-streamgear-api","title":"How to get started with StreamGear API?","text":"

Answer: First, refer to the Switching from OpenCV guide, then go through StreamGear documentation. If you still have doubts, ask us on Gitter \u27b6 Community channel.

"},{"location":"help/streamgear_faqs/#what-is-mpd-file-created-with-streamgear","title":"What is .mpd file created with StreamGear?","text":"

Answer: SteamGear also creates a Manifest file (such as MPD in-case of DASH) besides segments that describe these segment information (timing, URL, media characteristics like video resolution and bit rates) and is provided to the client before the streaming session.

"},{"location":"help/streamgear_faqs/#how-to-play-streaming-assets-created-with-streamgear-api","title":"How to play Streaming Assets created with StreamGear API?","text":"

Answer: You can easily feed Manifest file(.mpd) to DASH Supported Players Input but sure encoded chunks are present along with it. See this list of recommended players \u27b6

"},{"location":"help/streamgear_faqs/#what-adaptive-streaming-formats-are-supported-yet","title":"What Adaptive Streaming Formats are supported yet?","text":"

Answer: SteamGear currently only supports MPEG-DASH (Dynamic Adaptive Streaming over HTTP, ISO/IEC 23009-1) , but other adaptive streaming technologies such as Apple HLS, Microsoft Smooth Streaming, will be added soon.

"},{"location":"help/streamgear_faqs/#is-drm-encryption-supported-in-streamgear-api","title":"Is DRM Encryption supported in StreamGear API?","text":"

Answer: No, DRM Encryption is NOT supported yet.

"},{"location":"help/streamgear_faqs/#how-to-create-additional-streams-in-streamgear-api","title":"How to create additional streams in StreamGear API?","text":"

Answer: See this example \u27b6

"},{"location":"help/streamgear_faqs/#how-to-use-streamgear-api-with-opencv","title":"How to use StreamGear API with OpenCV?","text":"

Answer: See this example \u27b6

"},{"location":"help/streamgear_faqs/#how-to-use-streamgear-api-with-real-time-frames","title":"How to use StreamGear API with real-time frames?","text":"

Answer: See Real-time Frames Mode \u27b6

"},{"location":"help/streamgear_faqs/#how-to-use-hardwaregpu-encoder-for-transcoding-in-streamgear-api","title":"How to use Hardware/GPU encoder for transcoding in StreamGear API?","text":"

Answer: See this example \u27b6

"},{"location":"help/videogear_ex/","title":"Bonus Examples","text":""},{"location":"help/videogear_ex/#videogear-examples","title":"VideoGear Examples","text":""},{"location":"help/videogear_ex/#using-videogear-with-rosrobot-operating-system","title":"Using VideoGear with ROS(Robot Operating System)","text":"

We will be using cv_bridge to convert OpenCV frames to ROS image messages and vice-versa.

In this example, we'll create a node that convert OpenCV frames into ROS image messages, and then publishes them over ROS.

New in v0.2.2

This example was added in v0.2.2.

This example is vidgear implementation of this wiki example.

# import roslib\nimport roslib\n\nroslib.load_manifest(\"my_package\")\n\n# import other required libraries\nimport sys\nimport rospy\nimport cv2\nfrom std_msgs.msg import String\nfrom sensor_msgs.msg import Image\nfrom cv_bridge import CvBridge, CvBridgeError\nfrom vidgear.gears import VideoGear\n\n# custom publisher class\nclass image_publisher:\n    def __init__(self, source=0, logging=False):\n        # create CV bridge\n        self.bridge = CvBridge()\n        # define publisher topic\n        self.image_pub = rospy.Publisher(\"image_topic_pub\", Image)\n        # open stream with given parameters\n        self.stream = VideoGear(source=source, logging=logging).start()\n        # define publisher topic\n        rospy.Subscriber(\"image_topic_sub\", Image, self.callback)\n\n    def callback(self, data):\n\n        # {do something with received ROS node data here}\n\n        # read frames\n        frame = self.stream.read()\n        # check for frame if None-type\n        if not (frame is None):\n\n            # {do something with the frame here}\n\n            # publish our frame\n            try:\n                self.image_pub.publish(self.bridge.cv2_to_imgmsg(frame, \"bgr8\"))\n            except CvBridgeError as e:\n                # catch any errors\n                print(e)\n\n    def close(self):\n        # stop stream\n        self.stream.stop()\n\n\ndef main(args):\n    # !!! define your own video source here !!!\n    # Open any video stream such as live webcam\n    # video stream on first index(i.e. 0) device\n\n    # define publisher\n    ic = image_publisher(source=0, logging=True)\n    # initiate ROS node on publisher\n    rospy.init_node(\"image_publisher\", anonymous=True)\n    try:\n        # run node\n        rospy.spin()\n    except KeyboardInterrupt:\n        print(\"Shutting down\")\n    finally:\n        # close publisher\n        ic.close()\n\n\nif __name__ == \"__main__\":\n    main(sys.argv)\n

"},{"location":"help/videogear_ex/#using-videogear-for-capturing-rtsprtmp-urls","title":"Using VideoGear for capturing RTSP/RTMP URLs","text":"

Here's a high-level wrapper code around VideoGear API to enable auto-reconnection during capturing, plus stabilization is enabled (stabilize=True) in order to stabilize captured frames on-the-go:

New in v0.2.2

This example was added in v0.2.2.

Enforcing UDP stream

You can easily enforce UDP for RTSP streams inplace of default TCP, by putting following lines of code on the top of your existing code:

# import required libraries\nimport os\n\n# enforce UDP\nos.environ[\"OPENCV_FFMPEG_CAPTURE_OPTIONS\"] = \"rtsp_transport;udp\"\n

Finally, use backend parameter value as backend=cv2.CAP_FFMPEG in VideoGear.

from vidgear.gears import VideoGear\nimport cv2\nimport datetime\nimport time\n\n\nclass Reconnecting_VideoGear:\n    def __init__(self, cam_address, stabilize=False, reset_attempts=50, reset_delay=5):\n        self.cam_address = cam_address\n        self.stabilize = stabilize\n        self.reset_attempts = reset_attempts\n        self.reset_delay = reset_delay\n        self.source = VideoGear(\n            source=self.cam_address, stabilize=self.stabilize\n        ).start()\n        self.running = True\n\n    def read(self):\n        if self.source is None:\n            return None\n        if self.running and self.reset_attempts > 0:\n            frame = self.source.read()\n            if frame is None:\n                self.source.stop()\n                self.reset_attempts -= 1\n                print(\n                    \"Re-connection Attempt-{} occured at time:{}\".format(\n                        str(self.reset_attempts),\n                        datetime.datetime.now().strftime(\"%m-%d-%Y %I:%M:%S%p\"),\n                    )\n                )\n                time.sleep(self.reset_delay)\n                self.source = VideoGear(\n                    source=self.cam_address, stabilize=self.stabilize\n                ).start()\n                # return previous frame\n                return self.frame\n            else:\n                self.frame = frame\n                return frame\n        else:\n            return None\n\n    def stop(self):\n        self.running = False\n        self.reset_attempts = 0\n        self.frame = None\n        if not self.source is None:\n            self.source.stop()\n\n\nif __name__ == \"__main__\":\n    # open any valid video stream\n    stream = Reconnecting_VideoGear(\n        cam_address=\"rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov\",\n        reset_attempts=20,\n        reset_delay=5,\n    )\n\n    # loop over\n    while True:\n\n        # read frames from stream\n        frame = stream.read()\n\n        # check for frame if None-type\n        if frame is None:\n            break\n\n        # {do something with the frame here}\n\n        # Show output window\n        cv2.imshow(\"Output\", frame)\n\n        # check for 'q' key if pressed\n        key = cv2.waitKey(1) & 0xFF\n        if key == ord(\"q\"):\n            break\n\n    # close output window\n    cv2.destroyAllWindows()\n\n    # safely close video stream\n    stream.stop()\n

"},{"location":"help/videogear_ex/#using-videogear-for-real-time-stabilization-with-audio-encoding","title":"Using VideoGear for Real-time Stabilization with Audio Encoding","text":"

In this example code, we will be directly merging the audio from a Video-File (to be stabilized) with its processed stabilized frames into a compressed video output in real time:

New in v0.2.4

This example was added in v0.2.4.

Make sure this input video-file (to be stabilized) contains valid audio source, otherwise you could encounter multiple errors or no output at all.

You MUST use -input_framerate attribute to set exact value of input framerate when using external audio in Real-time Frames mode, otherwise audio delay will occur in output streams.

Use -disable_force_termination flag when video duration is too short(<60sec), otherwise WriteGear will not produce any valid output.

# import required libraries\nfrom vidgear.gears import WriteGear\nfrom vidgear.gears import VideoGear\nimport cv2\n\n# Give suitable video file path to be stabilized\nunstabilized_videofile = \"test.mp4\"\n\n# open any valid video path with stabilization enabled(`stabilize = True`)\nstream_stab = VideoGear(source=unstabilized_videofile, stabilize=True, logging=True).start()\n\n# define required FFmpeg optimizing parameters for your writer\noutput_params = {\n    \"-i\": unstabilized_videofile,\n    \"-c:a\": \"aac\",\n    \"-input_framerate\": stream_stab.framerate,\n    \"-clones\": [\"-shortest\"],\n    # !!! Uncomment following line if video duration is too short(<60sec). !!!\n    #\"-disable_force_termination\": True,\n}\n\n# Define writer with defined parameters and suitable output filename for e.g. `Output.mp4\nwriter = WriteGear(output=\"Output.mp4\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame_stab = stream_stab.read()\n\n    # check for frame if not grabbed\n    if frame_stab is None:\n        break\n\n    # {do something with the stabilized frame here}\n\n    # write stabilized frame to writer\n    writer.write(frame_stab)\n\n# safely close streams\nstream_stab.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"help/videogear_faqs/","title":"FAQs","text":""},{"location":"help/videogear_faqs/#videogear-faqs","title":"VideoGear FAQs","text":""},{"location":"help/videogear_faqs/#what-is-videogear-api-and-what-does-it-do","title":"What is VideoGear API and what does it do?","text":"

Answer: VideoGear provides a special internal wrapper around VidGear's exclusive Video Stabilizer class. It also act as a Common API, that provided an internal access to both CamGear and PiGear APIs and their parameters, with a special enablePiCamera boolean flag. For more info. see VideoGear doc \u27b6

"},{"location":"help/videogear_faqs/#whats-the-need-of-videogear-api","title":"What's the need of VideoGear API?","text":"

Answer: VideoGear is basically ideal when you need to switch to different video sources without changing your code much. Also, it enables easy stabilization for various video-streams (real-time or not) with minimum efforts and using way fewer lines of code. It also serve as backend for other powerful APIs, such WebGear and NetGear_Async.

"},{"location":"help/videogear_faqs/#which-apis-are-accessible-with-videogear-api","title":"Which APIs are accessible with VideoGear API?","text":"

Answer: VideoGear provided an internal access to both CamGear and PiGear APIs and their parameters, also it contains wrapper around Video Stabilizer class.

"},{"location":"help/videogear_faqs/#can-we-access-writegear-api-or-netgear-api-too-with-videogear","title":"Can we access WriteGear API or NetGear API too with VideoGear?","text":"

Answer: No, only selected VideoCapture APIs (anwsered above) are accessible.

"},{"location":"help/videogear_faqs/#does-using-videogear-instead-of-camgear-api-directly-affects-performance","title":"Does using VideoGear instead of CamGear API directly, affects performance?","text":"

Answer: No, there's no difference, as VideoGear just a high-level wrapper around CamGear API and without any modifications in-between.

"},{"location":"help/webgear_ex/","title":"Bonus Examples","text":""},{"location":"help/webgear_ex/#webgear-examples","title":"WebGear Examples","text":""},{"location":"help/webgear_ex/#using-webgear-with-raspberrypi-camera-module","title":"Using WebGear with RaspberryPi Camera Module","text":"

Because of WebGear API's flexible internal wapper around VideoGear, it can easily access any parameter of CamGear and PiGear videocapture APIs.

Following usage examples are just an idea of what can be done with WebGear API, you can try various VideoGear, CamGear and PiGear parameters directly in WebGear API in the similar manner.

Here's a bare-minimum example of using WebGear API with the Raspberry Pi camera module while tweaking its various properties in few lines of python code:

Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this backend, otherwise nothing will work.

New Picamera2 backendLegacy Picamera backend
# import libs\nimport uvicorn\nfrom libcamera import Transform\nfrom vidgear.gears.asyncio import WebGear\n\n# various WebGear_RTC performance \n# and Picamera2 API tweaks\noptions = {\n    \"frame_size_reduction\": 40,\n    \"jpeg_compression_quality\": 80,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": False,\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# initialize WebGear app\nweb = WebGear(\n    enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options\n)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import libs\nimport uvicorn\nfrom vidgear.gears.asyncio import WebGear\n\n# various webgear performance and Picamera API tweaks\noptions = {\n    \"frame_size_reduction\": 40,\n    \"jpeg_compression_quality\": 80,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": False,\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# initialize WebGear app\nweb = WebGear(\n    enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options\n)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

"},{"location":"help/webgear_ex/#using-webgear-with-real-time-video-stabilization-enabled","title":"Using WebGear with real-time Video Stabilization enabled","text":"

Here's an example of using WebGear API with real-time Video Stabilization enabled:

# import libs\nimport uvicorn\nfrom vidgear.gears.asyncio import WebGear\n\n# various webgear performance tweaks\noptions = {\n    \"frame_size_reduction\": 40,\n    \"jpeg_compression_quality\": 80,\n    \"jpeg_compression_fastdct\": True,\n    \"jpeg_compression_fastupsample\": False,\n}\n\n# initialize WebGear app  with a raw source and enable video stabilization(`stabilize=True`)\nweb = WebGear(source=\"foo.mp4\", stabilize=True, logging=True, **options)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

"},{"location":"help/webgear_ex/#display-two-sources-simultaneously-in-webgear","title":"Display Two Sources Simultaneously in WebGear","text":"

In this example, we'll be displaying two video feeds side-by-side simultaneously on browser using WebGear API by defining two separate frame generators:

New in v0.2.2

This example was added in v0.2.2.

Step-1 (Trigger Auto-Generation Process): Firstly, run this bare-minimum code to trigger the Auto-generation process, this will create .vidgear directory at current location (directory where you'll run this code):

# import required libraries\nimport uvicorn\nfrom vidgear.gears.asyncio import WebGear\n\n# provide current directory to save data files\noptions = {\"custom_data_location\": \"./\"}\n\n# initialize WebGear app\nweb = WebGear(source=0, logging=True, **options)\n\n# close app safely\nweb.shutdown()\n

Step-2 (Replace HTML file): Now, go inside .vidgear webgear templates directory at current location of your machine, and there replace content of index.html file with following:

{% extends \"base.html\" %}\n{% block content %}\n  <h1 class=\"glow\">WebGear Video Feed</h1>\n   <div class=\"rows\">\n     <img src=\"/video\" alt=\"Feed\"/>\n     <img src=\"/video2\" alt=\"Feed\"/>\n   </div>\n{% endblock %}\n

Step-3 (Build your own Frame Producers): Now, create a python script code with OpenCV source, as follows:

# import necessary libs\nimport uvicorn, asyncio, cv2\nfrom vidgear.gears.asyncio import WebGear\nfrom vidgear.gears.asyncio.helper import reducer\nfrom starlette.responses import StreamingResponse\nfrom starlette.routing import Route\n\n# provide current directory to load data files\noptions = {\"custom_data_location\": \"./\"}\n\n# initialize WebGear app without any source\nweb = WebGear(logging=True, **options)\n\n# create your own custom frame producer\nasync def my_frame_producer1():\n\n   # !!! define your first video source here !!!\n   # Open any video stream such as \"foo1.mp4\"\n   stream = cv2.VideoCapture(\"foo1.mp4\")\n   # loop over frames\n   while True:\n       # read frame from provided source\n       (grabbed, frame) = stream.read()\n       # break if NoneType\n       if not grabbed:\n           break\n\n       # do something with your OpenCV frame here\n\n       # reducer frames size if you want more performance otherwise comment this line\n       frame = await reducer(frame, percentage=30)  # reduce frame by 30%\n       # handle JPEG encoding\n       encodedImage = cv2.imencode(\".jpg\", frame)[1].tobytes()\n       # yield frame in byte format\n       yield (b\"--frame\\r\\nContent-Type:video/jpeg2000\\r\\n\\r\\n\" + encodedImage + b\"\\r\\n\")\n       await asyncio.sleep(0.00001)\n   # close stream\n   stream.release()\n\n\n# create your own custom frame producer\nasync def my_frame_producer2():\n\n   # !!! define your second video source here !!!\n   # Open any video stream such as \"foo2.mp4\"\n   stream = cv2.VideoCapture(\"foo2.mp4\")\n   # loop over frames\n   while True:\n       # read frame from provided source\n       (grabbed, frame) = stream.read()\n       # break if NoneType\n       if not grabbed:\n           break\n\n       # do something with your OpenCV frame here\n\n       # reducer frames size if you want more performance otherwise comment this line\n       frame = await reducer(frame, percentage=30)  # reduce frame by 30%\n       # handle JPEG encoding\n       encodedImage = cv2.imencode(\".jpg\", frame)[1].tobytes()\n       # yield frame in byte format\n       yield (b\"--frame\\r\\nContent-Type:video/jpeg2000\\r\\n\\r\\n\" + encodedImage + b\"\\r\\n\")\n       await asyncio.sleep(0.00001)\n   # close stream\n   stream.release()\n\n\nasync def custom_video_response(scope):\n   \"\"\"\n   Return a async video streaming response for `my_frame_producer2` generator\n   \"\"\"\n   assert scope[\"type\"] in [\"http\", \"https\"]\n   await asyncio.sleep(0.00001)\n   return StreamingResponse(\n       my_frame_producer2(),\n       media_type=\"multipart/x-mixed-replace; boundary=frame\",\n   )\n\n\n# add your custom frame producer to config\nweb.config[\"generator\"] = my_frame_producer1\n\n# append new route i.e. new custom route with custom response\nweb.routes.append(\n    Route(\"/video2\", endpoint=custom_video_response)\n    )\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

On successfully running this code, the output stream will be displayed at address http://localhost:8000/ in Browser.

"},{"location":"help/webgear_faqs/","title":"FAQs","text":""},{"location":"help/webgear_faqs/#webgear-faqs","title":"WebGear FAQs","text":""},{"location":"help/webgear_faqs/#what-is-webgear-api-and-what-does-it-do","title":"What is WebGear API and what does it do?","text":"

Answer: WebGear is a powerful ASGI Video-Broadcaster API ideal for transmitting Motion-JPEG-frames from a single source to multiple recipients via the browser. For more info. see WebGear doc \u27b6

"},{"location":"help/webgear_faqs/#how-to-get-started-with-webgear-api","title":"How to get started with WebGear API?","text":"

Answer: First, refer to the Switching from OpenCV guide, then go through WebGear documentation. If you still have doubts, ask us on Gitter \u27b6 Community channel.

"},{"location":"help/webgear_faqs/#webgear-is-throwing-modulenotfounderror-on-importing-why","title":"\"WebGear is throwing ModuleNotFoundError on importing\", Why?","text":"

Answer: This error means, VidGear is installed WITHOUT asyncio package support on your machine. For this support, see Requirements \u27b6.

"},{"location":"help/webgear_faqs/#can-webgear-always-need-active-internet-connection","title":"Can WebGear always need Active Internet Connection?","text":"

Answer: No, it just need internet only once during its Auto-Generation Process \u27b6 to download default data-files and it takes few seconds. You can also download files manually from Github Server, otherwise you can also add your own custom files. For more information see Data-Files Auto-Generation WorkFlow \u27b6

"},{"location":"help/webgear_faqs/#is-it-possible-to-stream-on-a-different-device-on-the-network-with-webgear","title":"Is it possible to stream on a different device on the network with WebGear?","text":"

If you set \"0.0.0.0\" as host value instead of \"localhost\" on Host Machine, then you must still use http://localhost:8000/ to access stream on that same host machine browser.

For accessing WebGear on different Client Devices on the network, use \"0.0.0.0\" as host value instead of \"localhost\" on Host Machine. Then type the IP-address of source machine followed by the defined port value in your desired Client Device's browser (for e.g. http://192.27.0.101:8000) to access the stream.

"},{"location":"help/webgear_faqs/#can-i-manually-place-default-files-for-webgear","title":"Can I manually place default files for WebGear?","text":"

Answer: Yes, you can either download default files from Github Server, and manually place at default location, OR, you can yourself create the require three critical files (i.e index.html, 404.html & 500.html) inside templates folder at the default location, thereby you don't need any internet connection at all. For more information see Data-Files Auto-Generation WorkFlow \u27b6

"},{"location":"help/webgear_faqs/#how-to-send-opencv-frames-directly-to-webgear-server","title":"How to send OpenCV frames directly to Webgear Server?","text":"

Answer: See this usage example \u27b6.

"},{"location":"help/webgear_faqs/#how-can-i-add-my-custom-webpage-to-webgear","title":"How can I add my custom WebPage to WebGear?","text":"

Answer: See this usage example \u27b6.

"},{"location":"help/webgear_faqs/#how-can-to-add-cors-headers-to-webgear","title":"How can to add CORS headers to WebGear?","text":"

Answer: See this usage example \u27b6.

"},{"location":"help/webgear_faqs/#can-i-change-the-default-location","title":"Can I change the default location?","text":"

Answer: Yes, you can use WebGear's custom_data_location attribute of option parameter in WebGear API, to change default location to somewhere else.

"},{"location":"help/webgear_faqs/#can-i-deleterename-the-webgear-default-data","title":"Can I delete/rename the WebGear default data?","text":"

Answer: Yes, but you've to follow these rules \u27b6

"},{"location":"help/webgear_faqs/#what-web-browser-are-supported-by-webgear-api","title":"What Web browser are supported by WebGear API?","text":"

Answer: All modern browser with Javascript support are supported by WebGear. If not, then discuss with us on Gitter \u27b6 Community channel.

"},{"location":"help/webgear_rtc_ex/","title":"Bonus Examples","text":""},{"location":"help/webgear_rtc_ex/#webgear_rtc_rtc-examples","title":"WebGear_RTC_RTC Examples","text":""},{"location":"help/webgear_rtc_ex/#using-webgear_rtc-with-raspberrypi-camera-module","title":"Using WebGear_RTC with RaspberryPi Camera Module","text":"

Because of WebGear_RTC API's flexible internal wapper around VideoGear, it can easily access any parameter of CamGear and PiGear videocapture APIs.

Following usage examples are just an idea of what can be done with WebGear_RTC API, you can try various VideoGear, CamGear and PiGear parameters directly in WebGear_RTC API in the similar manner.

Here's a bare-minimum example of using WebGear_RTC API with the Raspberry Pi camera module while tweaking its various properties in just one-liner:

Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide \u27b6 for its installation.

Make sure to complete Raspberry Pi Camera Hardware-specific settings prior using this backend, otherwise nothing will work.

New Picamera2 backendLegacy Picamera backend
# import libs\nimport uvicorn\nfrom libcamera import Transform\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# various WebGear_RTC performance \n# and Picamera2 API tweaks\noptions = {\n    \"frame_size_reduction\": 25,\n    \"queue\": True,\n    \"buffer_count\": 4,\n    \"controls\": {\"Brightness\": 0.5, \"ExposureValue\": 2.0},\n    \"transform\": Transform(hflip=1),\n    \"auto_align_output_config\": True,  # auto-align camera configuration\n}\n\n# initialize WebGear app\nweb = WebGear_RTC(\n    enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options\n)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.

However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.

It is advised to enable logging(logging=True) to see which backend is being used.

The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

# import libs\nimport uvicorn\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# various WebGear_RTC performance and Picamera API tweaks\noptions = {\n    \"frame_size_reduction\": 25,\n    \"hflip\": True,\n    \"exposure_mode\": \"auto\",\n    \"iso\": 800,\n    \"exposure_compensation\": 15,\n    \"awb_mode\": \"horizon\",\n    \"sensor_mode\": 0,\n}\n\n# initialize WebGear app\nweb = WebGear_RTC(\n    enablePiCamera=True, resolution=(640, 480), framerate=60, logging=True, **options\n)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

"},{"location":"help/webgear_rtc_ex/#using-webgear_rtc-with-real-time-video-stabilization-enabled","title":"Using WebGear_RTC with real-time Video Stabilization enabled","text":"

Here's an example of using WebGear_RTC API with real-time Video Stabilization enabled:

# import libs\nimport uvicorn\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# various webgear_rtc performance tweaks\noptions = {\n    \"frame_size_reduction\": 25,\n}\n\n# initialize WebGear_RTC app  with a raw source and enable video stabilization(`stabilize=True`)\nweb = WebGear_RTC(source=\"foo.mp4\", stabilize=True, logging=True, **options)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

"},{"location":"help/webgear_rtc_ex/#display-two-sources-simultaneously-in-webgear_rtc","title":"Display Two Sources Simultaneously in WebGear_RTC","text":"

In this example, we'll be displaying two video feeds side-by-side simultaneously on browser using WebGear_RTC API by simply concatenating frames in real-time:

New in v0.2.4

This example was added in v0.2.4.

# import necessary libs\nimport uvicorn, cv2\nimport numpy as np\nfrom vidgear.gears.helper import reducer\nfrom vidgear.gears.asyncio import WebGear_RTC\n\n# initialize WebGear_RTC app without any source\nweb = WebGear_RTC(logging=True)\n\n# frame concatenator\ndef get_conc_frame(frame1, frame2):\n    h1, w1 = frame1.shape[:2]\n    h2, w2 = frame2.shape[:2]\n\n    # create empty matrix\n    vis = np.zeros((max(h1, h2), w1 + w2, 3), np.uint8)\n\n    # combine 2 frames\n    vis[:h1, :w1, :3] = frame1\n    vis[:h2, w1 : w1 + w2, :3] = frame2\n\n    return vis\n\n\n# create your own custom streaming class\nclass Custom_Stream_Class:\n    \"\"\"\n    Custom Streaming using two OpenCV sources\n    \"\"\"\n\n    def __init__(self, source1=None, source2=None):\n\n        # !!! define your own video source here !!!\n        # check is source are provided\n        if source1 is None or source2 is None:\n            raise ValueError(\"Provide both source\")\n\n        # initialize global params\n        # define both source here\n        self.stream1 = cv2.VideoCapture(source1)\n        self.stream2 = cv2.VideoCapture(source2)\n\n        # define running flag\n        self.running = True\n\n    def read(self):\n\n        # don't forget this function!!!\n\n        # check if sources were initialized or not\n        if self.stream1 is None or self.stream2 is None:\n            return None\n\n        # check if we're still running\n        if self.running:\n            # read video frame\n            (grabbed1, frame1) = self.stream1.read()\n            (grabbed2, frame2) = self.stream2.read()\n\n            # if NoneType\n            if not grabbed1 or not grabbed2:\n\n                # do something with your OpenCV frame here\n\n                # concatenate frame\n                frame = get_conc_frame(frame1, frame2)\n\n                # reducer frames size if you want more performance otherwise comment this line\n                # frame = await reducer(frame, percentage=30)  # reduce frame by 30%\n\n                # return our gray frame\n                return frame\n            else:\n                # signal we're not running now\n                self.running = False\n        # return None-type\n        return None\n\n    def stop(self):\n\n        # don't forget this function!!!\n\n        # flag that we're not running\n        self.running = False\n        # close stream\n        if not (self.stream1 is None):\n            self.stream1.release()\n            self.stream1 = None\n\n        if not (self.stream2 is None):\n            self.stream2.release()\n            self.stream2 = None\n\n\n# assign your Custom Streaming Class with adequate two sources\n# to `custom_stream` attribute in options parameter\noptions = {\n    \"custom_stream\": Custom_Stream_Class(\n        source1=\"foo1.mp4\", source2=\"foo2.mp4\"\n    )\n}\n\n# initialize WebGear_RTC app without any source\nweb = WebGear_RTC(logging=True, **options)\n\n# run this app on Uvicorn server at address http://localhost:8000/\nuvicorn.run(web(), host=\"localhost\", port=8000)\n\n# close app safely\nweb.shutdown()\n

On successfully running this code, the output stream will be displayed at address http://localhost:8000/ in Browser.

"},{"location":"help/webgear_rtc_faqs/","title":"FAQs","text":""},{"location":"help/webgear_rtc_faqs/#webgear_rtc-faqs","title":"WebGear_RTC FAQs","text":""},{"location":"help/webgear_rtc_faqs/#what-is-webgear_rtc-api-and-what-does-it-do","title":"What is WebGear_RTC API and what does it do?","text":"

Answer: WebGear_RTC utilizes WebRTC technology under the hood, which makes it suitable for building powerful video-streaming solutions for all modern browsers as well as native clients available on all major platforms. For more info. see WebGear_RTC doc \u27b6

"},{"location":"help/webgear_rtc_faqs/#how-to-get-started-with-webgear_rtc-api","title":"How to get started with WebGear_RTC API?","text":"

Answer: First, refer to the Switching from OpenCV guide, then go through WebGear_RTC documentation. If you still have doubts, ask us on Gitter \u27b6 Community channel.

"},{"location":"help/webgear_rtc_faqs/#how-webgear_rtc-is-different-to-webgear-api-which-should-i-choose","title":"How WebGear_RTC is different to WebGear API, which should I choose?","text":"

Answer: WebGear_RTC is similar to WeGear API in many aspects but utilizes WebRTC technology under the hood instead of Motion JPEG. You can choose any API according to your application, but the quality would be better on WebGear API, on-the-other-hand latency would be better on WebGear_RTC API. Also, WebRTC protocol accepts a wide range of devices, whereas WebGear is limited only to modern browsers.

"},{"location":"help/webgear_rtc_faqs/#webgear_rtc-is-throwing-modulenotfounderror-on-importing-why","title":"\"WebGear_RTC is throwing ModuleNotFoundError on importing\", Why?","text":"

Answer: This error means, VidGear is installed WITHOUT asyncio package support on your machine. For this support, see Requirements \u27b6.

"},{"location":"help/webgear_rtc_faqs/#can-webgear_rtc-always-need-active-internet-connection","title":"Can WebGear_RTC always need Active Internet Connection?","text":"

Answer: No, it just need internet only once during its Auto-Generation Process \u27b6 to download default data-files and it takes few seconds. You can also download files manually from Github Server, otherwise you can also add your own custom files. For more information see Data-Files Auto-Generation WorkFlow \u27b6

"},{"location":"help/webgear_rtc_faqs/#is-it-possible-to-stream-on-a-different-device-on-the-network-with-webgear_rtc","title":"Is it possible to stream on a different device on the network with WebGear_RTC?","text":"

If you set \"0.0.0.0\" as host value instead of \"localhost\" on Host Machine, then you must still use http://localhost:8000/ to access stream on your host machine browser.

For accessing WebGear_RTC on different Client Devices on the network, use \"0.0.0.0\" as host value instead of \"localhost\" on Host Machine. Then type the IP-address of source machine followed by the defined port value in your desired Client Device's browser (for e.g. http://192.27.0.101:8000) to access the stream.

"},{"location":"help/webgear_rtc_faqs/#can-i-manually-place-default-files-for-webgear_rtc","title":"Can I manually place default files for WebGear_RTC?","text":"

Answer: Yes, you can either download default files from Github Server, and manually place at default location, OR, you can yourself create the require three critical files (i.e index.html, 404.html & 500.html) inside templates folder at the default location, thereby you don't need any internet connection at all. For more information see Data-Files Auto-Generation WorkFlow \u27b6

"},{"location":"help/webgear_rtc_faqs/#how-to-stream-webgear_rtc-server-output-to-multiple-clients","title":"How to stream Webgear_RTC Server output to multiple clients?","text":"

Answer: See this usage example \u27b6.

"},{"location":"help/webgear_rtc_faqs/#how-to-send-opencv-frames-directly-to-webgear_rtc-server","title":"How to send OpenCV frames directly to Webgear_RTC Server?","text":"

Answer: See this usage example \u27b6.

"},{"location":"help/webgear_rtc_faqs/#how-can-i-add-my-custom-webpage-to-webgear_rtc","title":"How can I add my custom WebPage to WebGear_RTC?","text":"

Answer: See this usage example \u27b6.

"},{"location":"help/webgear_rtc_faqs/#how-can-to-add-cors-headers-to-webgear_rtc","title":"How can to add CORS headers to WebGear_RTC?","text":"

Answer: See this usage example \u27b6.

"},{"location":"help/webgear_rtc_faqs/#can-i-change-the-default-location","title":"Can I change the default location?","text":"

Answer: Yes, you can use WebGear_RTC's custom_data_location attribute of option parameter in WebGear_RTC API, to change default location to somewhere else.

"},{"location":"help/webgear_rtc_faqs/#can-i-deleterename-the-webgear_rtc-default-data","title":"Can I delete/rename the WebGear_RTC default data?","text":"

Answer: Yes, but you've to follow these rules \u27b6

"},{"location":"help/writegear_ex/","title":"Bonus Examples","text":""},{"location":"help/writegear_ex/#writegear-examples","title":"WriteGear Examples","text":""},{"location":"help/writegear_ex/#using-writegears-compression-mode-for-rtsprtp-live-streaming","title":"Using WriteGear's Compression Mode for RTSP/RTP Live-Streaming","text":"

In Compression Mode, you can use WriteGear for livestreaming with traditional protocols such as RTSP/RTP. The example to achieve that is as follows:

New in v0.2.6

This example was added in v0.2.6.

Creating your own RTSP Server locally

If you want to create your RTSP Server locally, then checkout MediaMTX (formerly rtsp-simple-server) - ready-to-use and zero-dependency real-time media server and media proxy that allows to publish, read, proxy, record and playback video and audio streams.

This example assume you already have a RTSP Server running at specified RTSP address with format rtsp://[RTSP_ADDRESS]:[RTSP_PORT]/[RTSP_PATH] for publishing video frames.

Make sure to change RTSP address rtsp://localhost:8554/mystream with yours in following code before running!

# import required libraries\nimport cv2\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\n\n# open any valid video stream(for e.g `foo.mp4` file)\nstream = CamGear(source=\"foo.mp4\").start()\n\n# define required FFmpeg parameters for your writer\noutput_params = {\"-f\": \"rtsp\", \"-rtsp_transport\": \"tcp\"}\n\n# Define writer with defined parameters and RTSP address\n# [WARNING] Change your RTSP address `rtsp://localhost:8554/mystream` with yours!\nwriter = WriteGear(\n    output=\"rtsp://localhost:8554/mystream\", logging=True, **output_params\n)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"help/writegear_ex/#using-writegears-compression-mode-for-youtube-live-streaming","title":"Using WriteGear's Compression Mode for YouTube-Live Streaming","text":"

In Compression Mode, you can also use WriteGear for Youtube-Livestreaming. The example is as follows:

New in v0.2.1

This example was added in v0.2.1.

This example assume you already have a YouTube Account with Live-Streaming enabled for publishing video.

Make sure to change YouTube-Live Stream Key with yours in following code before running!

Without AudioWith Audio
# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# define and open video source\nstream = CamGear(source=\"/home/foo/foo.mp4\", logging=True).start()\n\n# define required FFmpeg parameters for your writer\noutput_params = {\n    \"-clones\": [\"-f\", \"lavfi\", \"-i\", \"anullsrc\"],\n    \"-vcodec\": \"libx264\",\n    \"-preset\": \"medium\",\n    \"-b:v\": \"4500k\",\n    \"-bufsize\": \"512k\",\n    \"-pix_fmt\": \"yuv420p\",\n    \"-f\": \"flv\",\n}\n\n# [WARNING] Change your YouTube-Live Stream Key here:\nYOUTUBE_STREAM_KEY = \"xxxx-xxxx-xxxx-xxxx-xxxx\"\n\n# Define writer with defined parameters\nwriter = WriteGear(\n    output=\"rtmp://a.rtmp.youtube.com/live2/{}\".format(YOUTUBE_STREAM_KEY),\n    logging=True,\n    **output_params\n)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

This code assume given input video source contains valid audio stream.

# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# define video source(with audio) here\nVIDEO_SOURCE = \"/home/foo/foo.mp4\"\n\n# Open stream\nstream = CamGear(source=VIDEO_SOURCE, logging=True).start()\n\n# define required FFmpeg parameters for your writer\n# [NOTE]: Added VIDEO_SOURCE as audio-source\noutput_params = {\n    \"-i\": VIDEO_SOURCE,\n    \"-acodec\": \"aac\",\n    \"-ar\": 44100,\n    \"-b:a\": 712000,\n    \"-vcodec\": \"libx264\",\n    \"-preset\": \"medium\",\n    \"-b:v\": \"4500k\",\n    \"-bufsize\": \"512k\",\n    \"-pix_fmt\": \"yuv420p\",\n    \"-f\": \"flv\",\n}\n\n# [WARNING] Change your YouTube-Live Stream Key here:\nYOUTUBE_STREAM_KEY = \"xxxx-xxxx-xxxx-xxxx-xxxx\"\n\n# Define writer with defined parameters\nwriter = WriteGear(\n    output=\"rtmp://a.rtmp.youtube.com/live2/{}\".format(YOUTUBE_STREAM_KEY),\n    logging=True,\n    **output_params\n)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"help/writegear_ex/#using-writegears-compression-mode-with-v4l2loopback-virtual-cameras","title":"Using WriteGear's Compression Mode with v4l2loopback Virtual Cameras","text":"

With WriteGear's Compression Mode, you can directly feed video-frames to v4l2loopback generated Virtual Camera devices on Linux Machines. The complete usage example is as follows:

New in v0.3.0

This example was added in v0.3.0.

Example Assumptions Creating your own Virtual Camera device with v4l2loopback module.

To install and create a v4l2loopback virtual camera device on Linux Mint OS/Ubuntu (may slightly differ for other distros), run following two terminal commands:

$ sudo apt-get install v4l2loopback-dkms v4l2loopback-utils linux-modules-extra-$(uname -r)\n\n$ sudo modprobe v4l2loopback devices=1 video_nr=0 exclusive_caps=1 card_label='VCamera'\n

For further information on parameters used, checkout v4l2loopback docs

Finally, You can check the loopback device you just created by listing contents of /sys/devices/virtual/video4linux directory with terminal command:

$ sudo ls -1 /sys/devices/virtual/video4linux\n\nvideo0 \n

Now you can use /dev/video0 Virtual Camera device path in WriteGear API.

v4l2: open /dev/videoX: Permission denied

If you got this error, then you must add your username to the video group by running following commands:

$ sudo adduser $(whoami) video\n$ sudo usermod -a -G video $(whoami)\n
Afterwards, restart your computer to finialize these changes.

Note: If the problem still persists, then try to run your python script as superuser with sudo command.

Default libx264 encoder is incompatible with v4l2loopback module.

Kindly use other encoders such as libxvid, mpeg4 etc.

# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# open any valid video stream(for e.g `foo.mp4` file)\nstream = CamGear(source=\"foo.mp4\").start()\n\n# define required FFmpeg parameters for your writer\n# also retrieve framerate from CamGear Stream and pass it as `-input_framerate` parameter\noutput_params = {\n    \"-input_framerate\": stream.framerate,\n    \"-vcodec\": \"libxvid\",\n    \"-f\": \"v4l2\",\n    \"-pix_fmt\": \"yuv420p\",\n}\n\n# Define writer with \"/dev/video0\" as source and user-defined parameters \nwriter = WriteGear(output=\"/dev/video0\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if None-type\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

The data sent to the v4l2loopback device /dev/video0 in this example with WriteGear API, can then be read by any v4l2-capable application (such as OpenCV, VLC, ffplay etc.)

"},{"location":"help/writegear_ex/#using-writegears-compression-mode-for-creating-mp4-segments","title":"Using WriteGear's Compression Mode for creating MP4 segments","text":"

In Compression Mode, you can also use WriteGear for creating MP4 segments from almost any video source. The example is as follows:

New in v0.2.1

This example was added in v0.2.1.

# import required libraries\nfrom vidgear.gears import VideoGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# Open any video source `foo.mp4`\nstream = VideoGear(\n    source=\"foo.mp4\", logging=True\n).start()\n\n# define required FFmpeg optimizing parameters for your writer\noutput_params = {\n    \"-c:v\": \"libx264\",\n    \"-crf\": 22,\n    \"-map\": 0,\n    \"-segment_time\": 9,\n    \"-g\": 9,\n    \"-sc_threshold\": 0,\n    \"-force_key_frames\": \"expr:gte(t,n_forced*9)\",\n    \"-clones\": [\"-f\", \"segment\"],\n}\n\n# Define writer with defined parameters\nwriter = WriteGear(output=\"output%03d.mp4\", logging=True, **output_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"help/writegear_ex/#using-writegears-compression-mode-to-add-external-audio-file-input-to-video-frames","title":"Using WriteGear's Compression Mode to add external audio file input to video frames","text":"

You can also use WriteGear for merging external audio with live video-source:

New in v0.2.1

This example was added in v0.2.1.

Make sure this -i audio-source it compatible with provided video-source, otherwise you could encounter multiple errors or no output at all.

# import required libraries\nfrom vidgear.gears import CamGear\nfrom vidgear.gears import WriteGear\nimport cv2\n\n# open any valid video stream(for e.g `foo_video.mp4` file)\nstream = CamGear(source=\"foo_video.mp4\").start()\n\n# add various parameters, along with custom audio\nstream_params = {\n    \"-input_framerate\": stream.framerate,  # controlled framerate for audio-video sync !!! don't forget this line !!!\n    \"-i\": \"foo_audio.aac\",  # assigns input audio-source: \"foo_audio.aac\"\n}\n\n# Define writer with defined parameters\nwriter = WriteGear(output=\"Output.mp4\", logging=True, **stream_params)\n\n# loop over\nwhile True:\n\n    # read frames from stream\n    frame = stream.read()\n\n    # check for frame if Nonetype\n    if frame is None:\n        break\n\n    # {do something with the frame here}\n\n    # write frame to writer\n    writer.write(frame)\n\n    # Show output window\n    cv2.imshow(\"Output Frame\", frame)\n\n    # check for 'q' key if pressed\n    key = cv2.waitKey(1) & 0xFF\n    if key == ord(\"q\"):\n        break\n\n# close output window\ncv2.destroyAllWindows()\n\n# safely close video stream\nstream.stop()\n\n# safely close writer\nwriter.close()\n

"},{"location":"help/writegear_ex/#using-writegears-compression-mode-for-generating-timely-accurate-video","title":"Using WriteGear's Compression Mode for generating Timely Accurate Video","text":"

If you need timely accurate video with exactly same speed as real-time input, then you need to use FFmpeg directly through its execute_ffmpeg_cmd method:

New in v0.2.4

This example was added in v0.2.4.

In this example we are capturing video from desktop screen in a Timely Accurate manner.

Windows Linux MacOS
# import required libraries\nfrom vidgear.gears import WriteGear\n\n# Define writer with defined parameters and with some dummy name\nwriter = WriteGear(output=\"Output.mp4\", logging=True)\n\n# format FFmpeg command to generate time accurate video\nffmpeg_command = [\n    \"-y\",\n    \"-f\",\n    \"gdigrab\",\n    \"-framerate\",\n    \"30\",\n    \"-i\",\n    \"desktop\",\n    \"Output.mkv\",\n]  # `-y` parameter is to overwrite outputfile if exists\n\n# execute FFmpeg command\nwriter.execute_ffmpeg_cmd(ffmpeg_command)\n\n# safely close writer\nwriter.close()\n
# import required libraries\nfrom vidgear.gears import WriteGear\n\n# Define writer with defined parameters and with some dummy name\nwriter = WriteGear(output=\"Output.mp4\", logging=True)\n\n# format FFmpeg command to generate time accurate video\nffmpeg_command = [\n    \"-y\",\n    \"-f\",\n    \"x11grab\",\n    \"-framerate\",\n    \"30\",\n    \"-i\",\n    \"default\",\n    \"Output.mkv\",\n]  # `-y` parameter is to overwrite outputfile if exists\n\n# execute FFmpeg command\nwriter.execute_ffmpeg_cmd(ffmpeg_command)\n\n# safely close writer\nwriter.close()\n
# import required libraries\nfrom vidgear.gears import WriteGear\n\n# Define writer with defined parameters and with some dummy name\nwriter = WriteGear(output=\"Output.mp4\", logging=True)\n\n# format FFmpeg command to generate time accurate video\nffmpeg_command = [\n    \"-y\",\n    \"-f\",\n    \"avfoundation\",\n    \"-framerate\",\n    \"30\",\n    \"-i\",\n    \"default\",\n    \"Output.mkv\",\n]  # `-y` parameter is to overwrite outputfile if exists\n\n# execute FFmpeg command\nwriter.execute_ffmpeg_cmd(ffmpeg_command)\n\n# safely close writer\nwriter.close()\n

"},{"location":"help/writegear_ex/#using-writegear-with-rosrobot-operating-system","title":"Using WriteGear with ROS(Robot Operating System)","text":"

We will be using cv_bridge to convert OpenCV frames to ROS image messages and vice-versa.

In this example, we'll create a node that listens to a ROS image message topic, converts the received images messages into OpenCV frames, draws a circle on it, and then process these frames into a lossless compressed file format in real-time.

New in v0.2.2

This example was added in v0.2.2.

This example is vidgear implementation of this wiki example.

# import roslib\nimport roslib\n\nroslib.load_manifest(\"my_package\")\n\n# import other required libraries\nimport sys\nimport rospy\nimport cv2\nfrom std_msgs.msg import String\nfrom sensor_msgs.msg import Image\nfrom cv_bridge import CvBridge, CvBridgeError\nfrom vidgear.gears import WriteGear\n\n# custom publisher class\nclass image_subscriber:\n    def __init__(self, output=\"Output.mp4\"):\n        # create CV bridge\n        self.bridge = CvBridge()\n        # define publisher topic\n        self.image_pub = rospy.Subscriber(\"image_topic_sub\", Image, self.callback)\n        # Define writer with default parameters\n        self.writer = WriteGear(output=output)\n\n    def callback(self, data):\n        # convert received data to frame\n        try:\n            cv_image = self.bridge.imgmsg_to_cv2(data, \"bgr8\")\n        except CvBridgeError as e:\n            print(e)\n\n        # check if frame is valid\n        if cv_image:\n\n            # {do something with the frame here}\n            # let's add a circle\n            (rows, cols, channels) = cv_image.shape\n            if cols > 60 and rows > 60:\n                cv2.circle(cv_image, (50, 50), 10, 255)\n\n            # write frame to writer\n            self.writer.write(cv_image)\n\n        def close(self):\n            # safely close video stream\n            self.writer.close()\n\n\ndef main(args):\n    # define publisher with suitable output filename\n    # such as `Output.mp4` for saving output\n    ic = image_subscriber(output=\"Output.mp4\")\n    # initiate ROS node on publisher\n    rospy.init_node(\"image_subscriber\", anonymous=True)\n    try:\n        # run node\n        rospy.spin()\n    except KeyboardInterrupt:\n        print(\"Shutting down\")\n    finally:\n        # close publisher\n        ic.close()\n\n\nif __name__ == \"__main__\":\n    main(sys.argv)\n

"},{"location":"help/writegear_faqs/","title":"FAQs","text":""},{"location":"help/writegear_faqs/#writegear-faqs","title":"WriteGear FAQs","text":""},{"location":"help/writegear_faqs/#what-is-writegear-api-and-what-does-it-do","title":"What is WriteGear API and what does it do?","text":"

Answer: WriteGear handles various powerful Writer Tools that provide us the freedom to do almost anything imagine with multimedia files. For more info. see WriteGear doc \u27b6

"},{"location":"help/writegear_faqs/#im-only-familiar-with-opencv-how-to-get-started-with-writegear-api","title":"I'm only familiar with OpenCV, how to get started with WriteGear API?","text":"

Answer: Answer: First, refer to the Switching from OpenCV guide, then go through WriteGear documentation. If you still have doubts, ask us on Gitter \u27b6 Community channel.

"},{"location":"help/writegear_faqs/#why-writegear-is-throwing-valueerror","title":"Why WriteGear is throwing ValueError?","text":"

Answer: WriteGear will exit with ValueError if you feed frames of different dimensions or channels.

"},{"location":"help/writegear_faqs/#how-to-install-and-configure-ffmpeg-correctly-for-writegear-on-my-machine","title":"How to install and configure FFmpeg correctly for WriteGear on my machine?","text":"

Answer: Follow these Installation Instructions \u27b6 for its installation.

"},{"location":"help/writegear_faqs/#can-i-use-writegear-directly-with-opencv","title":"Can I use WriteGear directly with OpenCV?","text":"

Answer: Yes,

"},{"location":"help/writegear_faqs/#what-ffmpegs-encoders-and-parameters-are-supported-by-writegear-in-compression-mode","title":"What FFmpeg's encoders and parameters are supported by WriteGear in compression mode?","text":"

Answer: See Supported Parameters \u27b6 and Supported encoders \u27b6

"},{"location":"help/writegear_faqs/#what-opencvs-fourcc-and-parameters-are-supported-by-writegear-in-non-compression-mode","title":"What OpenCV's FOURCC and parameters are supported by WriteGear in non-compression mode?","text":"

Answer: See Supported Parameters \u27b6 and Supported FOURCC \u27b6.

"},{"location":"help/writegear_faqs/#why-this-fourcc-is-not-working-for-me","title":"Why this FOURCC is not working for me?","text":"

Answer: Remember not all the FOURCC and Video extensions are compatible and supported by OpenCV VideoWriter Class. You\u2019ll need to try different combinations of FourCC and file extensions. Furthermore, OpenCV does not return any helpful error messages regarding this problem, so it\u2019s pretty much based on trial and error.

"},{"location":"help/writegear_faqs/#can-i-pass-my-custom-ffmpeg-commands-directly-in-writegear-api","title":"Can I pass my custom FFmpeg commands directly in WriteGear API?","text":"

Answer: Yes, See Custom FFmpeg Commands in WriteGear API \u27b6.

"},{"location":"help/writegear_faqs/#how-to-use-specific-hardware-encoder-in-writegear","title":"How to use specific Hardware Encoder in WriteGear?","text":"

Answer: See this usage example \u27b6

"},{"location":"help/writegear_faqs/#how-to-add-live-audio-to-writegear","title":"How to add live audio to WriteGear?","text":"

Answer: See this doc \u27b6

"},{"location":"help/writegear_faqs/#how-to-separate-and-merge-audio-fromto-video","title":"How to separate and merge audio from/to video?","text":"

Answer: See these usage examples \u27b6

"},{"location":"help/writegear_faqs/#can-i-live-stream-to-twitch-with-writegear-api","title":"Can I live stream to Twitch with WriteGear API?","text":"

Answer: Yes, See this usage example \u27b6

"},{"location":"help/writegear_faqs/#is-youtube-live-streaming-possible-with-writegear","title":"Is YouTube-Live Streaming possible with WriteGear?","text":"

Answer: Yes, See this bonus example \u27b6.

"},{"location":"help/writegear_faqs/#how-to-live-streaming-using-rtsprtp-protocol-with-writegear","title":"How to Live-Streaming using RTSP/RTP protocol with WriteGear?","text":"

Answer: See this bonus example \u27b6.

"},{"location":"help/writegear_faqs/#how-to-create-mp4-segments-from-a-video-stream-with-writegear","title":"How to create MP4 segments from a video stream with WriteGear?","text":"

Answer: See this bonus example \u27b6.

"},{"location":"help/writegear_faqs/#how-add-external-audio-file-input-to-video-frames","title":"How add external audio file input to video frames?","text":"

Answer: See this bonus example \u27b6.

"},{"location":"help/writegear_faqs/#why-this-ffmpeg-parameter-is-not-working-for-me-in-compression-mode","title":"Why this FFmpeg parameter is not working for me in compression mode?","text":"

Answer: If some FFmpeg parameter doesn't work for you, then tell us on Gitter \u27b6, and if that doesn't help, then finally report an issue \u27b6

"},{"location":"help/writegear_faqs/#why-writegear-is-switching-to-non-compression-mode-even-if-it-is-not-enable","title":"Why WriteGear is switching to Non-compression Mode, even if it is not enable?","text":"

Answer: In case WriteGear API fails to detect valid FFmpeg executables on your system (even if Compression Mode is enabled), it will automatically fallback to Non-Compression Mode. Follow Installation Instructions \u27b6 for FFmpeg installation.

"},{"location":"installation/pip_install/","title":"Install using pip","text":""},{"location":"installation/pip_install/#install-using-pip","title":"Install using pip","text":"

Best option for easily getting stable VidGear installed.

"},{"location":"installation/pip_install/#prerequisites","title":"Prerequisites","text":"

When installing VidGear with pip, you need to manually install following prerequisites:

Upgrade your pip

It strongly advised to upgrade to latest pip before installing vidgear to avoid any undesired installation error(s).

There are two mechanisms to upgrade pip:

pipensurepip

You can use existing pip to upgrade itself:

Install pip if not present Linux / MacOS Windows
python get-pip.py\n
py get-pip.py\n

More details about this script can be found in pypa/get-pip\u2019s README.

Linux / MacOS Windows
python -m pip install pip --upgrade\n
py -m pip install pip --upgrade\n

Python also comes with an ensurepip module1, which can easily upgrade/install pip in any Python environment.

Linux / MacOS Windows
python -m ensurepip --upgrade\n
py -m ensurepip --upgrade\n

"},{"location":"installation/pip_install/#critical-prerequisites","title":"Critical Prerequisites","text":""},{"location":"installation/pip_install/#opencv","title":"OpenCV","text":"

Must require OpenCV(3.0+) python binaries installed for all core functions. You easily install it directly via pip:

OpenCV installation from source

You can also follow online tutorials for building & installing OpenCV on Windows, Linux, MacOS and Raspberry Pi machines manually from its source.

Make sure not to install both pip and source version together. Otherwise installation will fail to work!

Other OpenCV binaries

OpenCV maintainers also provide additional binaries via pip that contains both main modules and contrib/extra modules opencv-contrib-python, and for server (headless) environments like opencv-python-headless and opencv-contrib-python-headless. You can also install any one of them in similar manner. More information can be found here.

pip install opencv-python       \n
"},{"location":"installation/pip_install/#api-specific-prerequisites","title":"API Specific Prerequisites","text":""},{"location":"installation/pip_install/#ffmpeg","title":"FFmpeg","text":"

Require only for the video compression and encoding compatibility within StreamGear API and WriteGear API's Compression Mode.

FFmpeg Installation

"},{"location":"installation/pip_install/#picamera2","title":"Picamera2","text":"

Required only if you're using Raspberry Pi Camera Modules (or USB webcams) with the PiGear API. Here's how to install Picamera2 python library:

Using Legacy picamera library with PiGear (v0.3.3 and above)

PiGear API (version 0.3.3 onwards) prioritizes the newer Picamera2 library under the hood for Raspberry Pi camera modules. However, if your operating system doesn't support Picamera2, you can still use the legacy picamera library. Here's how to easily install it using pip:

pip install picamera\n

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

Picamera2 is only supported on Raspberry Pi OS Bullseye (or later) images, both 32 and 64-bit.

Picamera2 is NOT supported on:

Installation using apt (Recommended)Installation using pip As of September 2022, Picamera2 is pre-installed on images downloaded from Raspberry Pi. So you don't have to install it manually.

If Picamera2 is not already installed, then your image is presumably older and you should start with system upgrade:

sudo apt update && upgrade\n

If you have installed Picamera2 previously using pip, then you should also uninstall this (pip3 uninstall picamera2).

Thereafter, you can install Picamera2 with all the GUI (Qt and OpenGL) dependencies using:

sudo apt install -y python3-picamera2\n

Or, If you DON'T want the GUI dependencies, use:

sudo apt install -y python3-picamera2 --no-install-recommends\n

This is NOT the recommended way to install Picamera2.

However, if you wish to install Picamera2 with all the GUI (Qt and OpenGL) dependencies with pip, use:

sudo apt install -y python3-libcamera python3-kms++\nsudo apt install -y python3-pyqt5 python3-prctl \nsudo apt install -y libatlas-base-dev ffmpeg python3-pip\npip3 install numpy --upgrade\npip3 install picamera2[gui]\n

Or, If you DON'T want the GUI dependencies, use:

sudo apt install -y python3-libcamera python3-kms++\nsudo apt install -y python3-prctl libatlas-base-dev\nsudo apt install -y ffmpeg libopenjp2-7 python3-pip\npip3 install numpy --upgrade\npip3 install picamera2\n
"},{"location":"installation/pip_install/#uvloop","title":"Uvloop","text":"

Required only if you're using the NetGear_Async API on UNIX machines for maximum performance. You can easily install it via pip:

uvloop is NOT yet supported on Windows Machines.

pip install uvloop\n
"},{"location":"installation/pip_install/#dxcam","title":"DXcam","text":"

Required only if you're using the ScreenGear API on Windows machines for better FPS performance. You can easily install it via pip:

FYI, DXcam is ONLY supported on Windows Machines.

pip install dxcam\n
"},{"location":"installation/pip_install/#installation","title":"Installation","text":"Installation command with pip has been changed in v0.2.4

The legacy pip install vidgear command now installs critical bare-minimum dependencies only. Therefore in order to automatically install all the API specific dependencies as previous versions, use pip install vidgear[core] command instead.

v0.2.4 and newerOlder
# Install latest stable release with all Core dependencies\npip install -U vidgear[core]\n

[core] keyword isn't available in versions older than v0.2.4

# Install older stable release with all Core dependencies\npip install vidgear<0.2.4\n

Similarly in your python project files like setup.py or requirements.txt or setup.cfg, use vidgear dependency as vidgear[core]>=0.2.4 instead.

This change does not affects pip install vidgear[asyncio] command.

Installation is as simple as:

Installing vidgear with only selective dependencies

Starting with version v0.2.2, you can now run any VidGear API by installing only just specific dependencies required by the API in use(except for some Core dependencies).

This is useful when you want to manually review, select and install minimal API-specific dependencies on bare-minimum vidgear from scratch on your system:

Windows Installation

If you are using Windows, some of the commands given below, may not work out-of-the-box.

A quick solution may be to preface every Python command with python -m like this:

# Install latest stable release with all Core dependencies\npython -m pip install -U vidgear[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npython -m pip install -U vidgear[asyncio]\n

And, If you don't have the privileges to the directory you're installing package. Then use --user flag, that makes pip install packages in your home directory instead:

# Install latest stable release with all Core dependencies\npython -m pip install --upgrade --user vidgear[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npython -m pip install --upgrade --user vidgear[asyncio]\n

Or, If you're using py as alias for installed python, then:

# Install latest stable release with all Core dependencies\npy -m pip install --upgrade --user vidgear[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npy -m pip install --upgrade --user vidgear[asyncio]\n
# Install latest stable release with all Core dependencies\npip install -U vidgear[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npip install -U vidgear[asyncio]\n

And if you prefer to install VidGear directly from the repository:

# Install latest stable release with all Core dependencies\npip install git+git://github.com/abhiTronix/vidgear@master#egg=vidgear[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npip install git+git://github.com/abhiTronix/vidgear@master#egg=vidgear[asyncio]\n

Or you can also download its wheel (.whl) package from our repository's releases section, and thereby can be installed as follows:

# Install latest stable release with all Core dependencies\npip install vidgear-0.3.3-py3-none-any.whl[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npip install vidgear-0.3.3-py3-none-any.whl[asyncio]\n

  1. The ensurepip module is missing/disabled on Ubuntu. Use pip method only.\u00a0\u21a9

"},{"location":"installation/source_install/","title":"Install from source","text":""},{"location":"installation/source_install/#install-from-source","title":"Install from source","text":"

Best option for trying latest patches(maybe experimental), forking for Pull Requests, or automatically installing all prerequisites(with a few exceptions).

"},{"location":"installation/source_install/#prerequisites","title":"Prerequisites","text":"

When installing VidGear from source, following are some API specific prerequisites you may need to install manually:

What about rest of the prerequisites?

Any other python prerequisites (Critical/API specific) will be automatically installed based on your OS/System specifications.

Upgrade your pip

It strongly advised to upgrade to latest pip before installing vidgear to avoid any undesired installation error(s).

There are two mechanisms to upgrade pip:

pipensurepip

You can use existing pip to upgrade itself:

Install pip if not present Linux/MacOSWindows
python get-pip.py\n
py get-pip.py\n

More details about this script can be found in pypa/get-pip\u2019s README.

Linux/MacOSWindows
python -m pip install pip --upgrade\n
py -m pip install pip --upgrade\n

Python also comes with an ensurepip module1, which can easily upgrade/install pip in any Python environment.

Linux/MacOSWindows
python -m ensurepip --upgrade\n
py -m ensurepip --upgrade\n
"},{"location":"installation/source_install/#api-specific-prerequisites","title":"API Specific Prerequisites","text":""},{"location":"installation/source_install/#ffmpeg","title":"FFmpeg","text":"

Require only for the video compression and encoding compatibility within StreamGear API and WriteGear API's Compression Mode.

FFmpeg Installation

"},{"location":"installation/source_install/#picamera2","title":"Picamera2","text":"

Required only if you're using Raspberry Pi Camera Modules (or USB webcams) with the PiGear API. Here's how to install Picamera2 python library:

Using Legacy picamera library with PiGear (v0.3.3 and above)

PiGear API (version 0.3.3 onwards) prioritizes the newer Picamera2 library under the hood for Raspberry Pi camera modules. However, if your operating system doesn't support Picamera2, you can still use the legacy picamera library. Here's how to easily install it using pip:

pip install picamera\n

You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.

Picamera2 is only supported on Raspberry Pi OS Bullseye (or later) images, both 32 and 64-bit.

Picamera2 is NOT supported on:

Installation using apt (Recommended)Installation using pip As of September 2022, Picamera2 is pre-installed on images downloaded from Raspberry Pi. So you don't have to install it manually.

If Picamera2 is not already installed, then your image is presumably older and you should start with system upgrade:

sudo apt update && upgrade\n

If you have installed Picamera2 previously using pip, then you should also uninstall this (pip3 uninstall picamera2).

Thereafter, you can install Picamera2 with all the GUI (Qt and OpenGL) dependencies using:

sudo apt install -y python3-picamera2\n

Or, If you DON'T want the GUI dependencies, use:

sudo apt install -y python3-picamera2 --no-install-recommends\n

This is NOT the recommended way to install Picamera2.

However, if you wish to install Picamera2 with all the GUI (Qt and OpenGL) dependencies with pip, use:

sudo apt install -y python3-libcamera python3-kms++\nsudo apt install -y python3-pyqt5 python3-prctl \nsudo apt install -y libatlas-base-dev ffmpeg python3-pip\npip3 install numpy --upgrade\npip3 install picamera2[gui]\n

Or, If you DON'T want the GUI dependencies, use:

sudo apt install -y python3-libcamera python3-kms++\nsudo apt install -y python3-prctl libatlas-base-dev\nsudo apt install -y ffmpeg libopenjp2-7 python3-pip\npip3 install numpy --upgrade\npip3 install picamera2\n
"},{"location":"installation/source_install/#installation","title":"Installation","text":"

If you want to checkout the latest beta testing branch , you can do so with the following commands:

This can be useful if you want to provide feedback for a new feature or bug fix in the testing branch.

DO NOT clone or install any other branch other than testing unless advised, as it is not tested with CI environments and possibly very unstable or unusable.

Installing vidgear with only selective dependencies

Starting with version v0.2.2, you can now run any VidGear API by installing only just specific dependencies required by the API in use(except for some Core dependencies).

This is useful when you want to manually review, select and install minimal API-specific dependencies on bare-minimum vidgear from scratch on your system:

Windows Installation

If you are using Windows, some of the commands given below, may not work out-of-the-box.

A quick solution may be to preface every Python command with python -m like this:

# Install latest stable release with all Core dependencies\npython -m pip install -U .[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npython -m pip install -U .[asyncio]\n

And, If you don't have the privileges to the directory you're installing package. Then use --user flag, that makes pip install packages in your home directory instead:

# Install latest stable release with all Core dependencies\npython -m pip install --upgrade --user .[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npython -m pip install --upgrade --user .[asyncio]\n

Or, If you're using py as alias for installed python, then:

# Install latest stable release with all Core dependencies\npy -m pip install --upgrade --user .[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npy -m pip install --upgrade --user .[asyncio]\n
# clone the repository and get inside\ngit clone https://github.com/abhiTronix/vidgear.git && cd vidgear\n\n# checkout the latest testing branch\ngit checkout testing\n\n# Install latest stable release with all Core dependencies\npip install -U .[core]\n\n# Or Install latest stable release with all Core & Asyncio dependencies\npip install -U .[asyncio]\n

  1. The ensurepip module is missing/disabled on Ubuntu. Use pip method only.\u00a0\u21a9

"}]} \ No newline at end of file diff --git a/v0.3.3-dev/sitemap.xml b/v0.3.3-dev/sitemap.xml index 149a49c1..141ace12 100644 --- a/v0.3.3-dev/sitemap.xml +++ b/v0.3.3-dev/sitemap.xml @@ -2,522 +2,522 @@ https://abhitronix.github.io/vidgear/v0.3.3-dev/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/changelog/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/contribution/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/installation/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/license/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/switch_from_cv/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/bonus/TQM/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/bonus/colorspace_manipulation/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/bonus/reference/camgear/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/bonus/reference/helper/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/bonus/reference/helper_async/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/bonus/reference/netgear/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/bonus/reference/netgear_async/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/bonus/reference/pigear/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/bonus/reference/screengear/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/bonus/reference/stabilizer/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/bonus/reference/streamgear/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/bonus/reference/videogear/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/bonus/reference/webgear/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/bonus/reference/webgear_rtc/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/bonus/reference/writegear/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/contribution/PR/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/contribution/issue/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/camgear/overview/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/camgear/params/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/camgear/usage/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/camgear/advanced/source_params/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/netgear/overview/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/netgear/params/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/netgear/usage/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/netgear/advanced/bidirectional_mode/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/netgear/advanced/compression/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/netgear/advanced/multi_client/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/netgear/advanced/multi_server/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/netgear/advanced/secure_mode/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/netgear/advanced/ssh_tunnel/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/netgear_async/overview/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/netgear_async/params/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/netgear_async/usage/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/netgear_async/advanced/bidirectional_mode/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/pigear/overview/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/pigear/params/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/pigear/usage/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/screengear/overview/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/screengear/params/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/screengear/usage/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/stabilizer/overview/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/stabilizer/params/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/stabilizer/usage/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/streamgear/ffmpeg_install/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/streamgear/introduction/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/streamgear/params/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/streamgear/rtfm/overview/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/streamgear/rtfm/usage/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/streamgear/ssm/overview/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/streamgear/ssm/usage/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/videogear/overview/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/videogear/params/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/videogear/usage/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/webgear/advanced/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/webgear/overview/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/webgear/params/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/webgear/usage/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/webgear_rtc/advanced/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/webgear_rtc/overview/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/webgear_rtc/params/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/webgear_rtc/usage/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/writegear/introduction/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/writegear/compression/overview/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/writegear/compression/params/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/writegear/compression/usage/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/writegear/compression/advanced/cciw/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/writegear/compression/advanced/ffmpeg_install/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/writegear/non_compression/overview/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/writegear/non_compression/params/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/gears/writegear/non_compression/usage/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/camgear_ex/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/camgear_faqs/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/general_faqs/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/get_help/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/netgear_async_ex/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/netgear_async_faqs/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/netgear_ex/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/netgear_faqs/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/pigear_ex/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/pigear_faqs/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/screengear_ex/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/screengear_faqs/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/stabilizer_ex/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/stabilizer_faqs/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/streamgear_ex/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/streamgear_faqs/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/videogear_ex/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/videogear_faqs/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/webgear_ex/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/webgear_faqs/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/webgear_rtc_ex/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/webgear_rtc_faqs/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/writegear_ex/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/help/writegear_faqs/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/installation/pip_install/ - 2024-06-20 + 2024-06-22 daily https://abhitronix.github.io/vidgear/v0.3.3-dev/installation/source_install/ - 2024-06-20 + 2024-06-22 daily \ No newline at end of file diff --git a/v0.3.3-dev/sitemap.xml.gz b/v0.3.3-dev/sitemap.xml.gz index 827eb2b4..2378bff5 100644 Binary files a/v0.3.3-dev/sitemap.xml.gz and b/v0.3.3-dev/sitemap.xml.gz differ