Add back pipfiles, README

This commit is contained in:
Bobrock
2020-12-18 08:48:56 -06:00
parent 856ede1b39
commit 84ed8481e2
7 changed files with 325 additions and 288 deletions

16
Pipfile Normal file
View File

@@ -0,0 +1,16 @@
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true
[dev-packages]
[packages]
pillow = "*"
pyyaml = "*"
requests = "*"
numpy = "*"
opencv-python = "*"
pysocks = "*"
[requires]

169
Pipfile.lock generated Normal file
View File

@@ -0,0 +1,169 @@
{
"_meta": {
"hash": {
"sha256": "6700bce6ed08db166eff9d3105158923ffd2ffbf35c814a4d0133552bda03b5a"
},
"pipfile-spec": 6,
"requires": {},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"certifi": {
"hashes": [
"sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3",
"sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f"
],
"version": "==2019.11.28"
},
"chardet": {
"hashes": [
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
],
"version": "==3.0.4"
},
"idna": {
"hashes": [
"sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb",
"sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"
],
"version": "==2.9"
},
"numpy": {
"hashes": [
"sha256:1786a08236f2c92ae0e70423c45e1e62788ed33028f94ca99c4df03f5be6b3c6",
"sha256:17aa7a81fe7599a10f2b7d95856dc5cf84a4eefa45bc96123cbbc3ebc568994e",
"sha256:20b26aaa5b3da029942cdcce719b363dbe58696ad182aff0e5dcb1687ec946dc",
"sha256:2d75908ab3ced4223ccba595b48e538afa5ecc37405923d1fea6906d7c3a50bc",
"sha256:39d2c685af15d3ce682c99ce5925cc66efc824652e10990d2462dfe9b8918c6a",
"sha256:56bc8ded6fcd9adea90f65377438f9fea8c05fcf7c5ba766bef258d0da1554aa",
"sha256:590355aeade1a2eaba17617c19edccb7db8d78760175256e3cf94590a1a964f3",
"sha256:70a840a26f4e61defa7bdf811d7498a284ced303dfbc35acb7be12a39b2aa121",
"sha256:77c3bfe65d8560487052ad55c6998a04b654c2fbc36d546aef2b2e511e760971",
"sha256:9537eecf179f566fd1c160a2e912ca0b8e02d773af0a7a1120ad4f7507cd0d26",
"sha256:9acdf933c1fd263c513a2df3dceecea6f3ff4419d80bf238510976bf9bcb26cd",
"sha256:ae0975f42ab1f28364dcda3dde3cf6c1ddab3e1d4b2909da0cb0191fa9ca0480",
"sha256:b3af02ecc999c8003e538e60c89a2b37646b39b688d4e44d7373e11c2debabec",
"sha256:b6ff59cee96b454516e47e7721098e6ceebef435e3e21ac2d6c3b8b02628eb77",
"sha256:b765ed3930b92812aa698a455847141869ef755a87e099fddd4ccf9d81fffb57",
"sha256:c98c5ffd7d41611407a1103ae11c8b634ad6a43606eca3e2a5a269e5d6e8eb07",
"sha256:cf7eb6b1025d3e169989416b1adcd676624c2dbed9e3bcb7137f51bfc8cc2572",
"sha256:d92350c22b150c1cae7ebb0ee8b5670cc84848f6359cf6b5d8f86617098a9b73",
"sha256:e422c3152921cece8b6a2fb6b0b4d73b6579bd20ae075e7d15143e711f3ca2ca",
"sha256:e840f552a509e3380b0f0ec977e8124d0dc34dc0e68289ca28f4d7c1d0d79474",
"sha256:f3d0a94ad151870978fb93538e95411c83899c9dc63e6fb65542f769568ecfa5"
],
"index": "pypi",
"version": "==1.18.1"
},
"opencv-python": {
"hashes": [
"sha256:0f2e739c582e8c5e432130648bc6d66a56bc65f4cd9ff0bc7033033d2130c7a3",
"sha256:0f3d159ad6cb9cbd188c726f87485f0799a067a0a15f34c25d7b5c8db3cb2e50",
"sha256:167a6aff9bd124a3a67e0ec25d0da5ecdc8d96a56405e3e5e7d586c4105eb1bb",
"sha256:1b90d50bc7a31e9573a8da1b80fcd1e4d9c86c0e5f76387858e1b87eb8b0332b",
"sha256:2baf1213ae2fd678991f905d7b2b94eddfdfb5f75757db0f0b31eebd48ca200d",
"sha256:312dda54c7e809c20d7409418060ae0e9cdbe82975e7ced429eb3c234ffc0d4a",
"sha256:32384e675f7cefe707cac40a95eeb142d6869065e39c5500374116297cd8ca6d",
"sha256:5c50634dd8f2f866fd99fd939292ce10e52bef82804ebc4e7f915221c3b7e951",
"sha256:6841bb9cc24751dbdf94e7eefc4e6d70ec297952501954471299fd12ab67391c",
"sha256:68c1c846dd267cd7e293d3fc0bb238db0a744aa1f2e721e327598f00cb982098",
"sha256:703910aaa1dcd25a412f78a190fb7a352d9a64ee7d9a35566d786f3cc66ebf20",
"sha256:8002959146ed21959e3118c60c8e94ceac02eea15b691da6c62cff4787c63f7f",
"sha256:889eef049d38488b5b4646c48a831feed37c0fd44f3d83c05cff80f4baded145",
"sha256:8c76983c9ec3e4cf3a4c1d172ec4285332d9fb1c7194d724aff0c518437471ee",
"sha256:9cd9bd72f4a9743ef6f11f0f96784bd215a542e996db1717d4c2d3d03eb81a1b",
"sha256:a1a5517301dc8d56243a14253d231ec755b94486b4fff2ae68269bc941bb1f2e",
"sha256:a2b08aec2eacae868723136383d9eb84a33062a7a7ec5ec3bd2c423bd1355946",
"sha256:a8529a79233f3581a66984acd16bce52ab0163f6f77568dd69e9ee4956d2e1db",
"sha256:afbc81a3870739610a9f9a1197374d6a45892cf1933c90fc5617d39790991ed3",
"sha256:baeb5dd8b21c718580687f5b4efd03f8139b1c56239cdf6b9805c6946e80f268",
"sha256:db1d49b753e6e6c76585f21d09c7e9812176732baa9bddb64bc2fc6cd24d4179",
"sha256:e242ed419aeb2488e0f9ee6410a34917f0f8d62b3ae96aa3170d83bae75004e2",
"sha256:e36a8857be2c849e54009f1bee25e8c34fbc683fcd38c6c700af4cba5f8d57c2",
"sha256:e699232fd033ef0053efec2cba0a7505514f374ba7b18c732a77cb5304311ef9",
"sha256:eae3da9231d87980f8082d181c276a04f7a6fdac130cebd467390b96dd05f944",
"sha256:ee6814c94dbf1cae569302afef9dd29efafc52373e8770ded0db549a3b6e0c00",
"sha256:f01a87a015227d8af407161eb48222fc3c8b01661cdc841e2b86eee4f1a7a417"
],
"index": "pypi",
"version": "==4.2.0.32"
},
"pillow": {
"hashes": [
"sha256:0a628977ac2e01ca96aaae247ec2bd38e729631ddf2221b4b715446fd45505be",
"sha256:4d9ed9a64095e031435af120d3c910148067087541131e82b3e8db302f4c8946",
"sha256:54ebae163e8412aff0b9df1e88adab65788f5f5b58e625dc5c7f51eaf14a6837",
"sha256:5bfef0b1cdde9f33881c913af14e43db69815c7e8df429ceda4c70a5e529210f",
"sha256:5f3546ceb08089cedb9e8ff7e3f6a7042bb5b37c2a95d392fb027c3e53a2da00",
"sha256:5f7ae9126d16194f114435ebb79cc536b5682002a4fa57fa7bb2cbcde65f2f4d",
"sha256:62a889aeb0a79e50ecf5af272e9e3c164148f4bd9636cc6bcfa182a52c8b0533",
"sha256:7406f5a9b2fd966e79e6abdaf700585a4522e98d6559ce37fc52e5c955fade0a",
"sha256:8453f914f4e5a3d828281a6628cf517832abfa13ff50679a4848926dac7c0358",
"sha256:87269cc6ce1e3dee11f23fa515e4249ae678dbbe2704598a51cee76c52e19cda",
"sha256:875358310ed7abd5320f21dd97351d62de4929b0426cdb1eaa904b64ac36b435",
"sha256:8ac6ce7ff3892e5deaab7abaec763538ffd011f74dc1801d93d3c5fc541feee2",
"sha256:91b710e3353aea6fc758cdb7136d9bbdcb26b53cefe43e2cba953ac3ee1d3313",
"sha256:9d2ba4ed13af381233e2d810ff3bab84ef9f18430a9b336ab69eaf3cd24299ff",
"sha256:a62ec5e13e227399be73303ff301f2865bf68657d15ea50b038d25fc41097317",
"sha256:ab76e5580b0ed647a8d8d2d2daee170e8e9f8aad225ede314f684e297e3643c2",
"sha256:bf4003aa538af3f4205c5fac56eacaa67a6dd81e454ffd9e9f055fff9f1bc614",
"sha256:bf598d2e37cf8edb1a2f26ed3fb255191f5232badea4003c16301cb94ac5bdd0",
"sha256:c18f70dc27cc5d236f10e7834236aff60aadc71346a5bc1f4f83a4b3abee6386",
"sha256:c5ed816632204a2fc9486d784d8e0d0ae754347aba99c811458d69fcdfd2a2f9",
"sha256:dc058b7833184970d1248135b8b0ab702e6daa833be14035179f2acb78ff5636",
"sha256:ff3797f2f16bf9d17d53257612da84dd0758db33935777149b3334c01ff68865"
],
"index": "pypi",
"version": "==7.0.0"
},
"pysocks": {
"hashes": [
"sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299",
"sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5",
"sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0"
],
"index": "pypi",
"version": "==1.7.1"
},
"pyyaml": {
"hashes": [
"sha256:059b2ee3194d718896c0ad077dd8c043e5e909d9180f387ce42012662a4946d6",
"sha256:1cf708e2ac57f3aabc87405f04b86354f66799c8e62c28c5fc5f88b5521b2dbf",
"sha256:24521fa2890642614558b492b473bee0ac1f8057a7263156b02e8b14c88ce6f5",
"sha256:4fee71aa5bc6ed9d5f116327c04273e25ae31a3020386916905767ec4fc5317e",
"sha256:70024e02197337533eef7b85b068212420f950319cc8c580261963aefc75f811",
"sha256:74782fbd4d4f87ff04159e986886931456a1894c61229be9eaf4de6f6e44b99e",
"sha256:940532b111b1952befd7db542c370887a8611660d2b9becff75d39355303d82d",
"sha256:cb1f2f5e426dc9f07a7681419fe39cee823bb74f723f36f70399123f439e9b20",
"sha256:dbbb2379c19ed6042e8f11f2a2c66d39cceb8aeace421bfc29d085d93eda3689",
"sha256:e3a057b7a64f1222b56e47bcff5e4b94c4f61faac04c7c4ecb1985e18caa3994",
"sha256:e9f45bd5b92c7974e59bcd2dcc8631a6b6cc380a904725fce7bc08872e691615"
],
"index": "pypi",
"version": "==5.3"
},
"requests": {
"hashes": [
"sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee",
"sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6"
],
"index": "pypi",
"version": "==2.23.0"
},
"urllib3": {
"hashes": [
"sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc",
"sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc"
],
"version": "==1.25.8"
}
},
"develop": {}
}

