使用IPEYE云监控服务进行内部延时拍摄

最近,每天都会出现一项任务,与一对连接到IPEYE的监控摄像机形成延时。 如果您对Python知识最少的人如何处理感兴趣,或者您想向我指出我的错误,请欢迎...

前言


我父亲决定搬家并在另一个地区盖房子。 他请我帮助进行视频监控。 输入数据:

  • 没有技术室。
  • 设备可能被盗。
  • 需要高质量的图片。
  • 摄像机必须在室外。
  • 您只需要2台摄像机。
  • 我真的想要带变焦的PTZ摄像机。
  • 我想要一个移动应用程序。

在查看了品牌设备商店的价格后,决定购买带阿里所有面包的noname PoE摄像机。 这些相机足够便宜-大约每个5千个。

我不想将DVR放置在施工现场,因此决定使用云解决方案。 摄像机到达后,我试图结交具有不同服务的朋友,以便一切正常,包括PTZ。 在我尝试过的所有服务中,结果发现只有IPEYE才使中国相机成为朋友。
这将结束介绍。 我认为现在每个人都很清楚为什么要进行有关此服务的对话。

IPEYE的经验


服务即服务。 承诺的一切都实现了。 技术支持会回答问题。 关键是您可以指定一个指向rstp流的链接,扭转PTZ设置,一切都会正常进行。 Android移动应用程序有效。 可以为其相机创建访客用户,并向每个亲戚授予访问权限。 Vivaldi中的Web界面有时会出现故障;在Chrome中,此类故障不太常见。 浏览档案有点沉闷。
可以下载相机中的档案,但是最多可以进行3个小时的下载。 该过程非常耗时。

一切似乎都很好,但您内心深处感到有些不对劲。 并且没有足够的存档深度。 您可以增加深度,但最昂贵的选择-12个月,一台摄像机的费用为每年25,000卢布(通过检测记录时)。

让我们想出些什么吗?


