I'd like to set up an opencv system to process either HLS streams or RMTP streams, however, I am running into a strange issue regarding a reduced frame-rate and an accumulating lag. It's as if the video gets further and further behind from where it is supposed to be in the stream.
I'm looking for a way to keep up to date with a live source even if it means dropping frames.
import cv2
cap = cv2.VideoCapture()
cap.open('https://videos3.earthcam.com/fecnetwork/9974.flv/chunklist_w1421640637.m3u8')
while (True):
_, frame = cap.read()
cv2.imshow("camCapture", frame)
cv2.waitKey(1)
I've validated the quality of the stream on VLC and it seems to work fine there.
.
The reasons are the following: Slow compiler. Necessity to compile the same code many times for all GPU architectures. A lot of templates instantiations in the module to support all possible types, flags, border extrapolation modes, interpolations, kernel sizes, etc.
The function waitKey waits for a key event infinitely (when delay≤0 ) or for delay milliseconds, ... You should input 500 instead of 0.5 . That will fix your error, but will not speed up your video. To speed up the video you should get the frame rate and play in double that rate.
Capture Video from Camera OpenCV allows a straightforward interface to capture live stream with the camera (webcam). It converts video into grayscale and display it. We need to create a VideoCapture object to capture a video. It accepts either the device index or the name of a video file.
My hypothesis is that the jitter is most likely due to network limitations and occurs when a frame packet is dropped. When a frame is dropped, this causes the program to display the last "good" frame which results in the display freezing. This is probably a hardware or bandwidth issue but we can alleviate some of this with software. Here are some possible changes:
1. Set maximum buffer size
We set the cv2.videoCapture()
object to have a limited buffer size with the cv2.CAP_PROP_BUFFERSIZE
parameter. The idea is that by limiting the buffer, we will always have the latest frame. This can also help to alleviate the problem of frames randomly jumping ahead.
2. Set frame retrieval delay
Currently, I believe the read()
is reading too fast even though it is in its own dedicated thread. This may be one reason why all the frames appear to pool up and suddenly burst in the next frame. For instance, say in a one second time interval, it may produce 15 new frames but in the next one second interval, only 3 frames are returned. This may be due to the network packet frame loss so to ensure that we obtain constant frame rates, we simply add a delay in the frame retrieval thread. A delay to obtain roughly ~30
FPS does a good job to "normalize" the frame rate and smooth the transition between frames incase there is packet loss.
Note: We should try to match the frame rate of the stream but I'm not sure what the FPS of the webcam is so I just guessed 30
FPS. Also, there is usually a "direct" stream link instead of going through a intermediate webserver which can greatly improve performance.
If you try using a saved .mp4
video file, you will notice that there is no jitter. This confirms my suspicion that the problem is most likely due to network latency.
from threading import Thread
import cv2, time
class ThreadedCamera(object):
def __init__(self, src=0):
self.capture = cv2.VideoCapture(src)
self.capture.set(cv2.CAP_PROP_BUFFERSIZE, 2)
# FPS = 1/X
# X = desired FPS
self.FPS = 1/30
self.FPS_MS = int(self.FPS * 1000)
# Start frame retrieval thread
self.thread = Thread(target=self.update, args=())
self.thread.daemon = True
self.thread.start()
def update(self):
while True:
if self.capture.isOpened():
(self.status, self.frame) = self.capture.read()
time.sleep(self.FPS)
def show_frame(self):
cv2.imshow('frame', self.frame)
cv2.waitKey(self.FPS_MS)
if __name__ == '__main__':
src = 'https://videos3.earthcam.com/fecnetwork/9974.flv/chunklist_w1421640637.m3u8'
threaded_camera = ThreadedCamera(src)
while True:
try:
threaded_camera.show_frame()
except AttributeError:
pass
I've attempted this solution from nathancy with minor success.
It involves:
import cv2
from threading import Thread
class ThreadedCamera(object):
def __init__(self, source = 0):
self.capture = cv2.VideoCapture(source)
self.thread = Thread(target = self.update, args = ())
self.thread.daemon = True
self.thread.start()
self.status = False
self.frame = None
def update(self):
while True:
if self.capture.isOpened():
(self.status, self.frame) = self.capture.read()
def grab_frame(self):
if self.status:
return self.frame
return None
if __name__ == '__main__':
stream_link = "https://videos3.earthcam.com/fecnetwork/9974.flv/chunklist_w1421640637.m3u8"
streamer = ThreadedCamera(stream_link)
while True:
frame = streamer.grab_frame()
if frame is not None:
cv2.imshow("Context", frame)
cv2.waitKey(1)
.
The streaming works. It maintains real-time. However, it is as if all the frames pool up and suddenly burst into the video. I would like somebody to explain that.
The real-time stream can be found here.
https://www.earthcam.com/usa/newyork/timessquare/?cam=tsstreet
This site is scraped for the m3u8
using python's streamlink
stream scraper.
import streamlink
streams = streamlink.streams("https://www.earthcam.com/usa/newyork/timessquare/?cam=tsstreet")
print(streams)
which yeilds:
OrderedDict([
('720p',<HLSStream('https://videos3.earthcam.com/fecnetwork/9974.flv/chunklist_w202109066.m3u8')>),
('live', <RTMPStream({'rtmp': 'rtmp://videos3.earthcam.com/fecnetwork/', 'playpath': '9974.flv', 'pageUrl': 'https://www.earthcam.com/usa/newyork/timessquare/?cam=tsstreet','swfUrl': 'http://static.earthcam.com/swf/streaming/stream_viewer_v3.swf', 'live': 'true'}, redirect=False>),
('worst', <HLSStream('https://videos3.earthcam.com/fecnetwork/9974.flv/chunklist_w202109066.m3u8')>),
('best', <RTMPStream({'rtmp': 'rtmp://videos3.earthcam.com/fecnetwork/', 'playpath': '9974.flv', 'pageUrl': 'https://www.earthcam.com/usa/newyork/timessquare/?cam=tsstreet', 'swfUrl': 'http://static.earthcam.com/swf/streaming/stream_viewer_v3.swf', 'live': 'true'}, redirect=False>)
])
The possibility that the streams are being read wrong.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With