Commit bb99fd8afdb08083c90876ab5f0d6d0e32f0c6e8

Authored by caotto
2 parents e9a774f1 d43c8217

Merge pull request #30 from biometrics/stream_processing

some updates to buffer management
Showing 1 changed file with 194 additions and 68 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 = 200) : 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 = 20) : 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,61 +87,176 @@ 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  
116   -// Interface for sequentially getting data from some data source.
117   -// Initialized off of a template, can represent a video file (stored in the template's filename)
118   -// or a set of images already loaded into memory stored as multiple matrices in an input template.
119   -class DataSource
  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
120 107 {
121 108 public:
122   - DataSource() {}
123   - virtual ~DataSource() {}
  109 + DoubleBuffer()
  110 + {
  111 + inputBuffer = &buffer1;
  112 + outputBuffer = &buffer2;
  113 + }
124 114  
125   - virtual FrameData * getNext() = 0;
126   - virtual void close() = 0;
127   - virtual bool open(Template & input) = 0;
128   - virtual bool isOpen() = 0;
  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;
129 195 };
130 196  
131   -// Read a video frame by frame using cv::VideoCapture
132   -class VideoDataSource : public DataSource
  197 +
  198 +// Interface for sequentially getting data from some data source.
  199 +// Initialized off of a template, can represent a video file (stored in the template's filename)
  200 +// or a set of images already loaded into memory stored as multiple matrices in an input template.
  201 +class DataSource