149
README.md
View File

@@ -1,15 +1,146 @@
# (Forked) Reolink Python Api Client <h1 align="center"> Reolink Python Api Client </h1>
A Reolink Camera client written in Python.
_NB! for the original API client of this fork, go [here](https://github.com/ReolinkCameraAPI/reolink-python-api)_ <p align="center">
<img alt="Reolink Approval" src="https://img.shields.io/badge/reolink-approved-blue?style=flat-square">
<img alt="GitHub" src="https://img.shields.io/github/license/ReolinkCameraApi/reolink-python-api?style=flat-square">
<img alt="GitHub tag (latest SemVer)" src="https://img.shields.io/github/v/tag/ReolinkCameraApi/reolink-python-api?style=flat-square">
<img alt="PyPI" src="https://img.shields.io/pypi/v/reolink-api?style=flat-square">
<img alt="Discord" src="https://img.shields.io/discord/773257004911034389?style=flat-square">
</p>
### Purpose ---
This repository's purpose is to deliver a complete API for the Reolink Camera's, (tested on RLC-522) A Reolink Camera client written in Python. This repository's purpose **(with Reolink's full support)** is to deliver a complete API for the Reolink Cameras,
although they have a basic API document - it does not satisfy the need for extensive camera communication.
### Installation Check out our documentation for more information on how to use the software at [https://reolink.oleaintueri.com](https://reolink.oleaintueri.com)
```bash
python3 -m pip install git+https://github.com/barretobrock/reolink-python-api.git
```
Other Supported Languages:
- Go: [reolinkapigo](https://github.com/ReolinkCameraAPI/reolinkapigo)
### Join us on Discord
https://discord.gg/8z3fdAmZJP
### Sponsorship
<a href="https://oleaintueri.com"><img src="https://oleaintueri.com/images/oliv.svg" width="60px"/><img width="200px" style="padding-bottom: 10px" src="https://oleaintueri.com/images/oleaintueri.svg"/></a>
[Oleaintueri](https://oleaintueri.com) is sponsoring the development and maintenance of these projects within their organisation.
---
### Get started
Implement a "Camera" object by passing it an IP address, Username and Password. By instantiating the object, it will try retrieve a login token from the Reolink Camera. This token is necessary to interact with the Camera using other commands.
See the `examples` directory.
### Using the library as a Python Module
Install the package via Pip
pip install reolink-api==0.0.5
Install from GitHub
pip install git+https://github.com/ReolinkCameraAPI/reolink-python-api.git
## Contributors
---
### Styling and Standards
This project intends to stick with [PEP8](https://www.python.org/dev/peps/pep-0008/)
### How can I become a contributor?
#### Step 1
Get the Restful API calls by looking through the HTTP Requests made in the camera's web UI. I use Google Chrome developer mode (ctr + shift + i) -> Network.
#### Step 2
Fork the repository and make your changes.
#### Step 3
Make a pull request.
### API Requests Implementation Plan:
Stream:
- [X] Blocking RTSP stream
- [X] Non-Blocking RTSP stream
GET:
- [X] Login
- [X] Logout
- [X] Display -> OSD
- [X] Recording -> Encode (Clear and Fluent Stream)
- [X] Recording -> Advance (Scheduling)
- [X] Network -> General
- [X] Network -> Advanced
- [X] Network -> DDNS
- [X] Network -> NTP
- [X] Network -> E-mail
- [X] Network -> FTP
- [X] Network -> Push
- [X] Network -> WIFI
- [X] Alarm -> Motion
- [X] System -> General
- [X] System -> DST
- [X] System -> Information
- [ ] System -> Maintenance
- [X] System -> Performance
- [ ] System -> Reboot
- [X] User -> Online User
- [X] User -> Add User
- [X] User -> Manage User
- [X] Device -> HDD/SD Card
- [ ] Zoom
- [ ] Focus
- [ ] Image (Brightness, Contrast, Saturation, Hue, Sharp, Mirror, Rotate)
- [ ] Advanced Image (Anti-flicker, Exposure, White Balance, DayNight, Backlight, LED light, 3D-NR)
- [X] Image Data -> "Snap" Frame from Video Stream
SET:
- [X] Display -> OSD
- [X] Recording -> Encode (Clear and Fluent Stream)
- [ ] Recording -> Advance (Scheduling)
- [X] Network -> General
- [X] Network -> Advanced
- [ ] Network -> DDNS
- [ ] Network -> NTP
- [ ] Network -> E-mail
- [ ] Network -> FTP
- [ ] Network -> Push
- [X] Network -> WIFI
- [ ] Alarm -> Motion
- [ ] System -> General
- [ ] System -> DST
- [X] System -> Reboot
- [X] User -> Online User
- [X] User -> Add User
- [X] User -> Manage User
- [X] Device -> HDD/SD Card (Format)
- [x] PTZ
- [x] Zoom
- [x] Focus
- [X] Image (Brightness, Contrast, Saturation, Hue, Sharp, Mirror, Rotate)
- [X] Advanced Image (Anti-flicker, Exposure, White Balance, DayNight, Backlight, LED light, 3D-NR)
### Supported Cameras
Any Reolink camera that has a web UI should work. The other's requiring special Reolink clients
do not work and is not supported here.
- RLC-411WS
- RLC-423
- RLC-420-5MP
- RLC-410-5MP
- RLC-520

View File

@@ -1,133 +0,0 @@
import requests
from reolink_api.resthandle import Request
from .alarm import AlarmAPIMixin
from .device import DeviceAPIMixin
from .display import DisplayAPIMixin
from .download import DownloadAPIMixin
from .image import ImageAPIMixin
from .motion import MotionAPIMixin
from .network import NetworkAPIMixin
from .ptz import PtzAPIMixin
from .recording import RecordingAPIMixin
from .system import SystemAPIMixin
from .user import UserAPIMixin
from .zoom import ZoomAPIMixin
class APIHandler(AlarmAPIMixin,
DeviceAPIMixin,
DisplayAPIMixin,
DownloadAPIMixin,
ImageAPIMixin,
MotionAPIMixin,
NetworkAPIMixin,
PtzAPIMixin,
RecordingAPIMixin,
SystemAPIMixin,
UserAPIMixin,
ZoomAPIMixin):
"""
The APIHandler class is the backend part of the API, the actual API calls
are implemented in Mixins.
This handles communication directly with the camera.
Current camera's tested: RLC-411WS
All Code will try to follow the PEP 8 standard as described here: https://www.python.org/dev/peps/pep-0008/
"""
def __init__(self, ip: str, username: str, password: str, https=False, **kwargs):
"""
Initialise the Camera API Handler (maps api calls into python)
:param ip:
:param username:
:param password:
:param proxy: Add a proxy dict for requests to consume.
eg: {"http":"socks5://[username]:[password]@[host]:[port], "https": ...}
More information on proxies in requests: https://stackoverflow.com/a/15661226/9313679
"""
scheme = 'https' if https else 'http'
self.url = f"{scheme}://{ip}/cgi-bin/api.cgi"
self.ip = ip
self.token = None
self.username = username
self.password = password
Request.proxies = kwargs.get("proxy") # Defaults to None if key isn't found
def login(self) -> bool:
"""
Get login token
Must be called first, before any other operation can be performed
:return: bool
"""
try:
body = [{"cmd": "Login", "action": 0,
"param": {"User": {"userName": self.username, "password": self.password}}}]
param = {"cmd": "Login", "token": "null"}
response = Request.post(self.url, data=body, params=param)
if response is not None:
data = response.json()[0]
code = data["code"]
if int(code) == 0:
self.token = data["value"]["Token"]["name"]
print("Login success")
return True
print(self.token)
return False
else:
print("Failed to login\nStatus Code:", response.status_code)
return False
except Exception as e:
print("Error Login\n", e)
raise
def logout(self) -> bool:
"""
Logout of the camera
:return: bool
"""
try:
data = [{"cmd": "Logout", "action": 0}]
self._execute_command('Logout', data)
# print(ret)
return True
except Exception as e:
print("Error Logout\n", e)
return False
def _execute_command(self, command, data, multi=False):
"""
Send a POST request to the IP camera with given data.
:param command: name of the command to send
:param data: object to send to the camera (send as json)
:param multi: whether the given command name should be added to the
url parameters of the request. Defaults to False. (Some multi-step
commands seem to not have a single command name)
:return: response JSON as python object
"""
params = {"token": self.token, 'cmd': command}
if multi:
del params['cmd']
try:
if self.token is None:
raise ValueError("Login first")
if command == 'Download':
# Special handling for downloading an mp4
# Pop the filepath from data
tgt_filepath = data[0].pop('filepath')
# Apply the data to the params
params.update(data[0])
with requests.get(self.url, params=params, stream=True) as req:
if req.status_code == 200:
with open(tgt_filepath, 'wb') as f:
f.write(req.content)
return True
else:
print(f'Error received: {req.status_code}')
return False
else:
response = Request.post(self.url, data=data, params=params)
return response.json()
except Exception as e:
print(f"Command {command} failed: {e}")
raise

View File

@@ -1,24 +0,0 @@
from reolink_api import APIHandler
class Camera(APIHandler):
def __init__(self, ip: str, username: str = "admin", password: str = "", https: bool = False):
"""
Initialise the Camera object by passing the ip address.
The default details {"username":"admin", "password":""} will be used if nothing passed
:param ip:
:param username:
:param password:
"""
# For when you need to connect to a camera behind a proxy, pass
# a proxy argument: proxy={"http": "socks5://127.0.0.1:8000"}
APIHandler.__init__(self, ip, username, password, https=https)
# Normal call without proxy:
# APIHandler.__init__(self, ip, username, password)
self.ip = ip
self.username = username
self.password = password
super().login()

View File

@@ -1,16 +0,0 @@
import io
import yaml
class ConfigHandler:
camera_settings = {}
@staticmethod
def load() -> yaml or None:
try:
stream = io.open("config.yml", 'r', encoding='utf8')
data = yaml.safe_load(stream)
return data
except Exception as e:
print("Config Property Error\n", e)
return None

View File

@@ -1,106 +0,0 @@
import os
from threading import ThreadError
import cv2
from reolink_api.util import threaded
class RtspClient:
"""
Inspiration from:
- https://benhowell.github.io/guide/2015/03/09/opencv-and-web-cam-streaming
- https://stackoverflow.com/questions/19846332/python-threading-inside-a-class
- https://stackoverflow.com/questions/55828451/video-streaming-from-ip-camera-in-python-using-opencv-cv2-videocapture
"""
def __init__(self, ip, username, password, port=554, profile="main", use_udp=True, callback=None, **kwargs):
"""
RTSP client is used to retrieve frames from the camera in a stream
:param ip: Camera IP
:param username: Camera Username
:param password: Camera User Password
:param port: RTSP port
:param profile: "main" or "sub"
:param use_upd: True to use UDP, False to use TCP
:param proxies: {"host": "localhost", "port": 8000}
"""
self.capture = None
self.thread_cancelled = False
self.callback = callback
capture_options = 'rtsp_transport;'
self.ip = ip
self.username = username
self.password = password
self.port = port
self.proxy = kwargs.get("proxies")
self.url = "rtsp://" + self.username + ":" + self.password + "@" + \
self.ip + ":" + str(self.port) + "//h264Preview_01_" + profile
if use_udp:
capture_options = capture_options + 'udp'
else:
capture_options = capture_options + 'tcp'
os.environ["OPENCV_FFMPEG_CAPTURE_OPTIONS"] = capture_options
# opens the stream capture, but does not retrieve any frames yet.
self._open_video_capture()
def _open_video_capture(self):
# To CAP_FFMPEG or not To ?
self.capture = cv2.VideoCapture(self.url, cv2.CAP_FFMPEG)
def _stream_blocking(self):
while True:
try:
if self.capture.isOpened():
ret, frame = self.capture.read()
if ret:
yield frame
else:
print("stream closed")
self.capture.release()
return
except Exception as e:
print(e)
self.capture.release()
return
@threaded
def _stream_non_blocking(self):
while not self.thread_cancelled:
try:
if self.capture.isOpened():
ret, frame = self.capture.read()
if ret:
self.callback(frame)
else:
print("stream is closed")
self.stop_stream()
except ThreadError as e:
print(e)
self.stop_stream()
def stop_stream(self):
self.capture.release()
self.thread_cancelled = True
def open_stream(self):
"""
Opens OpenCV Video stream and returns the result according to the OpenCV documentation
https://docs.opencv.org/3.4/d8/dfe/classcv_1_1VideoCapture.html#a473055e77dd7faa4d26d686226b292c1
"""
# Reset the capture object
if self.capture is None or not self.capture.isOpened():
self._open_video_capture()
print("opening stream")
if self.callback is None:
return self._stream_blocking()
else:
# reset the thread status if the object was not re-created
if not self.thread_cancelled:
self.thread_cancelled = False
return self._stream_non_blocking()