162 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			162 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
from PyQt5.QtCore import QSemaphore, QMutex
 | 
						|
from PyQt5.QtCore import QMutexLocker, QWaitCondition
 | 
						|
from queue import Queue
 | 
						|
 | 
						|
 | 
						|
class Buffer(object):
 | 
						|
 | 
						|
    def __init__(self, buffer_size=5):
 | 
						|
        self.buffer_size = buffer_size
 | 
						|
        self.free_slots = QSemaphore(self.buffer_size)
 | 
						|
        self.used_slots = QSemaphore(0)
 | 
						|
        self.clear_buffer_add = QSemaphore(1)
 | 
						|
        self.clear_buffer_get = QSemaphore(1)
 | 
						|
        self.queue_mutex = QMutex()
 | 
						|
        self.queue = Queue(self.buffer_size)
 | 
						|
 | 
						|
    def add(self, data, drop_if_full=False):
 | 
						|
        self.clear_buffer_add.acquire()
 | 
						|
        if drop_if_full:
 | 
						|
            if self.free_slots.tryAcquire():
 | 
						|
                self.queue_mutex.lock()
 | 
						|
                self.queue.put(data)
 | 
						|
                self.queue_mutex.unlock()
 | 
						|
                self.used_slots.release()
 | 
						|
        else:
 | 
						|
            self.free_slots.acquire()
 | 
						|
            self.queue_mutex.lock()
 | 
						|
            self.queue.put(data)
 | 
						|
            self.queue_mutex.unlock()
 | 
						|
            self.used_slots.release()
 | 
						|
 | 
						|
        self.clear_buffer_add.release()
 | 
						|
 | 
						|
    def get(self):
 | 
						|
        # acquire semaphores
 | 
						|
        self.clear_buffer_get.acquire()
 | 
						|
        self.used_slots.acquire()
 | 
						|
        self.queue_mutex.lock()
 | 
						|
        data = self.queue.get()
 | 
						|
        self.queue_mutex.unlock()
 | 
						|
        # release semaphores
 | 
						|
        self.free_slots.release()
 | 
						|
        self.clear_buffer_get.release()
 | 
						|
        # return item to caller
 | 
						|
        return data
 | 
						|
 | 
						|
    def clear(self):
 | 
						|
        # check if buffer contains items
 | 
						|
        if self.queue.qsize() > 0:
 | 
						|
            # stop adding items to buffer (will return false if an item is currently being added to the buffer)
 | 
						|
            if self.clear_buffer_add.tryAcquire():
 | 
						|
                # stop taking items from buffer (will return false if an item is currently being taken from the buffer)
 | 
						|
                if self.clear_buffer_get.tryAcquire():
 | 
						|
                    # release all remaining slots in queue
 | 
						|
                    self.free_slots.release(self.queue.qsize())
 | 
						|
                    # acquire all queue slots
 | 
						|
                    self.free_slots.acquire(self.buffer_size)
 | 
						|
                    # reset used_slots to zero
 | 
						|
                    self.used_slots.acquire(self.queue.qsize())
 | 
						|
                    # clear buffer
 | 
						|
                    for _ in range(self.queue.qsize()):
 | 
						|
                        self.queue.get()
 | 
						|
                    # release all slots
 | 
						|
                    self.free_slots.release(self.buffer_size)
 | 
						|
                    # allow get method to resume
 | 
						|
                    self.clear_buffer_get.release()
 | 
						|
                else:
 | 
						|
                    return False
 | 
						|
                # allow add method to resume
 | 
						|
                self.clear_buffer_add.release()
 | 
						|
                return True
 | 
						|
            else:
 | 
						|
                return False
 | 
						|
        else:
 | 
						|
            return False
 | 
						|
 | 
						|
    def size(self):
 | 
						|
        return self.queue.qsize()
 | 
						|
 | 
						|
    def maxsize(self):
 | 
						|
        return self.buffer_size
 | 
						|
 | 
						|
    def isfull(self):
 | 
						|
        return self.queue.qsize() == self.buffer_size
 | 
						|
 | 
						|
    def isempty(self):
 | 
						|
        return self.queue.qsize() == 0
 | 
						|
 | 
						|
 | 
						|
class MultiBufferManager(object):
 | 
						|
 | 
						|
    """
 | 
						|
    Class for synchronizing capture threads from different cameras.
 | 
						|
    """
 | 
						|
 | 
						|
    def __init__(self, do_sync=True):
 | 
						|
        self.sync_devices = set()
 | 
						|
        self.do_sync = do_sync
 | 
						|
        self.wc = QWaitCondition()
 | 
						|
        self.mutex = QMutex()
 | 
						|
        self.arrived = 0
 | 
						|
        self.buffer_maps = dict()
 | 
						|
 | 
						|
    def bind_thread(self, thread, buffer_size, sync=True):
 | 
						|
        self.create_buffer_for_device(thread.device_id, buffer_size, sync)
 | 
						|
        thread.buffer_manager = self
 | 
						|
 | 
						|
    def create_buffer_for_device(self, device_id, buffer_size, sync=True):
 | 
						|
        if sync:
 | 
						|
            with QMutexLocker(self.mutex):
 | 
						|
                self.sync_devices.add(device_id)
 | 
						|
 | 
						|
        self.buffer_maps[device_id] = Buffer(buffer_size)
 | 
						|
 | 
						|
    def get_device(self, device_id):
 | 
						|
        return self.buffer_maps[device_id]
 | 
						|
 | 
						|
    def remove_device(self, device_id):
 | 
						|
        self.buffer_maps.pop(device_id)
 | 
						|
        with QMutexLocker(self.mutex):
 | 
						|
            if device_id in self.sync_devices:
 | 
						|
                self.sync_devices.remove(device_id)
 | 
						|
                self.wc.wakeAll()
 | 
						|
 | 
						|
    def sync(self, device_id):
 | 
						|
        # only perform sync if enabled for specified device/stream
 | 
						|
        self.mutex.lock()
 | 
						|
        if device_id in self.sync_devices:
 | 
						|
            # increment arrived count
 | 
						|
            self.arrived += 1
 | 
						|
            # we are the last to arrive: wake all waiting threads
 | 
						|
            if self.do_sync and self.arrived == len(self.sync_devices):
 | 
						|
                self.wc.wakeAll()
 | 
						|
            # still waiting for other streams to arrive: wait
 | 
						|
            else:
 | 
						|
                self.wc.wait(self.mutex)
 | 
						|
            # decrement arrived count
 | 
						|
            self.arrived -= 1
 | 
						|
        self.mutex.unlock()
 | 
						|
 | 
						|
    def wake_all(self):
 | 
						|
        with QMutexLocker(self.mutex):
 | 
						|
            self.wc.wakeAll()
 | 
						|
 | 
						|
    def set_sync(self, enable):
 | 
						|
        self.do_sync = enable
 | 
						|
 | 
						|
    def sync_enabled(self):
 | 
						|
        return self.do_sync
 | 
						|
 | 
						|
    def sync_enabled_for_device(self, device_id):
 | 
						|
        return device_id in self.sync_devices
 | 
						|
 | 
						|
    def __contains__(self, device_id):
 | 
						|
        return device_id in self.buffer_maps
 | 
						|
 | 
						|
    def __str__(self):
 | 
						|
        return (self.__class__.__name__ + ":\n" + \
 | 
						|
                "sync: {}\n".format(self.do_sync) + \
 | 
						|
                "devices: {}\n".format(tuple(self.buffer_maps.keys())) + \
 | 
						|
                "sync enabled devices: {}".format(self.sync_devices))
 |