Skip to content

Commit

Permalink
Fix #12 and #13
Browse files Browse the repository at this point in the history
  • Loading branch information
egeakman committed Apr 3, 2024
1 parent c60b7f4 commit 568ae1d
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 27 deletions.
4 changes: 2 additions & 2 deletions mjpeg_streamer/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from .server import MjpegServer
from .server import MjpegServer, Server
from .stream import CustomStream, ManagedStream, Stream

__all__ = ["CustomStream", "ManagedStream", "MjpegServer", "Stream"]
__all__ = ["CustomStream", "ManagedStream", "MjpegServer", "Stream", "Server"]
__version__ = "2024.2.8"
28 changes: 13 additions & 15 deletions mjpeg_streamer/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from typing import List, Union

import aiohttp
import netifaces
from aiohttp import MultipartWriter, web
from aiohttp.web_runner import GracefulExit
from multidict import MultiDict
Expand Down Expand Up @@ -51,29 +50,20 @@ async def __call__(self, request: web.Request) -> web.StreamResponse:
return response


class MjpegServer:
class Server:
def __init__(
self, host: Union[str, List[str,]] = "localhost", port: int = 8080
) -> None:
if isinstance(host, str) and host != "0.0.0.0":
if isinstance(host, str):
self._host: List[str,] = [
host,
]
elif isinstance(host, list):
if "0.0.0.0" in host:
host.remove("0.0.0.0")
host = host + [
netifaces.ifaddresses(iface)[netifaces.AF_INET][0]["addr"]
for iface in netifaces.interfaces()
if netifaces.AF_INET in netifaces.ifaddresses(iface)
]
host = ["0.0.0.0"]
if "localhost" in host and "127.0.0.1" in host:
host.remove("localhost")
self._host = list(set(host))
else:
self._host = [
netifaces.ifaddresses(iface)[netifaces.AF_INET][0]["addr"]
for iface in netifaces.interfaces()
if netifaces.AF_INET in netifaces.ifaddresses(iface)
]
self._port = port
self._app: web.Application = web.Application()
self._app_is_running: bool = False
Expand Down Expand Up @@ -131,3 +121,11 @@ def stop(self) -> None:
print("\nServer stopped\n")
else:
print("\nServer is not running\n")


class MjpegServer(Server):
# Alias for Server, to maintain backwards compatibility
def __init__(
self, host: Union[str, List[str,]] = "localhost", port: int = 8080
) -> None:
super().__init__(host, port)
26 changes: 17 additions & 9 deletions mjpeg_streamer/stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,6 @@ def __init__(
self._deque_background_task: Optional[asyncio.Task] = None
self._active_viewers: Set[str,] = set()

async def _add_viewer(self, viewer_token: Optional[str] = None) -> str:
viewer_token = viewer_token or str(uuid.uuid4())
self._active_viewers.add(viewer_token)
return viewer_token

async def _remove_viewer(self, viewer_token: str) -> None:
self._active_viewers.discard(viewer_token)

async def __clear_deque(self) -> None:
while True:
await asyncio.sleep(1 / self.fps)
Expand All @@ -44,6 +36,14 @@ async def __clear_deque(self) -> None:
):
self._frame_buffer.clear()

async def _add_viewer(self, viewer_token: Optional[str] = None) -> str:
viewer_token = viewer_token or str(uuid.uuid4())
self._active_viewers.add(viewer_token)
return viewer_token

async def _remove_viewer(self, viewer_token: str) -> None:
self._active_viewers.discard(viewer_token)

async def _ensure_background_tasks(self) -> None:
if self._deque_background_task is None or self._deque_background_task.done():
self._deque_background_task = asyncio.create_task(self.__clear_deque())
Expand Down Expand Up @@ -85,7 +85,8 @@ def set_fps(self, fps: int) -> None:
async def _get_frame(self) -> np.ndarray:
# A little hacky, if you have a better way, please let me know
await self._ensure_background_tasks()
# Checking here to avoid continous polling
# Checking the encoding here instead of set_frame
# to avoid continous polling
if self._check_encoding(self._frame) != "jpeg":
raise ValueError(
"Input is not an encoded JPEG frame. Use OpenCV's imencode method to encode the frame to JPEG."
Expand Down Expand Up @@ -113,6 +114,7 @@ def __init__(
) -> None:
self.size = size
self.quality = max(1, min(quality, 100))
self._last_processed_frame: np.ndarray = np.zeros((320, 240, 1), dtype=np.uint8)
super().__init__(name, fps)

async def __process_current_frame(self) -> np.ndarray:
Expand All @@ -131,9 +133,12 @@ async def __process_current_frame(self) -> np.ndarray:
)
self._frame_buffer.append(len(frame.tobytes()))
self._bandwidth_last_modified_time = time.time()
self._last_processed_frame = frame
return frame

async def _get_frame(self) -> np.ndarray:
if time.time() - self._bandwidth_last_modified_time <= 1 / self.fps:
return self._last_processed_frame
await self._ensure_background_tasks()
async with self._lock:
return await self.__process_current_frame()
Expand Down Expand Up @@ -242,12 +247,15 @@ async def __process_current_frame(self) -> np.ndarray:
raise ValueError("Error encoding frame")
self._frame_buffer.append(len(frame.tobytes()))
self._bandwidth_last_modified_time = time.time()
self._last_processed_frame = frame
return frame

async def _get_frame(self) -> np.ndarray:
if not self._is_running:
print("Stream is not running, please call the start method first.")
return self._frame
if time.time() - self._bandwidth_last_modified_time <= 1 / self.fps:
return self._last_processed_frame
await self._ensure_background_tasks()
async with self._lock:
return await self.__process_current_frame()
Expand Down
1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ dependencies = [
'aiohttp==3.8.6; python_version >= "3.6" and python_version <= "3.7"',
'aiohttp==3.9.1; python_version == "3.8"',
'aiohttp; python_version >= "3.9"',
"netifaces",
'numpy==1.19.5; python_version == "3.6"',
'numpy==1.21.6; python_version == "3.7"',
'numpy==1.24.4; python_version == "3.8"',
Expand Down

0 comments on commit 568ae1d

Please sign in to comment.