glenn-jocher commited on
Commit
683cefe
1 Parent(s): f340235

YouTube stream ending fix (#3277)

Browse files

* YouTube stream ending fix

Properly terminates YouTube streams on video end. Should resolve issues #2769 and #3220.

* Update datasets.py

Files changed (1) hide show
  1. utils/datasets.py +15 -16
utils/datasets.py CHANGED
@@ -270,7 +270,7 @@ class LoadStreams: # multiple IP or RTSP cameras
270
  sources = [sources]
271
 
272
  n = len(sources)
273
- self.imgs = [None] * n
274
  self.sources = [clean_str(x) for x in sources] # clean source names for later
275
  for i, s in enumerate(sources): # index, source
276
  # Start thread to read frames from video stream
@@ -284,13 +284,13 @@ class LoadStreams: # multiple IP or RTSP cameras
284
  assert cap.isOpened(), f'Failed to open {s}'
285
  w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
286
  h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
287
- self.fps = (cap.get(cv2.CAP_PROP_FPS) % 100) or 30.0 # assume 30 FPS if cap gets 0 FPS
288
- self.frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
289
 
290
  _, self.imgs[i] = cap.read() # guarantee first frame
291
- thread = Thread(target=self.update, args=([i, cap]), daemon=True)
292
- print(f" success ({f'{self.frames} frames ' if self.frames else ''}{w}x{h} at {self.fps:.2f} FPS).")
293
- thread.start()
294
  print('') # newline
295
 
296
  # check for common shapes
@@ -299,18 +299,17 @@ class LoadStreams: # multiple IP or RTSP cameras
299
  if not self.rect:
300
  print('WARNING: Different stream shapes detected. For optimal performance supply similarly-shaped streams.')
301
 
302
- def update(self, index, cap):
303
- # Read next stream frame in a daemon thread
304
- n = 0
305
- while cap.isOpened():
306
  n += 1
307
  # _, self.imgs[index] = cap.read()
308
  cap.grab()
309
- if n == 4: # read every 4th frame
310
  success, im = cap.retrieve()
311
- self.imgs[index] = im if success else self.imgs[index] * 0
312
- n = 0
313
- time.sleep(1 / self.fps) # wait time
314
 
315
  def __iter__(self):
316
  self.count = -1
@@ -318,12 +317,12 @@ class LoadStreams: # multiple IP or RTSP cameras
318
 
319
  def __next__(self):
320
  self.count += 1
321
- img0 = self.imgs.copy()
322
- if cv2.waitKey(1) == ord('q'): # q to quit
323
  cv2.destroyAllWindows()
324
  raise StopIteration
325
 
326
  # Letterbox
 
327
  img = [letterbox(x, self.img_size, auto=self.rect, stride=self.stride)[0] for x in img0]
328
 
329
  # Stack
 
270
  sources = [sources]
271
 
272
  n = len(sources)
273
+ self.imgs, self.fps, self.frames, self.threads = [None] * n, [0] * n, [0] * n, [None] * n
274
  self.sources = [clean_str(x) for x in sources] # clean source names for later
275
  for i, s in enumerate(sources): # index, source
276
  # Start thread to read frames from video stream
 
284
  assert cap.isOpened(), f'Failed to open {s}'
285
  w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
286
  h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
287
+ self.fps[i] = (cap.get(cv2.CAP_PROP_FPS) % 100) or 30.0 # assume 30 FPS if cap gets 0 FPS
288
+ self.frames[i] = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) or float('inf') # assume infinite stream if 0 len
289
 
290
  _, self.imgs[i] = cap.read() # guarantee first frame
291
+ self.threads[i] = Thread(target=self.update, args=([i, cap]), daemon=True)
292
+ print(f" success ({self.frames[i]} frames {w}x{h} at {self.fps[i]:.2f} FPS)")
293
+ self.threads[i].start()
294
  print('') # newline
295
 
296
  # check for common shapes
 
299
  if not self.rect:
300
  print('WARNING: Different stream shapes detected. For optimal performance supply similarly-shaped streams.')
301
 
302
+ def update(self, i, cap):
303
+ # Read stream `i` frames in daemon thread
304
+ n, f = 0, self.frames[i]
305
+ while cap.isOpened() and n < f:
306
  n += 1
307
  # _, self.imgs[index] = cap.read()
308
  cap.grab()
309
+ if n % 4: # read every 4th frame
310
  success, im = cap.retrieve()
311
+ self.imgs[i] = im if success else self.imgs[i] * 0
312
+ time.sleep(1 / self.fps[i]) # wait time
 
313
 
314
  def __iter__(self):
315
  self.count = -1
 
317
 
318
  def __next__(self):
319
  self.count += 1
320
+ if not all(x.is_alive() for x in self.threads) or cv2.waitKey(1) == ord('q'): # q to quit
 
321
  cv2.destroyAllWindows()
322
  raise StopIteration
323
 
324
  # Letterbox
325
+ img0 = self.imgs.copy()
326
  img = [letterbox(x, self.img_size, auto=self.rect, stride=self.stride)[0] for x in img0]
327
 
328
  # Stack