那是我父亲问我的问题。 我父亲想抓住建筑的所有阶段。
解决此问题有哪些选择? 您可以每天访问Web界面,并从每个摄像机导出几段视频。 谁来执行这样沉闷的任务? 没有人! 一天几次打开广播摄像机并拍摄屏幕截图? 好吧,还有精神错乱。 将档案深度增加到1年? 好吧,一个非常便宜的解决方案。 Google告诉我,此服务已经内置了TimeLapse函数,但是分辨率很低,您无法下载它以备将来使用::(
UPD:原始游戏中时光倒流可以下载。 为此,请在Web界面中打开延时。 使用开发人员工具,检查网页并找到指向我们文件的链接。 连结格式:
sr <server_number> .ipeye.ru / api / v1 / stream / <uuid> / nvr / timelapse / 0 / <unixtimestamp> /100/video.mjpeg

决定写一些东西以某种方式保存相机的屏幕截图并形成最终的视频。

免责声明


该作品的作者不是程序员,也不打算成为一个程序员。 OOP初步了解基础知识。 敏捷等 没有研究。 一个月前,他观看了有关python的简短视频课程,并决定使用它来解决当前问题。

API


很高兴发现IPEYE服务是公开可用的API 。 该API仅提供用于PHP的示例,但事实证明它很有用。

基于我的计算机从未关闭的事实,开发出以下概念:

  1. Windows Scheduler每30分钟运行一次脚本。
  2. 通过API的脚本确定了我的相机的uuid。
  3. 通过API的脚本从相机接收照片并将其保存到目录中。
  4. 该脚本每天一次为每个摄像机生成一个视频文件。
  5. 包含源照片和最终视频的目录已绑定到云并共享。
  6. 亲戚想要观看游戏中时光倒流并按需要操作文件时。

为了使用API​​,我编写了两个函数:记录和执行对API服务器的请求。

writeLog()
def writeLog(logdata): if LogEnable == 1: log_time = datetime.now() log_time = log_time.isoformat(timespec='seconds') log_file = open(log_file_path, "a+") log_file.write(log_time + ": " + str(logdata) + "\n") log_file.close else: return True 


getApiResponse()
 def getApiResponse(method, api_uri): if method == "GET": try: r = requests.get(api_url + api_uri, timeout = api_timeout) r.raise_for_status() #   HTTP    except requests.exceptions.Timeout: writeLog("Error. Timeout. Request Uri:" + api_uri) except requests.exceptions.TooManyRedirects: writeLog("Error. TooManyRedirects or bad URL. Request Uri:" + api_uri) except requests.exceptions.RequestException as e: writeLog("Error. Fatal error: " + str(e) + " Request Uri:" + api_uri) sys.exit(1) except requests.exceptions.HTTPError as e: writeLog("Error. HTTP error: " + str(e) + " Request Uri:" + api_uri) if method == "POST": try: r = requests.post(api_url + api_uri, timeout = api_timeout) r.raise_for_status() #   HTTP    except requests.exceptions.Timeout: writeLog("Error. Timeout. Request Uri:" + api_uri) except requests.exceptions.TooManyRedirects: writeLog("Error. TooManyRedirects or bad URL. Request Uri:" + api_uri) except requests.exceptions.RequestException as e: writeLog("Error. Fatal error: " + str(e) + " Request Uri:" + api_uri) sys.exit(1) except requests.exceptions.HTTPError as e: writeLog("Error. HTTP error: " + str(e) + " Request Uri:" + api_uri) return r 


根据API,我们可以访问/ devices / all并获取所有线程上的信息。 如果您相信API文档,我立即感到困惑,不需要授权...当我询问/ devices / all时,出现错误:
致命错误:401客户端错误:未经授权的网址: api.ipeye.ru :8111 /设备/全部
我修改了getApiResponse函数以传递我的凭据,但同时收到401错误。我没有列出该函数,因为 将来没有用。

我不得不联系技术支持。 支持团队解释说,为了通过授权使用API​​,您必须首先与IPEYE达成协议并配置Web服务器。 什么样的网络服务器以及为什么不向我解释,但是同时,他们也暗示了在何处可以获取uuid相机并未经授权访问API。

对应支持
我:下午好。 是否可以使用API​​? 我在网络界面中找不到uuid相机。 当我询问api.ipeye.ru时,我想获取我的相机列表:8111 /设备/除我的用户名/密码链接之外的所有链接均无效。

IPEYE:作为登录名/密码,使用了API用户的授权数据,我们以合同方式提供访问权限。

因此,您将看到的摄像机列表不是您的登录名,而是用户API的所有摄像机。

我:访问两台摄像机的API需要花费多少钱?

IPEYE:那里的一切都更加复杂,您将需要启动您的网站并使用我们的API向其中添加相机,然后继续使用它们。
配备2台摄像机值得吗?

我:通常,为了理解,我仅需要访问API才能自动从相机下载屏幕截图。 为了进一步形成时间流逝。

IPEYE:在这种情况下, api.ipeye.ru / doc#AppDeviceJPEGOnline不适合什么?

我:在哪里可以找到相机的UUID?

IPEYE:例如,从浏览器的地址栏中。 相同的UUID作为devcode参数出现在“站点代码”中。



干残渣中有什么? 实际上,如果您查看GET参数,则可以在您的帐户中找到uuid相机。 有一个方法/设备/ jpeg /在线/:uuid /:拍摄屏幕快照的名称,有uuid,我们已经知道了相机的名称。

我创建了一个功能来保存流中的图像。

saveJpegFromStream()
 def saveJpegFromStream(uuid, name): # api_uri = "/device/thumb/online/" + uuid + "/1920/" + name api_uri = "/device/jpeg/online/" + uuid + "/" + name writeLog("Trying save Stream screenshot for camera: " + name) response = getApiResponse("GET", api_uri) content_type = response.headers.get('content-type') if content_type is None: writeLog("Nothing to save") return False if 'text' in content_type.lower() or 'html' in content_type.lower(): writeLog("Received text data: " + response.content.decode("utf-8")) return False else: filename = dirToSave + "\\" + name + "-" + today.strftime("%Y-%m-%d-%H-%M-%S") + ".jpg" screenshot = open(filename, "wb") screenshot.write(response.content) screenshot.close() writeLog("File saved as: " + filename) return True 


我提出了要求,并了解有些不对劲……我们收到了文件。 图片来自我的相机,但大小可疑-66Kb。 我看一下属性并了解608 * 342绝不是1920 * 1080。 当我请求/ device / thumb / online / uuid / 1920 / name时,我得到的文件为1920 * 1080,但这只是前一个文件608 * 342扩展到所需的比例。 当然,这种情况并不适合我。

同样在实验期间,发现没有检查或以任何方式使用name参数。 您可以发送任何您想要的。

之后,我得出结论,最有用的命令/ device / url / rtsp /是获得指向RTSP流的链接。 我不得不求助于Google,因为 尚不了解如何使用RTSP。

getStreamRTSP()
 def getStreamRTSP(uuid, name): api_uri = "/device/url/rtsp/" + uuid writeLog("Trying get stream RTSP link for " + name + " " + uuid) response = json.loads(getApiResponse("GET", api_uri).text) writeLog("Stream RTSP link for " + name + ": " + str(response["message"])) return str(response["message"]) 


saveJpegFromRTSP
 def saveJpegFromRTSP(name, rtspLink): writeLog("Trying save RTSP screenshot for camera: " + name) rtspClient = cv2.VideoCapture(rtspLink) if rtspClient.isOpened(): _,frame = rtspClient.read() rtspClient.release() #       if _ and frame is not None: filename = dirToSave + "\\" + name + "-" + today.strftime("%Y-%m-%d-%H-%M-%S") + ".jpg" cv2.imwrite(filename, frame) writeLog("File saved as: " + filename) return True else: writeLog("Can't read RTSP stream") return False 


在上面编写了几个函数后,我意识到对于初始任务根本不需要使用API​​ :)我直接链接到摄像机的rtsp流,使用最后一个函数,我可以从摄像机捕获图像。 但是我并没有在这一步开始改变这个概念。

