diff --git a/docs/gears/netgear/params.md b/docs/gears/netgear/params.md index c9562e362..949069b2f 100644 --- a/docs/gears/netgear/params.md +++ b/docs/gears/netgear/params.md @@ -169,7 +169,9 @@ This parameter provides the flexibility to alter various NetGear API's internal * **`max_retries`**(_integer_): This internal attribute controls the maximum retries before Server/Client exit itself, if it's unable to get any response/reply from the socket before a certain amount of time, when synchronous messaging patterns like (`zmq.PAIR` & `zmq.REQ/zmq.REP`) are being used. It's value can anything greater than `0`, and its default value is `3`. - * **`request_timeout`**(_integer_): This internal attribute controls the timeout value _(in seconds)_, after which the Server/Client exit itself if it's unable to get any response/reply from the socket, when synchronous messaging patterns like (`zmq.PAIR` & `zmq.REQ/zmq.REP`) are being used. It's value can anything greater than `0`, and its default value is `10` seconds. + * **`request_timeout`**(_integer_): This internal attribute controls the timeout value _(in seconds)_, after which the Server/Client exit itself with `Nonetype` value if it's unable to get any response/reply from the socket, when synchronous messaging patterns like (`zmq.PAIR` & `zmq.REQ/zmq.REP`) are being used. It's value can anything greater than `0`, and its default value is `10` seconds. + + * **`subscriber_timeout`**(_integer_): Similar to `request_timeout`, this internal attribute also controls the timeout value _(in seconds)_ but for non-synchronous `zmq.PUB/zmq.SUB` pattern in compression mode, after which the Client(Subscriber) exit itself with `Nonetype` value if it's unable to get any response from the socket. It's value can anything greater than `0`, and its disabled by default _(meaning the client will wait forever for response)_. * **`flag`**(_integer_): This PyZMQ attribute value can be either `0` or `zmq.NOBLOCK`_( i.e. 1)_. More information can be found [here ➶](https://pyzmq.readthedocs.io/en/latest/api/zmq.html). diff --git a/docs/gears/screengear/params.md b/docs/gears/screengear/params.md index 2202c912d..c12f96ad5 100644 --- a/docs/gears/screengear/params.md +++ b/docs/gears/screengear/params.md @@ -131,17 +131,18 @@ ScreenGear(colorspace="COLOR_BGR2HSV") This parameter provides the flexibility to manually set the dimensions of capture screen area. -!!! info "Supported Dimensional Parameters" +!!! info "Supported Dimensional Attributes" - Supported Dimensional Parameters are as follows: + ScreenGear API takes `left`, `top`, `width`, `height` coordinates of the bounding box of capture screen area(ROI), similar to [PIL.ImageGrab.grab](https://pillow.readthedocs.io/en/stable/reference/ImageGrab.html), defined below: + +

+ ScreenGear ROI region +

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

+ ScreenGear ROI region +

