GCC Code Coverage Report


Directory: ./
File: channeloutput/processors/OutputProcessor.cpp
Date: 2026-10-29 08:11:58
Exec Total Coverage
Lines: 164 179 91.6%
Functions: 16 16 100.0%
Branches: 195 358 54.5%

Line Branch Exec Source
1 /*
2 * This file is part of the Falcon Player (FPP) and is Copyright (C)
3 * 2013-2022 by the Falcon Player Developers.
4 *
5 * The Falcon Player (FPP) is free software, and is covered under
6 * multiple Open Source licenses. Please see the included 'LICENSES'
7 * file for descriptions of what files are covered by each license.
8 *
9 * This source file is covered under the LGPL v2.1 as described in the
10 * included LICENSE.LGPL file.
11 */
12
13 #include "fpp-pch.h"
14
15 #include "fpp-json.h"
16
17 #include <algorithm>
18 #include <stdexcept>
19 #include <string>
20 #include <vector>
21
22 #include "../../log.h"
23
24 #include "OutputProcessor.h"
25 #include "../../Warnings.h"
26
27 #include "BrightnessOutputProcessor.h"
28 #include "ClampValueOutputProcessor.h"
29 #include "ColorOrderOutputProcessor.h"
30 #include "FoldOutputProcessor.h"
31 #include "HoldValueOutputProcessor.h"
32 #include "OverrideZeroOutputProcessor.h"
33 #include "RemapOutputProcessor.h"
34 #include "ScaleValueOutputProcessor.h"
35 #include "SetValueOutputProcessor.h"
36 #include "ThreeToFourOutputProcessor.h"
37 #include "../../overlays/PixelOverlay.h"
38 #include "../../overlays/PixelOverlayModel.h"
39
40 2346 OutputProcessors::OutputProcessors() {
41 2346 }
42 458 OutputProcessors::~OutputProcessors() {
43 458 removeAll();
44 458 }
45
46 138483 void OutputProcessors::ProcessData(unsigned char* channelData) const {
47
1/2
✓ Branch 0 taken 138483 times.
✗ Branch 1 not taken.
138483 std::lock_guard<std::mutex> lock(processorsLock);
48
2/2
✓ Branch 0 taken 1485 times.
✓ Branch 1 taken 138483 times.
139968 for (OutputProcessor* a : processors) {
49
1/2
✓ Branch 0 taken 1485 times.
✗ Branch 1 not taken.
1485 if (a->isActive()) {
50
1/2
✓ Branch 0 taken 1485 times.
✗ Branch 1 not taken.
1485 a->ProcessData(channelData);
51 }
52 }
53 138483 }
54
55 // Whether the processor's channels lie inside the sequence buffer, recording a
56 // warning when they do not. Caller holds processorsLock.
57 114 bool OutputProcessors::rangeIsValid(OutputProcessor* p) {
58 // ProcessData() writes straight into the sequence buffer, which is
59 // FPPD_MAX_CHANNELS long, over the channels the processor reports here.
60 // The UI only checks that numbers are positive, so a start or a count (or
61 // count * loops) can put that past the end - where it overwrites the
62 // off/white sentinel channels just past the buffer, or faults on every
63 // frame. Refuse it rather than trust each processor to clamp. An
64 // inverted range is how a processor with nothing to do reports.
65 114 bool bad = false;
66 114 int badMin = 0;
67 114 int badMax = -1;
68
2/4
✓ Branch 0 taken 114 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 114 times.
✗ Branch 3 not taken.
114 p->GetRequiredChannelRanges([&bad, &badMin, &badMax](int m1, int m2) {
69
7/8
✓ Branch 0 taken 114 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 113 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 111 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 7 times.
✓ Branch 7 taken 104 times.
114 if (!bad && m2 >= m1 && (m1 < 0 || m2 >= (int)FPPD_MAX_CHANNELS)) {
70 9 bad = true;
71 9 badMin = m1;
72 9 badMax = m2;
73 }
74 114 });
75
2/2
✓ Branch 0 taken 105 times.
✓ Branch 1 taken 9 times.
114 if (!bad) {
76 105 return true;
77 }
78
2/4
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
18 std::string warning = "Output processor needs channels " + std::to_string(badMin + 1) + "-" +
79
3/6
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 9 times.
✗ Branch 5 not taken.
36 std::to_string(badMax + 1) + ", outside 1-" + std::to_string(FPPD_MAX_CHANNELS) +
80
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
9 ". Processor disabled.";
81
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
9 LogErr(VB_CHANNELOUT, "%s\n", warning.c_str());
82
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
9 WarningHolder::AddWarning(warning);
83
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
9 rejectedWarnings.push_back(warning);
84 9 return false;
85 9 }
86
87 2 void OutputProcessors::addProcessor(OutputProcessor* p) {
88
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (p == nullptr) {
89 ✗ return;
90 }
91
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 std::lock_guard<std::mutex> lock(processorsLock);
92
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 if (rangeIsValid(p)) {
93
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 processors.push_back(p);
94 } else {
95 ✗ delete p; // rejected; nothing else owns it
96 }
97 2 }
98 2 void OutputProcessors::removeProcessor(OutputProcessor* p) {
99 // ProcessData() walks the chain under processorsLock, so once the
100 // processor is out of the list (under the same lock) nothing can be using
101 // it. Removed processors used to be leaked.
102
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 std::lock_guard<std::mutex> lock(processorsLock);
103
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 auto it = std::find(processors.begin(), processors.end(), p);
104
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (it != processors.end()) {
105 2 processors.erase(it);
106 // One loaded from outputprocessors.json (a playlist Remap "remove"
107 // matches those too) is also on fromJsonProcessors, which the next
108 // loadFromJSON() frees: take it off, or that frees it a second time.
109 2 fromJsonProcessors.remove(p);
110
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 delete p;
111 }
112 2 }
113 458 void OutputProcessors::removeAll() {
114
1/2
✓ Branch 0 taken 458 times.
✗ Branch 1 not taken.
458 std::lock_guard<std::mutex> lock(processorsLock);
115 // Everything in the chain, and the JSON processors rangeIsValid() kept
116 // out of it, which are on fromJsonProcessors alone.
117
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 458 times.
458 for (OutputProcessor* a : fromJsonProcessors) {
118 ✗ if (std::find(processors.begin(), processors.end(), a) == processors.end()) {
119 ✗ delete a;
120 }
121 }
122 458 fromJsonProcessors.clear();
123
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 458 times.
458 for (OutputProcessor* a : processors) {
124 ✗ delete a;
125 }
126 458 processors.clear();
127 458 }
128
129 1930 void OutputProcessors::loadFromJSON(const Json::Value& config) {
130 // Build the whole new chain first and swap it in under one lock: removing
131 // the old processors, unlocking and adding the new ones one at a time let
132 // the output thread run a frame with part of the chain (a brightness or
133 // current limit missing for a frame). Only the processors created from
134 // JSON are replaced; plugins may have added their own.
135 1930 std::list<OutputProcessor*> created;
136 // A processor whose JSON holds a value of the wrong type ("1" for a
137 // number, a list, null) threw Json::LogicError out of its constructor,
138 // past everything up to std::terminate -- at startup, so with the config
139 // saved fppd crash looped. Refuse that one processor instead, with a
140 // warning (raised below, once last time's are retracted).
141 1930 std::vector<std::string> failed;
142 117 auto tryCreate = [this, &created, &failed](const Json::Value& v) {
143 try {
144
4/4
✓ Branch 0 taken 113 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 112 times.
✓ Branch 3 taken 1 times.
117 if (OutputProcessor* p = create(v)) {
145
1/2
✓ Branch 0 taken 112 times.
✗ Branch 1 not taken.
112 created.push_back(p);
146 }
147
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 } catch (const std::exception& e) {
148 4 std::string what;
149
6/12
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 4 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 4 times.
✗ Branch 11 not taken.
4 if (v.isObject() && v["type"].isString()) {
150
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 what = v["type"].asString();
151 }
152
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (what.empty()) {
153 ✗ what = "an entry";
154 }
155
4/8
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
8 failed.push_back("Output processor " + what + " could not be loaded (" + e.what() +
156
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 "). Processor disabled.");
157 4 }
158 117 };
159
6/10
✓ Branch 0 taken 1930 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 93 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2023 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2023 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 93 times.
✓ Branch 9 taken 1930 times.
2023 for (Json::Value::const_iterator itr = config.begin(); itr != config.end(); ++itr) {
160
2/4
✓ Branch 0 taken 93 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 93 times.
✗ Branch 3 not taken.
93 std::string name = itr.key().asString();
161
2/4
✓ Branch 0 taken 93 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 93 times.
✗ Branch 3 not taken.
93 if (name == "outputProcessors") {
162
2/4
✓ Branch 0 taken 93 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 93 times.
✗ Branch 3 not taken.
93 Json::Value val = *itr;
163
2/4
✓ Branch 0 taken 93 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 93 times.
✗ Branch 3 not taken.
93 if (val.isArray()) {
164
3/4
✓ Branch 0 taken 210 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 117 times.
✓ Branch 3 taken 93 times.
210 for (int x = 0; x < val.size(); x++) {
165
2/4
✓ Branch 0 taken 117 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 117 times.
✗ Branch 3 not taken.
117 tryCreate(val[x]);
166 }
167 } else {
168 ✗ tryCreate(val);
169 }
170 93 }
171 93 }
172
173 1930 std::list<OutputProcessor*> old;
174 {
175
1/2
✓ Branch 0 taken 1930 times.
✗ Branch 1 not taken.
1930 std::lock_guard<std::mutex> lock(processorsLock);
176
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1930 times.
1930 for (auto& w : rejectedWarnings) {
177 ✗ WarningHolder::RemoveWarning(w);
178 }
179 1930 rejectedWarnings.clear();
180
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1930 times.
1934 for (auto& w : failed) {
181
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 LogErr(VB_CHANNELOUT, "%s\n", w.c_str());
182
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 WarningHolder::AddWarning(w);
183
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 rejectedWarnings.push_back(w);
184 }
185
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 1930 times.
1972 for (auto a : fromJsonProcessors) {
186 42 processors.remove(a);
187 }
188 1930 old.swap(fromJsonProcessors);
189
2/2
✓ Branch 0 taken 112 times.
✓ Branch 1 taken 1930 times.
2042 for (auto p : created) {
190
3/4
✓ Branch 0 taken 112 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 103 times.
✓ Branch 3 taken 9 times.
112 if (rangeIsValid(p)) {
191
1/2
✓ Branch 0 taken 103 times.
✗ Branch 1 not taken.
103 processors.push_back(p);
192 }
193 // owned either way, freed on the next reload
194
1/2
✓ Branch 0 taken 112 times.
✗ Branch 1 not taken.
112 fromJsonProcessors.push_back(p);
195 }
196 1930 }
197 // not running any more, and no longer reachable by the output thread
198
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 1930 times.
1972 for (auto a : old) {
199
1/2
✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
42 delete a;
200 }
201 1930 }
202 117 OutputProcessor* OutputProcessors::create(const Json::Value& config) {
203 117 int active = config["active"].asInt();
204
1/2
✓ Branch 0 taken 117 times.
✗ Branch 1 not taken.
117 if (active == 1) {
205
2/4
✓ Branch 0 taken 117 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 117 times.
✗ Branch 3 not taken.
117 std::string type = config["type"].asString();
206
3/4
✓ Branch 0 taken 117 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 108 times.
117 if (type == "Remap") {
207
5/8
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 8 times.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
9 return new RemapOutputProcessor(config);
208
3/4
✓ Branch 0 taken 108 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 53 times.
✓ Branch 3 taken 55 times.
108 } else if (type == "Brightness") {
209
5/8
✓ Branch 0 taken 53 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 51 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 51 times.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
53 return new BrightnessOutputProcessor(config);
210
3/4
✓ Branch 0 taken 55 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 53 times.
55 } else if (type == "Hold Value") {
211
3/8
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
2 return new HoldValueOutputProcessor(config);
212
3/4
✓ Branch 0 taken 53 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 35 times.
✓ Branch 3 taken 18 times.
53 } else if (type == "Set Value") {
213
5/8
✓ Branch 0 taken 35 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 34 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 34 times.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
35 return new SetValueOutputProcessor(config);
214
3/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 17 times.
18 } else if (type == "Clamp Value") {
215
3/8
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
1 return new ClampValueOutputProcessor(config);
216
3/4
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 15 times.
17 } else if (type == "Scale Value") {
217
3/8
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
2 return new ScaleValueOutputProcessor(config);
218
3/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 12 times.
15 } else if (type == "Reorder Colors") {
219
3/8
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
3 return new ColorOrderOutputProcessor(config);
220
3/4
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 9 times.
12 } else if (type == "Three to Four") {
221
3/8
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
3 return new ThreeToFourOutputProcessor(config);
222
3/4
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 8 times.
9 } else if (type == "Override Zero") {
223
3/8
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
1 return new OverrideZeroOutputProcessor(config);
224
3/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 1 times.
8 } else if (type == "Fold") {
225
3/8
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 7 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
7 return new FoldOutputProcessor(config);
226 } else {
227
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 LogErr(VB_CHANNELOUT, "Unknown OutputProcessor type: %s\n", type.c_str());
228 }
229
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 112 times.
117 } else {
230 ✗ LogDebug(VB_CHANNELOUT, "Skipping disabled OutputProcessor\n");
231 }
232 1 return nullptr;
233 }
234
235 6 OutputProcessor* OutputProcessors::find(std::function<bool(OutputProcessor*)> f) const {
236
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 std::lock_guard<std::mutex> lock(processorsLock);
237
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 for (OutputProcessor* a : processors) {
238
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 if (f(a)) {
239 4 return a;
240 }
241 }
242 2 return nullptr;
243 6 }
244
245 2147 void OutputProcessors::GetRequiredChannelRanges(const std::function<void(int, int)>& addRange) {
246 // a playlist Remap entry adds and removes processors from other threads
247
1/2
✓ Branch 0 taken 2147 times.
✗ Branch 1 not taken.
2147 std::lock_guard<std::mutex> lock(processorsLock);
248
2/2
✓ Branch 0 taken 103 times.
✓ Branch 1 taken 2147 times.
2250 for (OutputProcessor* a : processors) {
249
1/2
✓ Branch 0 taken 103 times.
✗ Branch 1 not taken.
103 a->GetRequiredChannelRanges(addRange);
250 }
251 2147 }
252
253 118 OutputProcessor::OutputProcessor() :
254 118 description(),
255 118 active(true) {
256 118 }
257
258 96 OutputProcessor::~OutputProcessor() {
259 96 }
260
261 105 void ProcessModelConfig(const Json::Value& config, std::string& model, int& start, int& count) {
262
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 103 times.
105 if (config.isMember("model")) {
263
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 model = config["model"].asString();
264
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (model != "<Use Start Channel>") {
265
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 auto m_model = PixelOverlayManager::INSTANCE.getModelShared(model);
266
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!m_model) {
267 ✗ LogErr(VB_CHANNELOUT, "Invalid Pixel Overlay Model: '%s'\n", model.c_str());
268 } else {
269
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 int m_channel = m_model->getChannelCount();
270
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 LogDebug(VB_CHANNELOUT, "Before Model applied: %d-%d => Model: %s (%d channels)\n",
271 start, start + count - 1,
272 model.c_str(), m_channel);
273
274 // start arrives 1-based within the model; getStartChannel() is the
275 // model's 0-based absolute start, so the span still available from
276 // offset is m_channel - offset + 1 channels.
277 2 int offset = start;
278
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 start = m_model->getStartChannel() + start - 1;
279
280
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (offset + count - 1 > m_channel) {
281 2 count = m_channel - offset + 1;
282
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (count < 0) {
283 ✗ count = 0;
284 }
285
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 LogWarn(VB_CHANNELOUT, "Output processor for Model: %s starting at channel %d would run past the end of the model (%d channels). Restricting to %d channels\n",
286 model.c_str(), offset, m_channel, count);
287 ✗ } else if (offset + count - 1 < m_channel) {
288 ✗ LogInfo(VB_CHANNELOUT, "Output processor for Model: %s is using less channels (%d starting at %d) than overlay model has (%d). This may be intentional\n",
289 model.c_str(), count, offset, m_channel);
290 }
291 }
292 2 } else {
293 ✗ model = "";
294 ✗ --start;
295 }
296 } else {
297 103 model = "";
298 103 --start;
299 }
300 // Processors report start + count * (1, 3 or 4) in an int. A start or a
301 // count in the billions overflowed it into the inverted range that means
302 // "nothing to do", rangeIsValid() let the processor through, and its
303 // ProcessData() then wrote count channels from start: at boot, from
304 // outputprocessors.json, so fppd crash looped. Pin both just outside the
305 // buffer instead, where the range still fails the check, and can't overflow.
306
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 103 times.
105 if (start < 0) {
307 2 start = -1;
308
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 101 times.
103 } else if (start > (int)FPPD_MAX_CHANNELS) {
309 2 start = FPPD_MAX_CHANNELS;
310 }
311
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 103 times.
105 if (count > (int)FPPD_MAX_CHANNELS + 1) {
312 2 count = FPPD_MAX_CHANNELS + 1;
313 }
314 105 }
315