Commit d43c8217fc072cc0e34aae2894902473be314898

Authored by Charles Otto
1 parent 46985ff5

Update buffer classes slightly

Simplify SingleBuffer since controlling the total amount of frames being
processed works quite well.
Add a double-buffer scheme, which leads to a really marginal performance
improvement (but I think it's cool).
Showing 1 changed file with 102 additions and 20 deletions
sdk/plugins/stream.cpp
1   -#define QT_NO_DEBUG_OUTPUT
2   -
3 1 #include <openbr_plugin.h>
4 2 #include <QReadWriteLock>
5 3 #include <QWaitCondition>
... ... @@ -29,19 +27,15 @@ public:
29 27 class SharedBuffer
30 28 {
31 29 public:
32   - SharedBuffer(int _maxItems = 200000) : maxItems(_maxItems) {}
  30 + SharedBuffer() {}
33 31 virtual ~SharedBuffer() {}
34 32  
35 33 virtual void addItem(FrameData * input)=0;
36 34  
37 35 virtual FrameData * getItem()=0;
38 36  
39   - int getMaxItems() { return maxItems; }
40   -
41 37 virtual void stoppedInput() =0;
42 38 virtual void startInput() = 0;
43   -protected:
44   - int maxItems;
45 39 };
46 40  
47 41 // For 1 - 1 boundaries, a buffer class with a single shared buffer, a mutex
... ... @@ -49,7 +43,7 @@ protected:
49 43 class SingleBuffer : public SharedBuffer
50 44 {
51 45 public:
52   - SingleBuffer(unsigned _maxItems = 20000) : SharedBuffer(_maxItems) { no_input = false; }
  46 + SingleBuffer() { no_input = false; }
53 47  
54 48 void stoppedInput()
55 49 {
... ... @@ -70,14 +64,8 @@ public:
70 64 {
71 65 QMutexLocker bufferLock(&bufferGuard);
72 66  
73   - // If the buffer is too full, wait for space to become available
74   - if (buffer.size() >= maxItems) {
75   - availableOutputSpace.wait(&bufferGuard);
76   - }
77   -
78 67 buffer.append(input);
79 68  
80   - // Wait for certain # of items?
81 69 availableInput.wakeOne();
82 70 }
83 71  
... ... @@ -99,20 +87,114 @@ public:
99 87  
100 88 FrameData * output = buffer.first();
101 89 buffer.removeFirst();
102   - if (buffer.size() < maxItems / 2)
103   - availableOutputSpace.wakeAll();
104 90 return output;
105 91 }
106 92  
107 93 private:
108 94 QMutex bufferGuard;
109 95 QWaitCondition availableInput;
110   - QWaitCondition availableOutputSpace;
111 96 bool no_input;
112 97  
113 98 QList<FrameData *> buffer;
114 99 };
115 100  
  101 +// For 1 - 1 boundaries, a double buffering scheme
  102 +// Producer/consumer read/write from separate buffers, and switch if their
  103 +// buffer runs out/overflows. Synchronization is handled by a read/write lock
  104 +// threads are "reading" if they are adding to/removing from their individual
  105 +// buffer, and writing if they access or swap with the other buffer.
  106 +class DoubleBuffer : public SharedBuffer
  107 +{
  108 +public:
  109 + DoubleBuffer()
  110 + {
  111 + inputBuffer = &buffer1;
  112 + outputBuffer = &buffer2;
  113 + }
  114 +
  115 + void stoppedInput()
  116 + {
  117 + QWriteLocker bufferLock(&bufferGuard);
  118 + no_input = true;
  119 + // Release anything waiting for input items.
  120 + availableInput.wakeAll();
  121 + }
  122 +
  123 + // There will be more input
  124 + void startInput()
  125 + {
  126 + QWriteLocker bufferLock(&bufferGuard);
  127 + no_input = false;
  128 + }
  129 +
  130 + // called from the producer thread
  131 + void addItem(FrameData * input)
  132 + {
  133 + QReadLocker readLock(&bufferGuard);
  134 + inputBuffer->append(input);
  135 + availableInput.wakeOne();
  136 + }
  137 +
  138 + // Called from the consumer thread
  139 + FrameData * getItem() {
  140 + QReadLocker readLock(&bufferGuard);
  141 +
  142 + // There is something for us to get
  143 + if (!outputBuffer->empty()) {
  144 + FrameData * output = outputBuffer->first();
  145 + outputBuffer->removeFirst();
  146 + return output;
  147 + }
  148 +
  149 + // Outputbuffer is empty, try to swap with the input buffer, we need a
  150 + // write lock to do that.
  151 + readLock.unlock();
  152 + QWriteLocker writeLock(&bufferGuard);
  153 +
  154 + // Nothing on the input buffer either?
  155 + if (inputBuffer->empty()) {
  156 + // If nothing else is coming, return null
  157 + if (no_input)
  158 + return NULL;
  159 + //otherwise, wait on the input buffer
  160 + availableInput.wait(&bufferGuard);
  161 + // Did we get woken up because no more input is coming? if so
  162 + // we're done here
  163 + if (no_input && inputBuffer->empty())
  164 + return NULL;
  165 + }
  166 +
  167 + // input buffer is non-empty, so swap the buffers
  168 + std::swap(inputBuffer, outputBuffer);
  169 +
  170 + // Return a frame
  171 + FrameData * output = outputBuffer->first();
  172 + outputBuffer->removeFirst();
  173 + return output;
  174 + }
  175 +
  176 +private:
  177 + // The read-write lock. The thread adding to this buffer can add
  178 + // to the current input buffer if it has a read lock. The thread
  179 + // removing from this buffer can remove things from the current
  180 + // output buffer if it has a read lock, or swap the buffers if it
  181 + // has a write lock.
  182 + // Checking/modifying no_input requires a write lock.
  183 + QReadWriteLock bufferGuard;
  184 + QWaitCondition availableInput;
  185 + bool no_input;
  186 +
  187 + // The buffer that is currently being added to
  188 + QList<FrameData *> * inputBuffer;
  189 + // The buffer that is currently being removed from
  190 + QList<FrameData *> * outputBuffer;
  191 +
  192 + // The buffers pointed at by inputBuffer/outputBuffer
  193 + QList<FrameData *> buffer1;
  194 + QList<FrameData *> buffer2;
  195 +};
  196 +
  197 +
116 198 // Interface for sequentially getting data from some data source.
117 199 // Initialized off of a template, can represent a video file (stored in the template's filename)
118 200 // or a set of images already loaded into memory stored as multiple matrices in an input template.
... ... @@ -166,7 +248,7 @@ public:
166 248 virtual bool getNext(FrameData & input) = 0;
167 249  
168 250 protected:
169   - SingleBuffer allFrames;
  251 + DoubleBuffer allFrames;
170 252 };
171 253  
172 254 // Read a video frame by frame using cv::VideoCapture
... ... @@ -526,7 +608,7 @@ public:
526 608 }
527 609  
528 610 // buffer 0 -- output buffer for the read stage
529   - sharedBuffers.append(new SingleBuffer());
  611 + sharedBuffers.append(new DoubleBuffer());
530 612 readStage.outputBuffer = sharedBuffers.last();
531 613 readStage.stage_id = 0;
532 614  
... ... @@ -542,7 +624,7 @@ public:
542 624 processingStages.last()->inputBuffer = sharedBuffers[lastBufferIdx];
543 625 lastBufferIdx++;
544 626  
545   - sharedBuffers.append(new SingleBuffer());
  627 + sharedBuffers.append(new DoubleBuffer());
546 628 processingStages.last()->outputBuffer = sharedBuffers.last();
547 629 processingStages.last()->transform = transforms[i];
548 630 }
... ...