使用API​​时,有两个优点:IPEYE服务器初始化流的速度比摄像机快得多,并且rtsp摄像机流可以被防火墙关闭。

jpg2mp4


最后一步是将一台摄像机的所有图像添加到视频中。 我选择mp4v编解码器,因为 MEGA允许您在Web界面中播放这些视频文件。

makeVideoFile()
 def makeVideoFile(name): height = 1080 width = 1920 # video = cv2.VideoWriter(dirToSave + "\\Video" + name + ".avi", cv2.VideoWriter_fourcc(*'DIVX'), 1,(width,height)) video = cv2.VideoWriter(dirToSave + "\\Video" + name + ".mp4", cv2.VideoWriter_fourcc(*'mp4v'), 1,(width,height)) files = os.listdir(dirToSave) screenshots = list(filter(lambda x: x.startswith(name + "-"), files)) for screenshot in screenshots: origImage = cv2.imread(dirToSave + "\\" + screenshot) #  ,    ,      -   ...   #  ,        FullHD ?           API heightOrig, widthOrig, channelsOrig = origImage.shape if height != heightOrig or width != widthOrig: img = cv2.resize(origImage, (width, height)) video.write(img) else: video.write(origImage) cv2.destroyAllWindows() video.release() 


供参考:45个jpg文件,视频格式的总容量为37.3 MB,占用16.9 MB。

感谢您阅读我的第一篇公开文章。 我试图以故事的形式描述一切,而不是简单地做,因为我不想在出路时拿到枯燥的文章。

我会很高兴发表评论,因为我不到一个月前遇到了python。

带有扩展注释的完整脚本可以在github上找到。 此外,该脚本考虑了时区的差异,并且编写了所有其余功能以快速缩放到不同数量的摄像机。

Source: https://habr.com/ru/post/zh-CN475154/


All Articles