"""Functions for fetching video frames, meta data and file locations"""
import warnings
import re
from datetime import timedelta
from pathlib import Path
import cv2
import numpy as np
from brainbox.core import Bunch
from oneibl.one import ONE
from oneibl import params
VIDEO_LABELS = ('left', 'right', 'body')
[docs]class VideoStreamer:
"""
Provides a wrapper to stream a video from a password protected HTTP server using opencv
"""
def __init__(self, url_vid):
"""
TODO Allow auth as input
:param url_vid: full url of the video or dataset dictionary as output by alyx rest datasets
:returns cv2.VideoCapture object
"""
# pop the data url from the dataset record if the input is a dictionary
if isinstance(url_vid, dict):
url_vid = next(fr['data_url'] for fr in url_vid['file_records'] if fr['data_url'])
self.url = url_vid
self._par = params.get(silent=True)
self.cap = cv2.VideoCapture(self._url)
self.total_frames = int(self.cap.get(cv2.CAP_PROP_FRAME_COUNT))
@property
def _url(self):
username = self._par.HTTP_DATA_SERVER_LOGIN
password = self._par.HTTP_DATA_SERVER_PWD
return re.sub(r'(^https?://)', r'\1' + f'{username}:{password}@', self.url)
[docs] def get_frame(self, frame_index):
self.cap.set(cv2.CAP_PROP_POS_FRAMES, frame_index)
return self.cap.read()
[docs]def url_from_eid(eid, label=None, one=None):
"""Return the video URL(s) for a given eid
:param eid: The session id
:param label: The video label (e.g. 'body') or a tuple thereof
:param one: An instance of ONE
:return: The URL string if the label is a string, otherwise a dict of urls with labels as keys
"""
warnings.warn('Support for oneibl.one will soon be removed, use one.api instead',
category=DeprecationWarning)
valid_labels = ('left', 'right', 'body')
if not (label is None or np.isin(label, ('left', 'right', 'body')).all()):
raise ValueError('labels must be one of ("%s")' % '", "'.join(valid_labels))
one = one or ONE()
datasets = one.list_datasets(eid, details=True)
def match(dataset):
if dataset['dataset_type'] != '_iblrig_Camera.raw':
return False
if label:
name = re.match(r'(?:_iblrig_)([a-z]+)(?=Camera.raw.mp4$)', dataset['name']).group(1)
return name in label
else:
return True
datasets = [ds for ds in datasets if match(ds)]
urls = [next(r['data_url'] for r in ds['file_records'] if r['data_url']) for ds in datasets]
# If one label specified, return the url, otherwise return a dict
if isinstance(label, str):
return urls[0]
urls_dict = {label_from_path(url): url for url in urls}
return {**dict.fromkeys(label), **urls_dict} if label else urls_dict
[docs]def label_from_path(video_name):
"""
Return the video label, i.e. 'left', 'right' or 'body'
:param video_name: A file path, URL or file name for the video
:return: The string label or None if the video doesn't match
"""
result = re.search(r'(?<=_)([a-z]+)(?=Camera)', str(video_name))
return result.group() if result else None