Commit b2eb42a0b959c5abbbc1af7400adfd0bd67983e7
1 parent
e11b68d6
Some long overdue cleanup/reorganization in stream.cpp
Merge Datasource/DataSourceManager, leaving DataSource as the primary interface for opening a template list. Introduce an alternate hieararchy for things that are done to individual templates. Rename FirstStage->ReadStage. Drop LastStage as a unique class, it can be represented as a single threaded stage with a transform that just collects the templates it receives. Introduce a separate class contaiing the stream mode enum. This avoids a lot of circular dependencies, giving us a more sane file layout.
Showing
1 changed file
with
536 additions
and
692 deletions
openbr/plugins/stream.cpp
| @@ -16,6 +16,16 @@ using namespace cv; | @@ -16,6 +16,16 @@ using namespace cv; | ||
| 16 | namespace br | 16 | namespace br |
| 17 | { | 17 | { |
| 18 | 18 | ||
| 19 | +class Idiocy : public QObject | ||
| 20 | +{ | ||
| 21 | + Q_OBJECT | ||
| 22 | +public: | ||
| 23 | + enum StreamModes { StreamVideo, | ||
| 24 | + DistributeFrames, | ||
| 25 | + Auto}; | ||
| 26 | + | ||
| 27 | + Q_ENUMS(StreamModes) | ||
| 28 | +}; | ||
| 19 | 29 | ||
| 20 | class FrameData | 30 | class FrameData |
| 21 | { | 31 | { |
| @@ -181,10 +191,138 @@ private: | @@ -181,10 +191,138 @@ private: | ||
| 181 | QList<FrameData *> buffer2; | 191 | QList<FrameData *> buffer2; |
| 182 | }; | 192 | }; |
| 183 | 193 | ||
| 194 | +// Given a template as input, return N templates as output, one at a time on subsequent | ||
| 195 | +// calls to getNext | ||
| 196 | +class TemplateProcessor | ||
| 197 | +{ | ||
| 198 | +public: | ||
| 199 | + virtual ~TemplateProcessor() {} | ||
| 200 | + virtual bool open(Template & input)=0; | ||
| 201 | + virtual bool isOpen()=0; | ||
| 202 | + virtual void close()=0; | ||
| 203 | + virtual bool getNextTemplate(Template & output)=0; | ||
| 204 | +protected: | ||
| 205 | + Template basis; | ||
| 206 | +}; | ||
| 207 | + | ||
| 208 | +static QMutex openLock; | ||
| 209 | + | ||
| 210 | +// Read a video frame by frame using cv::VideoCapture | ||
| 211 | +class VideoReader : public TemplateProcessor | ||
| 212 | +{ | ||
| 213 | +public: | ||
| 214 | + VideoReader() {} | ||
| 215 | + | ||
| 216 | + bool open(Template &input) | ||
| 217 | + { | ||
| 218 | + basis = input; | ||
| 219 | + | ||
| 220 | + // We can open either files (well actually this includes addresses of ip cameras | ||
| 221 | + // through ffmpeg), or webcams. Webcam VideoCaptures are created through a separate | ||
| 222 | + // overload of open that takes an integer, not a string. | ||
| 223 | + // So, does this look like an integer? | ||
| 224 | + bool is_int = false; | ||
| 225 | + int anInt = input.file.name.toInt(&is_int); | ||
| 226 | + if (is_int) | ||
| 227 | + { | ||
| 228 | + bool rc = video.open(anInt); | ||
| 229 | + | ||
| 230 | + if (!rc) | ||
| 231 | + { | ||
| 232 | + qDebug("open failed!"); | ||
| 233 | + } | ||
| 234 | + if (!video.isOpened()) | ||
| 235 | + { | ||
| 236 | + qDebug("Video not open!"); | ||
| 237 | + } | ||
| 238 | + } else { | ||
| 239 | + // Yes, we should specify absolute path: | ||
| 240 | + // http://stackoverflow.com/questions/9396459/loading-a-video-in-opencv-in-python | ||
| 241 | + QString fileName = (Globals->path.isEmpty() ? "" : Globals->path + "/") + input.file.name; | ||
| 242 | + // On windows, this appears to not be thread-safe | ||
| 243 | + QMutexLocker lock(&openLock); | ||
| 244 | + video.open(QFileInfo(fileName).absoluteFilePath().toStdString()); | ||
| 245 | + } | ||
| 246 | + | ||
| 247 | + return video.isOpened(); | ||
| 248 | + } | ||
| 249 | + | ||
| 250 | + bool isOpen() { return video.isOpened(); } | ||
| 251 | + | ||
| 252 | + void close() { video.release(); } | ||
| 253 | + | ||
| 254 | + bool getNextTemplate(Template & output) | ||
| 255 | + { | ||
| 256 | + if (!isOpen()) { | ||
| 257 | + qDebug("video source is not open"); | ||
| 258 | + return false; | ||
| 259 | + } | ||
| 260 | + output.file = basis.file; | ||
| 261 | + output.m() = cv::Mat(); | ||
| 262 | + | ||
| 263 | + cv::Mat temp; | ||
| 264 | + bool res = video.read(temp); | ||
| 265 | + | ||
| 266 | + if (!res) { | ||
| 267 | + // The video capture broke, return false. | ||
| 268 | + output.m() = cv::Mat(); | ||
| 269 | + close(); | ||
| 270 | + return false; | ||
| 271 | + } | ||
| 272 | + | ||
| 273 | + // This clone is critical, if we don't do it then the matrix will | ||
| 274 | + // be an alias of an internal buffer of the video source, leading | ||
| 275 | + // to various problems later. | ||
| 276 | + output.m() = temp.clone(); | ||
| 277 | + return true; | ||
| 278 | + } | ||
| 279 | +protected: | ||
| 280 | + cv::VideoCapture video; | ||
| 281 | +}; | ||
| 282 | + | ||
| 283 | + | ||
| 284 | +class DirectReturn : public TemplateProcessor | ||
| 285 | +{ | ||
| 286 | +public: | ||
| 287 | + DirectReturn() | ||
| 288 | + { | ||
| 289 | + data_ok = false; | ||
| 290 | + } | ||
| 291 | + | ||
| 292 | + // We don't do anything, just prepare to return input when getNext is called. | ||
| 293 | + bool open(Template &input) | ||
| 294 | + { | ||
| 295 | + basis = input; | ||
| 296 | + data_ok =true; | ||
| 297 | + return data_ok; | ||
| 298 | + } | ||
| 299 | + | ||
| 300 | + bool isOpen() { return data_ok; } | ||
| 301 | + | ||
| 302 | + void close() | ||
| 303 | + { | ||
| 304 | + data_ok = false; | ||
| 305 | + basis.clear(); | ||
| 306 | + } | ||
| 307 | + | ||
| 308 | + bool getNextTemplate(Template & output) | ||
| 309 | + { | ||
| 310 | + if (!data_ok) | ||
| 311 | + return false; | ||
| 312 | + output = basis; | ||
| 313 | + data_ok = false; | ||
| 314 | + return true; | ||
| 315 | + } | ||
| 316 | + | ||
| 317 | +protected: | ||
| 318 | + // Have we sent our template yet? | ||
| 319 | + bool data_ok; | ||
| 320 | +}; | ||
| 321 | + | ||
| 184 | 322 | ||
| 185 | // Interface for sequentially getting data from some data source. | 323 | // Interface for sequentially getting data from some data source. |
| 186 | -// Initialized off of a template, can represent a video file (stored in the template's filename) | ||
| 187 | -// or a set of images already loaded into memory stored as multiple matrices in an input template. | 324 | +// Given a TemplateList, return single template frames sequentially by applying a TemplateProcessor |
| 325 | +// to each individual template. | ||
| 188 | class DataSource | 326 | class DataSource |
| 189 | { | 327 | { |
| 190 | public: | 328 | public: |
| @@ -196,6 +334,7 @@ public: | @@ -196,6 +334,7 @@ public: | ||
| 196 | { | 334 | { |
| 197 | allFrames.addItem(new FrameData()); | 335 | allFrames.addItem(new FrameData()); |
| 198 | } | 336 | } |
| 337 | + frameSource = NULL; | ||
| 199 | } | 338 | } |
| 200 | 339 | ||
| 201 | virtual ~DataSource() | 340 | virtual ~DataSource() |
| @@ -209,6 +348,71 @@ public: | @@ -209,6 +348,71 @@ public: | ||
| 209 | } | 348 | } |
| 210 | } | 349 | } |
| 211 | 350 | ||
| 351 | + void close() | ||
| 352 | + { | ||
| 353 | + if (this->frameSource) | ||
| 354 | + { | ||
| 355 | + frameSource->close(); | ||
| 356 | + delete frameSource; | ||
| 357 | + frameSource = NULL; | ||
| 358 | + } | ||
| 359 | + } | ||
| 360 | + | ||
| 361 | + int size() | ||
| 362 | + { | ||
| 363 | + return this->templates.size(); | ||
| 364 | + } | ||
| 365 | + | ||
| 366 | + bool open(TemplateList & input, br::Idiocy::StreamModes _mode) | ||
| 367 | + { | ||
| 368 | + // Set up variables specific to us | ||
| 369 | + current_template_idx = 0; | ||
| 370 | + templates = input; | ||
| 371 | + mode = _mode; | ||
| 372 | + | ||
| 373 | + is_broken = false; | ||
| 374 | + allReturned = false; | ||
| 375 | + | ||
| 376 | + // The last frame isn't initialized yet | ||
| 377 | + final_frame = -1; | ||
| 378 | + // Start our sequence numbers from the input index | ||
| 379 | + next_sequence_number = 0; | ||
| 380 | + | ||
| 381 | + // Actually open the data source | ||
| 382 | + bool open_res = openNextTemplate(); | ||
| 383 | + | ||
| 384 | + // We couldn't open the data source | ||
| 385 | + if (!open_res) { | ||
| 386 | + is_broken = true; | ||
| 387 | + return false; | ||
| 388 | + } | ||
| 389 | + | ||
| 390 | + // Try to get a frame from the global pool | ||
| 391 | + FrameData * firstFrame = allFrames.tryGetItem(); | ||
| 392 | + | ||
| 393 | + // If this fails, things have gone pretty badly. | ||
| 394 | + if (firstFrame == NULL) { | ||
| 395 | + is_broken = true; | ||
| 396 | + return false; | ||
| 397 | + } | ||
| 398 | + | ||
| 399 | + // Read a frame from the video source | ||
| 400 | + bool res = getNextFrame(*firstFrame); | ||
| 401 | + | ||
| 402 | + // the data source broke already, we couldn't even get one frame | ||
| 403 | + // from it even though it claimed to have opened successfully. | ||
| 404 | + if (!res) { | ||
| 405 | + is_broken = true; | ||
| 406 | + return false; | ||
| 407 | + } | ||
| 408 | + | ||
| 409 | + // We read one frame ahead of the last one returned, this allows | ||
| 410 | + // us to know which frame is the final frame when we return it. | ||
| 411 | + lookAhead.append(firstFrame); | ||
| 412 | + return true; | ||
| 413 | + } | ||
| 414 | + | ||
| 415 | + | ||
| 212 | // non-blocking version of getFrame | 416 | // non-blocking version of getFrame |
| 213 | // Returns a NULL FrameData if too many frames are out, or the | 417 | // Returns a NULL FrameData if too many frames are out, or the |
| 214 | // data source is broken. Sets last_frame to true iff the FrameData | 418 | // data source is broken. Sets last_frame to true iff the FrameData |
| @@ -228,7 +432,7 @@ public: | @@ -228,7 +432,7 @@ public: | ||
| 228 | return NULL; | 432 | return NULL; |
| 229 | 433 | ||
| 230 | // Try to actually read a frame, if this returns false the data source is broken | 434 | // Try to actually read a frame, if this returns false the data source is broken |
| 231 | - bool res = getNext(*aFrame); | 435 | + bool res = getNextFrame(*aFrame); |
| 232 | 436 | ||
| 233 | // The datasource broke, update final_frame | 437 | // The datasource broke, update final_frame |
| 234 | if (!res) | 438 | if (!res) |
| @@ -291,282 +495,119 @@ public: | @@ -291,282 +495,119 @@ public: | ||
| 291 | return true; | 495 | return true; |
| 292 | } | 496 | } |
| 293 | 497 | ||
| 294 | - bool open(Template & output, int start_index = 0) | 498 | +protected: |
| 499 | + | ||
| 500 | + bool openNextTemplate() | ||
| 295 | { | 501 | { |
| 296 | - is_broken = false; | ||
| 297 | - allReturned = false; | 502 | + if (this->current_template_idx >= this->templates.size()) |
| 503 | + return false; | ||
| 298 | 504 | ||
| 299 | - // The last frame isn't initialized yet | ||
| 300 | - final_frame = -1; | ||
| 301 | - // Start our sequence numbers from the input index | ||
| 302 | - next_sequence_number = start_index; | 505 | + bool open_res = false; |
| 506 | + while (!open_res) | ||
| 507 | + { | ||
| 508 | + if (frameSource) | ||
| 509 | + frameSource->close(); | ||
| 303 | 510 | ||
| 304 | - // Actually open the data source | ||
| 305 | - bool open_res = concreteOpen(output); | 511 | + if (mode == br::Idiocy::Auto) |
| 512 | + { | ||
| 513 | + delete frameSource; | ||
| 514 | + if (this->templates[this->current_template_idx].empty()) | ||
| 515 | + frameSource = new VideoReader(); | ||
| 516 | + else | ||
| 517 | + frameSource = new DirectReturn(); | ||
| 518 | + } | ||
| 519 | + else if (mode == br::Idiocy::DistributeFrames) | ||
| 520 | + { | ||
| 521 | + if (!frameSource) | ||
| 522 | + frameSource = new DirectReturn(); | ||
| 523 | + } | ||
| 524 | + else if (mode == br::Idiocy::StreamVideo) | ||
| 525 | + { | ||
| 526 | + if (!frameSource) | ||
| 527 | + frameSource = new VideoReader(); | ||
| 528 | + } | ||
| 306 | 529 | ||
| 307 | - // We couldn't open the data source | ||
| 308 | - if (!open_res) { | ||
| 309 | - is_broken = true; | ||
| 310 | - return false; | 530 | + open_res = frameSource->open(this->templates[current_template_idx]); |
| 531 | + if (!open_res) | ||
| 532 | + { | ||
| 533 | + current_template_idx++; | ||
| 534 | + if (current_template_idx >= this->templates.size()) | ||
| 535 | + return false; | ||
| 536 | + } | ||
| 311 | } | 537 | } |
| 538 | + return true; | ||
| 539 | + } | ||
| 312 | 540 | ||
| 313 | - // Try to get a frame from the global pool | ||
| 314 | - FrameData * firstFrame = allFrames.tryGetItem(); | 541 | + bool getNextFrame(FrameData & output) |
| 542 | + { | ||
| 543 | + bool got_frame = false; | ||
| 315 | 544 | ||
| 316 | - // If this fails, things have gone pretty badly. | ||
| 317 | - if (firstFrame == NULL) { | ||
| 318 | - is_broken = true; | ||
| 319 | - return false; | ||
| 320 | - } | 545 | + Template aTemplate; |
| 321 | 546 | ||
| 322 | - // Read a frame from the video source | ||
| 323 | - bool res = getNext(*firstFrame); | 547 | + while (!got_frame) |
| 548 | + { | ||
| 549 | + got_frame = frameSource->getNextTemplate(aTemplate); | ||
| 550 | + | ||
| 551 | + // OK we got a frame | ||
| 552 | + if (got_frame) { | ||
| 553 | + // set the sequence number and tempalte of this frame | ||
| 554 | + output.sequenceNumber = next_sequence_number; | ||
| 555 | + output.data.append(aTemplate); | ||
| 556 | + // set the frame number in the template's metadata | ||
| 557 | + output.data.last().file.set("FrameNumber", output.sequenceNumber); | ||
| 558 | + next_sequence_number++; | ||
| 559 | + return true; | ||
| 560 | + } | ||
| 324 | 561 | ||
| 325 | - // the data source broke already, we couldn't even get one frame | ||
| 326 | - // from it even though it claimed to have opened successfully. | ||
| 327 | - if (!res) { | ||
| 328 | - is_broken = true; | ||
| 329 | - return false; | 562 | + // advance to the next tempalte in our list |
| 563 | + this->current_template_idx++; | ||
| 564 | + bool open_res = this->openNextTemplate(); | ||
| 565 | + | ||
| 566 | + // couldn't get the next template? nothing to do, otherwise we try to read | ||
| 567 | + // a frame at the top of this loop. | ||
| 568 | + if (!open_res) { | ||
| 569 | + return false; | ||
| 570 | + } | ||
| 330 | } | 571 | } |
| 331 | 572 | ||
| 332 | - // We read one frame ahead of the last one returned, this allows | ||
| 333 | - // us to know which frame is the final frame when we return it. | ||
| 334 | - lookAhead.append(firstFrame); | ||
| 335 | - return true; | 573 | + return false; |
| 336 | } | 574 | } |
| 337 | 575 | ||
| 338 | - /* | ||
| 339 | - * Pure virtual methods | ||
| 340 | - */ | 576 | + // Index of the template in the templatelist we are currently reading from |
| 577 | + int current_template_idx; | ||
| 341 | 578 | ||
| 342 | - // isOpen doesn't appear to particularly work when used on opencv | ||
| 343 | - // VideoCaptures, so we don't use it for anything important. | ||
| 344 | - virtual bool isOpen()=0; | ||
| 345 | - // Called from open, open the data source specified by the input | ||
| 346 | - // template, don't worry about setting any of the state variables | ||
| 347 | - // set in open. | ||
| 348 | - virtual bool concreteOpen(Template & output) = 0; | ||
| 349 | - // Get the next frame from the data source, store the results in | ||
| 350 | - // FrameData (including the actual frame and appropriate sequence | ||
| 351 | - // number). | ||
| 352 | - virtual bool getNext(FrameData & input) = 0; | ||
| 353 | - // close the currently open data source. | ||
| 354 | - virtual void close() = 0; | 579 | + // What do we do to each template |
| 580 | + br::Idiocy::StreamModes mode; | ||
| 581 | + | ||
| 582 | + // list of templates we are workign from | ||
| 583 | + TemplateList templates; | ||
| 584 | + | ||
| 585 | + // processor for the current template | ||
| 586 | + TemplateProcessor * frameSource; | ||
| 355 | 587 | ||
| 356 | int next_sequence_number; | 588 | int next_sequence_number; |
| 357 | -protected: | ||
| 358 | - DoubleBuffer allFrames; | ||
| 359 | int final_frame; | 589 | int final_frame; |
| 360 | bool is_broken; | 590 | bool is_broken; |
| 361 | bool allReturned; | 591 | bool allReturned; |
| 592 | + | ||
| 593 | + DoubleBuffer allFrames; | ||
| 362 | QList<FrameData *> lookAhead; | 594 | QList<FrameData *> lookAhead; |
| 363 | 595 | ||
| 364 | QWaitCondition lastReturned; | 596 | QWaitCondition lastReturned; |
| 365 | QMutex last_frame_update; | 597 | QMutex last_frame_update; |
| 366 | }; | 598 | }; |
| 367 | 599 | ||
| 368 | -static QMutex openLock; | ||
| 369 | -// Read a video frame by frame using cv::VideoCapture | ||
| 370 | -class VideoDataSource : public DataSource | 600 | +class ProcessingStage; |
| 601 | + | ||
| 602 | +class BasicLoop : public QRunnable, public QFutureInterface<void> | ||
| 371 | { | 603 | { |
| 372 | public: | 604 | public: |
| 373 | - VideoDataSource(int maxFrames) : DataSource(maxFrames) {} | ||
| 374 | - | ||
| 375 | - bool concreteOpen(Template &input) | 605 | + BasicLoop() |
| 376 | { | 606 | { |
| 377 | - basis = input; | 607 | + this->reportStarted(); |
| 608 | + } | ||
| 378 | 609 | ||
| 379 | - // We can open either files (well actually this includes addresses of ip cameras | ||
| 380 | - // through ffmpeg), or webcams. Webcam VideoCaptures are created through a separate | ||
| 381 | - // overload of open that takes an integer, not a string. | ||
| 382 | - // So, does this look like an integer? | ||
| 383 | - bool is_int = false; | ||
| 384 | - int anInt = input.file.name.toInt(&is_int); | ||
| 385 | - if (is_int) | ||
| 386 | - { | ||
| 387 | - bool rc = video.open(anInt); | ||
| 388 | - | ||
| 389 | - if (!rc) | ||
| 390 | - { | ||
| 391 | - qDebug("open failed!"); | ||
| 392 | - } | ||
| 393 | - if (!video.isOpened()) | ||
| 394 | - { | ||
| 395 | - qDebug("Video not open!"); | ||
| 396 | - } | ||
| 397 | - } else { | ||
| 398 | - // Yes, we should specify absolute path: | ||
| 399 | - // http://stackoverflow.com/questions/9396459/loading-a-video-in-opencv-in-python | ||
| 400 | - QString fileName = (Globals->path.isEmpty() ? "" : Globals->path + "/") + input.file.name; | ||
| 401 | - // On windows, this appears to not be thread-safe | ||
| 402 | - QMutexLocker lock(&openLock); | ||
| 403 | - video.open(QFileInfo(fileName).absoluteFilePath().toStdString()); | ||
| 404 | - } | ||
| 405 | - | ||
| 406 | - return video.isOpened(); | ||
| 407 | - } | ||
| 408 | - | ||
| 409 | - bool isOpen() { return video.isOpened(); } | ||
| 410 | - | ||
| 411 | - void close() { | ||
| 412 | - video.release(); | ||
| 413 | - } | ||
| 414 | - | ||
| 415 | -private: | ||
| 416 | - bool getNext(FrameData & output) | ||
| 417 | - { | ||
| 418 | - if (!isOpen()) { | ||
| 419 | - qDebug("video source is not open"); | ||
| 420 | - return false; | ||
| 421 | - } | ||
| 422 | - | ||
| 423 | - output.data.append(Template(basis.file)); | ||
| 424 | - output.data.last().m() = cv::Mat(); | ||
| 425 | - | ||
| 426 | - output.sequenceNumber = next_sequence_number; | ||
| 427 | - next_sequence_number++; | ||
| 428 | - | ||
| 429 | - cv::Mat temp; | ||
| 430 | - bool res = video.read(temp); | ||
| 431 | - | ||
| 432 | - if (!res) { | ||
| 433 | - // The video capture broke, return false. | ||
| 434 | - output.data.last().m() = cv::Mat(); | ||
| 435 | - close(); | ||
| 436 | - return false; | ||
| 437 | - } | ||
| 438 | - | ||
| 439 | - // This clone is critical, if we don't do it then the matrix will | ||
| 440 | - // be an alias of an internal buffer of the video source, leading | ||
| 441 | - // to various problems later. | ||
| 442 | - output.data.last().m() = temp.clone(); | ||
| 443 | - | ||
| 444 | - output.data.last().file.set("FrameNumber", output.sequenceNumber); | ||
| 445 | - return true; | ||
| 446 | - } | ||
| 447 | - | ||
| 448 | - cv::VideoCapture video; | ||
| 449 | - Template basis; | ||
| 450 | -}; | ||
| 451 | - | ||
| 452 | -// Given a template as input, return its matrices one by one on subsequent calls | ||
| 453 | -// to getNext | ||
| 454 | -class TemplateDataSource : public DataSource | ||
| 455 | -{ | ||
| 456 | -public: | ||
| 457 | - TemplateDataSource(int maxFrames) : DataSource(maxFrames) | ||
| 458 | - { | ||
| 459 | - current_matrix_idx = INT_MAX; | ||
| 460 | - data_ok = false; | ||
| 461 | - } | ||
| 462 | - | ||
| 463 | - // To "open" it we just set appropriate indices, we assume that if this | ||
| 464 | - // is an image, it is already loaded into memory. | ||
| 465 | - bool concreteOpen(Template &input) | ||
| 466 | - { | ||
| 467 | - basis = input; | ||
| 468 | - current_matrix_idx = 0; | ||
| 469 | - | ||
| 470 | - data_ok = current_matrix_idx < basis.size(); | ||
| 471 | - qDebug("concrete open res is %d %d %d", data_ok, current_matrix_idx, basis.size()); | ||
| 472 | - return data_ok; | ||
| 473 | - } | ||
| 474 | - | ||
| 475 | - bool isOpen() { | ||
| 476 | - return data_ok; | ||
| 477 | - } | ||
| 478 | - | ||
| 479 | - void close() | ||
| 480 | - { | ||
| 481 | - current_matrix_idx = INT_MAX; | ||
| 482 | - basis.clear(); | ||
| 483 | - } | ||
| 484 | - | ||
| 485 | -private: | ||
| 486 | - bool getNext(FrameData & output) | ||
| 487 | - { | ||
| 488 | - data_ok = current_matrix_idx < basis.size(); | ||
| 489 | - if (!data_ok) | ||
| 490 | - return false; | ||
| 491 | - | ||
| 492 | - | ||
| 493 | - output.data.append(basis[current_matrix_idx]); | ||
| 494 | - output.data.last().file = basis.file; | ||
| 495 | - current_matrix_idx++; | ||
| 496 | - | ||
| 497 | - output.sequenceNumber = next_sequence_number; | ||
| 498 | - next_sequence_number++; | ||
| 499 | - | ||
| 500 | - output.data.last().file.set("FrameNumber", output.sequenceNumber); | ||
| 501 | - return true; | ||
| 502 | - } | ||
| 503 | - | ||
| 504 | - Template basis; | ||
| 505 | - // Index of the next matrix to output from the template | ||
| 506 | - int current_matrix_idx; | ||
| 507 | - | ||
| 508 | - // is current_matrix_idx in bounds? | ||
| 509 | - bool data_ok; | ||
| 510 | -}; | ||
| 511 | - | ||
| 512 | -class SingleDataSource : public DataSource | ||
| 513 | -{ | ||
| 514 | -public: | ||
| 515 | - SingleDataSource(int maxFrames) : DataSource(maxFrames) | ||
| 516 | - { | ||
| 517 | - data_ok = false; | ||
| 518 | - } | ||
| 519 | - | ||
| 520 | - // To "open" it we just set appropriate indices, we assume that if this | ||
| 521 | - // is an image, it is already loaded into memory. | ||
| 522 | - bool concreteOpen(Template &input) | ||
| 523 | - { | ||
| 524 | - basis = input; | ||
| 525 | -// basis.file.name = (Globals->path.isEmpty() ? "" : Globals->path + "/") + basis.file.name; | ||
| 526 | - | ||
| 527 | - data_ok =true; | ||
| 528 | - return data_ok; | ||
| 529 | - } | ||
| 530 | - | ||
| 531 | - bool isOpen() { | ||
| 532 | - return data_ok; | ||
| 533 | - } | ||
| 534 | - | ||
| 535 | - void close() | ||
| 536 | - { | ||
| 537 | - data_ok = false; | ||
| 538 | - basis.clear(); | ||
| 539 | - } | ||
| 540 | - | ||
| 541 | -private: | ||
| 542 | - bool getNext(FrameData & output) | ||
| 543 | - { | ||
| 544 | - if (!data_ok) | ||
| 545 | - return false; | ||
| 546 | - | ||
| 547 | - output.data.append(basis); | ||
| 548 | - data_ok = false; | ||
| 549 | - return true; | ||
| 550 | - } | ||
| 551 | - | ||
| 552 | - Template basis; | ||
| 553 | - | ||
| 554 | - // Have we sent our template yet? | ||
| 555 | - bool data_ok; | ||
| 556 | -}; | ||
| 557 | - | ||
| 558 | - | ||
| 559 | -class ProcessingStage; | ||
| 560 | - | ||
| 561 | -class BasicLoop : public QRunnable, public QFutureInterface<void> | ||
| 562 | -{ | ||
| 563 | -public: | ||
| 564 | - BasicLoop() | ||
| 565 | - { | ||
| 566 | - this->reportStarted(); | ||
| 567 | - } | ||
| 568 | - | ||
| 569 | - void run(); | 610 | + void run(); |
| 570 | 611 | ||
| 571 | QList<ProcessingStage *> * stages; | 612 | QList<ProcessingStage *> * stages; |
| 572 | int start_idx; | 613 | int start_idx; |
| @@ -785,106 +826,129 @@ public: | @@ -785,106 +826,129 @@ public: | ||
| 785 | 826 | ||
| 786 | }; | 827 | }; |
| 787 | 828 | ||
| 788 | -// Appened to the end of a Stream's transform sequence. Collects the output | ||
| 789 | -// from each frame on a single templatelist | ||
| 790 | -class LastStage : public SingleThreadStage | 829 | +// Semi-functional, doesn't do anything productive outside of stream::train |
| 830 | +class CollectSets : public TimeVaryingTransform | ||
| 791 | { | 831 | { |
| 832 | + Q_OBJECT | ||
| 792 | public: | 833 | public: |
| 793 | - LastStage(bool _prev_stage_variance) : SingleThreadStage(_prev_stage_variance) {} | ||
| 794 | - TemplateList getOutput() | 834 | + CollectSets() : TimeVaryingTransform(false, false) {} |
| 835 | + | ||
| 836 | + QList<TemplateList> sets; | ||
| 837 | + | ||
| 838 | + void projectUpdate(const TemplateList &src, TemplateList &dst) | ||
| 795 | { | 839 | { |
| 796 | - return collectedOutput; | 840 | + (void) dst; |
| 841 | + sets.append(src); | ||
| 797 | } | 842 | } |
| 798 | 843 | ||
| 799 | -private: | ||
| 800 | - TemplateList collectedOutput; | 844 | + void train(const TemplateList & data) |
| 845 | + { | ||
| 846 | + (void) data; | ||
| 847 | + } | ||
| 848 | + | ||
| 849 | +}; | ||
| 850 | + | ||
| 851 | +// This stage reads new frames from the data source. | ||
| 852 | +class ReadStage : public SingleThreadStage | ||
| 853 | +{ | ||
| 801 | public: | 854 | public: |
| 855 | + ReadStage(int activeFrames = 100) : SingleThreadStage(true), dataSource(activeFrames){ } | ||
| 856 | + | ||
| 857 | + DataSource dataSource; | ||
| 802 | 858 | ||
| 803 | void reset() | 859 | void reset() |
| 804 | { | 860 | { |
| 805 | - collectedOutput.clear(); | 861 | + dataSource.close(); |
| 806 | SingleThreadStage::reset(); | 862 | SingleThreadStage::reset(); |
| 807 | } | 863 | } |
| 808 | 864 | ||
| 809 | FrameData * run(FrameData * input, bool & should_continue) | 865 | FrameData * run(FrameData * input, bool & should_continue) |
| 810 | { | 866 | { |
| 811 | - if (input == NULL) { | ||
| 812 | - qFatal("NULL input to stage %d", this->stage_id); | ||
| 813 | - } | ||
| 814 | - | ||
| 815 | - if (input->sequenceNumber != next_target) | ||
| 816 | - { | ||
| 817 | - qFatal("out of order frames for stage %d, got %d expected %d", this->stage_id, input->sequenceNumber, this->next_target); | ||
| 818 | - } | ||
| 819 | - next_target = input->sequenceNumber + 1; | ||
| 820 | - | ||
| 821 | - // add the item to our output buffer | ||
| 822 | - collectedOutput.append(input->data); | 867 | + if (input == NULL) |
| 868 | + qFatal("NULL frame in input stage"); | ||
| 823 | 869 | ||
| 824 | - // Can we enter the read stage? | 870 | + // Can we enter the next stage? |
| 825 | should_continue = nextStage->tryAcquireNextStage(input); | 871 | should_continue = nextStage->tryAcquireNextStage(input); |
| 826 | 872 | ||
| 827 | - // Is there anything on our input buffer? If so we should start a thread | ||
| 828 | - // in this stage to process that frame. | 873 | + // Try to get a frame from the datasource, we keep working on |
| 874 | + // the frame we have, but we will queue another job for the next | ||
| 875 | + // frame if a frame is currently available. | ||
| 829 | QWriteLocker lock(&statusLock); | 876 | QWriteLocker lock(&statusLock); |
| 830 | - FrameData * newItem = inputBuffer->tryGetItem(); | ||
| 831 | - if (!newItem) | ||
| 832 | - { | ||
| 833 | - this->currentStatus = STOPPING; | 877 | + bool last_frame = false; |
| 878 | + FrameData * newFrame = dataSource.tryGetFrame(last_frame); | ||
| 879 | + | ||
| 880 | + // Were we able to get a frame? | ||
| 881 | + if (newFrame) startThread(newFrame); | ||
| 882 | + // If not this stage will enter a stopped state. | ||
| 883 | + else { | ||
| 884 | + currentStatus = STOPPING; | ||
| 834 | } | 885 | } |
| 835 | - lock.unlock(); | ||
| 836 | 886 | ||
| 837 | - if (newItem) | ||
| 838 | - startThread(newItem); | 887 | + lock.unlock(); |
| 839 | 888 | ||
| 840 | return input; | 889 | return input; |
| 841 | } | 890 | } |
| 842 | 891 | ||
| 843 | - void status(){ | ||
| 844 | - qDebug("Collection stage %d, status starting? %d, next %d buffer size %d", this->stage_id, this->currentStatus == SingleThreadStage::STARTING, this->next_target, this->inputBuffer->size()); | ||
| 845 | - } | 892 | + // The last stage, trying to access the first stage |
| 893 | + bool tryAcquireNextStage(FrameData *& input) | ||
| 894 | + { | ||
| 895 | + // Return the frame, was it the last one? | ||
| 896 | + bool was_last = dataSource.returnFrame(input); | ||
| 897 | + input = NULL; | ||
| 846 | 898 | ||
| 847 | -}; | 899 | + // OK we won't continue. |
| 900 | + if (was_last) { | ||
| 901 | + return false; | ||
| 902 | + } | ||
| 848 | 903 | ||
| 849 | -// Semi-functional, doesn't do anything productive outside of stream::train | ||
| 850 | -class CollectSets : public TimeVaryingTransform | ||
| 851 | -{ | ||
| 852 | - Q_OBJECT | ||
| 853 | -public: | ||
| 854 | - CollectSets() : TimeVaryingTransform(false, false) {} | 904 | + QReadLocker lock(&statusLock); |
| 905 | + // If the first stage is already active we will just end. | ||
| 906 | + if (currentStatus == STARTING) | ||
| 907 | + { | ||
| 908 | + return false; | ||
| 909 | + } | ||
| 855 | 910 | ||
| 856 | - QList<TemplateList> sets; | 911 | + // Otherwise we will try to continue, but to do so we have to |
| 912 | + // escalate the lock, and sadly there is no way to do so without | ||
| 913 | + // releasing the read-mode lock, and getting a new write-mode lock. | ||
| 914 | + lock.unlock(); | ||
| 857 | 915 | ||
| 858 | - void projectUpdate(const TemplateList &src, TemplateList &dst) | ||
| 859 | - { | ||
| 860 | - (void) dst; | ||
| 861 | - sets.append(src); | ||
| 862 | - } | 916 | + QWriteLocker writeLock(&statusLock); |
| 917 | + // currentStatus might have changed in the gap between releasing the read | ||
| 918 | + // lock and getting the write lock. | ||
| 919 | + if (currentStatus == STARTING) | ||
| 920 | + { | ||
| 921 | + return false; | ||
| 922 | + } | ||
| 863 | 923 | ||
| 864 | - void train(const TemplateList & data) | ||
| 865 | - { | ||
| 866 | - (void) data; | 924 | + bool last_frame = false; |
| 925 | + // Try to get a frame from the data source, if we get one we will | ||
| 926 | + // continue to the first stage. | ||
| 927 | + input = dataSource.tryGetFrame(last_frame); | ||
| 928 | + | ||
| 929 | + if (!input) { | ||
| 930 | + return false; | ||
| 931 | + } | ||
| 932 | + | ||
| 933 | + currentStatus = STARTING; | ||
| 934 | + | ||
| 935 | + return true; | ||
| 867 | } | 936 | } |
| 868 | 937 | ||
| 938 | + void status(){ | ||
| 939 | + qDebug("Read stage %d, status starting? %d, next frame %d buffer size %d", this->stage_id, this->currentStatus == SingleThreadStage::STARTING, this->next_target, this->dataSource.size()); | ||
| 940 | + } | ||
| 869 | }; | 941 | }; |
| 870 | 942 | ||
| 871 | -class FirstStage; | ||
| 872 | - | ||
| 873 | class DirectStreamTransform : public CompositeTransform | 943 | class DirectStreamTransform : public CompositeTransform |
| 874 | { | 944 | { |
| 875 | Q_OBJECT | 945 | Q_OBJECT |
| 876 | public: | 946 | public: |
| 877 | 947 | ||
| 878 | - enum StreamModes { StreamVideo, | ||
| 879 | - DistributeFrames, | ||
| 880 | - Auto}; | ||
| 881 | - | ||
| 882 | - Q_ENUMS(StreamModes) | ||
| 883 | - | ||
| 884 | Q_PROPERTY(int activeFrames READ get_activeFrames WRITE set_activeFrames RESET reset_activeFrames) | 948 | Q_PROPERTY(int activeFrames READ get_activeFrames WRITE set_activeFrames RESET reset_activeFrames) |
| 885 | - Q_PROPERTY(StreamModes readMode READ get_readMode WRITE set_readMode RESET reset_readMode) | 949 | + Q_PROPERTY(br::Idiocy::StreamModes readMode READ get_readMode WRITE set_readMode RESET reset_readMode) |
| 886 | BR_PROPERTY(int, activeFrames, 100) | 950 | BR_PROPERTY(int, activeFrames, 100) |
| 887 | - BR_PROPERTY(StreamModes, readMode, Auto) | 951 | + BR_PROPERTY(br::Idiocy::StreamModes, readMode, br::Idiocy::Auto) |
| 888 | 952 | ||
| 889 | friend class StreamTransfrom; | 953 | friend class StreamTransfrom; |
| 890 | 954 | ||
| @@ -958,8 +1022,163 @@ public: | @@ -958,8 +1022,163 @@ public: | ||
| 958 | // on all child transforms as part of projectUpdate | 1022 | // on all child transforms as part of projectUpdate |
| 959 | } | 1023 | } |
| 960 | 1024 | ||
| 961 | - void projectUpdate(const TemplateList & src, TemplateList & dst); | ||
| 962 | - void init(); | 1025 | + // start processing, consider all templates in src a continuous |
| 1026 | + // 'video' | ||
| 1027 | + void projectUpdate(const TemplateList & src, TemplateList & dst) | ||
| 1028 | + { | ||
| 1029 | + dst = src; | ||
| 1030 | + | ||
| 1031 | + bool res = readStage->dataSource.open(dst,readMode); | ||
| 1032 | + if (!res) { | ||
| 1033 | + qDebug("stream failed to open %s", qPrintable(dst[0].file.name)); | ||
| 1034 | + return; | ||
| 1035 | + } | ||
| 1036 | + | ||
| 1037 | + // Start the first thread in the stream. | ||
| 1038 | + QWriteLocker lock(&readStage->statusLock); | ||
| 1039 | + readStage->currentStatus = SingleThreadStage::STARTING; | ||
| 1040 | + | ||
| 1041 | + // We have to get a frame before starting the thread | ||
| 1042 | + bool last_frame = false; | ||
| 1043 | + FrameData * firstFrame = readStage->dataSource.tryGetFrame(last_frame); | ||
| 1044 | + if (firstFrame == NULL) | ||
| 1045 | + qFatal("Failed to read first frame of video"); | ||
| 1046 | + | ||
| 1047 | + readStage->startThread(firstFrame); | ||
| 1048 | + lock.unlock(); | ||
| 1049 | + | ||
| 1050 | + // Wait for the stream to process the last frame available from | ||
| 1051 | + // the data source. | ||
| 1052 | + bool wait_res = false; | ||
| 1053 | + wait_res = readStage->dataSource.waitLast(); | ||
| 1054 | + | ||
| 1055 | + // Now that there are no more incoming frames, call finalize | ||
| 1056 | + // on each transform in turn to collect any last templates | ||
| 1057 | + // they wish to issue. | ||
| 1058 | + TemplateList final_output; | ||
| 1059 | + | ||
| 1060 | + // Push finalize through the stages | ||
| 1061 | + for (int i=0; i < this->transforms.size(); i++) | ||
| 1062 | + { | ||
| 1063 | + TemplateList output_set; | ||
| 1064 | + transforms[i]->finalize(output_set); | ||
| 1065 | + | ||
| 1066 | + for (int j=i+1; j < transforms.size();j++) | ||
| 1067 | + { | ||
| 1068 | + transforms[j]->projectUpdate(output_set); | ||
| 1069 | + } | ||
| 1070 | + final_output.append(output_set); | ||
| 1071 | + } | ||
| 1072 | + | ||
| 1073 | + // dst is set to all output received by the final stage, along | ||
| 1074 | + // with anything output via the calls to finalize. | ||
| 1075 | + //dst = collectionStage->getOutput(); | ||
| 1076 | + foreach(const TemplateList & list, collector->sets) { | ||
| 1077 | + dst.append(list); | ||
| 1078 | + } | ||
| 1079 | + collector->sets.clear(); | ||
| 1080 | + | ||
| 1081 | + dst.append(final_output); | ||
| 1082 | + | ||
| 1083 | + foreach(ProcessingStage * stage, processingStages) { | ||
| 1084 | + stage->reset(); | ||
| 1085 | + } | ||
| 1086 | + } | ||
| 1087 | + | ||
| 1088 | + | ||
| 1089 | + // Create and link stages | ||
| 1090 | + void init() | ||
| 1091 | + { | ||
| 1092 | + if (transforms.isEmpty()) return; | ||
| 1093 | + | ||
| 1094 | + for (int i=0; i < processingStages.size();i++) | ||
| 1095 | + delete processingStages[i]; | ||
| 1096 | + processingStages.clear(); | ||
| 1097 | + | ||
| 1098 | + // call CompositeTransform::init so that trainable is set | ||
| 1099 | + // correctly. | ||
| 1100 | + CompositeTransform::init(); | ||
| 1101 | + | ||
| 1102 | + // We share a thread pool across streams attached to the same | ||
| 1103 | + // parent tranform, retrieve or create a thread pool based | ||
| 1104 | + // on our parent transform. | ||
| 1105 | + QMutexLocker poolLock(&poolsAccess); | ||
| 1106 | + QHash<QObject *, QThreadPool *>::Iterator it; | ||
| 1107 | + if (!pools.contains(this->parent())) { | ||
| 1108 | + it = pools.insert(this->parent(), new QThreadPool(this->parent())); | ||
| 1109 | + it.value()->setMaxThreadCount(Globals->parallelism); | ||
| 1110 | + } | ||
| 1111 | + else it = pools.find(this->parent()); | ||
| 1112 | + threads = it.value(); | ||
| 1113 | + poolLock.unlock(); | ||
| 1114 | + | ||
| 1115 | + // Are our children time varying or not? This decides whether | ||
| 1116 | + // we run them in single threaded or multi threaded stages | ||
| 1117 | + stage_variance.clear(); | ||
| 1118 | + stage_variance.reserve(transforms.size()); | ||
| 1119 | + foreach (const br::Transform *transform, transforms) { | ||
| 1120 | + stage_variance.append(transform->timeVarying()); | ||
| 1121 | + } | ||
| 1122 | + | ||
| 1123 | + // Additionally, we have a separate stage responsible for reading | ||
| 1124 | + // frames from the data source | ||
| 1125 | + readStage = new ReadStage(activeFrames); | ||
| 1126 | + | ||
| 1127 | + processingStages.push_back(readStage); | ||
| 1128 | + readStage->stage_id = 0; | ||
| 1129 | + readStage->stages = &this->processingStages; | ||
| 1130 | + readStage->threads = this->threads; | ||
| 1131 | + | ||
| 1132 | + // Initialize and link a processing stage for each of our child | ||
| 1133 | + // transforms. | ||
| 1134 | + int next_stage_id = 1; | ||
| 1135 | + bool prev_stage_variance = true; | ||
| 1136 | + for (int i =0; i < transforms.size(); i++) | ||
| 1137 | + { | ||
| 1138 | + if (stage_variance[i]) | ||
| 1139 | + // Whether or not the previous stage is multi-threaded controls | ||
| 1140 | + // the type of input buffer we need in a single threaded stage. | ||
| 1141 | + processingStages.append(new SingleThreadStage(prev_stage_variance)); | ||
| 1142 | + else | ||
| 1143 | + processingStages.append(new MultiThreadStage(Globals->parallelism)); | ||
| 1144 | + | ||
| 1145 | + processingStages.last()->stage_id = next_stage_id++; | ||
| 1146 | + | ||
| 1147 | + // link nextStage pointers, the stage we just appeneded is i+1 since | ||
| 1148 | + // the read stage was added before this loop | ||
| 1149 | + processingStages[i]->nextStage = processingStages[i+1]; | ||
| 1150 | + | ||
| 1151 | + processingStages.last()->stages = &this->processingStages; | ||
| 1152 | + processingStages.last()->threads = this->threads; | ||
| 1153 | + | ||
| 1154 | + processingStages.last()->transform = transforms[i]; | ||
| 1155 | + prev_stage_variance = stage_variance[i]; | ||
| 1156 | + } | ||
| 1157 | + | ||
| 1158 | + // We also have the last stage, which just puts the output of the | ||
| 1159 | + // previous stages on a template list. | ||
| 1160 | + collectionStage = new SingleThreadStage(prev_stage_variance); | ||
| 1161 | + collectionStage->transform = this->collector.data(); | ||
| 1162 | + | ||
| 1163 | + | ||
| 1164 | + processingStages.append(collectionStage); | ||
| 1165 | + collectionStage->stage_id = next_stage_id; | ||
| 1166 | + collectionStage->stages = &this->processingStages; | ||
| 1167 | + collectionStage->threads = this->threads; | ||
| 1168 | + | ||
| 1169 | + // the last transform stage points to collection stage | ||
| 1170 | + processingStages[processingStages.size() - 2]->nextStage = collectionStage; | ||
| 1171 | + | ||
| 1172 | + // And the collection stage points to the read stage, because this is | ||
| 1173 | + // a ring buffer. | ||
| 1174 | + collectionStage->nextStage = readStage; | ||
| 1175 | + } | ||
| 1176 | + | ||
| 1177 | + DirectStreamTransform() | ||
| 1178 | + { | ||
| 1179 | + this->collector = QSharedPointer<CollectSets>(new CollectSets()); | ||
| 1180 | + } | ||
| 1181 | + | ||
| 963 | ~DirectStreamTransform() | 1182 | ~DirectStreamTransform() |
| 964 | { | 1183 | { |
| 965 | // Delete all the stages | 1184 | // Delete all the stages |
| @@ -972,8 +1191,9 @@ public: | @@ -972,8 +1191,9 @@ public: | ||
| 972 | protected: | 1191 | protected: |
| 973 | QList<bool> stage_variance; | 1192 | QList<bool> stage_variance; |
| 974 | 1193 | ||
| 975 | - FirstStage * readStage; | ||
| 976 | - LastStage * collectionStage; | 1194 | + ReadStage * readStage; |
| 1195 | + SingleThreadStage * collectionStage; | ||
| 1196 | + QSharedPointer<CollectSets> collector; | ||
| 977 | 1197 | ||
| 978 | QList<ProcessingStage *> processingStages; | 1198 | QList<ProcessingStage *> processingStages; |
| 979 | 1199 | ||
| @@ -1023,10 +1243,10 @@ public: | @@ -1023,10 +1243,10 @@ public: | ||
| 1023 | } | 1243 | } |
| 1024 | 1244 | ||
| 1025 | Q_PROPERTY(int activeFrames READ get_activeFrames WRITE set_activeFrames RESET reset_activeFrames) | 1245 | Q_PROPERTY(int activeFrames READ get_activeFrames WRITE set_activeFrames RESET reset_activeFrames) |
| 1026 | - Q_PROPERTY(br::DirectStreamTransform::StreamModes readMode READ get_readMode WRITE set_readMode RESET reset_readMode) | 1246 | + Q_PROPERTY(br::Idiocy::StreamModes readMode READ get_readMode WRITE set_readMode RESET reset_readMode) |
| 1027 | 1247 | ||
| 1028 | BR_PROPERTY(int, activeFrames, 100) | 1248 | BR_PROPERTY(int, activeFrames, 100) |
| 1029 | - BR_PROPERTY(br::DirectStreamTransform::StreamModes, readMode, br::DirectStreamTransform::Auto) | 1249 | + BR_PROPERTY(br::Idiocy::StreamModes, readMode, br::Idiocy::Auto) |
| 1030 | 1250 | ||
| 1031 | bool timeVarying() const { return true; } | 1251 | bool timeVarying() const { return true; } |
| 1032 | 1252 | ||
| @@ -1150,382 +1370,6 @@ private: | @@ -1150,382 +1370,6 @@ private: | ||
| 1150 | 1370 | ||
| 1151 | BR_REGISTER(Transform, StreamTransform) | 1371 | BR_REGISTER(Transform, StreamTransform) |
| 1152 | 1372 | ||
| 1153 | -// Given a templatelist as input, create appropriate data source for each | ||
| 1154 | -// individual template | ||
| 1155 | -class DataSourceManager : public DataSource | ||
| 1156 | -{ | ||
| 1157 | -public: | ||
| 1158 | - DataSourceManager(int activeFrames=100) : DataSource(activeFrames) | ||
| 1159 | - { | ||
| 1160 | - actualSource = NULL; | ||
| 1161 | - } | ||
| 1162 | - | ||
| 1163 | - ~DataSourceManager() | ||
| 1164 | - { | ||
| 1165 | - close(); | ||
| 1166 | - } | ||
| 1167 | - | ||
| 1168 | - int size() | ||
| 1169 | - { | ||
| 1170 | - return this->allFrames.size(); | ||
| 1171 | - } | ||
| 1172 | - | ||
| 1173 | - void close() | ||
| 1174 | - { | ||
| 1175 | - if (actualSource) { | ||
| 1176 | - actualSource->close(); | ||
| 1177 | - delete actualSource; | ||
| 1178 | - actualSource = NULL; | ||
| 1179 | - } | ||
| 1180 | - } | ||
| 1181 | - | ||
| 1182 | - // We are used through a call to open(TemplateList) | ||
| 1183 | - bool open(TemplateList & input, DirectStreamTransform::StreamModes _mode) | ||
| 1184 | - { | ||
| 1185 | - // Set up variables specific to us | ||
| 1186 | - current_template_idx = 0; | ||
| 1187 | - templates = input; | ||
| 1188 | - mode = _mode; | ||
| 1189 | - | ||
| 1190 | - // Call datasourece::open on the first template to set up | ||
| 1191 | - // state variables | ||
| 1192 | - return DataSource::open(templates[current_template_idx]); | ||
| 1193 | - } | ||
| 1194 | - void projectUpdate(const TemplateList & src, TemplateList & dst); | ||
| 1195 | - | ||
| 1196 | - // Create an actual data source of appropriate type for this template | ||
| 1197 | - // (initially called via the call to DataSource::open, called later | ||
| 1198 | - // as we run out of frames on our templates). | ||
| 1199 | - bool concreteOpen(Template & input) | ||
| 1200 | - { | ||
| 1201 | - close(); | ||
| 1202 | - | ||
| 1203 | - bool open_res = false; | ||
| 1204 | - | ||
| 1205 | - if (mode == DirectStreamTransform::DistributeFrames) | ||
| 1206 | - { | ||
| 1207 | - actualSource = new SingleDataSource(0); | ||
| 1208 | - open_res = actualSource->concreteOpen(input); | ||
| 1209 | - } | ||
| 1210 | - // Input has no matrices? Its probably a video that hasn't been loaded yet | ||
| 1211 | - else if (mode == DirectStreamTransform::StreamVideo || (mode == DirectStreamTransform::Auto && input.empty()) ) { | ||
| 1212 | - actualSource = new VideoDataSource(0); | ||
| 1213 | - open_res = actualSource->concreteOpen(input); | ||
| 1214 | - } | ||
| 1215 | - // If the input is not empty, we assume it is a set of frames already | ||
| 1216 | - // in memory. | ||
| 1217 | - else { | ||
| 1218 | - qDebug("in template open"); | ||
| 1219 | - actualSource = new TemplateDataSource(0); | ||
| 1220 | - open_res = actualSource->concreteOpen(input); | ||
| 1221 | - } | ||
| 1222 | - | ||
| 1223 | - // The data source failed to open | ||
| 1224 | - if (!open_res) { | ||
| 1225 | - delete actualSource; | ||
| 1226 | - actualSource = NULL; | ||
| 1227 | - return false; | ||
| 1228 | - } | ||
| 1229 | - return true; | ||
| 1230 | - } | ||
| 1231 | - | ||
| 1232 | - bool isOpen() { return !actualSource ? false : actualSource->isOpen(); } | ||
| 1233 | - | ||
| 1234 | -protected: | ||
| 1235 | - // Index of the template in the templatelist we are currently reading from | ||
| 1236 | - int current_template_idx; | ||
| 1237 | - DirectStreamTransform::StreamModes mode; | ||
| 1238 | - TemplateList templates; | ||
| 1239 | - DataSource * actualSource; | ||
| 1240 | - // Get the next frame, if we run out of frames on the current template | ||
| 1241 | - // move on to the next one. | ||
| 1242 | - bool getNext(FrameData & output) | ||
| 1243 | - { | ||
| 1244 | - bool res = actualSource->getNext(output); | ||
| 1245 | - output.sequenceNumber = next_sequence_number; | ||
| 1246 | - | ||
| 1247 | - // OK we got a frame | ||
| 1248 | - if (res) { | ||
| 1249 | - // Override the sequence number set by actualSource | ||
| 1250 | - output.data.last().file.set("FrameNumber", output.sequenceNumber); | ||
| 1251 | - next_sequence_number++; | ||
| 1252 | -// if (output.data.last().last().empty()) | ||
| 1253 | -// qDebug("broken matrix"); | ||
| 1254 | - return true; | ||
| 1255 | - } | ||
| 1256 | - | ||
| 1257 | - // We didn't get a frame, try to move on to the next template. | ||
| 1258 | - while(!res) { | ||
| 1259 | - output.data.clear(); | ||
| 1260 | - current_template_idx++; | ||
| 1261 | - | ||
| 1262 | - // No more templates? We're done | ||
| 1263 | - if (current_template_idx >= templates.size()) | ||
| 1264 | - return false; | ||
| 1265 | - | ||
| 1266 | - // open the next data source | ||
| 1267 | - bool open_res = concreteOpen(templates[current_template_idx]); | ||
| 1268 | - // We couldn't open it, give up? We could maybe continue here | ||
| 1269 | - // but don't currently. | ||
| 1270 | - if (!open_res) | ||
| 1271 | - return false; | ||
| 1272 | - | ||
| 1273 | - // get a frame from the newly opened data source, if that fails | ||
| 1274 | - // we continue to open the next one. | ||
| 1275 | - res = actualSource->getNext(output); | ||
| 1276 | - } | ||
| 1277 | - // Finally, set the sequence number for the frame we actually return. | ||
| 1278 | - output.sequenceNumber = next_sequence_number++; | ||
| 1279 | - output.data.last().file.set("FrameNumber", output.sequenceNumber); | ||
| 1280 | - | ||
| 1281 | -// if (output.data.last().last().empty()) | ||
| 1282 | -// qDebug("broken matrix"); | ||
| 1283 | - | ||
| 1284 | - return res; | ||
| 1285 | - } | ||
| 1286 | - | ||
| 1287 | -}; | ||
| 1288 | - | ||
| 1289 | -// This stage reads new frames from the data source. | ||
| 1290 | -class FirstStage : public SingleThreadStage | ||
| 1291 | -{ | ||
| 1292 | -public: | ||
| 1293 | - FirstStage(int activeFrames = 100) : SingleThreadStage(true), dataSource(activeFrames){ } | ||
| 1294 | - | ||
| 1295 | - DataSourceManager dataSource; | ||
| 1296 | - | ||
| 1297 | - void reset() | ||
| 1298 | - { | ||
| 1299 | - dataSource.close(); | ||
| 1300 | - SingleThreadStage::reset(); | ||
| 1301 | - } | ||
| 1302 | - | ||
| 1303 | - FrameData * run(FrameData * input, bool & should_continue) | ||
| 1304 | - { | ||
| 1305 | - if (input == NULL) | ||
| 1306 | - qFatal("NULL frame in input stage"); | ||
| 1307 | - | ||
| 1308 | - // Can we enter the next stage? | ||
| 1309 | - should_continue = nextStage->tryAcquireNextStage(input); | ||
| 1310 | - | ||
| 1311 | - // Try to get a frame from the datasource, we keep working on | ||
| 1312 | - // the frame we have, but we will queue another job for the next | ||
| 1313 | - // frame if a frame is currently available. | ||
| 1314 | - QWriteLocker lock(&statusLock); | ||
| 1315 | - bool last_frame = false; | ||
| 1316 | - FrameData * newFrame = dataSource.tryGetFrame(last_frame); | ||
| 1317 | - | ||
| 1318 | - // Were we able to get a frame? | ||
| 1319 | - if (newFrame) startThread(newFrame); | ||
| 1320 | - // If not this stage will enter a stopped state. | ||
| 1321 | - else { | ||
| 1322 | - currentStatus = STOPPING; | ||
| 1323 | - } | ||
| 1324 | - | ||
| 1325 | - lock.unlock(); | ||
| 1326 | - | ||
| 1327 | - return input; | ||
| 1328 | - } | ||
| 1329 | - | ||
| 1330 | - // The last stage, trying to access the first stage | ||
| 1331 | - bool tryAcquireNextStage(FrameData *& input) | ||
| 1332 | - { | ||
| 1333 | - // Return the frame, was it the last one? | ||
| 1334 | - bool was_last = dataSource.returnFrame(input); | ||
| 1335 | - input = NULL; | ||
| 1336 | - | ||
| 1337 | - // OK we won't continue. | ||
| 1338 | - if (was_last) { | ||
| 1339 | - return false; | ||
| 1340 | - } | ||
| 1341 | - | ||
| 1342 | - QReadLocker lock(&statusLock); | ||
| 1343 | - // If the first stage is already active we will just end. | ||
| 1344 | - if (currentStatus == STARTING) | ||
| 1345 | - { | ||
| 1346 | - return false; | ||
| 1347 | - } | ||
| 1348 | - | ||
| 1349 | - // Otherwise we will try to continue, but to do so we have to | ||
| 1350 | - // escalate the lock, and sadly there is no way to do so without | ||
| 1351 | - // releasing the read-mode lock, and getting a new write-mode lock. | ||
| 1352 | - lock.unlock(); | ||
| 1353 | - | ||
| 1354 | - QWriteLocker writeLock(&statusLock); | ||
| 1355 | - // currentStatus might have changed in the gap between releasing the read | ||
| 1356 | - // lock and getting the write lock. | ||
| 1357 | - if (currentStatus == STARTING) | ||
| 1358 | - { | ||
| 1359 | - return false; | ||
| 1360 | - } | ||
| 1361 | - | ||
| 1362 | - bool last_frame = false; | ||
| 1363 | - // Try to get a frame from the data source, if we get one we will | ||
| 1364 | - // continue to the first stage. | ||
| 1365 | - input = dataSource.tryGetFrame(last_frame); | ||
| 1366 | - | ||
| 1367 | - if (!input) { | ||
| 1368 | - return false; | ||
| 1369 | - } | ||
| 1370 | - | ||
| 1371 | - currentStatus = STARTING; | ||
| 1372 | - | ||
| 1373 | - return true; | ||
| 1374 | - } | ||
| 1375 | - | ||
| 1376 | - void status(){ | ||
| 1377 | - qDebug("Read stage %d, status starting? %d, next frame %d buffer size %d", this->stage_id, this->currentStatus == SingleThreadStage::STARTING, this->next_target, this->dataSource.size()); | ||
| 1378 | - } | ||
| 1379 | - | ||
| 1380 | - | ||
| 1381 | -}; | ||
| 1382 | - | ||
| 1383 | - | ||
| 1384 | -// start processing, consider all templates in src a continuous | ||
| 1385 | -// 'video' | ||
| 1386 | -void DirectStreamTransform::projectUpdate(const TemplateList & src, TemplateList & dst) | ||
| 1387 | -{ | ||
| 1388 | - dst = src; | ||
| 1389 | - | ||
| 1390 | - bool res = readStage->dataSource.open(dst,readMode); | ||
| 1391 | - if (!res) { | ||
| 1392 | - qDebug("stream failed to open %s", qPrintable(dst[0].file.name)); | ||
| 1393 | - return; | ||
| 1394 | - } | ||
| 1395 | - | ||
| 1396 | - // Start the first thread in the stream. | ||
| 1397 | - QWriteLocker lock(&readStage->statusLock); | ||
| 1398 | - readStage->currentStatus = SingleThreadStage::STARTING; | ||
| 1399 | - | ||
| 1400 | - // We have to get a frame before starting the thread | ||
| 1401 | - bool last_frame = false; | ||
| 1402 | - FrameData * firstFrame = readStage->dataSource.tryGetFrame(last_frame); | ||
| 1403 | - if (firstFrame == NULL) | ||
| 1404 | - qFatal("Failed to read first frame of video"); | ||
| 1405 | - | ||
| 1406 | - readStage->startThread(firstFrame); | ||
| 1407 | - lock.unlock(); | ||
| 1408 | - | ||
| 1409 | - // Wait for the stream to process the last frame available from | ||
| 1410 | - // the data source. | ||
| 1411 | - bool wait_res = false; | ||
| 1412 | - wait_res = readStage->dataSource.waitLast(); | ||
| 1413 | - | ||
| 1414 | - // Now that there are no more incoming frames, call finalize | ||
| 1415 | - // on each transform in turn to collect any last templates | ||
| 1416 | - // they wish to issue. | ||
| 1417 | - TemplateList final_output; | ||
| 1418 | - | ||
| 1419 | - // Push finalize through the stages | ||
| 1420 | - for (int i=0; i < this->transforms.size(); i++) | ||
| 1421 | - { | ||
| 1422 | - TemplateList output_set; | ||
| 1423 | - transforms[i]->finalize(output_set); | ||
| 1424 | - | ||
| 1425 | - for (int j=i+1; j < transforms.size();j++) | ||
| 1426 | - { | ||
| 1427 | - transforms[j]->projectUpdate(output_set); | ||
| 1428 | - } | ||
| 1429 | - final_output.append(output_set); | ||
| 1430 | - } | ||
| 1431 | - | ||
| 1432 | - // dst is set to all output received by the final stage, along | ||
| 1433 | - // with anything output via the calls to finalize. | ||
| 1434 | - dst = collectionStage->getOutput(); | ||
| 1435 | - dst.append(final_output); | ||
| 1436 | - | ||
| 1437 | - foreach(ProcessingStage * stage, processingStages) { | ||
| 1438 | - stage->reset(); | ||
| 1439 | - } | ||
| 1440 | -} | ||
| 1441 | - | ||
| 1442 | - | ||
| 1443 | -// Create and link stages | ||
| 1444 | -void DirectStreamTransform::init() | ||
| 1445 | -{ | ||
| 1446 | - if (transforms.isEmpty()) return; | ||
| 1447 | - | ||
| 1448 | - for (int i=0; i < processingStages.size();i++) | ||
| 1449 | - delete processingStages[i]; | ||
| 1450 | - processingStages.clear(); | ||
| 1451 | - | ||
| 1452 | - // call CompositeTransform::init so that trainable is set | ||
| 1453 | - // correctly. | ||
| 1454 | - CompositeTransform::init(); | ||
| 1455 | - | ||
| 1456 | - // We share a thread pool across streams attached to the same | ||
| 1457 | - // parent tranform, retrieve or create a thread pool based | ||
| 1458 | - // on our parent transform. | ||
| 1459 | - QMutexLocker poolLock(&poolsAccess); | ||
| 1460 | - QHash<QObject *, QThreadPool *>::Iterator it; | ||
| 1461 | - if (!pools.contains(this->parent())) { | ||
| 1462 | - it = pools.insert(this->parent(), new QThreadPool(this->parent())); | ||
| 1463 | - it.value()->setMaxThreadCount(Globals->parallelism); | ||
| 1464 | - } | ||
| 1465 | - else it = pools.find(this->parent()); | ||
| 1466 | - threads = it.value(); | ||
| 1467 | - poolLock.unlock(); | ||
| 1468 | - | ||
| 1469 | - // Are our children time varying or not? This decides whether | ||
| 1470 | - // we run them in single threaded or multi threaded stages | ||
| 1471 | - stage_variance.clear(); | ||
| 1472 | - stage_variance.reserve(transforms.size()); | ||
| 1473 | - foreach (const br::Transform *transform, transforms) { | ||
| 1474 | - stage_variance.append(transform->timeVarying()); | ||
| 1475 | - } | ||
| 1476 | - | ||
| 1477 | - // Additionally, we have a separate stage responsible for reading | ||
| 1478 | - // frames from the data source | ||
| 1479 | - readStage = new FirstStage(activeFrames); | ||
| 1480 | - | ||
| 1481 | - processingStages.push_back(readStage); | ||
| 1482 | - readStage->stage_id = 0; | ||
| 1483 | - readStage->stages = &this->processingStages; | ||
| 1484 | - readStage->threads = this->threads; | ||
| 1485 | - | ||
| 1486 | - // Initialize and link a processing stage for each of our child | ||
| 1487 | - // transforms. | ||
| 1488 | - int next_stage_id = 1; | ||
| 1489 | - bool prev_stage_variance = true; | ||
| 1490 | - for (int i =0; i < transforms.size(); i++) | ||
| 1491 | - { | ||
| 1492 | - if (stage_variance[i]) | ||
| 1493 | - // Whether or not the previous stage is multi-threaded controls | ||
| 1494 | - // the type of input buffer we need in a single threaded stage. | ||
| 1495 | - processingStages.append(new SingleThreadStage(prev_stage_variance)); | ||
| 1496 | - else | ||
| 1497 | - processingStages.append(new MultiThreadStage(Globals->parallelism)); | ||
| 1498 | - | ||
| 1499 | - processingStages.last()->stage_id = next_stage_id++; | ||
| 1500 | - | ||
| 1501 | - // link nextStage pointers, the stage we just appeneded is i+1 since | ||
| 1502 | - // the read stage was added before this loop | ||
| 1503 | - processingStages[i]->nextStage = processingStages[i+1]; | ||
| 1504 | - | ||
| 1505 | - processingStages.last()->stages = &this->processingStages; | ||
| 1506 | - processingStages.last()->threads = this->threads; | ||
| 1507 | - | ||
| 1508 | - processingStages.last()->transform = transforms[i]; | ||
| 1509 | - prev_stage_variance = stage_variance[i]; | ||
| 1510 | - } | ||
| 1511 | - | ||
| 1512 | - // We also have the last stage, which just puts the output of the | ||
| 1513 | - // previous stages on a template list. | ||
| 1514 | - collectionStage = new LastStage(prev_stage_variance); | ||
| 1515 | - processingStages.append(collectionStage); | ||
| 1516 | - collectionStage->stage_id = next_stage_id; | ||
| 1517 | - collectionStage->stages = &this->processingStages; | ||
| 1518 | - collectionStage->threads = this->threads; | ||
| 1519 | - | ||
| 1520 | - // the last transform stage points to collection stage | ||
| 1521 | - processingStages[processingStages.size() - 2]->nextStage = collectionStage; | ||
| 1522 | - | ||
| 1523 | - // And the collection stage points to the read stage, because this is | ||
| 1524 | - // a ring buffer. | ||
| 1525 | - collectionStage->nextStage = readStage; | ||
| 1526 | -} | ||
| 1527 | - | ||
| 1528 | - | ||
| 1529 | } // namespace br | 1373 | } // namespace br |
| 1530 | 1374 | ||
| 1531 | #include "stream.moc" | 1375 | #include "stream.moc" |