Almost working on multiple apis

This commit is contained in:
Anne de Jong 2020-11-05 17:13:09 +01:00
parent c1f713c8fb
commit 4e0c09d356
8 changed files with 155 additions and 109 deletions

View File

@ -29,14 +29,14 @@ vector<DaqApi> DaqApi::getAvailableApis() {
vector<DaqApi> apis; vector<DaqApi> apis;
apis.resize(6); apis.resize(6);
#ifdef HAS_ULDAQ_API #ifdef HAS_ULDAQ_API
apis[uldaqapi.apicode] = uldaqapi; apis.at(uldaqapi.apicode) = uldaqapi;
#endif #endif
#ifdef HAS_RTAUDIO_API #ifdef HAS_RTAUDIO_API
apis[rtaudioAlsaApi.apicode] = rtaudioAlsaApi; apis.at(rtaudioAlsaApi.apicode) = rtaudioAlsaApi;
apis[rtaudioPulseaudioApi.apicode] = rtaudioPulseaudioApi; apis.at(rtaudioPulseaudioApi.apicode) = rtaudioPulseaudioApi;
apis[rtaudioWasapiApi.apicode] = rtaudioWasapiApi; apis.at(rtaudioWasapiApi.apicode) = rtaudioWasapiApi;
apis[rtaudioDsApi.apicode] = rtaudioDsApi; apis.at(rtaudioDsApi.apicode) = rtaudioDsApi;
apis[rtaudioAsioApi.apicode] = rtaudioAsioApi; apis.at(rtaudioAsioApi.apicode) = rtaudioAsioApi;
#endif #endif
return apis; return apis;
} }
@ -85,26 +85,26 @@ bool DaqConfiguration::match(const DeviceInfo& dev) const {
int DaqConfiguration::getHighestInChannel() const { int DaqConfiguration::getHighestInChannel() const {
for(int i=eninchannels.size()-1; i>-1;i--) { for(int i=eninchannels.size()-1; i>-1;i--) {
if(eninchannels[i]) return i; if(eninchannels.at(i)) return i;
} }
return -1; return -1;
} }
int DaqConfiguration::getHighestOutChannel() const { int DaqConfiguration::getHighestOutChannel() const {
for(us i=enoutchannels.size()-1; i>=0;i--) { for(us i=enoutchannels.size()-1; i>=0;i--) {
if(enoutchannels[i]) return i; if(enoutchannels.at(i)) return i;
} }
return -1; return -1;
} }
int DaqConfiguration::getLowestInChannel() const { int DaqConfiguration::getLowestInChannel() const {
for(us i=0; i<eninchannels.size();i++) { for(us i=0; i<eninchannels.size();i++) {
if(eninchannels[i]) return i; if(eninchannels.at(i)) return i;
} }
return -1; return -1;
} }
int DaqConfiguration::getLowestOutChannel() const { int DaqConfiguration::getLowestOutChannel() const {
for(us i=0; i<enoutchannels.size();i++) { for(us i=0; i<enoutchannels.size();i++) {
if(enoutchannels[i]) return i; if(enoutchannels.at(i)) return i;
} }
return -1; return -1;
} }
@ -118,15 +118,29 @@ Daq *Daq::createDaq(const DeviceInfo& devinfo,
// Some basic sanity checks // Some basic sanity checks
if ((devinfo.ninchannels != config.eninchannels.size())) { if ((devinfo.ninchannels != config.eninchannels.size())) {
/* cerr << "devinfo.ninchannels: " << devinfo.ninchannels << endl; */
/* cerr << "config.eninchannels.size(): " << config.eninchannels.size() << endl; */
throw runtime_error("Invalid length of enabled input channels specified"); throw runtime_error("Invalid length of enabled input channels specified");
} }
if ((devinfo.noutchannels != config.enoutchannels.size())) { if ((devinfo.noutchannels != config.enoutchannels.size())) {
throw runtime_error("outvalid length of enabled output channels specified"); throw runtime_error("outvalid length of enabled output channels specified");
} }
if (devinfo.api == uldaqapi) { int apicode = devinfo.api.apicode;
if(devinfo.api == DaqApi()) {
throw std::runtime_error(string("Unable to match API: ") + devinfo.api.apiname);
}
#ifdef HAS_ULDAQ_API
else if (devinfo.api == uldaqapi) {
return createUlDaqDevice(devinfo, config); return createUlDaqDevice(devinfo, config);
} else { }
#endif
#ifdef HAS_RTAUDIO_API
else if(apicode >= 1 && apicode <= 5) {
return createRtAudioDevice(devinfo, config);
}
#endif
else {
throw std::runtime_error(string("Unable to match API: ") + throw std::runtime_error(string("Unable to match API: ") +
devinfo.api.apiname); devinfo.api.apiname);
} }
@ -154,14 +168,14 @@ Daq::Daq(const DeviceInfo &devinfo, const DaqConfiguration &config)
double Daq::samplerate() const { double Daq::samplerate() const {
mutexlock lock(mutex); mutexlock lock(mutex);
assert(sampleRateIndex < availableSampleRates.size()); assert(sampleRateIndex < availableSampleRates.size());
return availableSampleRates[sampleRateIndex]; return availableSampleRates.at(sampleRateIndex);
} }
DataType Daq::dataType() const { DataType Daq::dataType() const {
mutexlock lock(mutex); mutexlock lock(mutex);
assert((us)dataTypeIndex < availableDataTypes.size()); assert((us)dataTypeIndex < availableDataTypes.size());
return availableDataTypes[dataTypeIndex]; return availableDataTypes.at(dataTypeIndex);
} }
@ -171,7 +185,7 @@ double Daq::inputRangeForChannel(us ch) const {
} }
mutexlock lock(mutex); mutexlock lock(mutex);
assert(inputRangeIndices.size() == eninchannels.size()); assert(inputRangeIndices.size() == eninchannels.size());
return availableInputRanges[inputRangeIndices[ch]]; return availableInputRanges.at(inputRangeIndices.at(ch));
} }