+ + * **`left`:** the x-coordinate of the upper-left corner of the region + * **`top`:** the y-coordinate of the upper-left corner of the region + * **`width`:** the width of the complete region from left to the bottom-right corner of the region. + * **`height`:** the height of the complete region from top to the bottom-right corner of the region. + The complete usage example is as follows: diff --git a/docs/gears/writegear/compression/params.md b/docs/gears/writegear/compression/params.md index f81393aa3..d2949d451 100644 --- a/docs/gears/writegear/compression/params.md +++ b/docs/gears/writegear/compression/params.md @@ -176,6 +176,17 @@ This parameter allows us to exploit almost all FFmpeg supported parameters effor "-ffpreheaders": ["-re"], # executes as `ffmpeg -re ` } ``` + + * **`-disable_ffmpeg_window`** _(bool)_: sets a special flag to enable detached subprocess creation on Windows OS, and can be useful while creating an `.exe` file for a python script that uses WriteGear API. On Windows, in certain cases, even after creating the `.exe` file in windowed mode or no-console mode, the FFmpeg commandline window would pop up while its being used by WriteGear API. Its usage is as follows: + + ??? new "New in v0.3.2" + This feature was added in `v0.3.2`. + + !!! warning "`-disable_ffmpeg_window` is only available on Windows OS with logging disabled(`logging=False`) in compression mode." + + ```python + output_params = {"-disable_ffmpeg_window": True} # disables FFmpeg creation window + ``` * **`-disable_force_termination`** _(bool)_: sets a special flag to manually disable the default forced-termination behaviour in WriteGear API when `-i` FFmpeg parameter is used _(For more details, see issue: #149)_. Its usage is as follows: diff --git a/docs/gears/writegear/non_compression/params.md b/docs/gears/writegear/non_compression/params.md index b9ae65f2a..034e7b351 100644 --- a/docs/gears/writegear/non_compression/params.md +++ b/docs/gears/writegear/non_compression/params.md @@ -149,7 +149,7 @@ To assign desired parameters in Non-Compression Mode, you can format it as dicti # format parameter as dictionary attribute output_params = {"-fps":30} # and then, assign it -WriteGear(output = 'output.mp4', **output_params) +WriteGear(output = 'output.mp4', compression_mode=False, **output_params) ``` !!! example "Its usage example can be found [here ➶](../usage/#using-non-compression-mode-with-videocapture-gears)." @@ -170,7 +170,7 @@ To select desired FOURCC codec in Non-Compression Mode, you can format it as dic # format codec as dictionary attribute output_params = {"-fourcc":"MJPG"} # and then, assign it -WriteGear(output = 'output.mp4', **output_params) +WriteGear(output = 'output.mp4', compression_mode=False, **output_params) ``` !!! example "Its usage example can be found [here ➶](../usage/#using-non-compression-mode-with-videocapture-gears)." @@ -188,7 +188,7 @@ This parameter enables logging _(if `True`)_, essential for debugging. **Usage:** ```python -WriteGear(output = 'output.mp4', logging=True) +WriteGear(output = 'output.mp4', compression_mode=False, logging=True) ```   diff --git a/docs/overrides/assets/images/screengear_region.png b/docs/overrides/assets/images/screengear_region.png new file mode 100644 index 000000000..a884fb261 Binary files /dev/null and b/docs/overrides/assets/images/screengear_region.png differ diff --git a/scripts/bash/prepare_dataset.sh b/scripts/bash/prepare_dataset.sh index e7fcdac8b..8701ef4e1 100644 --- a/scripts/bash/prepare_dataset.sh +++ b/scripts/bash/prepare_dataset.sh @@ -26,7 +26,7 @@ mkdir -p "$TMPFOLDER"/Downloads mkdir -p "$TMPFOLDER"/Downloads/{FFmpeg_static,Test_videos} # Acknowledging machine architecture -MACHINE_BIT=$(uname -m) +# MACHINE_BIT=$(uname -m) #Defining alternate ffmpeg static binaries date/version ALTBINARIES_DATE="12-07-2022" @@ -48,12 +48,12 @@ msys*) esac #Download and Configure FFmpeg Static -cd "$TMPFOLDER"/Downloads/FFmpeg_static +cd "$TMPFOLDER"/Downloads/FFmpeg_static || exit if [ $OS_NAME = "linux" ]; then echo "Downloading Linux64 Static FFmpeg Binaries..." - curl -LO https://github.com/abhiTronix/ffmpeg-static-builds/raw/master/$ALTBINARIES_DATE/linux/ffmpeg-git-amd64-static.tar.xz + curl -LO https://gitlab.com/abhiTronix/ffmpeg-static-builds/-/raw/master/$ALTBINARIES_DATE/linux/ffmpeg-git-amd64-static.tar.xz tar -xJf ffmpeg-git-amd64-static.tar.xz rm *.tar.* mv ffmpeg* ffmpeg @@ -61,7 +61,7 @@ if [ $OS_NAME = "linux" ]; then elif [ $OS_NAME = "windows" ]; then echo "Downloading Win64 Static FFmpeg Binaries..." - curl -LO https://github.com/abhiTronix/ffmpeg-static-builds/raw/master/$ALTBINARIES_DATE/windows/ffmpeg-latest-win64-static.zip + curl -LO https://gitlab.com/abhiTronix/ffmpeg-static-builds/-/raw/master/$ALTBINARIES_DATE/windows/ffmpeg-latest-win64-static.zip unzip -qq ffmpeg-latest-win64-static.zip rm ffmpeg-latest-win64-static.zip mv ffmpeg-latest-win64-static ffmpeg @@ -69,7 +69,7 @@ elif [ $OS_NAME = "windows" ]; then else echo "Downloading MacOS64 Static FFmpeg Binary..." - curl -LO https://github.com/abhiTronix/ffmpeg-static-builds/raw/master/$ALTBINARIES_DATE/macOS/ffmpeg-latest-macos64-static.zip + curl -LO https://gitlab.com/abhiTronix/ffmpeg-static-builds/-/raw/master/$ALTBINARIES_DATE/macOS/ffmpeg-latest-macos64-static.zip unzip -qq ffmpeg-latest-macos64-static.zip rm ffmpeg-latest-macos64-static.zip mv ffmpeg-latest-macos64-static ffmpeg diff --git a/vidgear/gears/netgear.py b/vidgear/gears/netgear.py index 2765f8ba7..3ef639a84 100644 --- a/vidgear/gears/netgear.py +++ b/vidgear/gears/netgear.py @@ -119,7 +119,6 @@ def __init__( logging=False, **options ): - """ This constructor method initializes the object state and attributes of the NetGear class. @@ -241,6 +240,9 @@ def __init__( self.__max_retries = 3 # request timeout self.__request_timeout = 4000 # 4 secs + else: + # subscriber timeout + self.__subscriber_timeout = None # Handle user-defined options dictionary values # reformat dictionary @@ -384,6 +386,7 @@ def __init__( self.__max_retries = value else: logger.warning("Invalid `max_retries` value skipped!") + # assign request timeout in synchronous patterns elif key == "request_timeout" and isinstance(value, int) and pattern < 2: if value >= 4: @@ -391,6 +394,15 @@ def __init__( else: logger.warning("Invalid `request_timeout` value skipped!") + # assign subscriber timeout + elif ( + key == "subscriber_timeout" and isinstance(value, int) and pattern == 2 + ): + if value > 0: + self.__subscriber_timeout = value * 1000 # covert to milliseconds + else: + logger.warning("Invalid `request_timeout` value skipped!") + # handle ZMQ flags elif key == "flag" and isinstance(value, int): self.__msg_flag = value @@ -403,7 +415,6 @@ def __init__( # Handle Secure mode if self.__secure_mode: - # activate and log if overwriting is enabled if overwrite_cert: if not receive_mode: @@ -525,7 +536,6 @@ def __init__( # check whether `receive_mode` is enabled if self.__receive_mode: - # define connection address if address is None: address = "*" # define address @@ -609,9 +619,15 @@ def __init__( # enable CURVE connection for this socket self.__msg_socket.curve_server = True - # define exclusive socket options for patterns + # define exclusive socket options for `patterns=2` if self.__pattern == 2: self.__msg_socket.setsockopt_string(zmq.SUBSCRIBE, "") + self.__subscriber_timeout and self.__msg_socket.setsockopt( + zmq.RCVTIMEO, self.__subscriber_timeout + ) + self.__subscriber_timeout and self.__msg_socket.setsockopt( + zmq.LINGER, 0 + ) # if multiserver_mode is enabled, then assign port addresses to zmq socket if self.__multiserver_mode: @@ -640,12 +656,17 @@ def __init__( ) self.__msg_pattern = msg_pattern[1] self.__poll.register(self.__msg_socket, zmq.POLLIN) - self.__logging and logger.debug( "Reliable transmission is enabled for this pattern with max-retries: {} and timeout: {} secs.".format( self.__max_retries, self.__request_timeout / 1000 ) ) + else: + self.__logging and self.__subscriber_timeout and logger.debug( + "Timeout: {} secs is enabled for this system.".format( + self.__subscriber_timeout / 1000 + ) + ) except Exception as e: # otherwise log and raise error @@ -721,7 +742,6 @@ def __init__( logger.debug("Receive Mode is now activated.") else: - # otherwise default to `Send Mode` # define connection address @@ -931,7 +951,6 @@ def __init__( ) def __recv_handler(self): - """ A threaded receiver handler, that keep iterating data from ZMQ socket to a internally monitored deque, until the thread is terminated, or socket disconnects. @@ -941,7 +960,6 @@ def __recv_handler(self): # keep looping infinitely until the thread is terminated while not self.__terminate: - # check queue buffer for overflow if len(self.__queue) >= 96: # stop iterating if overflowing occurs @@ -986,7 +1004,14 @@ def __recv_handler(self): continue else: - msg_json = self.__msg_socket.recv_json(flags=self.__msg_flag) + try: + msg_json = self.__msg_socket.recv_json(flags=self.__msg_flag) + except zmq.ZMQError as e: + if e.errno == zmq.EAGAIN: + logger.critical("Connection Timeout. Exiting!") + self.__terminate = True + self.__queue.append(None) + break # check if terminate_flag` received if msg_json["terminate_flag"]: @@ -1302,7 +1327,6 @@ def send(self, frame, message=None): if self.__pattern < 2: # check if Bidirectional data transmission is enabled if self.__bi_mode or self.__multiclient_mode: - # handles return data recvd_data = None @@ -1452,9 +1476,12 @@ def send(self, frame, message=None): # log confirmation self.__logging and logger.debug(recv_confirmation) - def close(self): + def close(self, kill=False): """ Safely terminates the threads, and NetGear resources. + + Parameters: + kill (bool): Kills ZMQ context instead of graceful exiting in receive mode. """ # log it self.__logging and logger.debug( @@ -1469,20 +1496,29 @@ def close(self): self.__queue.clear() # call immediate termination self.__terminate = True - # wait until stream resources are released (producer thread might be still grabbing frame) + # properly close the socket + self.__logging and logger.debug("Terminating. Please wait...") + # wait until stream resources are released + # (producer thread might be still grabbing frame) if self.__thread is not None: # properly handle thread exit - self.__thread.join() + if self.__thread.is_alive() and kill: + # force close if still alive + logger.warning("Thread still running...Killing it forcefully!") + self.__msg_context.destroy() + self.__thread.join() + else: + self.__thread.join() + self.__msg_socket.close(linger=0) self.__thread = None - self.__logging and logger.debug("Terminating. Please wait...") - # properly close the socket - self.__msg_socket.close(linger=0) self.__logging and logger.debug("Terminated Successfully!") - else: # indicate that process should be terminated self.__terminate = True - + # log if kill enabled + kill and logger.warning( + "`kill` parmeter is only available in the receive mode." + ) # check if all attempts of reconnecting failed, then skip to closure if (self.__pattern < 2 and not self.__max_retries) or ( self.__multiclient_mode and not self.__port_buffer diff --git a/vidgear/gears/screengear.py b/vidgear/gears/screengear.py index c5aa17318..64ab630ee 100644 --- a/vidgear/gears/screengear.py +++ b/vidgear/gears/screengear.py @@ -106,7 +106,11 @@ def __init__( } # check whether user-defined dimensions are provided if screen_dims and len(screen_dims) == 4: - key_order = ("top", "left", "width", "height") + key_order = ( + ("top", "left", "width", "height") + if self.__backend != "dxcam" + else ("left", "top", "width", "height") + ) screen_dims = OrderedDict((k, screen_dims[k]) for k in key_order) logging and logger.debug( "Setting Capture-Area dimensions: {}".format(json.dumps(screen_dims)) @@ -165,11 +169,7 @@ def __init__( # raise error(s) for critical Class imports import_dependency_safe("pyscreenshot" if pysct is None else "") # reset backend if not provided - self.__backend = ( - "pil" - if self.__backend is None or self.__backend == "mss" - else self.__backend - ) + self.__backend = "pil" if self.__backend is None else self.__backend # check if valid backend assert ( self.__backend in pysct.backends() diff --git a/vidgear/gears/stabilizer.py b/vidgear/gears/stabilizer.py index 1bbe557e8..5bd998ab2 100644 --- a/vidgear/gears/stabilizer.py +++ b/vidgear/gears/stabilizer.py @@ -59,7 +59,6 @@ def __init__( crop_n_zoom=False, logging=False, ): - """ This constructor method initializes the object state and attributes of the Stabilizer class. @@ -67,7 +66,7 @@ def __init__( smoothing_radius (int): alter averaging window size. border_type (str): changes the extended border type. border_size (int): enables and set the value for extended border size to reduce the black borders. - crop_n_zoom (bool): enables croping and zooming of frames(to original size) to reduce the black borders. + crop_n_zoom (bool): enables cropping and zooming of frames(to original size) to reduce the black borders. logging (bool): enables/disables logging. """ # print current version @@ -88,9 +87,9 @@ def __init__( # initialize global vars self.__smoothing_radius = smoothing_radius # averaging window, handles the quality of stabilization at expense of latency and sudden panning self.__smoothed_path = None # handles the smoothed path with box filter - self.__path = None # handles path i.e cumulative sum of pevious_2_current transformations along a axis - self.__transforms = [] # handles pevious_2_current transformations [dx,dy,da] - self.__frame_transforms_smoothed = None # handles smoothed array of pevious_2_current transformations w.r.t to frames + self.__path = None # handles path i.e cumulative sum of previous_2_current transformations along a axis + self.__transforms = [] # handles previous_2_current transformations [dx,dy,da] + self.__frame_transforms_smoothed = None # handles smoothed array of previous_2_current transformations w.r.t to frames self.__previous_gray = None # handles previous gray frame self.__previous_keypoints = ( None # handles previous detect_GFTTed keypoints w.r.t previous gray frame @@ -193,24 +192,13 @@ def stabilize(self, frame): : ] # save gray frame clone for further processing - elif self.__frame_queue_indexes[-1] <= self.__smoothing_radius - 1: + elif self.__frame_queue_indexes[-1] < self.__smoothing_radius - 1: # for rest of frames self.__frame_queue.append(frame) # save frame to deque self.__frame_queue_indexes.append( self.__frame_queue_indexes[-1] + 1 ) # save frame index self.__generate_transformations() # generate transformations - if self.__frame_queue_indexes[-1] == self.__smoothing_radius - 1: - # calculate smooth path once transformation capturing is completed - for i in range(3): - # apply normalized box filter to the path - self.__smoothed_path[:, i] = self.__box_filter_convolve( - (self.__path[:, i]), window_size=self.__smoothing_radius - ) - # calculate deviation of path from smoothed path - deviation = self.__smoothed_path - self.__path - # save smoothed transformation - self.__frame_transforms_smoothed = self.frame_transform + deviation else: # start applying transformations self.__frame_queue.append(frame) # save frame to deque @@ -253,7 +241,7 @@ def __generate_transformations(self): status == 1 ] # previous - # calculate optimal affine transformation between pevious_2_current key-points + # calculate optimal affine transformation between previous_2_current key-points if self.__cv2_version == 3: # backward compatibility with OpenCV3 transformation = cv2.estimateRigidTransform( @@ -270,11 +258,11 @@ def __generate_transformations(self): # check if transformation is not None if not (transformation is None): - # pevious_2_current translation in x direction + # previous_2_current translation in x direction dx = transformation[0, 2] - # pevious_2_current translation in y direction + # previous_2_current translation in y direction dy = transformation[1, 2] - # pevious_2_current rotation in angle + # previous_2_current rotation in angle da = np.arctan2(transformation[1, 0], transformation[0, 0]) else: # otherwise zero it diff --git a/vidgear/gears/writegear.py b/vidgear/gears/writegear.py index 8c537d8a8..1d041d7a6 100644 --- a/vidgear/gears/writegear.py +++ b/vidgear/gears/writegear.py @@ -83,7 +83,6 @@ def __init__( logging=False, **output_params ): - """ This constructor method initializes the object state and attributes of the WriteGear class. @@ -92,6 +91,7 @@ def __init__( compression_mode (bool): selects the WriteGear's Primary Mode of Operation. custom_ffmpeg (str): assigns the location of custom path/directory for custom FFmpeg executables. logging (bool): enables/disables logging. + ffmpeg_subprocess_creation_window: output_params (dict): provides the flexibility to control supported internal parameters and FFmpeg properities. """ # print current version @@ -122,6 +122,9 @@ def __init__( self.__initiate_process = ( True # handles initiate one-time process for generating pipeline ) + self.__ffmpeg_window_disabler_patch = ( + False # handles disabling window for ffmpeg subprocess on Windows + ) self.__out_file = None # handles output gstpipeline_mode = False # handles GStreamer Pipeline Mode @@ -244,6 +247,22 @@ def __init__( True if ("-i" in self.__output_parameters) else False ) + # handles disabling window for ffmpeg subprocess on Windows OS (only for Compression mode) + # this patch prevents ffmpeg creation window from opening when building exe files + ffmpeg_window_disabler_patch = self.__output_parameters.pop( + "-disable_ffmpeg_window", False + ) + # check if value is valid + if not self.__os_windows or logging: + logger.warning( + "Optional `-disable_ffmpeg_window` flag is only available on Windows OS with `logging=False`. Discarding!" + ) + elif isinstance(ffmpeg_window_disabler_patch, bool): + self.__ffmpeg_window_disabler_patch = ffmpeg_window_disabler_patch + else: + # handle improper values + self.__ffmpeg_window_disabler_patch = False + # validate the FFmpeg path/binaries and returns valid executable FFmpeg # location/path (also auto-downloads static binaries on Windows OS) self.__ffmpeg = get_valid_ffmpeg_path( @@ -352,7 +371,6 @@ def __init__( ) def write(self, frame, rgb_mode=False): - """ Pipelines `ndarray` frames to respective API _(**FFmpeg** in Compression Mode & **OpenCV's VideoWriter API** in Non-Compression Mode)_. @@ -516,7 +534,6 @@ def __PreprocessFFParams(self, channels, dtype=None, rgb=False): ) def __start_FFProcess(self, input_params, output_params): - """ An Internal method that launches FFmpeg subprocess pipeline in Compression Mode for pipelining frames to `stdin`. @@ -595,7 +612,13 @@ def __start_FFProcess(self, input_params, output_params): else: # In silent mode self.__process = sp.Popen( - cmd, stdin=sp.PIPE, stdout=sp.DEVNULL, stderr=sp.STDOUT + cmd, + stdin=sp.PIPE, + stdout=sp.DEVNULL, + stderr=sp.STDOUT, + creationflags=( # this prevents ffmpeg creation window from opening when building exe files on Windows + sp.DETACHED_PROCESS if self.__ffmpeg_window_disabler_patch else 0 + ), ) def __enter__(self): diff --git a/vidgear/tests/network_tests/test_netgear.py b/vidgear/tests/network_tests/test_netgear.py index 42aecf4f1..6875dc76a 100644 --- a/vidgear/tests/network_tests/test_netgear.py +++ b/vidgear/tests/network_tests/test_netgear.py @@ -133,7 +133,13 @@ def test_patterns(pattern): Testing NetGear different messaging patterns """ # define parameters - options = {"flag": 0, "copy": False, "track": False, "jpeg_compression": False} + options = { + "flag": 0, + "copy": False, + "track": False, + "jpeg_compression": False, + "subscriber_timeout": 5, + } # initialize frame_server = None stream = None @@ -167,9 +173,9 @@ def test_patterns(pattern): if not (stream is None): stream.release() if not (server is None): - server.close() + server.close(kill=True) if not (client is None): - client.close() + client.close(kill=True) @pytest.mark.parametrize( @@ -239,9 +245,9 @@ def test_compression(options_server): if not (stream is None): stream.stop() if not (server is None): - server.close() + server.close(kill=True) if not (client is None): - client.close() + client.close(kill=True) test_data_class = [ @@ -304,9 +310,9 @@ def test_secure_mode(pattern, security_mech, custom_cert_location, overwrite_cer if not (stream is None): stream.release() if not (server is None): - server.close() + server.close(kill=True) if not (client is None): - client.close() + client.close(kill=True) @pytest.mark.parametrize( @@ -418,9 +424,9 @@ def test_bidirectional_mode(pattern, target_data, options): if not (stream is None): stream.stop() if not (server is None): - server.close() + server.close(kill=True) if not (client is None): - client.close() + client.close(kill=True) @pytest.mark.parametrize( @@ -455,6 +461,7 @@ def test_bidirectional_mode(pattern, target_data, options): { "multiserver_mode": True, "ssh_tunnel_mode": "new@sdf.org", + "subscriber_timeout": 0, }, ), ], @@ -647,6 +654,7 @@ def test_multiclient_mode(pattern): }, {"max_retries": 2, "request_timeout": 4, "multiclient_mode": True}, {"max_retries": 2, "request_timeout": -1, "multiserver_mode": True}, + {"subscriber_timeout": 4}, ], ) def test_client_reliablity(options): @@ -658,7 +666,7 @@ def test_client_reliablity(options): try: # define params client = NetGear( - pattern=1, + pattern=2 if "subscriber_timeout" in options.keys() else 1, port=[5587] if "multiserver_mode" in options.keys() else 6657, receive_mode=True, logging=True, @@ -747,8 +755,8 @@ def test_server_reliablity(options): @pytest.mark.parametrize( "server_ports, client_ports, options", [ - (0, 5555, {"multiserver_mode": True}), - (5555, 0, {"multiclient_mode": True}), + (None, 5555, {"multiserver_mode": True}), + (5555, None, {"multiclient_mode": True}), ], ) @pytest.mark.xfail(raises=ValueError) diff --git a/vidgear/tests/writer_tests/test_compression_mode.py b/vidgear/tests/writer_tests/test_compression_mode.py index aac1fa472..262877ded 100644 --- a/vidgear/tests/writer_tests/test_compression_mode.py +++ b/vidgear/tests/writer_tests/test_compression_mode.py @@ -229,6 +229,7 @@ def test_output_dimensions(): output_params = { "-output_dimensions": dimensions, "-ffmpeg_download_path": tempfile.gettempdir(), + "-disable_ffmpeg_window": True, } else: output_params = {"-output_dimensions": dimensions} @@ -264,7 +265,13 @@ def test_output_dimensions(): ( "Output2.mp4", "", - {"-vcodec": "libx264", "-crf": 0, "-preset": "fast", "-ffpreheaders": False}, + { + "-vcodec": "libx264", + "-crf": 0, + "-preset": "fast", + "-ffpreheaders": False, + "-disable_ffmpeg_window": True, + }, True, ), ( @@ -276,6 +283,7 @@ def test_output_dimensions(): "-crf": 0, "-preset": "veryfast", "-ffpreheaders": ["-re"], + "-disable_ffmpeg_window": "Invalid", }, True, ), diff --git a/vidgear/tests/writer_tests/test_non_compression_mode.py b/vidgear/tests/writer_tests/test_non_compression_mode.py index 69f555e60..c9c45bfd2 100644 --- a/vidgear/tests/writer_tests/test_non_compression_mode.py +++ b/vidgear/tests/writer_tests/test_non_compression_mode.py @@ -134,6 +134,7 @@ def test_write(conversion): "-fourcc": "DIVX", "-fps": 25, "-backend": "CAP_FFMPEG", + "-disable_ffmpeg_window": True, "-color": True, "-gst_pipeline_mode": False, },