133 202 {
134 203 public:
135   - VideoDataSource() {}
136   -
137   - FrameData * getNext()
  204 + DataSource(int maxFrames=100)
138 205 {
139   - if (!isOpen())
140   - return NULL;
  206 + for (int i=0; i < maxFrames;i++)
  207 + {
  208 + allFrames.addItem(new FrameData());
  209 + }
  210 + allFrames.startInput();
  211 + }
141 212  
142   - FrameData * output = new FrameData();
143   - output->data.append(Template(basis.file));
144   - output->data.last().append(cv::Mat());
  213 + virtual ~DataSource()
  214 + {
  215 + allFrames.stoppedInput();
  216 + while (true)
  217 + {
  218 + FrameData * frame = allFrames.getItem();
  219 + if (frame == NULL)
  220 + break;
  221 + delete frame;
  222 + }
  223 + }
145 224  
146   - output->sequenceNumber = next_idx;
147   - next_idx++;
  225 + FrameData * getFrame()
  226 + {
  227 + FrameData * aFrame = allFrames.getItem();
  228 + aFrame->data.clear();
  229 + aFrame->sequenceNumber = -1;
148 230  
149   - bool res = video.read(output->data.last().last());
  231 + bool res = getNext(*aFrame);
150 232 if (!res) {
151   - delete output;
  233 + allFrames.addItem(aFrame);
152 234 return NULL;
153 235 }
154   - return output;
  236 + return aFrame;
155 237 }
156 238  
  239 + void returnFrame(FrameData * inputFrame)
  240 + {
  241 + allFrames.addItem(inputFrame);
  242 + }
  243 +
  244 + virtual void close() = 0;
  245 + virtual bool open(Template & output) = 0;
  246 + virtual bool isOpen() = 0;
  247 +
  248 + virtual bool getNext(FrameData & input) = 0;
  249 +
  250 +protected:
  251 + DoubleBuffer allFrames;
  252 +};
  253 +
  254 +// Read a video frame by frame using cv::VideoCapture
  255 +class VideoDataSource : public DataSource
  256 +{
  257 +public:
  258 + VideoDataSource(int maxFrames) : DataSource(maxFrames) {}
  259 +
157 260 bool open(Template &input)
158 261 {
159 262 next_idx = 0;
... ... @@ -167,6 +270,24 @@ public:
167 270 void close() { video.release(); }
168 271  
169 272 private:
  273 + bool getNext(FrameData & output)
  274 + {
  275 + if (!isOpen())
  276 + return false;
  277 +
  278 + output.data.append(Template(basis.file));
  279 + output.data.last().append(cv::Mat());
  280 +
  281 + output.sequenceNumber = next_idx;
  282 + next_idx++;
  283 +
  284 + bool res = video.read(output.data.last().last());
  285 + if (!res) {
  286 + return false;
  287 + }
  288 + return true;
  289 + }
  290 +
170 291 cv::VideoCapture video;
171 292 Template basis;
172 293 int next_idx;
... ... @@ -177,21 +298,9 @@ private:
177 298 class TemplateDataSource : public DataSource
178 299 {
179 300 public:
180   - TemplateDataSource() { current_idx = INT_MAX; }
181   -
182   - FrameData * getNext()
  301 + TemplateDataSource(int maxFrames) : DataSource(maxFrames)
183 302 {
184   - if (!isOpen())
185   - return NULL;
186   -
187   - FrameData * output = new FrameData();
188   - output->data.append(basis[current_idx]);
189   - current_idx++;
190   -
191   - output->sequenceNumber = next_sequence;
192   - next_sequence++;
193   -
194   - return output;
  303 + current_idx = INT_MAX;
195 304 }
196 305  
197 306 bool open(Template &input)
... ... @@ -211,6 +320,20 @@ public:
211 320 }
212 321  
213 322 private:
  323 + bool getNext(FrameData & output)
  324 + {
  325 + if (!isOpen())
  326 + return false;
  327 +
  328 + output.data.append(basis[current_idx]);
  329 + current_idx++;
  330 +
  331 + output.sequenceNumber = next_sequence;
  332 + next_sequence++;
  333 +
  334 + return true;
  335 + }
  336 +
214 337 Template basis;
215 338 int current_idx;
216 339 int next_sequence;
... ... @@ -232,12 +355,6 @@ public:
232 355 close();
233 356 }
234 357  
235   - FrameData * getNext()
236   - {
237   - if (!isOpen()) return NULL;
238   - return actualSource->getNext();
239   - }
240   -
241 358 void close()
242 359 {
243 360 if (actualSource) {
... ... @@ -253,13 +370,13 @@ public:
253 370 bool open_res = false;
254 371 // Input has no matrices? Its probably a video that hasn't been loaded yet
255 372 if (input.empty()) {
256   - actualSource = new VideoDataSource();
  373 + actualSource = new VideoDataSource(0);
257 374 open_res = actualSource->open(input);
258 375 qDebug("created video resource status %d", open_res);
259 376 }
260 377 else {
261 378 // create frame dealer
262   - actualSource = new TemplateDataSource();
  379 + actualSource = new TemplateDataSource(0);
263 380 open_res = actualSource->open(input);
264 381 }
265 382 if (!isOpen()) {
... ... @@ -274,6 +391,11 @@ public:
274 391  
275 392 protected:
276 393 DataSource * actualSource;
  394 + bool getNext(FrameData & output)
  395 + {
  396 + return actualSource->getNext(output);
  397 + }
  398 +
277 399 };
278 400  
279 401 class ProcessingStage : public QRunnable
... ... @@ -347,7 +469,8 @@ public:
347 469 {
348 470 forever
349 471 {
350   - FrameData * aFrame = dataSource.getNext();
  472 + //FrameData * aFrame = dataSource.getNext();
  473 + FrameData * aFrame = dataSource.getFrame();
351 474 if (aFrame == NULL)
352 475 break;
353 476 outputBuffer->addItem(aFrame);
... ... @@ -367,6 +490,7 @@ public:
367 490 private:
368 491 TemplateList collectedOutput;
369 492 public:
  493 + DataSource * data;
370 494 void run()
371 495 {
372 496 forever
... ... @@ -377,7 +501,8 @@ public:
377 501 break;
378 502 // Just put the item on collectedOutput
379 503 collectedOutput.append(frame->data);
380   - delete frame;
  504 + // Return the frame to the input frame buffer
  505 + data->returnFrame(frame);
381 506 }
382 507 this->markStop();
383 508 }
... ... @@ -483,7 +608,7 @@ public:
483 608 }
484 609  
485 610 // buffer 0 -- output buffer for the read stage
486   - sharedBuffers.append(new SingleBuffer());
  611 + sharedBuffers.append(new DoubleBuffer());
487 612 readStage.outputBuffer = sharedBuffers.last();
488 613 readStage.stage_id = 0;
489 614  
... ... @@ -499,12 +624,13 @@ public:
499 624 processingStages.last()->inputBuffer = sharedBuffers[lastBufferIdx];
500 625 lastBufferIdx++;
501 626  
502   - sharedBuffers.append(new SingleBuffer());
  627 + sharedBuffers.append(new DoubleBuffer());
503 628 processingStages.last()->outputBuffer = sharedBuffers.last();
504 629 processingStages.last()->transform = transforms[i];
505 630 }
506 631  
507 632 collectionStage.inputBuffer = sharedBuffers.last();
  633 + collectionStage.data = &readStage.dataSource;
508 634 collectionStage.stage_id = next_stage_id;
509 635 }
510 636  
... ...