View File

@ -120,7 +120,7 @@ public:
double prefSampleRate() const { double prefSampleRate() const {
if (((us)prefSampleRateIndex < availableSampleRates.size()) && if (((us)prefSampleRateIndex < availableSampleRates.size()) &&
(prefSampleRateIndex >= 0)) { (prefSampleRateIndex >= 0)) {
return availableSampleRates[prefSampleRateIndex]; return availableSampleRates.at(prefSampleRateIndex);
} else { } else {
throw std::runtime_error("No prefered sample rate available"); throw std::runtime_error("No prefered sample rate available");
} }
@ -209,7 +209,7 @@ public:
us framesPerBlock() const { us framesPerBlock() const {
mutexlock lock(mutex); mutexlock lock(mutex);
return availableFramesPerBlock[framesPerBlockIndex]; return availableFramesPerBlock.at(framesPerBlockIndex);
} }
bool duplexMode() const { bool duplexMode() const {
return (neninchannels(false) > 0 && nenoutchannels() > 0); return (neninchannels(false) > 0 && nenoutchannels() > 0);

View File

@ -116,17 +116,22 @@ class AudioDaq: public Daq {
nFramesPerBlock = this->framesPerBlock(); nFramesPerBlock = this->framesPerBlock();
if(neninchannels(false) > 0) { if(neninchannels(false) > 0) {
instreamparams = new RtAudio::StreamParameters(); instreamparams = new RtAudio::StreamParameters();
instreamparams->nChannels = getHighestInChannel(); instreamparams->nChannels = getHighestInChannel() + 1;
if(instreamparams->nChannels < 1) {
throw runtime_error("Invalid input number of channels");
}
instreamparams->firstChannel = 0; instreamparams->firstChannel = 0;
instreamparams->deviceId = devinfo.api_specific_devindex; instreamparams->deviceId = devinfo.api_specific_devindex;
} }
if(nenoutchannels() > 0) { if(nenoutchannels() > 0) {
outstreamparams = new RtAudio::StreamParameters(); outstreamparams = new RtAudio::StreamParameters();
outstreamparams->nChannels = getHighestOutChannel(); outstreamparams->nChannels = getHighestOutChannel() + 1;
if(outstreamparams->nChannels < 1) {
throw runtime_error("Invalid output number of channels");
}
outstreamparams->firstChannel = 0; outstreamparams->firstChannel = 0;
outstreamparams->deviceId = devinfo.api_specific_devindex; outstreamparams->deviceId = devinfo.api_specific_devindex;
} }

View File

@ -42,6 +42,15 @@ public:
DT9837A(const DeviceInfo &devinfo, const DaqConfiguration &config) DT9837A(const DeviceInfo &devinfo, const DaqConfiguration &config)
: Daq(devinfo, config) { : Daq(devinfo, config) {
// Some sanity checks
if (eninchannels.size() != 4) {
throw runtime_error("Invalid length of enabled inChannels vector");
}
if (enoutchannels.size() != 1) {
throw runtime_error("Invalid length of enabled outChannels vector");
}
stopThread = false; stopThread = false;
nFramesPerBlock = availableFramesPerBlock[framesPerBlockIndex]; nFramesPerBlock = availableFramesPerBlock[framesPerBlockIndex];

View File

@ -6,5 +6,8 @@ cdef class Daq:
cdef: cdef:
PyStreamData *sd PyStreamData *sd
cppDaq* daq_device cppDaq* daq_device
cdef public:
double samplerate
unsigned int nFramesPerBlock
cdef cleanupStream(self, PyStreamData* sd) cdef cleanupStream(self, PyStreamData* sd)

View File

@ -7,7 +7,7 @@ from .lasp_device_common import AvType
__all__ = ['Daq'] __all__ = ['Daq']
cdef cnp.NPY_TYPES getNumpyDataType(DataType& dt): cdef cnp.NPY_TYPES getCNumpyDataType(DataType& dt):
if(dt == dtype_fl32): if(dt == dtype_fl32):
return cnp.NPY_FLOAT32 return cnp.NPY_FLOAT32
elif(dt == dtype_fl64): elif(dt == dtype_fl64):
@ -21,6 +21,19 @@ cdef cnp.NPY_TYPES getNumpyDataType(DataType& dt):
else: else:
raise ValueError('Unknown data type') raise ValueError('Unknown data type')
cdef getNumpyDataType(DataType& dt):
if(dt == dtype_fl32):
return np.float32
elif(dt == dtype_fl64):
return np.float64
elif(dt == dtype_int8):
return np.int8
elif(dt == dtype_int16):
return np.int16
elif(dt == dtype_int32):
return np.int32
else:
raise ValueError('Unknown data type')
ctypedef struct PyStreamData: ctypedef struct PyStreamData:
@ -132,21 +145,47 @@ cdef void audioCallbackPythonThreadFunction(void* voidsd) nogil:
cdef class Daq: cdef class Daq:
def __cinit__(self): def __cinit__(self, DeviceInfo pydevinfo, DaqConfiguration pydaqconfig):
""" """
Acquires a daq handle, and opens the device Acquires a daq handle, and opens the device
""" """
cdef:
cppDaqConfiguration* daqconfig
cppDeviceInfo* devinfo
vector[cppDeviceInfo] devinfos
self.daq_device = NULL self.daq_device = NULL
self.sd = NULL self.sd = NULL
daqconfig = &(pydaqconfig.config)
devinfo = &(pydevinfo.devinfo)
self.daq_device = cppDaq.createDaq(devinfo[0], daqconfig[0])
self.nFramesPerBlock = self.daq_device.framesPerBlock()
self.samplerate = self.daq_device.samplerate()
if self.nFramesPerBlock > 8192 or self.nFramesPerBlock < 512:
del self.daq_device
raise ValueError('Invalid number of nFramesPerBlock')
def __dealloc__(self): def __dealloc__(self):
# fprintf(stderr, "UlDaq.__dealloc__\n") # fprintf(stderr, "UlDaq.__dealloc__\n")
if self.sd is not NULL: if self.sd is not NULL:
fprintf(stderr, "UlDaq.__dealloc__: stopping stream.\n") fprintf(stderr, "UlDaq.__dealloc__: stopping stream.\n")
self.stop() self.stop()
if self.daq_device is not NULL:
del self.daq_device
self.daq_device = NULL
def getNumpyDataType(self):
cdef:
DataType dt = self.daq_device.dataType()
return getNumpyDataType(dt)
def isRunning(self): def isRunning(self):
return self.sd is not NULL return self.sd is not NULL
@ -166,7 +205,7 @@ cdef class Daq:
return pydevinfo return pydevinfo
@cython.nonecheck(True) @cython.nonecheck(True)
def start(self, avstream): def start(self, audiocallback):
""" """
Opens a stream with specified parameters Opens a stream with specified parameters
@ -175,36 +214,15 @@ cdef class Daq:
Returns: None Returns: None
""" """
cdef:
DaqConfiguration pydaqconfig
DeviceInfo pydevinfo
cppDaqConfiguration* daqconfig
cppDeviceInfo* devinfo
vector[cppDeviceInfo] devinfos
if self.sd is not NULL: if self.sd is not NULL:
assert self.daq_device is not NULL assert self.daq_device is not NULL
raise RuntimeError('Stream is already opened.') raise RuntimeError('Stream is already opened.')
pydaqconfig = avstream.daqconfig
avtype = avstream.avtype
pydevinfo = avstream.deviceinfo
daqconfig = &(pydaqconfig.config)
devinfo = &(pydevinfo.devinfo)
self.daq_device = cppDaq.createDaq(devinfo[0], daqconfig[0])
cdef: cdef:
cppDaq* daq = self.daq_device cppDaq* daq = self.daq_device
unsigned nFramesPerBlock = self.nFramesPerBlock
cdef: DataType dtype = self.daq_device.dataType()
unsigned int nFramesPerBlock = daq.framesPerBlock() double samplerate = self.samplerate
double samplerate = daq.samplerate()
DataType dtype = daq.dataType()
if nFramesPerBlock > 8192 or nFramesPerBlock < 512:
del self.daq_device
raise ValueError('Invalid number of nFramesPerBlock')
# All set, allocate the stream! # All set, allocate the stream!
@ -225,17 +243,17 @@ cdef class Daq:
self.sd.noutchannels = daq.nenoutchannels() self.sd.noutchannels = daq.nenoutchannels()
self.sd.nBytesPerChan = nFramesPerBlock*dtype.sw self.sd.nBytesPerChan = nFramesPerBlock*dtype.sw
self.sd.nFramesPerBlock = nFramesPerBlock self.sd.nFramesPerBlock = nFramesPerBlock
self.sd.npy_format = getNumpyDataType(dtype) self.sd.npy_format = getCNumpyDataType(dtype)
if daq.neninchannels() > 0: if daq.neninchannels() > 0:
self.sd.inQueue = new SafeQueue[void*]() self.sd.inQueue = new SafeQueue[void*]()
if daq.nenoutchannels() > 0: if daq.nenoutchannels() > 0:
self.sd.inQueue = new SafeQueue[void*]() self.sd.inQueue = new SafeQueue[void*]()
self.sd.pyCallback = <PyObject*> avstream._audioCallback self.sd.pyCallback = <PyObject*> audiocallback
# Increase reference count to the callback # Increase reference count to the callback
Py_INCREF(<object> avstream._audioCallback) Py_INCREF(<object> audiocallback)
with nogil: with nogil:
self.sd.thread = new CPPThread[void*, void (*)(void*)](audioCallbackPythonThreadFunction, self.sd.thread = new CPPThread[void*, void (*)(void*)](audioCallbackPythonThreadFunction,
@ -248,15 +266,11 @@ cdef class Daq:
self.sd.inQueue, self.sd.inQueue,
self.sd.outQueue) self.sd.outQueue)
return nFramesPerBlock, self.daq_device.samplerate() return self.daq_device.samplerate()
def stop(self): def stop(self):
if self.sd is NULL: if self.sd is NULL:
raise RuntimeError('Stream is not opened') raise RuntimeError('Stream is not opened')
if self.daq_device is not NULL:
self.daq_device.stop()
del self.daq_device
self.daq_device = NULL
self.cleanupStream(self.sd) self.cleanupStream(self.sd)
self.sd = NULL self.sd = NULL

View File

@ -107,6 +107,9 @@ cdef class DaqConfiguration:
config.outchannel_names = [outchname.encode('utf-8') for outchname in config.outchannel_names = [outchname.encode('utf-8') for outchname in
pydict['outchannel_names']] pydict['outchannel_names']]
config.inchannel_sensitivities = pydict['inchannel_sensitivities']
config.outchannel_sensitivities = pydict['outchannel_sensitivities']
config.sampleRateIndex = pydict['sampleRateIndex'] config.sampleRateIndex = pydict['sampleRateIndex']
config.framesPerBlockIndex = pydict['framesPerBlockIndex'] config.framesPerBlockIndex = pydict['framesPerBlockIndex']
config.dataTypeIndex = pydict['dataTypeIndex'] config.dataTypeIndex = pydict['dataTypeIndex']
@ -138,6 +141,16 @@ cdef class DaqConfiguration:
outchannel_names = [name.decode('utf-8') for name in outchannel_names = [name.decode('utf-8') for name in
self.config.outchannel_names], self.config.outchannel_names],
inchannel_sensitivities = [sens for sens in
self.config.inchannel_sensitivities],
outchannel_sensitivities = [sens for sens in
self.config.outchannel_sensitivities],
inchannel_metadata = [inchmeta.decode('utf-8') for inchmeta in
self.config.inchannel_metadata],
outchannel_metadata = [outchmeta.decode('utf-8') for outchmeta in
self.config.outchannel_metadata],
sampleRateIndex = self.config.sampleRateIndex, sampleRateIndex = self.config.sampleRateIndex,
dataTypeIndex = self.config.dataTypeIndex, dataTypeIndex = self.config.dataTypeIndex,
framesPerBlockIndex = self.config.framesPerBlockIndex, framesPerBlockIndex = self.config.framesPerBlockIndex,
@ -147,27 +160,23 @@ cdef class DaqConfiguration:
inputACCouplingMode = self.config.inputACCouplingMode, inputACCouplingMode = self.config.inputACCouplingMode,
inputRangeIndices = self.config.inputRangeIndices, inputRangeIndices = self.config.inputRangeIndices,
inchannel_metadata = [inchmeta.decode('utf-8') for inchmeta in
self.config.inchannel_metadata],
outchannel_metadata = [outchmeta.decode('utf-8') for outchmeta in
self.config.outchannel_metadata]
)) ))
def getInChannel(self, i:int): def getInChannel(self, i:int):
return DaqChannel( return DaqChannel(
channel_enabled=self.config.eninchannels[i], channel_enabled=self.config.eninchannels.at(i),
channel_name=self.config.inchannel_names[i].decode('utf-8'), channel_name=self.config.inchannel_names.at(i).decode('utf-8'),
sensitivity=self.config.inchannel_sensitivities[i], sensitivity=self.config.inchannel_sensitivities.at(i),
range_index=self.config.inputRangeIndices[i], range_index=self.config.inputRangeIndices.at(i),
ACCoupling_enabled=self.config.inputACCouplingMode[i], ACCoupling_enabled=self.config.inputACCouplingMode.at(i),
IEPE_enabled=self.config.inputIEPEEnabled[i], IEPE_enabled=self.config.inputIEPEEnabled.at(i),
channel_metadata=self.config.inchannel_metadata[i].decode('utf-8'), channel_metadata=self.config.inchannel_metadata.at(i).decode('utf-8'),
) )
def getOutChannel(self, i:int): def getOutChannel(self, i:int):
return DaqChannel( return DaqChannel(
channel_enabled=self.config.enoutchannels[i], channel_enabled=self.config.enoutchannels.at(i),
channel_name=self.config.outchannel_names[i].decode('utf-8'), channel_name=self.config.outchannel_names.at(i).decode('utf-8'),
channel_metadata=self.config.outchannel_metadata[i].decode('utf-8'), channel_metadata=self.config.outchannel_metadata.at(i).decode('utf-8'),
) )
def setInChannel(self, i:int, ch: DaqChannel): def setInChannel(self, i:int, ch: DaqChannel):
@ -184,6 +193,25 @@ cdef class DaqConfiguration:
self.config.outchannel_names[i] = ch.channel_name.encode('utf-8') self.config.outchannel_names[i] = ch.channel_name.encode('utf-8')
self.config.outchannel_metadata[i] = ch.channel_metadata.encode('utf-8') self.config.outchannel_metadata[i] = ch.channel_metadata.encode('utf-8')
def getEnabledInChannels(self, include_monitor=True):
inch = []
for i, enabled in enumerate(self.config.eninchannels):
if enabled:
inch.append(self.getInChannel(i))
if include_monitor:
outch = self.getEnabledOutChannels()
if len(outch) > 0:
inch.insert(0, outch[0])
return inch
def getEnabledOutChannels(self):
outch = []
for i, enabled in enumerate(self.config.enoutchannels):
if enabled:
outch.append(self.getOutChannel(i))
return outch
@property @property
def api(self): def api(self):
return self.config.api.apiname.decode('utf-8') return self.config.api.apiname.decode('utf-8')

View File

@ -13,8 +13,6 @@ class DAQConfiguration:
import time import time
from .device import (Daq, DeviceInfo, from .device import (Daq, DeviceInfo,
# get_numpy_dtype_from_format_string,
# get_sampwidth_from_format_string,
AvType, AvType,
) )
@ -29,8 +27,8 @@ class AvStream:
processing the data.""" processing the data."""
def __init__(self, def __init__(self,
device: DeviceInfo,
avtype: AvType, avtype: AvType,
device: DeviceInfo,
daqconfig: DAQConfiguration, daqconfig: DAQConfiguration,
video=None): video=None):
"""Open a stream for audio in/output and video input. For audio output, """Open a stream for audio in/output and video input. For audio output,
@ -49,46 +47,20 @@ class AvStream:
self.daqconfig = daqconfig self.daqconfig = daqconfig
self.device = device self.device = device
self.avtype = avtype self.avtype = avtype
self.duplex_mode = daqconfig.duplex_mode
self.output_channel_names = [ch.channel_name for ch in daqconfig.getEnabledOutputChannels()] en_out_ch = daqconfig.getEnabledOutChannels()
en_in_ch = daqconfig.getEnabledInChannels(include_monitor=True)
if daqconfig.monitor_gen: self.output_channel_names = [ch.channel_name for ch in en_out_ch]
self.input_channel_names = self.output_channel_names self.input_channel_names = [ch.channel_name for ch in en_in_ch]
self.input_sensitivity = daqconfig.getEnabledOutputChannelSensitivities()
self.input_qtys = daqconfig.getEnabledOutputChannelQtys() self.input_sensitivity = [ch.sensitivity for ch in en_in_ch]
else: self.input_qtys = [ch.qty for ch in en_in_ch]
self.input_channel_names = []
self.input_sensitivity = []
self.input_qtys = []
self.input_sensitivity += daqconfig.getEnabledInputChannelSensitivities()
self.input_sensitivity = np.asarray(self.input_sensitivity) self.input_sensitivity = np.asarray(self.input_sensitivity)
self.input_qtys += daqconfig.getEnabledInputChannelQtys()
# Fill in numpy data type, and sample width # Fill in numpy data type, and sample width
self.input_numpy_dtype = get_numpy_dtype_from_format_string( datatype = daqconfig.dataTypeIndex
daqconfig.en_input_sample_format)
self.input_sampwidth = get_sampwidth_from_format_string(
daqconfig.en_input_sample_format)
self.input_channel_names += [
ch.channel_name for ch in daqconfig.getEnabledInputChannels()]
self.input_samplerate = float(daqconfig.en_input_rate)
if self.duplex_mode:
self.output_samplerate = float(daqconfig.en_input_rate)
self.output_sampwidth = self.input_sampwidth
self.output_numpy_dtype = self.input_numpy_dtype
else:
self.output_samplerate = float(daqconfig.en_output_rate)
self.output_sampwidth = get_sampwidth_from_format_string(
daqconfig.en_output_sample_format)
self.output_numpy_dtype = get_numpy_dtype_from_format_string(
daqconfig.en_output_sample_format)
# Counters for the number of frames that have been coming in # Counters for the number of frames that have been coming in
self._aframectr = Atomic(0) self._aframectr = Atomic(0)
@ -113,8 +85,9 @@ class AvStream:
self._videothread = None self._videothread = None
# self._audiobackend = RtAudio(daqconfig.api) # self._audiobackend = RtAudio(daqconfig.api)
self._audiobackend = UlDaq() self._daq = Daq(device, daqconfig)
self.blocksize = daqconfig.nFramesPerBlock self.blocksize = self._daq.nFramesPerBlock
self.samplerate = self._daq.samplerate
def nCallbacks(self): def nCallbacks(self):
"""Returns the current number of installed callbacks.""" """Returns the current number of installed callbacks."""
@ -154,7 +127,7 @@ class AvStream:
else: else:
self._video_started <<= True self._video_started <<= True
self.blocksize, self.samplerate = self._audiobackend.start(self) self.samplerate = self._daq.start(self._audioCallback)
def _videoThread(self): def _videoThread(self):
cap = cv.VideoCapture(self._video) cap = cv.VideoCapture(self._video)