Commit 1aceddc3cc411d3d567968a0cb1834c14e7f46c0
1 parent
8dea5a3e
Add an explicit open mode parameter to stream
Allows users to directly specify how stream handles input data (i.e. reads from a video source, or just distributes the templates)
Showing
1 changed file
with
381 additions
and
361 deletions
openbr/plugins/stream.cpp
| ... | ... | @@ -16,6 +16,7 @@ using namespace cv; |
| 16 | 16 | namespace br |
| 17 | 17 | { |
| 18 | 18 | |
| 19 | + | |
| 19 | 20 | class FrameData |
| 20 | 21 | { |
| 21 | 22 | public: |
| ... | ... | @@ -505,132 +506,6 @@ private: |
| 505 | 506 | bool data_ok; |
| 506 | 507 | }; |
| 507 | 508 | |
| 508 | -// Given a templatelist as input, create appropriate data source for each | |
| 509 | -// individual template | |
| 510 | -class DataSourceManager : public DataSource | |
| 511 | -{ | |
| 512 | -public: | |
| 513 | - DataSourceManager(int activeFrames=100) : DataSource(activeFrames) | |
| 514 | - { | |
| 515 | - actualSource = NULL; | |
| 516 | - } | |
| 517 | - | |
| 518 | - ~DataSourceManager() | |
| 519 | - { | |
| 520 | - close(); | |
| 521 | - } | |
| 522 | - | |
| 523 | - int size() | |
| 524 | - { | |
| 525 | - return this->allFrames.size(); | |
| 526 | - } | |
| 527 | - | |
| 528 | - void close() | |
| 529 | - { | |
| 530 | - if (actualSource) { | |
| 531 | - actualSource->close(); | |
| 532 | - delete actualSource; | |
| 533 | - actualSource = NULL; | |
| 534 | - } | |
| 535 | - } | |
| 536 | - | |
| 537 | - // We are used through a call to open(TemplateList) | |
| 538 | - bool open(TemplateList & input) | |
| 539 | - { | |
| 540 | - // Set up variables specific to us | |
| 541 | - current_template_idx = 0; | |
| 542 | - templates = input; | |
| 543 | - | |
| 544 | - // Call datasourece::open on the first template to set up | |
| 545 | - // state variables | |
| 546 | - return DataSource::open(templates[current_template_idx]); | |
| 547 | - } | |
| 548 | - | |
| 549 | - // Create an actual data source of appropriate type for this template | |
| 550 | - // (initially called via the call to DataSource::open, called later | |
| 551 | - // as we run out of frames on our templates). | |
| 552 | - bool concreteOpen(Template & input) | |
| 553 | - { | |
| 554 | - close(); | |
| 555 | - | |
| 556 | - bool open_res = false; | |
| 557 | - // Input has no matrices? Its probably a video that hasn't been loaded yet | |
| 558 | - if (input.empty()) { | |
| 559 | - actualSource = new VideoDataSource(0); | |
| 560 | - open_res = actualSource->concreteOpen(input); | |
| 561 | - } | |
| 562 | - // If the input is not empty, we assume it is a set of frames already | |
| 563 | - // in memory. | |
| 564 | - else { | |
| 565 | - actualSource = new TemplateDataSource(0); | |
| 566 | - open_res = actualSource->concreteOpen(input); | |
| 567 | - } | |
| 568 | - | |
| 569 | - // The data source failed to open | |
| 570 | - if (!open_res) { | |
| 571 | - delete actualSource; | |
| 572 | - actualSource = NULL; | |
| 573 | - return false; | |
| 574 | - } | |
| 575 | - return true; | |
| 576 | - } | |
| 577 | - | |
| 578 | - bool isOpen() { return !actualSource ? false : actualSource->isOpen(); } | |
| 579 | - | |
| 580 | -protected: | |
| 581 | - // Index of the template in the templatelist we are currently reading from | |
| 582 | - int current_template_idx; | |
| 583 | - | |
| 584 | - TemplateList templates; | |
| 585 | - DataSource * actualSource; | |
| 586 | - // Get the next frame, if we run out of frames on the current template | |
| 587 | - // move on to the next one. | |
| 588 | - bool getNext(FrameData & output) | |
| 589 | - { | |
| 590 | - bool res = actualSource->getNext(output); | |
| 591 | - output.sequenceNumber = next_sequence_number; | |
| 592 | - | |
| 593 | - // OK we got a frame | |
| 594 | - if (res) { | |
| 595 | - // Override the sequence number set by actualSource | |
| 596 | - output.data.last().file.set("FrameNumber", output.sequenceNumber); | |
| 597 | - next_sequence_number++; | |
| 598 | - if (output.data.last().last().empty()) | |
| 599 | - qDebug("broken matrix"); | |
| 600 | - return true; | |
| 601 | - } | |
| 602 | - | |
| 603 | - // We didn't get a frame, try to move on to the next template. | |
| 604 | - while(!res) { | |
| 605 | - output.data.clear(); | |
| 606 | - current_template_idx++; | |
| 607 | - | |
| 608 | - // No more templates? We're done | |
| 609 | - if (current_template_idx >= templates.size()) | |
| 610 | - return false; | |
| 611 | - | |
| 612 | - // open the next data source | |
| 613 | - bool open_res = concreteOpen(templates[current_template_idx]); | |
| 614 | - // We couldn't open it, give up? We could maybe continue here | |
| 615 | - // but don't currently. | |
| 616 | - if (!open_res) | |
| 617 | - return false; | |
| 618 | - | |
| 619 | - // get a frame from the newly opened data source, if that fails | |
| 620 | - // we continue to open the next one. | |
| 621 | - res = actualSource->getNext(output); | |
| 622 | - } | |
| 623 | - // Finally, set the sequence number for the frame we actually return. | |
| 624 | - output.sequenceNumber = next_sequence_number++; | |
| 625 | - output.data.last().file.set("FrameNumber", output.sequenceNumber); | |
| 626 | - | |
| 627 | - if (output.data.last().last().empty()) | |
| 628 | - qDebug("broken matrix"); | |
| 629 | - | |
| 630 | - return res; | |
| 631 | - } | |
| 632 | - | |
| 633 | -}; | |
| 634 | 509 | |
| 635 | 510 | class ProcessingStage; |
| 636 | 511 | |
| ... | ... | @@ -861,100 +736,6 @@ public: |
| 861 | 736 | |
| 862 | 737 | }; |
| 863 | 738 | |
| 864 | -// This stage reads new frames from the data source. | |
| 865 | -class FirstStage : public SingleThreadStage | |
| 866 | -{ | |
| 867 | -public: | |
| 868 | - FirstStage(int activeFrames = 100) : SingleThreadStage(true), dataSource(activeFrames){ } | |
| 869 | - | |
| 870 | - DataSourceManager dataSource; | |
| 871 | - | |
| 872 | - void reset() | |
| 873 | - { | |
| 874 | - dataSource.close(); | |
| 875 | - SingleThreadStage::reset(); | |
| 876 | - } | |
| 877 | - | |
| 878 | - FrameData * run(FrameData * input, bool & should_continue) | |
| 879 | - { | |
| 880 | - if (input == NULL) | |
| 881 | - qFatal("NULL frame in input stage"); | |
| 882 | - | |
| 883 | - // Can we enter the next stage? | |
| 884 | - should_continue = nextStage->tryAcquireNextStage(input); | |
| 885 | - | |
| 886 | - // Try to get a frame from the datasource, we keep working on | |
| 887 | - // the frame we have, but we will queue another job for the next | |
| 888 | - // frame if a frame is currently available. | |
| 889 | - QWriteLocker lock(&statusLock); | |
| 890 | - bool last_frame = false; | |
| 891 | - FrameData * newFrame = dataSource.tryGetFrame(last_frame); | |
| 892 | - | |
| 893 | - // Were we able to get a frame? | |
| 894 | - if (newFrame) startThread(newFrame); | |
| 895 | - // If not this stage will enter a stopped state. | |
| 896 | - else { | |
| 897 | - currentStatus = STOPPING; | |
| 898 | - } | |
| 899 | - | |
| 900 | - lock.unlock(); | |
| 901 | - | |
| 902 | - return input; | |
| 903 | - } | |
| 904 | - | |
| 905 | - // The last stage, trying to access the first stage | |
| 906 | - bool tryAcquireNextStage(FrameData *& input) | |
| 907 | - { | |
| 908 | - // Return the frame, was it the last one? | |
| 909 | - bool was_last = dataSource.returnFrame(input); | |
| 910 | - input = NULL; | |
| 911 | - | |
| 912 | - // OK we won't continue. | |
| 913 | - if (was_last) { | |
| 914 | - return false; | |
| 915 | - } | |
| 916 | - | |
| 917 | - QReadLocker lock(&statusLock); | |
| 918 | - // If the first stage is already active we will just end. | |
| 919 | - if (currentStatus == STARTING) | |
| 920 | - { | |
| 921 | - return false; | |
| 922 | - } | |
| 923 | - | |
| 924 | - // Otherwise we will try to continue, but to do so we have to | |
| 925 | - // escalate the lock, and sadly there is no way to do so without | |
| 926 | - // releasing the read-mode lock, and getting a new write-mode lock. | |
| 927 | - lock.unlock(); | |
| 928 | - | |
| 929 | - QWriteLocker writeLock(&statusLock); | |
| 930 | - // currentStatus might have changed in the gap between releasing the read | |
| 931 | - // lock and getting the write lock. | |
| 932 | - if (currentStatus == STARTING) | |
| 933 | - { | |
| 934 | - return false; | |
| 935 | - } | |
| 936 | - | |
| 937 | - bool last_frame = false; | |
| 938 | - // Try to get a frame from the data source, if we get one we will | |
| 939 | - // continue to the first stage. | |
| 940 | - input = dataSource.tryGetFrame(last_frame); | |
| 941 | - | |
| 942 | - if (!input) { | |
| 943 | - return false; | |
| 944 | - } | |
| 945 | - | |
| 946 | - currentStatus = STARTING; | |
| 947 | - | |
| 948 | - return true; | |
| 949 | - } | |
| 950 | - | |
| 951 | - void status(){ | |
| 952 | - 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()); | |
| 953 | - } | |
| 954 | - | |
| 955 | - | |
| 956 | -}; | |
| 957 | - | |
| 958 | 739 | // Appened to the end of a Stream's transform sequence. Collects the output |
| 959 | 740 | // from each frame on a single templatelist |
| 960 | 741 | class LastStage : public SingleThreadStage |
| ... | ... | @@ -1038,12 +819,23 @@ public: |
| 1038 | 819 | |
| 1039 | 820 | }; |
| 1040 | 821 | |
| 822 | +class FirstStage; | |
| 823 | + | |
| 1041 | 824 | class DirectStreamTransform : public CompositeTransform |
| 1042 | 825 | { |
| 1043 | 826 | Q_OBJECT |
| 1044 | 827 | public: |
| 828 | + | |
| 829 | + enum StreamModes { StreamVideo, | |
| 830 | + DistributeFrames, | |
| 831 | + Auto}; | |
| 832 | + | |
| 833 | + Q_ENUMS(StreamModes) | |
| 834 | + | |
| 1045 | 835 | Q_PROPERTY(int activeFrames READ get_activeFrames WRITE set_activeFrames RESET reset_activeFrames) |
| 836 | + Q_PROPERTY(StreamModes readMode READ get_readMode WRITE set_readMode RESET reset_readMode) | |
| 1046 | 837 | BR_PROPERTY(int, activeFrames, 100) |
| 838 | + BR_PROPERTY(StreamModes, readMode, Auto) | |
| 1047 | 839 | |
| 1048 | 840 | friend class StreamTransfrom; |
| 1049 | 841 | |
| ... | ... | @@ -1109,63 +901,6 @@ public: |
| 1109 | 901 | qFatal("whatever"); |
| 1110 | 902 | } |
| 1111 | 903 | |
| 1112 | - // start processing, consider all templates in src a continuous | |
| 1113 | - // 'video' | |
| 1114 | - void projectUpdate(const TemplateList & src, TemplateList & dst) | |
| 1115 | - { | |
| 1116 | - dst = src; | |
| 1117 | - | |
| 1118 | - bool res = readStage->dataSource.open(dst); | |
| 1119 | - if (!res) { | |
| 1120 | - qDebug("stream failed to open %s", qPrintable(dst[0].file.name)); | |
| 1121 | - return; | |
| 1122 | - } | |
| 1123 | - | |
| 1124 | - // Start the first thread in the stream. | |
| 1125 | - QWriteLocker lock(&readStage->statusLock); | |
| 1126 | - readStage->currentStatus = SingleThreadStage::STARTING; | |
| 1127 | - | |
| 1128 | - // We have to get a frame before starting the thread | |
| 1129 | - bool last_frame = false; | |
| 1130 | - FrameData * firstFrame = readStage->dataSource.tryGetFrame(last_frame); | |
| 1131 | - if (firstFrame == NULL) | |
| 1132 | - qFatal("Failed to read first frame of video"); | |
| 1133 | - | |
| 1134 | - readStage->startThread(firstFrame); | |
| 1135 | - lock.unlock(); | |
| 1136 | - | |
| 1137 | - // Wait for the stream to process the last frame available from | |
| 1138 | - // the data source. | |
| 1139 | - bool wait_res = false; | |
| 1140 | - wait_res = readStage->dataSource.waitLast(); | |
| 1141 | - | |
| 1142 | - // Now that there are no more incoming frames, call finalize | |
| 1143 | - // on each transform in turn to collect any last templates | |
| 1144 | - // they wish to issue. | |
| 1145 | - TemplateList final_output; | |
| 1146 | - | |
| 1147 | - // Push finalize through the stages | |
| 1148 | - for (int i=0; i < this->transforms.size(); i++) | |
| 1149 | - { | |
| 1150 | - TemplateList output_set; | |
| 1151 | - transforms[i]->finalize(output_set); | |
| 1152 | - | |
| 1153 | - for (int j=i+1; j < transforms.size();j++) | |
| 1154 | - { | |
| 1155 | - transforms[j]->projectUpdate(output_set); | |
| 1156 | - } | |
| 1157 | - final_output.append(output_set); | |
| 1158 | - } | |
| 1159 | - | |
| 1160 | - // dst is set to all output received by the final stage, along | |
| 1161 | - // with anything output via the calls to finalize. | |
| 1162 | - dst = collectionStage->getOutput(); | |
| 1163 | - dst.append(final_output); | |
| 1164 | - | |
| 1165 | - foreach(ProcessingStage * stage, processingStages) { | |
| 1166 | - stage->reset(); | |
| 1167 | - } | |
| 1168 | - } | |
| 1169 | 904 | |
| 1170 | 905 | virtual void finalize(TemplateList & output) |
| 1171 | 906 | { |
| ... | ... | @@ -1174,90 +909,8 @@ public: |
| 1174 | 909 | // on all child transforms as part of projectUpdate |
| 1175 | 910 | } |
| 1176 | 911 | |
| 1177 | - // Create and link stages | |
| 1178 | - void init() | |
| 1179 | - { | |
| 1180 | - if (transforms.isEmpty()) return; | |
| 1181 | - | |
| 1182 | - for (int i=0; i < processingStages.size();i++) | |
| 1183 | - delete processingStages[i]; | |
| 1184 | - processingStages.clear(); | |
| 1185 | - | |
| 1186 | - // call CompositeTransform::init so that trainable is set | |
| 1187 | - // correctly. | |
| 1188 | - CompositeTransform::init(); | |
| 1189 | - | |
| 1190 | - // We share a thread pool across streams attached to the same | |
| 1191 | - // parent tranform, retrieve or create a thread pool based | |
| 1192 | - // on our parent transform. | |
| 1193 | - QMutexLocker poolLock(&poolsAccess); | |
| 1194 | - QHash<QObject *, QThreadPool *>::Iterator it; | |
| 1195 | - if (!pools.contains(this->parent())) { | |
| 1196 | - it = pools.insert(this->parent(), new QThreadPool(this)); | |
| 1197 | - it.value()->setMaxThreadCount(Globals->parallelism); | |
| 1198 | - } | |
| 1199 | - else it = pools.find(this->parent()); | |
| 1200 | - threads = it.value(); | |
| 1201 | - poolLock.unlock(); | |
| 1202 | - | |
| 1203 | - // Are our children time varying or not? This decides whether | |
| 1204 | - // we run them in single threaded or multi threaded stages | |
| 1205 | - stage_variance.reserve(transforms.size()); | |
| 1206 | - foreach (const br::Transform *transform, transforms) { | |
| 1207 | - stage_variance.append(transform->timeVarying()); | |
| 1208 | - } | |
| 1209 | - | |
| 1210 | - // Additionally, we have a separate stage responsible for reading | |
| 1211 | - // frames from the data source | |
| 1212 | - readStage = new FirstStage(activeFrames); | |
| 1213 | - | |
| 1214 | - processingStages.push_back(readStage); | |
| 1215 | - readStage->stage_id = 0; | |
| 1216 | - readStage->stages = &this->processingStages; | |
| 1217 | - readStage->threads = this->threads; | |
| 1218 | - | |
| 1219 | - // Initialize and link a processing stage for each of our child | |
| 1220 | - // transforms. | |
| 1221 | - int next_stage_id = 1; | |
| 1222 | - bool prev_stage_variance = true; | |
| 1223 | - for (int i =0; i < transforms.size(); i++) | |
| 1224 | - { | |
| 1225 | - if (stage_variance[i]) | |
| 1226 | - // Whether or not the previous stage is multi-threaded controls | |
| 1227 | - // the type of input buffer we need in a single threaded stage. | |
| 1228 | - processingStages.append(new SingleThreadStage(prev_stage_variance)); | |
| 1229 | - else | |
| 1230 | - processingStages.append(new MultiThreadStage(Globals->parallelism)); | |
| 1231 | - | |
| 1232 | - processingStages.last()->stage_id = next_stage_id++; | |
| 1233 | - | |
| 1234 | - // link nextStage pointers, the stage we just appeneded is i+1 since | |
| 1235 | - // the read stage was added before this loop | |
| 1236 | - processingStages[i]->nextStage = processingStages[i+1]; | |
| 1237 | - | |
| 1238 | - processingStages.last()->stages = &this->processingStages; | |
| 1239 | - processingStages.last()->threads = this->threads; | |
| 1240 | - | |
| 1241 | - processingStages.last()->transform = transforms[i]; | |
| 1242 | - prev_stage_variance = stage_variance[i]; | |
| 1243 | - } | |
| 1244 | - | |
| 1245 | - // We also have the last stage, which just puts the output of the | |
| 1246 | - // previous stages on a template list. | |
| 1247 | - collectionStage = new LastStage(prev_stage_variance); | |
| 1248 | - processingStages.append(collectionStage); | |
| 1249 | - collectionStage->stage_id = next_stage_id; | |
| 1250 | - collectionStage->stages = &this->processingStages; | |
| 1251 | - collectionStage->threads = this->threads; | |
| 1252 | - | |
| 1253 | - // the last transform stage points to collection stage | |
| 1254 | - processingStages[processingStages.size() - 2]->nextStage = collectionStage; | |
| 1255 | - | |
| 1256 | - // And the collection stage points to the read stage, because this is | |
| 1257 | - // a ring buffer. | |
| 1258 | - collectionStage->nextStage = readStage; | |
| 1259 | - } | |
| 1260 | - | |
| 912 | + void projectUpdate(const TemplateList & src, TemplateList & dst); | |
| 913 | + void init(); | |
| 1261 | 914 | ~DirectStreamTransform() |
| 1262 | 915 | { |
| 1263 | 916 | // Delete all the stages |
| ... | ... | @@ -1448,6 +1101,373 @@ private: |
| 1448 | 1101 | |
| 1449 | 1102 | BR_REGISTER(Transform, StreamTransform) |
| 1450 | 1103 | |
| 1104 | +// Given a templatelist as input, create appropriate data source for each | |
| 1105 | +// individual template | |
| 1106 | +class DataSourceManager : public DataSource | |
| 1107 | +{ | |
| 1108 | +public: | |
| 1109 | + DataSourceManager(int activeFrames=100) : DataSource(activeFrames) | |
| 1110 | + { | |
| 1111 | + actualSource = NULL; | |
| 1112 | + } | |
| 1113 | + | |
| 1114 | + ~DataSourceManager() | |
| 1115 | + { | |
| 1116 | + close(); | |
| 1117 | + } | |
| 1118 | + | |
| 1119 | + int size() | |
| 1120 | + { | |
| 1121 | + return this->allFrames.size(); | |
| 1122 | + } | |
| 1123 | + | |
| 1124 | + void close() | |
| 1125 | + { | |
| 1126 | + if (actualSource) { | |
| 1127 | + actualSource->close(); | |
| 1128 | + delete actualSource; | |
| 1129 | + actualSource = NULL; | |
| 1130 | + } | |
| 1131 | + } | |
| 1132 | + | |
| 1133 | + // We are used through a call to open(TemplateList) | |
| 1134 | + bool open(TemplateList & input, DirectStreamTransform::StreamModes _mode) | |
| 1135 | + { | |
| 1136 | + // Set up variables specific to us | |
| 1137 | + current_template_idx = 0; | |
| 1138 | + templates = input; | |
| 1139 | + mode = _mode; | |
| 1140 | + | |
| 1141 | + // Call datasourece::open on the first template to set up | |
| 1142 | + // state variables | |
| 1143 | + return DataSource::open(templates[current_template_idx]); | |
| 1144 | + } | |
| 1145 | + void projectUpdate(const TemplateList & src, TemplateList & dst); | |
| 1146 | + | |
| 1147 | + // Create an actual data source of appropriate type for this template | |
| 1148 | + // (initially called via the call to DataSource::open, called later | |
| 1149 | + // as we run out of frames on our templates). | |
| 1150 | + bool concreteOpen(Template & input) | |
| 1151 | + { | |
| 1152 | + close(); | |
| 1153 | + | |
| 1154 | + bool open_res = false; | |
| 1155 | + | |
| 1156 | + // Input has no matrices? Its probably a video that hasn't been loaded yet | |
| 1157 | + if (mode == DirectStreamTransform::StreamVideo || mode ~= DirectStreamTransform::DistributeFrames && input.empty()) { | |
| 1158 | + actualSource = new VideoDataSource(0); | |
| 1159 | + open_res = actualSource->concreteOpen(input); | |
| 1160 | + } | |
| 1161 | + // If the input is not empty, we assume it is a set of frames already | |
| 1162 | + // in memory. | |
| 1163 | + else { | |
| 1164 | + actualSource = new TemplateDataSource(0); | |
| 1165 | + open_res = actualSource->concreteOpen(input); | |
| 1166 | + } | |
| 1167 | + | |
| 1168 | + // The data source failed to open | |
| 1169 | + if (!open_res) { | |
| 1170 | + delete actualSource; | |
| 1171 | + actualSource = NULL; | |
| 1172 | + return false; | |
| 1173 | + } | |
| 1174 | + return true; | |
| 1175 | + } | |
| 1176 | + | |
| 1177 | + bool isOpen() { return !actualSource ? false : actualSource->isOpen(); } | |
| 1178 | + | |
| 1179 | +protected: | |
| 1180 | + // Index of the template in the templatelist we are currently reading from | |
| 1181 | + int current_template_idx; | |
| 1182 | + DirectStreamTransform::StreamModes mode; | |
| 1183 | + TemplateList templates; | |
| 1184 | + DataSource * actualSource; | |
| 1185 | + // Get the next frame, if we run out of frames on the current template | |
| 1186 | + // move on to the next one. | |
| 1187 | + bool getNext(FrameData & output) | |
| 1188 | + { | |
| 1189 | + bool res = actualSource->getNext(output); | |
| 1190 | + output.sequenceNumber = next_sequence_number; | |
| 1191 | + | |
| 1192 | + // OK we got a frame | |
| 1193 | + if (res) { | |
| 1194 | + // Override the sequence number set by actualSource | |
| 1195 | + output.data.last().file.set("FrameNumber", output.sequenceNumber); | |
| 1196 | + next_sequence_number++; | |
| 1197 | + if (output.data.last().last().empty()) | |
| 1198 | + qDebug("broken matrix"); | |
| 1199 | + return true; | |
| 1200 | + } | |
| 1201 | + | |
| 1202 | + // We didn't get a frame, try to move on to the next template. | |
| 1203 | + while(!res) { | |
| 1204 | + output.data.clear(); | |
| 1205 | + current_template_idx++; | |
| 1206 | + | |
| 1207 | + // No more templates? We're done | |
| 1208 | + if (current_template_idx >= templates.size()) | |
| 1209 | + return false; | |
| 1210 | + | |
| 1211 | + // open the next data source | |
| 1212 | + bool open_res = concreteOpen(templates[current_template_idx]); | |
| 1213 | + // We couldn't open it, give up? We could maybe continue here | |
| 1214 | + // but don't currently. | |
| 1215 | + if (!open_res) | |
| 1216 | + return false; | |
| 1217 | + | |
| 1218 | + // get a frame from the newly opened data source, if that fails | |
| 1219 | + // we continue to open the next one. | |
| 1220 | + res = actualSource->getNext(output); | |
| 1221 | + } | |
| 1222 | + // Finally, set the sequence number for the frame we actually return. | |
| 1223 | + output.sequenceNumber = next_sequence_number++; | |
| 1224 | + output.data.last().file.set("FrameNumber", output.sequenceNumber); | |
| 1225 | + | |
| 1226 | + if (output.data.last().last().empty()) | |
| 1227 | + qDebug("broken matrix"); | |
| 1228 | + | |
| 1229 | + return res; | |
| 1230 | + } | |
| 1231 | + | |
| 1232 | +}; | |
| 1233 | + | |
| 1234 | +// This stage reads new frames from the data source. | |
| 1235 | +class FirstStage : public SingleThreadStage | |
| 1236 | +{ | |
| 1237 | +public: | |
| 1238 | + FirstStage(int activeFrames = 100) : SingleThreadStage(true), dataSource(activeFrames){ } | |
| 1239 | + | |
| 1240 | + DataSourceManager dataSource; | |
| 1241 | + | |
| 1242 | + void reset() | |
| 1243 | + { | |
| 1244 | + dataSource.close(); | |
| 1245 | + SingleThreadStage::reset(); | |
| 1246 | + } | |
| 1247 | + | |
| 1248 | + FrameData * run(FrameData * input, bool & should_continue) | |
| 1249 | + { | |
| 1250 | + if (input == NULL) | |
| 1251 | + qFatal("NULL frame in input stage"); | |
| 1252 | + | |
| 1253 | + // Can we enter the next stage? | |
| 1254 | + should_continue = nextStage->tryAcquireNextStage(input); | |
| 1255 | + | |
| 1256 | + // Try to get a frame from the datasource, we keep working on | |
| 1257 | + // the frame we have, but we will queue another job for the next | |
| 1258 | + // frame if a frame is currently available. | |
| 1259 | + QWriteLocker lock(&statusLock); | |
| 1260 | + bool last_frame = false; | |
| 1261 | + FrameData * newFrame = dataSource.tryGetFrame(last_frame); | |
| 1262 | + | |
| 1263 | + // Were we able to get a frame? | |
| 1264 | + if (newFrame) startThread(newFrame); | |
| 1265 | + // If not this stage will enter a stopped state. | |
| 1266 | + else { | |
| 1267 | + currentStatus = STOPPING; | |
| 1268 | + } | |
| 1269 | + | |
| 1270 | + lock.unlock(); | |
| 1271 | + | |
| 1272 | + return input; | |
| 1273 | + } | |
| 1274 | + | |
| 1275 | + // The last stage, trying to access the first stage | |
| 1276 | + bool tryAcquireNextStage(FrameData *& input) | |
| 1277 | + { | |
| 1278 | + // Return the frame, was it the last one? | |
| 1279 | + bool was_last = dataSource.returnFrame(input); | |
| 1280 | + input = NULL; | |
| 1281 | + | |
| 1282 | + // OK we won't continue. | |
| 1283 | + if (was_last) { | |
| 1284 | + return false; | |
| 1285 | + } | |
| 1286 | + | |
| 1287 | + QReadLocker lock(&statusLock); | |
| 1288 | + // If the first stage is already active we will just end. | |
| 1289 | + if (currentStatus == STARTING) | |
| 1290 | + { | |
| 1291 | + return false; | |
| 1292 | + } | |
| 1293 | + | |
| 1294 | + // Otherwise we will try to continue, but to do so we have to | |
| 1295 | + // escalate the lock, and sadly there is no way to do so without | |
| 1296 | + // releasing the read-mode lock, and getting a new write-mode lock. | |
| 1297 | + lock.unlock(); | |
| 1298 | + | |
| 1299 | + QWriteLocker writeLock(&statusLock); | |
| 1300 | + // currentStatus might have changed in the gap between releasing the read | |
| 1301 | + // lock and getting the write lock. | |
| 1302 | + if (currentStatus == STARTING) | |
| 1303 | + { | |
| 1304 | + return false; | |
| 1305 | + } | |
| 1306 | + | |
| 1307 | + bool last_frame = false; | |
| 1308 | + // Try to get a frame from the data source, if we get one we will | |
| 1309 | + // continue to the first stage. | |
| 1310 | + input = dataSource.tryGetFrame(last_frame); | |
| 1311 | + | |
| 1312 | + if (!input) { | |
| 1313 | + return false; | |
| 1314 | + } | |
| 1315 | + | |
| 1316 | + currentStatus = STARTING; | |
| 1317 | + | |
| 1318 | + return true; | |
| 1319 | + } | |
| 1320 | + | |
| 1321 | + void status(){ | |
| 1322 | + 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()); | |
| 1323 | + } | |
| 1324 | + | |
| 1325 | + | |
| 1326 | +}; | |
| 1327 | + | |
| 1328 | + | |
| 1329 | +// start processing, consider all templates in src a continuous | |
| 1330 | +// 'video' | |
| 1331 | +void DirectStreamTransform::projectUpdate(const TemplateList & src, TemplateList & dst) | |
| 1332 | +{ | |
| 1333 | + dst = src; | |
| 1334 | + | |
| 1335 | + bool res = readStage->dataSource.open(dst,readMode); | |
| 1336 | + if (!res) { | |
| 1337 | + qDebug("stream failed to open %s", qPrintable(dst[0].file.name)); | |
| 1338 | + return; | |
| 1339 | + } | |
| 1340 | + | |
| 1341 | + // Start the first thread in the stream. | |
| 1342 | + QWriteLocker lock(&readStage->statusLock); | |
| 1343 | + readStage->currentStatus = SingleThreadStage::STARTING; | |
| 1344 | + | |
| 1345 | + // We have to get a frame before starting the thread | |
| 1346 | + bool last_frame = false; | |
| 1347 | + FrameData * firstFrame = readStage->dataSource.tryGetFrame(last_frame); | |
| 1348 | + if (firstFrame == NULL) | |
| 1349 | + qFatal("Failed to read first frame of video"); | |
| 1350 | + | |
| 1351 | + readStage->startThread(firstFrame); | |
| 1352 | + lock.unlock(); | |
| 1353 | + | |
| 1354 | + // Wait for the stream to process the last frame available from | |
| 1355 | + // the data source. | |
| 1356 | + bool wait_res = false; | |
| 1357 | + wait_res = readStage->dataSource.waitLast(); | |
| 1358 | + | |
| 1359 | + // Now that there are no more incoming frames, call finalize | |
| 1360 | + // on each transform in turn to collect any last templates | |
| 1361 | + // they wish to issue. | |
| 1362 | + TemplateList final_output; | |
| 1363 | + | |
| 1364 | + // Push finalize through the stages | |
| 1365 | + for (int i=0; i < this->transforms.size(); i++) | |
| 1366 | + { | |
| 1367 | + TemplateList output_set; | |
| 1368 | + transforms[i]->finalize(output_set); | |
| 1369 | + | |
| 1370 | + for (int j=i+1; j < transforms.size();j++) | |
| 1371 | + { | |
| 1372 | + transforms[j]->projectUpdate(output_set); | |
| 1373 | + } | |
| 1374 | + final_output.append(output_set); | |
| 1375 | + } | |
| 1376 | + | |
| 1377 | + // dst is set to all output received by the final stage, along | |
| 1378 | + // with anything output via the calls to finalize. | |
| 1379 | + dst = collectionStage->getOutput(); | |
| 1380 | + dst.append(final_output); | |
| 1381 | + | |
| 1382 | + foreach(ProcessingStage * stage, processingStages) { | |
| 1383 | + stage->reset(); | |
| 1384 | + } | |
| 1385 | +} | |
| 1386 | + | |
| 1387 | + | |
| 1388 | +// Create and link stages | |
| 1389 | +void DirectStreamTransform::init() | |
| 1390 | +{ | |
| 1391 | + if (transforms.isEmpty()) return; | |
| 1392 | + | |
| 1393 | + for (int i=0; i < processingStages.size();i++) | |
| 1394 | + delete processingStages[i]; | |
| 1395 | + processingStages.clear(); | |
| 1396 | + | |
| 1397 | + // call CompositeTransform::init so that trainable is set | |
| 1398 | + // correctly. | |
| 1399 | + CompositeTransform::init(); | |
| 1400 | + | |
| 1401 | + // We share a thread pool across streams attached to the same | |
| 1402 | + // parent tranform, retrieve or create a thread pool based | |
| 1403 | + // on our parent transform. | |
| 1404 | + QMutexLocker poolLock(&poolsAccess); | |
| 1405 | + QHash<QObject *, QThreadPool *>::Iterator it; | |
| 1406 | + if (!pools.contains(this->parent())) { | |
| 1407 | + it = pools.insert(this->parent(), new QThreadPool(this->parent())); | |
| 1408 | + it.value()->setMaxThreadCount(Globals->parallelism); | |
| 1409 | + } | |
| 1410 | + else it = pools.find(this->parent()); | |
| 1411 | + threads = it.value(); | |
| 1412 | + poolLock.unlock(); | |
| 1413 | + | |
| 1414 | + // Are our children time varying or not? This decides whether | |
| 1415 | + // we run them in single threaded or multi threaded stages | |
| 1416 | + stage_variance.reserve(transforms.size()); | |
| 1417 | + foreach (const br::Transform *transform, transforms) { | |
| 1418 | + stage_variance.append(transform->timeVarying()); | |
| 1419 | + } | |
| 1420 | + | |
| 1421 | + // Additionally, we have a separate stage responsible for reading | |
| 1422 | + // frames from the data source | |
| 1423 | + readStage = new FirstStage(activeFrames); | |
| 1424 | + | |
| 1425 | + processingStages.push_back(readStage); | |
| 1426 | + readStage->stage_id = 0; | |
| 1427 | + readStage->stages = &this->processingStages; | |
| 1428 | + readStage->threads = this->threads; | |
| 1429 | + | |
| 1430 | + // Initialize and link a processing stage for each of our child | |
| 1431 | + // transforms. | |
| 1432 | + int next_stage_id = 1; | |
| 1433 | + bool prev_stage_variance = true; | |
| 1434 | + for (int i =0; i < transforms.size(); i++) | |
| 1435 | + { | |
| 1436 | + if (stage_variance[i]) | |
| 1437 | + // Whether or not the previous stage is multi-threaded controls | |
| 1438 | + // the type of input buffer we need in a single threaded stage. | |
| 1439 | + processingStages.append(new SingleThreadStage(prev_stage_variance)); | |
| 1440 | + else | |
| 1441 | + processingStages.append(new MultiThreadStage(Globals->parallelism)); | |
| 1442 | + | |
| 1443 | + processingStages.last()->stage_id = next_stage_id++; | |
| 1444 | + | |
| 1445 | + // link nextStage pointers, the stage we just appeneded is i+1 since | |
| 1446 | + // the read stage was added before this loop | |
| 1447 | + processingStages[i]->nextStage = processingStages[i+1]; | |
| 1448 | + | |
| 1449 | + processingStages.last()->stages = &this->processingStages; | |
| 1450 | + processingStages.last()->threads = this->threads; | |
| 1451 | + | |
| 1452 | + processingStages.last()->transform = transforms[i]; | |
| 1453 | + prev_stage_variance = stage_variance[i]; | |
| 1454 | + } | |
| 1455 | + | |
| 1456 | + // We also have the last stage, which just puts the output of the | |
| 1457 | + // previous stages on a template list. | |
| 1458 | + collectionStage = new LastStage(prev_stage_variance); | |
| 1459 | + processingStages.append(collectionStage); | |
| 1460 | + collectionStage->stage_id = next_stage_id; | |
| 1461 | + collectionStage->stages = &this->processingStages; | |
| 1462 | + collectionStage->threads = this->threads; | |
| 1463 | + | |
| 1464 | + // the last transform stage points to collection stage | |
| 1465 | + processingStages[processingStages.size() - 2]->nextStage = collectionStage; | |
| 1466 | + | |
| 1467 | + // And the collection stage points to the read stage, because this is | |
| 1468 | + // a ring buffer. | |
| 1469 | + collectionStage->nextStage = readStage; | |
| 1470 | +} | |
| 1451 | 1471 | |
| 1452 | 1472 | |
| 1453 | 1473 | } // namespace br | ... | ... |