This repository has been archived by the owner on Dec 25, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathmjpegclient.py
120 lines (107 loc) · 4.55 KB
/
mjpegclient.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
"""
Created on Apr 12, 2017
@author: sgoldsmith
Copyright (c) Steven P. Goldsmith
All rights reserved.
"""
import socket, base64, numpy, cv2, framebase
from urllib.parse import urlparse
class mjpegclient(framebase.framebase):
"""MJPEG frame grabber class.
Used for socket based frame grabber.
"""
def __init__(self, url, timeout):
"""Connect to stream"""
# Set socket timeout
socket.setdefaulttimeout(timeout)
# Parse URL
parsed = urlparse(url)
port = parsed.port
# Set port to default if not set
if not port:
port = 80
# See if query string present
if not parsed.query:
path = parsed.path
else:
path = "%s%s%s" % (parsed.path, "?", parsed.query)
# See if we need to do HTTP basic access authentication
if parsed.username is None:
# Build HTTP header
lines = [
"GET %s HTTP/1.1" % path,
"Host: %s" % parsed.hostname,
]
else:
# Base64 encode username and password
token = base64.b64encode(("%s:%s" % (parsed.username, parsed.password)).encode('utf-8')).decode('utf-8')
# Build HTTP header
lines = [
"GET %s HTTP/1.1" % path,
"Host: %s" % parsed.hostname,
"Authorization: Basic %s" % token,
]
# AF_INET: IPv4 protocols (both TCP and UDP)
# SOCK_STREAM: a connection-oriented, TCP byte stream
self.streamSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.streamSock.connect((parsed.hostname, port))
# Socket file in read, write, binary mode and no buffer
self.socketFile = self.streamSock.makefile("rwb", buffering=None)
# Send HTTP GET for MJPEG stream
self.socketFile.write("\r\n".join(lines).encode('utf-8') + b"\r\n\r\n")
self.socketFile.flush()
# Read in HTTP headers
self.line = self.socketFile.readline()
self.boundary = b""
while len(self.line) > 0 and self.line.strip() != b"" and self.boundary == b"":
if self.line.lower().find(b"content-type: multipart") >= 0:
parts = self.line.split(b":")
if len(parts) > 1 and parts[0].lower() == b"content-type":
# Extract boundary string from content-type
content_type = parts[1].strip()
self.boundary = content_type.split(b";")[1].split(b"=")[1]
self.line = self.socketFile.readline()
# See how many lines need to be skipped after 'content-length'
while len(self.line) > 0 and self.line.strip().lower().find(b"content-length") < 0:
self.line = self.socketFile.readline()
# Find start of image
self.skipLines = -1
while len(self.line) > 0 and self.line.strip().lower().find(bytes.fromhex('ffd8')) != 0:
self.line = self.socketFile.readline()
self.skipLines += 1
# Set basic params
frame = self.getFrame()
image = self.decodeFrame(frame)
self.frameHeight, self.frameWidth, channels = image.shape
self.fps = 0
def getFrameLength(self):
"""Get frame length from stream"""
self.line = self.socketFile.readline()
# Find boundary
while len(self.line) > 0 and self.line.count(self.boundary) == 0:
self.line = self.socketFile.readline()
length = 0
# Read in chunk headers
while len(self.line) > 0 and self.line.strip() != "" and length == 0:
parts = self.line.split(b":")
if len(parts) > 1 and parts[0].lower().count(b"content-length") > 0:
# Grab chunk length
length = int(parts[1].strip())
# Skip lines before image data
i = self.skipLines
while i > 0:
self.line = self.socketFile.readline()
i -= 1
else:
self.line = self.socketFile.readline()
return length
def getFrame(self):
"""Get raw frame data from stream"""
return self.socketFile.read(self.getFrameLength())
def decodeFrame(self, image):
""" Convert raw image format to something OpenCV understands """
return cv2.imdecode(numpy.fromstring(image, numpy.uint8), cv2.IMREAD_COLOR)
def close(self):
"""Clean up resources"""
self.socketFile.close()
self.streamSock.close()