mirror of
https://github.com/openmv/openmv.git
synced 2025-11-04 14:49:50 +08:00
scripts/libraries: Fix RSTP library.
This commit is contained in:
parent
a9e5c41a71
commit
2faa7c20f1
@ -15,8 +15,8 @@ import network, omv, rtsp, sensor, time
|
|||||||
|
|
||||||
sensor.reset()
|
sensor.reset()
|
||||||
|
|
||||||
sensor.set_pixformat(sensor.JPEG) # Only supported by the OV2640/OV5640.
|
sensor.set_pixformat(sensor.RGB565)
|
||||||
sensor.set_framesize(sensor.UXGA)
|
sensor.set_framesize(sensor.VGA)
|
||||||
|
|
||||||
# Turn off the frame buffer connection to the IDE from the OpenMV Cam side.
|
# Turn off the frame buffer connection to the IDE from the OpenMV Cam side.
|
||||||
#
|
#
|
||||||
@ -45,10 +45,15 @@ server = rtsp.rtsp_server(network_if)
|
|||||||
# `session` is random number that will change when a new connection is established. You can use
|
# `session` is random number that will change when a new connection is established. You can use
|
||||||
# session with a dictionary to differentiate different accesses to the same file name.
|
# session with a dictionary to differentiate different accesses to the same file name.
|
||||||
|
|
||||||
|
# Track the current FPS.
|
||||||
|
clock = time.clock()
|
||||||
|
|
||||||
def setup_callback(pathname, session):
|
def setup_callback(pathname, session):
|
||||||
print("Opening \"%s\" in session %d" % (pathname, session))
|
print("Opening \"%s\" in session %d" % (pathname, session))
|
||||||
|
|
||||||
def play_callback(pathname, session):
|
def play_callback(pathname, session):
|
||||||
|
clock.reset()
|
||||||
|
clock.tick()
|
||||||
print("Playing \"%s\" in session %d" % (pathname, session))
|
print("Playing \"%s\" in session %d" % (pathname, session))
|
||||||
|
|
||||||
def pause_callback(pathname, session): # VLC only pauses locally. This is never called.
|
def pause_callback(pathname, session): # VLC only pauses locally. This is never called.
|
||||||
@ -62,18 +67,15 @@ server.register_play_cb(play_callback)
|
|||||||
server.register_pause_cb(pause_callback)
|
server.register_pause_cb(pause_callback)
|
||||||
server.register_teardown_cb(teardown_callback)
|
server.register_teardown_cb(teardown_callback)
|
||||||
|
|
||||||
# Track the current FPS.
|
|
||||||
clock = time.clock()
|
|
||||||
|
|
||||||
# Called each time a new frame is needed.
|
# Called each time a new frame is needed.
|
||||||
def image_callback(pathname, session):
|
def image_callback(pathname, session):
|
||||||
clock.tick()
|
|
||||||
img = sensor.snapshot()
|
img = sensor.snapshot()
|
||||||
# Markup image and/or do various things.
|
# Markup image and/or do various things.
|
||||||
print(clock.fps())
|
print(clock.fps())
|
||||||
|
clock.tick()
|
||||||
return img
|
return img
|
||||||
|
|
||||||
# Stream does not return. It will call `image_callback` when it needs to get an image object to send
|
# Stream does not return. It will call `image_callback` when it needs to get an image object to send
|
||||||
# to the remote rtsp client connecting to the server.
|
# to the remote rtsp client connecting to the server.
|
||||||
|
|
||||||
server.stream(image_callback)
|
server.stream(image_callback, quality=70)
|
||||||
|
|||||||
@ -15,8 +15,8 @@ import network, omv, rtsp, sensor, time
|
|||||||
|
|
||||||
sensor.reset()
|
sensor.reset()
|
||||||
|
|
||||||
sensor.set_pixformat(sensor.JPEG) # Only supported by the OV2640/OV5640.
|
sensor.set_pixformat(sensor.RGB565)
|
||||||
sensor.set_framesize(sensor.UXGA)
|
sensor.set_framesize(sensor.VGA)
|
||||||
|
|
||||||
# Turn off the frame buffer connection to the IDE from the OpenMV Cam side.
|
# Turn off the frame buffer connection to the IDE from the OpenMV Cam side.
|
||||||
#
|
#
|
||||||
@ -31,6 +31,9 @@ omv.disable_fb(True)
|
|||||||
network_if = network.WLAN(network.STA_IF)
|
network_if = network.WLAN(network.STA_IF)
|
||||||
network_if.active(True)
|
network_if.active(True)
|
||||||
network_if.connect('your-ssid', 'your-password')
|
network_if.connect('your-ssid', 'your-password')
|
||||||
|
while not network_if.isconnected():
|
||||||
|
print("Trying to connect. Note this may take a while...")
|
||||||
|
time.sleep_ms(1000)
|
||||||
|
|
||||||
# Setup RTSP Server
|
# Setup RTSP Server
|
||||||
|
|
||||||
@ -45,10 +48,15 @@ server = rtsp.rtsp_server(network_if)
|
|||||||
# `session` is random number that will change when a new connection is established. You can use
|
# `session` is random number that will change when a new connection is established. You can use
|
||||||
# session with a dictionary to differentiate different accesses to the same file name.
|
# session with a dictionary to differentiate different accesses to the same file name.
|
||||||
|
|
||||||
|
# Track the current FPS.
|
||||||
|
clock = time.clock()
|
||||||
|
|
||||||
def setup_callback(pathname, session):
|
def setup_callback(pathname, session):
|
||||||
print("Opening \"%s\" in session %d" % (pathname, session))
|
print("Opening \"%s\" in session %d" % (pathname, session))
|
||||||
|
|
||||||
def play_callback(pathname, session):
|
def play_callback(pathname, session):
|
||||||
|
clock.reset()
|
||||||
|
clock.tick()
|
||||||
print("Playing \"%s\" in session %d" % (pathname, session))
|
print("Playing \"%s\" in session %d" % (pathname, session))
|
||||||
|
|
||||||
def pause_callback(pathname, session): # VLC only pauses locally. This is never called.
|
def pause_callback(pathname, session): # VLC only pauses locally. This is never called.
|
||||||
@ -62,18 +70,15 @@ server.register_play_cb(play_callback)
|
|||||||
server.register_pause_cb(pause_callback)
|
server.register_pause_cb(pause_callback)
|
||||||
server.register_teardown_cb(teardown_callback)
|
server.register_teardown_cb(teardown_callback)
|
||||||
|
|
||||||
# Track the current FPS.
|
|
||||||
clock = time.clock()
|
|
||||||
|
|
||||||
# Called each time a new frame is needed.
|
# Called each time a new frame is needed.
|
||||||
def image_callback(pathname, session):
|
def image_callback(pathname, session):
|
||||||
clock.tick()
|
|
||||||
img = sensor.snapshot()
|
img = sensor.snapshot()
|
||||||
# Markup image and/or do various things.
|
# Markup image and/or do various things.
|
||||||
print(clock.fps())
|
print(clock.fps())
|
||||||
|
clock.tick()
|
||||||
return img
|
return img
|
||||||
|
|
||||||
# Stream does not return. It will call `image_callback` when it needs to get an image object to send
|
# Stream does not return. It will call `image_callback` when it needs to get an image object to send
|
||||||
# to the remote rtsp client connecting to the server.
|
# to the remote rtsp client connecting to the server.
|
||||||
|
|
||||||
server.stream(image_callback)
|
server.stream(image_callback, quality=70)
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
# This file is part of the OpenMV project.
|
# This file is part of the OpenMV project.
|
||||||
#
|
#
|
||||||
# Copyright (c) 2013-2020 Ibrahim Abdelkader <iabdalkader@openmv.io>
|
# Copyright (c) 2013-2023 Ibrahim Abdelkader <iabdalkader@openmv.io>
|
||||||
# Copyright (c) 2013-2020 Kwabena W. Agyeman <kwagyeman@openmv.io>
|
# Copyright (c) 2013-2023 Kwabena W. Agyeman <kwagyeman@openmv.io>
|
||||||
#
|
#
|
||||||
# This work is licensed under the MIT license, see the file LICENSE for details.
|
# This work is licensed under the MIT license, see the file LICENSE for details.
|
||||||
|
|
||||||
|
import errno
|
||||||
import pyb
|
import pyb
|
||||||
import re
|
import re
|
||||||
import socket
|
import socket
|
||||||
@ -16,38 +17,71 @@ class rtsp_server:
|
|||||||
if self.__tcp__socket is None:
|
if self.__tcp__socket is None:
|
||||||
try:
|
try:
|
||||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
s.bind(self.__myaddr)
|
try:
|
||||||
s.listen(0)
|
if hasattr(socket, "SO_REUSEADDR"):
|
||||||
s.settimeout(5)
|
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
self.__tcp__socket, self.__client_addr = s.accept()
|
s.bind(self.__myaddr)
|
||||||
|
s.listen(0)
|
||||||
|
s.settimeout(5)
|
||||||
|
self.__tcp__socket, self.__client_addr = s.accept()
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
s.close()
|
s.close()
|
||||||
except OSError:
|
except OSError:
|
||||||
self.__tcp__socket = None
|
self.__tcp__socket = None
|
||||||
return self.__tcp__socket is not None
|
return self.__tcp__socket is not None
|
||||||
|
|
||||||
def __close_tcp_socket(self): # private
|
def __close_tcp_socket(self): # private
|
||||||
self.__tcp__socket.close()
|
if self.__tcp__socket is not None:
|
||||||
self.__tcp__socket = None
|
self.__tcp__socket.close()
|
||||||
|
self.__tcp__socket = None
|
||||||
|
if self.__playing:
|
||||||
|
self.__playing = False
|
||||||
|
if self.__teardown_cb:
|
||||||
|
self.__teardown_cb(self.__pathname, self.__playing_session)
|
||||||
|
|
||||||
def __valid_udp_socket(self): # private
|
def __valid_udp_socket(self): # private
|
||||||
if self.__udp__socket is None:
|
if self.__udp_rtp__socket is None:
|
||||||
try:
|
try:
|
||||||
self.__udp__socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
self.__udp_rtp__socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
self.__udp__socket.bind((self.__myip, self.__client_rtp_port))
|
try:
|
||||||
|
if hasattr(socket, "SO_REUSEADDR"):
|
||||||
|
self.__udp_rtp__socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
|
self.__udp_rtp__socket.bind((self.__myip, self.__client_rtp_port))
|
||||||
|
except OSError:
|
||||||
|
self.__udp_rtp__socket.close()
|
||||||
|
self.__udp_rtp__socket = None
|
||||||
except OSError:
|
except OSError:
|
||||||
self.__udp__socket = None
|
self.__udp_rtp__socket = None
|
||||||
return self.__udp__socket is not None
|
if self.__udp_rtcp__socket is None:
|
||||||
|
try:
|
||||||
|
self.__udp_rtcp__socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
try:
|
||||||
|
if hasattr(socket, "SO_REUSEADDR"):
|
||||||
|
self.__udp_rtcp__socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
|
self.__udp_rtcp__socket.bind((self.__myip, self.__client_rtcp_port))
|
||||||
|
except OSError:
|
||||||
|
self.__udp_rtcp__socket.close()
|
||||||
|
self.__udp_rtcp__socket = None
|
||||||
|
except OSError:
|
||||||
|
self.__udp_rtcp__socket = None
|
||||||
|
return self.__udp_rtp__socket is not None and self.__udp_rtcp__socket is not None
|
||||||
|
|
||||||
def __close_udp_socket(self): # private
|
def __close_udp_socket(self): # private
|
||||||
self.__udp__socket.close()
|
if self.__udp_rtp__socket is not None:
|
||||||
self.__udp__socket = None
|
self.__udp_rtp__socket.close()
|
||||||
|
self.__udp_rtp__socket = None
|
||||||
|
if self.__udp_rtcp__socket is not None:
|
||||||
|
self.__udp_rtcp__socket.close()
|
||||||
|
self.__udp_rtcp__socket = None
|
||||||
|
|
||||||
def __init__(self, network_if, port=554): # private
|
def __init__(self, network_if, port=554): # private
|
||||||
self.__network = network_if
|
self.__network = network_if
|
||||||
self.__myip = self.__network.ifconfig()[0]
|
self.__myip = self.__network.ifconfig()[0]
|
||||||
self.__myaddr = (self.__myip, port)
|
self.__myaddr = (self.__myip, port)
|
||||||
self.__tcp__socket = None
|
self.__tcp__socket = None
|
||||||
self.__udp__socket = None
|
self.__udp_rtp__socket = None
|
||||||
|
self.__udp_rtcp__socket = None
|
||||||
self.__setup_cb = None
|
self.__setup_cb = None
|
||||||
self.__play_cb = None
|
self.__play_cb = None
|
||||||
self.__pause_cb = None
|
self.__pause_cb = None
|
||||||
@ -57,6 +91,7 @@ class rtsp_server:
|
|||||||
self.__transport_is_tcp = False
|
self.__transport_is_tcp = False
|
||||||
self.__client_rtp_addr = None
|
self.__client_rtp_addr = None
|
||||||
self.__playing = False
|
self.__playing = False
|
||||||
|
self.__playing_session = 0
|
||||||
self.__sequence_number = pyb.rng() & 0xFFFF
|
self.__sequence_number = pyb.rng() & 0xFFFF
|
||||||
self.__ssrc = pyb.rng()
|
self.__ssrc = pyb.rng()
|
||||||
print("IP Address:Port %s:%d\nRunning..." % self.__myaddr)
|
print("IP Address:Port %s:%d\nRunning..." % self.__myaddr)
|
||||||
@ -152,6 +187,7 @@ class rtsp_server:
|
|||||||
)
|
)
|
||||||
self.__session = pyb.rng()
|
self.__session = pyb.rng()
|
||||||
self.__ssrc = pyb.rng()
|
self.__ssrc = pyb.rng()
|
||||||
|
self.__valid_udp_socket()
|
||||||
self.__send_rtsp_response_ok(
|
self.__send_rtsp_response_ok(
|
||||||
seq,
|
seq,
|
||||||
"%s;server_port=%d-%d;ssrc=%d\r\nSession: %d\r\n"
|
"%s;server_port=%d-%d;ssrc=%d\r\nSession: %d\r\n"
|
||||||
@ -187,13 +223,14 @@ class rtsp_server:
|
|||||||
m = re.match("Session: (\d+)", s[2])
|
m = re.match("Session: (\d+)", s[2])
|
||||||
if m:
|
if m:
|
||||||
self.__playing = True
|
self.__playing = True
|
||||||
|
self.__playing_session = int(m.group(1))
|
||||||
self.__send_rtsp_response_ok(
|
self.__send_rtsp_response_ok(
|
||||||
seq,
|
seq,
|
||||||
"RTP-Info: url=%s;seq=%d\r\n"
|
"RTP-Info: url=%s;seq=%d\r\n"
|
||||||
% (line0[1], self.__sequence_number),
|
% (line0[1], self.__sequence_number),
|
||||||
)
|
)
|
||||||
if self.__play_cb:
|
if self.__play_cb:
|
||||||
self.__play_cb(self.__pathname, int(m.group(1)))
|
self.__play_cb(self.__pathname, self.__playing_session)
|
||||||
return
|
return
|
||||||
elif request == "PAUSE":
|
elif request == "PAUSE":
|
||||||
if len(s) >= 3:
|
if len(s) >= 3:
|
||||||
@ -218,6 +255,9 @@ class rtsp_server:
|
|||||||
return
|
return
|
||||||
self.__send_rtsp_response(400, "Bad Request")
|
self.__send_rtsp_response(400, "Bad Request")
|
||||||
|
|
||||||
|
def __parse_rtcp_packet(self, data): # private
|
||||||
|
pass
|
||||||
|
|
||||||
def __valid_socket(self): # private
|
def __valid_socket(self): # private
|
||||||
if self.__transport_is_tcp:
|
if self.__transport_is_tcp:
|
||||||
return self.__client_rtp_channel is not None
|
return self.__client_rtp_channel is not None
|
||||||
@ -228,24 +268,32 @@ class rtsp_server:
|
|||||||
if self.__transport_is_tcp:
|
if self.__transport_is_tcp:
|
||||||
self.__tcp__socket.settimeout(timeout)
|
self.__tcp__socket.settimeout(timeout)
|
||||||
else:
|
else:
|
||||||
self.__udp__socket.settimeout(timeout)
|
self.__udp_rtp__socket.settimeout(timeout)
|
||||||
|
self.__udp_rtcp__socket.settimeout(timeout)
|
||||||
|
|
||||||
def __send(self, data): # private
|
def __send(self, data): # private
|
||||||
if self.__transport_is_tcp:
|
if self.__transport_is_tcp:
|
||||||
return self.__tcp__socket.send(
|
self.__tcp__socket.sendall(
|
||||||
struct.pack(">BBH", 0x24, self.__client_rtp_channel, len(data)) + data
|
struct.pack(">BBH", 0x24, self.__client_rtp_channel, len(data)) + data
|
||||||
)
|
)
|
||||||
|
return True
|
||||||
else:
|
else:
|
||||||
return self.__udp__socket.sendto(data, self.__client_rtp_addr)
|
return self.__udp_rtp__socket.sendto(data, self.__client_rtp_addr)
|
||||||
|
|
||||||
|
def __recv(self): # private
|
||||||
|
if self.__transport_is_tcp:
|
||||||
|
return self.__tcp__socket.recv(1400)
|
||||||
|
else:
|
||||||
|
return self.__udp_rtcp__socket.recv(1400)
|
||||||
|
|
||||||
def __close_socket(self): # private
|
def __close_socket(self): # private
|
||||||
if self.__transport_is_tcp:
|
if self.__transport_is_tcp:
|
||||||
pass
|
self.__close_tcp_socket()
|
||||||
else:
|
else:
|
||||||
self.__close_udp_socket()
|
self.__close_udp_socket()
|
||||||
|
|
||||||
def __send_rtp(self, image_callback, quality): # private
|
def __send_rtp(self, image_callback, quality): # private
|
||||||
img = image_callback(self.__pathname, self.__session).compress(quality)
|
img = image_callback(self.__pathname, self.__session).to_jpeg(quality=quality)
|
||||||
if img.width() >= 2040:
|
if img.width() >= 2040:
|
||||||
raise ValueError("Maximum width is 2040")
|
raise ValueError("Maximum width is 2040")
|
||||||
if img.height() >= 2040:
|
if img.height() >= 2040:
|
||||||
@ -259,8 +307,9 @@ class rtsp_server:
|
|||||||
max_packet_size -= 4
|
max_packet_size -= 4
|
||||||
if self.__valid_socket():
|
if self.__valid_socket():
|
||||||
try:
|
try:
|
||||||
self.__settimeout(0.1)
|
self.__settimeout(5)
|
||||||
timestamp = (pyb.millis() * 90) & 0xFFFFFFFF
|
timestamp = (pyb.millis() * 90) & 0xFFFFFFFF
|
||||||
|
mv = memoryview(img)
|
||||||
while l:
|
while l:
|
||||||
rtp_header = struct.pack(
|
rtp_header = struct.pack(
|
||||||
">BBHII",
|
">BBHII",
|
||||||
@ -274,10 +323,9 @@ class rtsp_server:
|
|||||||
jpeg_header = struct.pack(
|
jpeg_header = struct.pack(
|
||||||
">IBBBB", i, 0, quality, int(img.width() // 8), int(img.height() // 8)
|
">IBBBB", i, 0, quality, int(img.width() // 8), int(img.height() // 8)
|
||||||
)
|
)
|
||||||
img_data = img.bytearray()[i : i + min(l, max_packet_size)]
|
img_data = mv[i : i + min(l, max_packet_size)]
|
||||||
img_data_len = len(img_data)
|
img_data_len = len(img_data)
|
||||||
data_len = self.__send(rtp_header + jpeg_header + img_data)
|
if not self.__send(rtp_header + jpeg_header + img_data):
|
||||||
if not data_len:
|
|
||||||
break
|
break
|
||||||
i += img_data_len
|
i += img_data_len
|
||||||
l -= img_data_len
|
l -= img_data_len
|
||||||
@ -285,17 +333,35 @@ class rtsp_server:
|
|||||||
self.__close_socket()
|
self.__close_socket()
|
||||||
except OSError:
|
except OSError:
|
||||||
self.__close_socket()
|
self.__close_socket()
|
||||||
|
if not self.__transport_is_tcp and self.__valid_socket():
|
||||||
|
try:
|
||||||
|
self.__settimeout(0.001)
|
||||||
|
try:
|
||||||
|
data = self.__recv()
|
||||||
|
if data and len(data):
|
||||||
|
self.__parse_rtcp_packet(data)
|
||||||
|
except OSError as e:
|
||||||
|
if e.errno != errno.EAGAIN and e.errno != errno.ETIMEDOUT:
|
||||||
|
raise e
|
||||||
|
except OSError:
|
||||||
|
self.__close_socket()
|
||||||
|
|
||||||
def stream(self, image_callback, quality=90): # public
|
def stream(self, image_callback, quality=90): # public
|
||||||
while True:
|
while True:
|
||||||
if self.__valid_tcp_socket():
|
if self.__valid_tcp_socket():
|
||||||
try:
|
try:
|
||||||
self.__tcp__socket.settimeout(0.1)
|
self.__tcp__socket.settimeout(0.001)
|
||||||
while True:
|
try:
|
||||||
data = self.__tcp__socket.recv(1400)
|
data = self.__tcp__socket.recv(1400)
|
||||||
if data and len(data):
|
if data and len(data):
|
||||||
self.__parse_rtsp_request(data)
|
self.__parse_rtsp_request(data)
|
||||||
if self.__playing:
|
else:
|
||||||
self.__send_rtp(image_callback, quality)
|
raise OSError
|
||||||
|
except OSError as e:
|
||||||
|
if e.errno != errno.EAGAIN and e.errno != errno.ETIMEDOUT:
|
||||||
|
raise e
|
||||||
|
if self.__playing:
|
||||||
|
self.__send_rtp(image_callback, quality)
|
||||||
except OSError:
|
except OSError:
|
||||||
self.__close_tcp_socket()
|
self.__close_tcp_socket()
|
||||||
|
self.__close_udp_socket()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user