| 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 | #include "fpphttp.h" // drogon/HTTP helpers used here; no longer pulled transitively (see fpphttp_types.h) | ||
| 17 | |||
| 18 | #include <atomic> | ||
| 19 | #include <chrono> | ||
| 20 | #include <cstdlib> | ||
| 21 | #include <ctime> | ||
| 22 | #include <functional> | ||
| 23 | #include <string> | ||
| 24 | #include <vector> | ||
| 25 | |||
| 26 | #include "Events.h" | ||
| 27 | #include "common.h" | ||
| 28 | #include "log.h" | ||
| 29 | #include "commands/Commands.h" | ||
| 30 | |||
| 31 | #include "Player.h" | ||
| 32 | |||
| 33 | // Playlists retired by the main loop are destroyed HERE, on the main thread, | ||
| 34 | // never by whichever thread happened to drop the last snapshot: ~Playlist runs | ||
| 35 | // Cleanup(), which parks every entry on Playlist.cpp's PL_ENTRY_CLEANUPS — an | ||
| 36 | // unsynchronized list that only the main thread drains. So the shared_ptr | ||
| 37 | // deleter does not delete; it parks the raw pointer here, and | ||
| 38 | // Playlist::Process() calls DrainRetiredPlaylists() at exactly the point in the | ||
| 39 | // frame where the old drain ran `delete p`. | ||
| 40 | // | ||
| 41 | // Declared ahead of Player::INSTANCE so it is constructed first and therefore | ||
| 42 | // destroyed last — ~Player drains it during static destruction. | ||
| 43 | static std::mutex s_retiredPlaylistLock; | ||
| 44 | static std::vector<Playlist*> s_retiredPlaylists; | ||
| 45 | |||
| 46 | 496 | static void RetirePlaylist(Playlist* p) { | |
| 47 |
1/2✓ Branch 0 taken 496 times.
✗ Branch 1 not taken.
|
496 | std::lock_guard<std::mutex> lk(s_retiredPlaylistLock); |
| 48 |
1/2✓ Branch 0 taken 496 times.
✗ Branch 1 not taken.
|
496 | s_retiredPlaylists.push_back(p); |
| 49 | 496 | } | |
| 50 | |||
| 51 | Player Player::INSTANCE; | ||
| 52 | |||
| 53 | // A count: a command holds one around StartPlaylist(), whose PlayImpl() holds | ||
| 54 | // its own. | ||
| 55 | static std::atomic<int> s_switchesInFlight{ 0 }; | ||
| 56 | // When the count last left 0. A command's scope also covers the presets and | ||
| 57 | // plugin callbacks its stop and start fire, and a script plugin's callback is | ||
| 58 | // waited for with no timeout: past this long, the switch no longer holds the | ||
| 59 | // scheduler back. | ||
| 60 | static std::atomic<int64_t> s_switchSinceMs{ 0 }; | ||
| 61 | static std::atomic<bool> s_switchOverdueLogged{ false }; | ||
| 62 | static constexpr int64_t MAX_SWITCH_MS = 5000; | ||
| 63 | |||
| 64 | 3999 | static int64_t SteadyMs() { | |
| 65 |
1/2✓ Branch 0 taken 3999 times.
✗ Branch 1 not taken.
|
3999 | return std::chrono::duration_cast<std::chrono::milliseconds>( |
| 66 | 7998 | std::chrono::steady_clock::now().time_since_epoch()) | |
| 67 | 7998 | .count(); | |
| 68 | } | ||
| 69 | |||
| 70 | 2835 | Player::SwitchScope::SwitchScope() { | |
| 71 |
2/2✓ Branch 0 taken 1542 times.
✓ Branch 1 taken 1293 times.
|
2835 | if (s_switchesInFlight++ == 0) { |
| 72 | 1542 | s_switchSinceMs = SteadyMs(); | |
| 73 | 1542 | s_switchOverdueLogged = false; | |
| 74 | } | ||
| 75 | 2835 | } | |
| 76 | |||
| 77 | 2835 | Player::SwitchScope::~SwitchScope() { | |
| 78 | 2835 | s_switchesInFlight--; | |
| 79 | 2835 | } | |
| 80 | |||
| 81 | 5804 | bool Player::Switching() { | |
| 82 |
2/2✓ Branch 0 taken 3347 times.
✓ Branch 1 taken 2457 times.
|
5804 | if (s_switchesInFlight == 0) |
| 83 | 3347 | return false; | |
| 84 | 2457 | int64_t ms = SteadyMs() - s_switchSinceMs; | |
| 85 |
2/2✓ Branch 0 taken 2452 times.
✓ Branch 1 taken 5 times.
|
2457 | if (ms < MAX_SWITCH_MS) |
| 86 | 2452 | return true; | |
| 87 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
|
5 | if (!s_switchOverdueLogged.exchange(true)) |
| 88 | 1 | LogWarn(VB_PLAYLIST, "A playlist switch is still running after %lld ms (a slow PLAYLIST_STOPPED/STARTED preset or plugin callback?); the scheduler carries on\n", (long long)ms); | |
| 89 | 5 | return false; | |
| 90 | } | ||
| 91 | |||
| 92 | 2346 | Player::Player() : | |
| 93 | 2346 | lastCheckTime(std::time(nullptr)), | |
| 94 | 2346 | origStartTime(0), | |
| 95 | 2346 | origStopTime(0), | |
| 96 | 2346 | startTime(0), | |
| 97 | 2346 | stopTime(0), | |
| 98 | 2346 | stopMethod(0), | |
| 99 | 2346 | priority(1000), | |
| 100 | 2346 | allowScheduleOverride(true), | |
| 101 | 4692 | forceStopped(false) { | |
| 102 | 2346 | } | |
| 103 | |||
| 104 | 458 | Player::~Player() { | |
| 105 | // Static destruction: nothing will call Process() again, so drop the owning | ||
| 106 | // references and run the deferred deletes inline. The entries those | ||
| 107 | // destructors park on PL_ENTRY_CLEANUPS are left for the process to | ||
| 108 | // reclaim, exactly as the old `delete playlist` did. | ||
| 109 | 458 | m_playlist.store(nullptr); | |
| 110 | 458 | std::vector<std::shared_ptr<Playlist>> owned; | |
| 111 | { | ||
| 112 | 458 | std::lock_guard<std::mutex> lk(m_ownedPlaylistsLock); | |
| 113 | 458 | owned.swap(m_ownedPlaylists); | |
| 114 | 458 | } | |
| 115 | 458 | owned.clear(); | |
| 116 | 458 | DrainRetiredPlaylists(); | |
| 117 | 458 | } | |
| 118 | |||
| 119 | 2385 | std::shared_ptr<Playlist> Player::CreatePlaylist(std::shared_ptr<Playlist> parent) { | |
| 120 |
4/10✓ Branch 0 taken 2385 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2385 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2385 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 2385 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
|
2385 | std::shared_ptr<Playlist> pl(new Playlist(std::move(parent)), RetirePlaylist); |
| 121 |
1/2✓ Branch 0 taken 2385 times.
✗ Branch 1 not taken.
|
2385 | std::lock_guard<std::mutex> lk(m_ownedPlaylistsLock); |
| 122 |
1/2✓ Branch 0 taken 2385 times.
✗ Branch 1 not taken.
|
2385 | m_ownedPlaylists.push_back(pl); |
| 123 | 2385 | return pl; | |
| 124 | 2385 | } | |
| 125 | |||
| 126 | 2734 | void Player::SetCurrentPlaylist(Playlist* p) { | |
| 127 | 2734 | std::shared_ptr<Playlist> owned; | |
| 128 | { | ||
| 129 |
1/2✓ Branch 0 taken 2734 times.
✗ Branch 1 not taken.
|
2734 | std::lock_guard<std::mutex> lk(m_ownedPlaylistsLock); |
| 130 |
1/2✓ Branch 0 taken 3470 times.
✗ Branch 1 not taken.
|
3470 | for (auto& sp : m_ownedPlaylists) { |
| 131 |
2/2✓ Branch 0 taken 2734 times.
✓ Branch 1 taken 736 times.
|
3470 | if (sp.get() == p) { |
| 132 | 2734 | owned = sp; | |
| 133 | 2734 | break; | |
| 134 | } | ||
| 135 | } | ||
| 136 | 2734 | } | |
| 137 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2734 times.
|
2734 | if (!owned) { |
| 138 | // Invariant: only an instance this player created and has not yet | ||
| 139 | // released can become current. Getting here means a raw Playlist* | ||
| 140 | // outlived its ownership — publishing it (or publishing null) would | ||
| 141 | // hand every reader a dangling or absent playlist, so keep the current | ||
| 142 | // one and make the breakage visible instead. | ||
| 143 | ✗ | LogErr(VB_PLAYLIST, "Player::SetCurrentPlaylist() called with an unowned Playlist %p — keeping the current playlist\n", | |
| 144 | (void*)p); | ||
| 145 | ✗ | return; | |
| 146 | } | ||
| 147 |
1/2✓ Branch 0 taken 2734 times.
✗ Branch 1 not taken.
|
2734 | m_playlist.store(std::move(owned)); |
| 148 |
1/2✓ Branch 0 taken 2734 times.
✗ Branch 1 not taken.
|
2734 | } |
| 149 | |||
| 150 | 497 | void Player::ReleasePlaylist(Playlist* p) { | |
| 151 | // Declared before the lock so the reference is dropped — and the deleter | ||
| 152 | // run, if this was the last one — after m_ownedPlaylistsLock is released. | ||
| 153 | 497 | std::shared_ptr<Playlist> dropped; | |
| 154 | { | ||
| 155 |
1/2✓ Branch 0 taken 497 times.
✗ Branch 1 not taken.
|
497 | std::lock_guard<std::mutex> lk(m_ownedPlaylistsLock); |
| 156 |
1/2✓ Branch 0 taken 1045 times.
✗ Branch 1 not taken.
|
1045 | for (auto it = m_ownedPlaylists.begin(); it != m_ownedPlaylists.end(); ++it) { |
| 157 |
2/2✓ Branch 0 taken 497 times.
✓ Branch 1 taken 548 times.
|
1045 | if (it->get() == p) { |
| 158 | 497 | dropped = std::move(*it); | |
| 159 |
1/2✓ Branch 0 taken 497 times.
✗ Branch 1 not taken.
|
497 | m_ownedPlaylists.erase(it); |
| 160 | 497 | break; | |
| 161 | } | ||
| 162 | } | ||
| 163 | 497 | } | |
| 164 | 497 | } | |
| 165 | |||
| 166 | 446801 | void Player::DrainRetiredPlaylists() { | |
| 167 | while (true) { | ||
| 168 | 446801 | Playlist* p = nullptr; | |
| 169 | { | ||
| 170 |
1/2✓ Branch 0 taken 446801 times.
✗ Branch 1 not taken.
|
446801 | std::lock_guard<std::mutex> lk(s_retiredPlaylistLock); |
| 171 |
2/2✓ Branch 0 taken 446305 times.
✓ Branch 1 taken 496 times.
|
446801 | if (s_retiredPlaylists.empty()) |
| 172 | 892610 | return; | |
| 173 | 496 | p = s_retiredPlaylists.back(); | |
| 174 | 496 | s_retiredPlaylists.pop_back(); | |
| 175 |
2/2✓ Branch 0 taken 496 times.
✓ Branch 1 taken 446305 times.
|
446801 | } |
| 176 | // Deliberately unlocked: ~Playlist runs arbitrary entry destructors. | ||
| 177 |
1/2✓ Branch 0 taken 496 times.
✗ Branch 1 not taken.
|
496 | delete p; |
| 178 | 496 | } | |
| 179 | } | ||
| 180 | |||
| 181 | 1887 | void Player::Init() { | |
| 182 | /* | ||
| 183 | * Start Playlist Callback | ||
| 184 | */ | ||
| 185 | std::function<void(const std::string&, const std::string&)> | ||
| 186 | 1887 | playlist_callback = [](const std::string& topic_in, | |
| 187 | const std::string& payload) { | ||
| 188 | 116 | std::string emptyStr; | |
| 189 |
1/2✓ Branch 0 taken 116 times.
✗ Branch 1 not taken.
|
116 | std::string topic = topic_in; |
| 190 |
1/2✓ Branch 0 taken 116 times.
✗ Branch 1 not taken.
|
116 | topic.replace(0, 14, emptyStr); // Replace until /# |
| 191 | |||
| 192 | 116 | int pos = topic.find("/"); | |
| 193 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 116 times.
|
116 | if (pos == std::string::npos) { |
| 194 | ✗ | LogWarn(VB_PLAYLIST, "Ignoring Invalid playlist topic: playlist/%s\n", | |
| 195 | topic.c_str()); | ||
| 196 | ✗ | return; | |
| 197 | } | ||
| 198 | |||
| 199 |
1/2✓ Branch 0 taken 116 times.
✗ Branch 1 not taken.
|
116 | std::string newPlaylistName = topic.substr(0, pos); |
| 200 |
1/2✓ Branch 0 taken 116 times.
✗ Branch 1 not taken.
|
116 | std::string topicEnd = topic.substr(pos); |
| 201 | |||
| 202 |
3/4✓ Branch 0 taken 116 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 58 times.
✓ Branch 3 taken 58 times.
|
116 | if (topicEnd == "/start") { |
| 203 | 58 | pos = 0; | |
| 204 |
2/2✓ Branch 0 taken 48 times.
✓ Branch 1 taken 10 times.
|
58 | if (!payload.empty()) { |
| 205 | 48 | pos = std::atoi(payload.c_str()); | |
| 206 | } | ||
| 207 | |||
| 208 |
1/2✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
|
58 | LogDebug(VB_CONTROL, "Starting Playlist '%s' with message '%s'\n", |
| 209 | newPlaylistName.c_str(), payload.c_str()); | ||
| 210 | // Stop the running playlist first, as Start Playlist does. | ||
| 211 | // Stopped inside the start instead, its PLAYLIST_STOPPED | ||
| 212 | // preset and plugin "stop" callback are deferred and run | ||
| 213 | // after the new playlist has started, undoing its lead-in. | ||
| 214 |
1/2✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
|
58 | Player::SwitchScope switching; |
| 215 |
3/4✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 36 times.
✓ Branch 3 taken 22 times.
|
58 | if (Player::INSTANCE.GetStatus() != FPP_STATUS_IDLE) { |
| 216 |
1/2✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
|
36 | Player::INSTANCE.StopNow(1); |
| 217 | } | ||
| 218 |
1/2✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
|
58 | Player::INSTANCE.StartPlaylist(newPlaylistName, -1, pos); |
| 219 |
1/2✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
|
58 | LogDebug(VB_CONTROL, "Call to Player::INSTANCE.StartPlaylist complete\n"); |
| 220 | 58 | } else { | |
| 221 | // Runs on the MQTT thread; the snapshot pins the instance for | ||
| 222 | // the call. | ||
| 223 |
1/2✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
|
58 | std::shared_ptr<Playlist> pl = Player::INSTANCE.PlaylistSnapshot(); |
| 224 |
1/2✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
|
58 | if (pl) |
| 225 |
3/6✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 58 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 56 times.
✗ Branch 5 not taken.
|
58 | pl->MQTTHandler(topic, payload); |
| 226 | 56 | }; | |
| 227 | |||
| 228 |
1/2✓ Branch 0 taken 114 times.
✗ Branch 1 not taken.
|
114 | LogDebug(VB_CONTROL, "exit playlist_callback (MQTT)\n"); |
| 229 |
2/4✓ Branch 0 taken 114 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 114 times.
✗ Branch 3 not taken.
|
2001 | }; |
| 230 |
2/4✓ Branch 0 taken 1887 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1887 times.
✗ Branch 3 not taken.
|
1887 | Events::AddCallback("/set/playlist/#", playlist_callback); |
| 231 | |||
| 232 |
3/4✓ Branch 0 taken 1887 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 36 times.
✓ Branch 3 taken 1851 times.
|
1887 | if (Events::HasEventHandlers()) { |
| 233 | // Deprecated topics. Registered here, once, rather than per Playlist | ||
| 234 | // instance: a per-instance callback captured that instance, outlived | ||
| 235 | // it, and was re-added (growing the handler's topic list) by every | ||
| 236 | // inserted playlist. Same snapshot dispatch as /set/playlist/#. | ||
| 237 | std::function<void(const std::string&, const std::string&)> legacy_callback = | ||
| 238 | 36 | [](const std::string& t, const std::string& payload) { | |
| 239 |
1/2✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
|
84 | LogDebug(VB_CONTROL, "Received deprecated MQTT Topic: '%s' \n", t.c_str()); |
| 240 |
1/2✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
|
84 | std::string topic = t; |
| 241 |
1/2✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
|
84 | topic.replace(0, 10, ""); // strip "/playlist/" |
| 242 |
1/2✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
|
84 | std::shared_ptr<Playlist> pl = Player::INSTANCE.PlaylistSnapshot(); |
| 243 |
1/2✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
|
84 | if (pl) |
| 244 |
3/6✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 84 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 84 times.
✗ Branch 5 not taken.
|
84 | pl->MQTTHandler(topic, payload); |
| 245 | 120 | }; | |
| 246 |
2/4✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 36 times.
✗ Branch 3 not taken.
|
72 | Events::AddCallback("/playlist/name/set", legacy_callback); |
| 247 |
2/4✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 36 times.
✗ Branch 3 not taken.
|
72 | Events::AddCallback("/playlist/repeat/set", legacy_callback); |
| 248 |
2/4✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 36 times.
✗ Branch 3 not taken.
|
36 | Events::AddCallback("/playlist/sectionPosition/set", legacy_callback); |
| 249 | 36 | } | |
| 250 | |||
| 251 | // The resident playlist. Created here rather than in the constructor | ||
| 252 | // because Playlist's own constructor reaches for Events, PluginManager and | ||
| 253 | // CommandManager, which are not yet built during static initialization. | ||
| 254 | // From this point on PlaylistSnapshot() never returns null: the resident | ||
| 255 | // instance is only ever replaced (SwitchToInsertedPlaylist, SetIdle's walk | ||
| 256 | // back up the parent chain), never cleared. | ||
| 257 |
1/2✓ Branch 0 taken 1887 times.
✗ Branch 1 not taken.
|
1887 | std::shared_ptr<Playlist> pl = CreatePlaylist(nullptr); |
| 258 |
1/2✓ Branch 0 taken 1887 times.
✗ Branch 1 not taken.
|
1887 | SetCurrentPlaylist(pl.get()); |
| 259 | 1887 | } | |
| 260 | |||
| 261 | 1070 | int Player::StartPlaylist(const std::string& name, const int repeat, | |
| 262 | const int startPosition, const int endPosition, const int manualPriority, | ||
| 263 | const bool allowScheduleOverride) { | ||
| 264 | { | ||
| 265 |
1/2✓ Branch 0 taken 1070 times.
✗ Branch 1 not taken.
|
1070 | std::lock_guard<std::mutex> lk(m_stateLock); |
| 266 |
5/6✓ Branch 0 taken 1070 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 55 times.
✓ Branch 3 taken 1015 times.
✓ Branch 4 taken 1017 times.
✓ Branch 5 taken 53 times.
|
1125 | if ((GetStatus() == FPP_STATUS_IDLE) || |
| 267 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 53 times.
|
55 | (name != playlistName)) { |
| 268 |
1/2✓ Branch 0 taken 1017 times.
✗ Branch 1 not taken.
|
1017 | playlistName = name; |
| 269 | 1017 | startTime = std::time(nullptr); | |
| 270 | 1017 | stopTime = 0; | |
| 271 | 1017 | origStopTime = 0; | |
| 272 | 1017 | stopMethod = 0; | |
| 273 | 1017 | priority = manualPriority; | |
| 274 | 1017 | this->allowScheduleOverride = allowScheduleOverride; | |
| 275 | } | ||
| 276 | |||
| 277 | 1070 | forceStopped = false; | |
| 278 |
1/2✓ Branch 0 taken 1070 times.
✗ Branch 1 not taken.
|
1070 | forceStoppedPlaylist = ""; |
| 279 | 1070 | } | |
| 280 | |||
| 281 |
1/2✓ Branch 0 taken 1070 times.
✗ Branch 1 not taken.
|
1070 | std::shared_ptr<Playlist> pl = PlaylistSnapshot(); |
| 282 |
5/6✓ Branch 0 taken 58 times.
✓ Branch 1 taken 1012 times.
✓ Branch 2 taken 253 times.
✓ Branch 3 taken 817 times.
✓ Branch 4 taken 1070 times.
✗ Branch 5 not taken.
|
1070 | LogDebug(VB_PLAYLIST, "Manually starting %srepeating playlist '%s'\n", |
| 283 | (repeat == -1 ? pl->GetRepeat() : repeat) ? "" : "non-", | ||
| 284 | name.c_str()); | ||
| 285 | |||
| 286 |
1/2✓ Branch 0 taken 1070 times.
✗ Branch 1 not taken.
|
2140 | return pl->Play(name, startPosition, repeat, -1, endPosition); |
| 287 | 1070 | } | |
| 288 | |||
| 289 | 89 | int Player::StartScheduledPlaylist(const std::string& name, const int position, | |
| 290 | const int repeat, const int scheduleEntry, const int scheduledPriority, | ||
| 291 | const time_t sTime, const time_t eTime, const int method) { | ||
| 292 | { | ||
| 293 |
1/2✓ Branch 0 taken 89 times.
✗ Branch 1 not taken.
|
89 | std::lock_guard<std::mutex> lk(m_stateLock); |
| 294 |
1/2✓ Branch 0 taken 89 times.
✗ Branch 1 not taken.
|
89 | playlistName = name; |
| 295 | 89 | origStartTime = sTime; | |
| 296 | 89 | origStopTime = eTime; | |
| 297 | 89 | startTime = std::time(nullptr); | |
| 298 | 89 | stopTime = eTime; | |
| 299 | 89 | stopMethod = method; | |
| 300 | 89 | priority = scheduledPriority; | |
| 301 | 89 | forceStopped = false; | |
| 302 |
1/2✓ Branch 0 taken 89 times.
✗ Branch 1 not taken.
|
89 | forceStoppedPlaylist = ""; |
| 303 | 89 | } | |
| 304 | |||
| 305 |
7/8✓ Branch 0 taken 13 times.
✓ Branch 1 taken 76 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 11 times.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 77 times.
✓ Branch 7 taken 12 times.
|
89 | LogDebug(VB_PLAYLIST, "Starting %srepeating playlist '%s' with scheduled '%s' in %d seconds\n", |
| 306 | repeat ? "" : "non-", | ||
| 307 | name.c_str(), | ||
| 308 | method == 0 ? "Graceful Stop" : method == 1 ? "Hard Stop" | ||
| 309 | : method == 2 ? "Graceful Stop After Loop" | ||
| 310 | : "", | ||
| 311 | (int)(eTime - std::time(nullptr))); | ||
| 312 | |||
| 313 |
2/4✓ Branch 0 taken 89 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 89 times.
✗ Branch 3 not taken.
|
89 | return PlaylistSnapshot()->Play(name, position, repeat, scheduleEntry); |
| 314 | } | ||
| 315 | |||
| 316 | 3 | int Player::AdjustPlaylistStopTime(const int seconds) { | |
| 317 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | if ((GetStatus() != FPP_STATUS_PLAYLIST_PLAYING) && |
| 318 |
2/8✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 3 times.
|
3 | (GetStatus() != FPP_STATUS_STOPPING_GRACEFULLY) && |
| 319 | ✗ | (GetStatus() != FPP_STATUS_STOPPING_GRACEFULLY_AFTER_LOOP)) { | |
| 320 | ✗ | LogInfo(VB_SCHEDULE, "Tried to extend a running playlist, but there is no playlist running.\n"); | |
| 321 | ✗ | return 0; | |
| 322 | } | ||
| 323 | |||
| 324 |
2/4✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
|
3 | if (!WasScheduled()) { |
| 325 | ✗ | LogInfo(VB_SCHEDULE, "Tried to extend running playlist, but it was manually started.\n"); | |
| 326 | ✗ | return 0; | |
| 327 | } | ||
| 328 | |||
| 329 | // UI should catch this, but also check here | ||
| 330 |
2/4✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
|
3 | if ((seconds > (12 * 60 * 60)) || |
| 331 | (seconds < (-3 * 60 * 60))) { | ||
| 332 | ✗ | return 0; | |
| 333 | } | ||
| 334 | |||
| 335 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | std::lock_guard<std::mutex> lk(m_stateLock); |
| 336 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
|
3 | if (seconds >= 0) { |
| 337 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | LogDebug(VB_PLAYLIST, "Extending scheduled playlist '%s' by %d seconds\n", |
| 338 | playlistName.c_str(), seconds); | ||
| 339 | } else { | ||
| 340 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | LogDebug(VB_PLAYLIST, "Shortening scheduled playlist '%s' by %d seconds\n", |
| 341 | playlistName.c_str(), seconds); | ||
| 342 | } | ||
| 343 | |||
| 344 | 3 | stopTime += seconds; | |
| 345 | |||
| 346 | char timeBuf[32]; | ||
| 347 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | LogDebug(VB_PLAYLIST, "New end time is now: %s", ctime_r(&stopTime, timeBuf)); |
| 348 | |||
| 349 | 3 | return 1; | |
| 350 | 3 | } | |
| 351 | |||
| 352 | 9 | void Player::InsertPlaylistAsNext(const std::string& filename, const int startPosition, const int endPos) { | |
| 353 |
2/4✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
|
9 | PlaylistSnapshot()->InsertPlaylistAsNext(filename, startPosition, endPos); |
| 354 | 9 | } | |
| 355 | |||
| 356 | 582 | void Player::InsertPlaylistImmediate(const std::string& filename, const int startPosition, const int endPos) { | |
| 357 |
2/4✓ Branch 0 taken 582 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 582 times.
✗ Branch 3 not taken.
|
582 | PlaylistSnapshot()->InsertPlaylistImmediate(filename, startPosition, endPos); |
| 358 | 582 | } | |
| 359 | |||
| 360 | 812 | void Player::RecordForceStop(int forceStop) { | |
| 361 |
1/2✓ Branch 0 taken 812 times.
✗ Branch 1 not taken.
|
812 | std::lock_guard<std::mutex> lk(m_stateLock); |
| 362 | 812 | forceStopped = (bool)forceStop; | |
| 363 |
2/2✓ Branch 0 taken 802 times.
✓ Branch 1 taken 10 times.
|
812 | if (forceStopped) |
| 364 |
1/2✓ Branch 0 taken 802 times.
✗ Branch 1 not taken.
|
802 | forceStoppedPlaylist = playlistName; |
| 365 | else | ||
| 366 |
1/2✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
|
10 | forceStoppedPlaylist = ""; |
| 367 | 812 | } | |
| 368 | |||
| 369 | 801 | int Player::StopNow(int forceStop) { | |
| 370 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 800 times.
|
801 | if (InHandedBackStoppedPreset()) { |
| 371 | 1 | LogInfo(VB_PLAYLIST, "Ignoring Stop Now from the PLAYLIST_STOPPED preset of an inserted playlist: it has already stopped, and the playlist it interrupted carries on\n"); | |
| 372 | 1 | return 1; | |
| 373 | } | ||
| 374 | 800 | RecordForceStop(forceStop); | |
| 375 |
2/4✓ Branch 0 taken 800 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 798 times.
✗ Branch 3 not taken.
|
800 | return PlaylistSnapshot()->StopNow(forceStop); |
| 376 | } | ||
| 377 | |||
| 378 | 12 | int Player::StopGracefully(int forceStop, int afterCurrentLoop) { | |
| 379 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | if (InHandedBackStoppedPreset()) { |
| 380 | ✗ | LogInfo(VB_PLAYLIST, "Ignoring Stop Gracefully from the PLAYLIST_STOPPED preset of an inserted playlist: it has already stopped, and the playlist it interrupted carries on\n"); | |
| 381 | ✗ | return 1; | |
| 382 | } | ||
| 383 | 12 | RecordForceStop(forceStop); | |
| 384 |
2/4✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
|
12 | return PlaylistSnapshot()->StopGracefully(forceStop, afterCurrentLoop); |
| 385 | } | ||
| 386 | |||
| 387 | 7 | void Player::StopNowAllLevels(int forceStop) { | |
| 388 |
2/4✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
|
7 | int levels = PlaylistSnapshot()->GetLevelCount(); |
| 389 |
5/6✓ Branch 0 taken 7 times.
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 7 times.
✓ Branch 5 taken 7 times.
|
14 | for (int i = 0; i < levels && GetStatus() != FPP_STATUS_IDLE; i++) { |
| 390 | 7 | StopNow(forceStop); | |
| 391 | } | ||
| 392 | 7 | } | |
| 393 | |||
| 394 | 4622 | std::time_t Player::GetOrigStartTime() { | |
| 395 |
1/2✓ Branch 0 taken 4622 times.
✗ Branch 1 not taken.
|
4622 | std::lock_guard<std::mutex> lk(m_stateLock); |
| 396 | 4622 | return origStartTime; | |
| 397 | 4622 | } | |
| 398 | 2454 | std::time_t Player::GetOrigStopTime() { | |
| 399 |
1/2✓ Branch 0 taken 2454 times.
✗ Branch 1 not taken.
|
2454 | std::lock_guard<std::mutex> lk(m_stateLock); |
| 400 | 2454 | return origStopTime; | |
| 401 | 2454 | } | |
| 402 | 2456 | std::time_t Player::GetStartTime() { | |
| 403 |
1/2✓ Branch 0 taken 2456 times.
✗ Branch 1 not taken.
|
2456 | std::lock_guard<std::mutex> lk(m_stateLock); |
| 404 | 2456 | return startTime; | |
| 405 | 2456 | } | |
| 406 | 4582 | std::time_t Player::GetStopTime() { | |
| 407 |
1/2✓ Branch 0 taken 4582 times.
✗ Branch 1 not taken.
|
4582 | std::lock_guard<std::mutex> lk(m_stateLock); |
| 408 | 4582 | return stopTime; | |
| 409 | 4582 | } | |
| 410 | 117 | void Player::ClearForceStopped() { | |
| 411 |
1/2✓ Branch 0 taken 117 times.
✗ Branch 1 not taken.
|
117 | std::lock_guard<std::mutex> lk(m_stateLock); |
| 412 | 117 | forceStopped = false; | |
| 413 | 117 | } | |
| 414 | 858 | bool Player::GetForceStopped() { | |
| 415 |
1/2✓ Branch 0 taken 858 times.
✗ Branch 1 not taken.
|
858 | std::lock_guard<std::mutex> lk(m_stateLock); |
| 416 | 858 | return forceStopped; | |
| 417 | 858 | } | |
| 418 | 40 | std::string Player::GetForceStoppedPlaylist() { | |
| 419 |
1/2✓ Branch 0 taken 40 times.
✗ Branch 1 not taken.
|
40 | std::lock_guard<std::mutex> lk(m_stateLock); |
| 420 |
1/2✓ Branch 0 taken 40 times.
✗ Branch 1 not taken.
|
80 | return forceStoppedPlaylist; |
| 421 | 40 | } | |
| 422 | 2268 | int Player::GetPriority() { | |
| 423 |
1/2✓ Branch 0 taken 2268 times.
✗ Branch 1 not taken.
|
2268 | std::lock_guard<std::mutex> lk(m_stateLock); |
| 424 | 2268 | return priority; | |
| 425 | 2268 | } | |
| 426 | 8 | bool Player::GetAllowScheduleOverride() { | |
| 427 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | std::lock_guard<std::mutex> lk(m_stateLock); |
| 428 | 8 | return allowScheduleOverride; | |
| 429 | 8 | } | |
| 430 | |||
| 431 | 298513 | int Player::Process() { | |
| 432 | 298513 | std::time_t procTime = std::time(nullptr); | |
| 433 |
2/4✓ Branch 0 taken 298513 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 298513 times.
✗ Branch 3 not taken.
|
298513 | PlaylistStatus status = PlaylistSnapshot()->getPlaylistStatus(); |
| 434 | |||
| 435 | // See if we need to stop a scheduled playlist. A paused one counts: it | ||
| 436 | // used to be skipped, so a playlist paused during its window never | ||
| 437 | // stopped, and as the scheduler waits for idle, every later scheduled show | ||
| 438 | // was skipped too. | ||
| 439 | 298513 | bool check = false; | |
| 440 | 298513 | std::time_t stop = 0; | |
| 441 | 298513 | std::time_t origStop = 0; | |
| 442 | 298513 | int method = 0; | |
| 443 | 298513 | std::string name; | |
| 444 | { | ||
| 445 |
1/2✓ Branch 0 taken 298513 times.
✗ Branch 1 not taken.
|
298513 | std::lock_guard<std::mutex> lk(m_stateLock); |
| 446 |
4/4✓ Branch 0 taken 8286 times.
✓ Branch 1 taken 290227 times.
✓ Branch 2 taken 3311 times.
✓ Branch 3 taken 4975 times.
|
298513 | if (((status == FPP_STATUS_PLAYLIST_PLAYING) || (status == FPP_STATUS_PLAYLIST_PAUSED)) && |
| 447 |
2/2✓ Branch 0 taken 31791 times.
✓ Branch 1 taken 261747 times.
|
293538 | (stopTime) && |
| 448 |
2/2✓ Branch 0 taken 350 times.
✓ Branch 1 taken 31441 times.
|
31791 | (lastCheckTime != procTime)) { |
| 449 | 350 | lastCheckTime = procTime; | |
| 450 | 350 | check = true; | |
| 451 | 350 | stop = stopTime; | |
| 452 | 350 | origStop = origStopTime; | |
| 453 | 350 | method = stopMethod; | |
| 454 |
1/2✓ Branch 0 taken 350 times.
✗ Branch 1 not taken.
|
350 | name = playlistName; |
| 455 | } | ||
| 456 | 298513 | } | |
| 457 | |||
| 458 |
2/2✓ Branch 0 taken 350 times.
✓ Branch 1 taken 298163 times.
|
298513 | if (check) { |
| 459 |
2/2✓ Branch 0 taken 341 times.
✓ Branch 1 taken 9 times.
|
350 | if (stop > procTime) { |
| 460 | 341 | bool logSwitch = false; | |
| 461 | 341 | int diff = stop - procTime; | |
| 462 | |||
| 463 | // Print the countdown more frequently as we get closer | ||
| 464 |
6/6✓ Branch 0 taken 140 times.
✓ Branch 1 taken 201 times.
✓ Branch 2 taken 138 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 143 times.
✓ Branch 5 taken 196 times.
|
341 | if (((diff > 300) && ((diff % 300) == 0)) || |
| 465 |
5/6✓ Branch 0 taken 5 times.
✓ Branch 1 taken 138 times.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 254 times.
✓ Branch 5 taken 85 times.
|
339 | ((diff > 60) && (diff <= 300) && ((diff % 60) == 0)) || |
| 466 |
6/6✓ Branch 0 taken 111 times.
✓ Branch 1 taken 143 times.
✓ Branch 2 taken 105 times.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 85 times.
✓ Branch 5 taken 248 times.
|
339 | ((diff > 10) && (diff <= 60) && ((diff % 10) == 0)) || |
| 467 | ((diff <= 10))) { | ||
| 468 | 93 | logSwitch = true; | |
| 469 | } | ||
| 470 | |||
| 471 |
2/2✓ Branch 0 taken 93 times.
✓ Branch 1 taken 248 times.
|
341 | if (logSwitch) { |
| 472 |
8/10✓ Branch 0 taken 9 times.
✓ Branch 1 taken 84 times.
✓ Branch 2 taken 61 times.
✓ Branch 3 taken 32 times.
✓ Branch 4 taken 12 times.
✓ Branch 5 taken 49 times.
✓ Branch 6 taken 12 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 93 times.
✗ Branch 9 not taken.
|
93 | LogDebug(VB_PLAYLIST, "Playlist '%s' will switch to '%s' in %d second%s\n", |
| 473 | name.c_str(), | ||
| 474 | method == 0 ? "Stopping Gracefully" : method == 1 ? "Hard Stop" | ||
| 475 | : method == 2 ? "Stopping Gracefully After Loop" | ||
| 476 | : "", | ||
| 477 | diff, | ||
| 478 | diff == 1 ? "" : "s"); | ||
| 479 | } | ||
| 480 | } | ||
| 481 | |||
| 482 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 341 times.
|
350 | if (stop <= procTime) { |
| 483 | // if user shortened the scheduled end time mark as force stopped | ||
| 484 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 8 times.
|
9 | int forceStop = (stop < origStop) ? 1 : 0; |
| 485 | |||
| 486 | // A paused playlist just stops at the end of its window: a | ||
| 487 | // graceful stop would first resume it, bringing the show back on | ||
| 488 | // after its time to finish the interrupted entry. | ||
| 489 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 8 times.
|
9 | if (status == FPP_STATUS_PLAYLIST_PAUSED) |
| 490 | 1 | method = 1; | |
| 491 | |||
| 492 |
3/3✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 6 times.
|
9 | switch (method) { |
| 493 | 2 | case 0: // Gracefully | |
| 494 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | StopGracefully(forceStop); |
| 495 | 2 | break; | |
| 496 | 1 | case 2: // After Loop | |
| 497 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | StopGracefully(forceStop, 1); |
| 498 | 1 | break; | |
| 499 | 6 | case 1: // Hard Stop | |
| 500 | default: | ||
| 501 |
1/2✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
|
6 | StopNowAllLevels(forceStop); |
| 502 | 6 | break; | |
| 503 | } | ||
| 504 | } | ||
| 505 | } | ||
| 506 | |||
| 507 | // Deliberately a second snapshot rather than one hoisted to the top of the | ||
| 508 | // method: the stop handling above can legitimately change which playlist is | ||
| 509 | // current (a hard stop of an inserted playlist unwinds one level and hands | ||
| 510 | // the player back to the parent), and this tick must process whichever one | ||
| 511 | // is current now — which is what the raw global did. | ||
| 512 |
2/4✓ Branch 0 taken 298513 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 298513 times.
✗ Branch 3 not taken.
|
597026 | return PlaylistSnapshot()->Process(); |
| 513 | 298513 | } | |
| 514 | |||
| 515 | 11389 | void Player::ProcessMedia() { | |
| 516 |
2/4✓ Branch 0 taken 11389 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11389 times.
✗ Branch 3 not taken.
|
11389 | PlaylistSnapshot()->ProcessMedia(); |
| 517 | 11389 | } | |
| 518 | |||
| 519 | 1383000 | int Player::IsPlaying() { | |
| 520 |
2/4✓ Branch 0 taken 1383000 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1383000 times.
✗ Branch 3 not taken.
|
1383000 | return PlaylistSnapshot()->IsPlaying(); |
| 521 | } | ||
| 522 | |||
| 523 | 391060 | std::string Player::GetPlaylistName() { | |
| 524 |
2/4✓ Branch 0 taken 391060 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 391060 times.
✗ Branch 3 not taken.
|
391060 | return PlaylistSnapshot()->GetPlaylistName(); |
| 525 | } | ||
| 526 | |||
| 527 | 1421443 | PlaylistStatus Player::GetStatus() { | |
| 528 |
2/4✓ Branch 0 taken 1421443 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1421443 times.
✗ Branch 3 not taken.
|
1421443 | return PlaylistSnapshot()->getPlaylistStatus(); |
| 529 | } | ||
| 530 | |||
| 531 | 290239 | int Player::GetRepeat() { | |
| 532 |
1/2✓ Branch 0 taken 290239 times.
✗ Branch 1 not taken.
|
290239 | return PlaylistSnapshot()->GetRepeat(); |
| 533 | } | ||
| 534 | |||
| 535 | 3 | void Player::SetRepeat(int repeat) { | |
| 536 |
2/4✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
|
3 | PlaylistSnapshot()->SetRepeat(repeat); |
| 537 | 3 | } | |
| 538 | |||
| 539 | 2454 | int Player::GetStopMethod() { | |
| 540 |
1/2✓ Branch 0 taken 2454 times.
✗ Branch 1 not taken.
|
2454 | std::lock_guard<std::mutex> lk(m_stateLock); |
| 541 | 2454 | return stopMethod; | |
| 542 | 2454 | } | |
| 543 | |||
| 544 | 290232 | int Player::GetPosition() { | |
| 545 |
2/4✓ Branch 0 taken 290232 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 290232 times.
✗ Branch 3 not taken.
|
290232 | return PlaylistSnapshot()->GetPosition(); |
| 546 | } | ||
| 547 | |||
| 548 | 6 | int Player::GetResumePosition() { | |
| 549 |
2/4✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
|
6 | return PlaylistSnapshot()->GetResumePosition(); |
| 550 | } | ||
| 551 | |||
| 552 | 25 | Json::Value Player::GetInfo(void) { | |
| 553 |
2/4✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25 times.
✗ Branch 3 not taken.
|
25 | return PlaylistSnapshot()->GetInfo(); |
| 554 | } | ||
| 555 | |||
| 556 | 2484 | int Player::GetScheduleEntry() { | |
| 557 |
1/2✓ Branch 0 taken 2484 times.
✗ Branch 1 not taken.
|
2484 | return PlaylistSnapshot()->GetScheduleEntry(); |
| 558 | } | ||
| 559 | |||
| 560 | 5 | uint64_t Player::GetFileTime() { | |
| 561 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | return PlaylistSnapshot()->GetFileTime(); |
| 562 | } | ||
| 563 | |||
| 564 | 6 | Json::Value Player::GetConfig() { | |
| 565 |
2/4✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
|
6 | return PlaylistSnapshot()->GetConfig(); |
| 566 | } | ||
| 567 | |||
| 568 | 4 | Json::Value Player::GetMqttStatusJSON() { | |
| 569 |
2/4✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
4 | return PlaylistSnapshot()->GetMqttStatusJSON(); |
| 570 | } | ||
| 571 | |||
| 572 | 490381 | int Player::WasScheduled() { | |
| 573 |
2/4✓ Branch 0 taken 490381 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 490381 times.
✗ Branch 3 not taken.
|
490381 | return PlaylistSnapshot()->WasScheduled(); |
| 574 | } | ||
| 575 | |||
| 576 | ✗ | int Player::FindPosForMS(uint64_t& ms, bool itemDefinedOnly) { | |
| 577 | ✗ | return PlaylistSnapshot()->FindPosForMS(ms, itemDefinedOnly); | |
| 578 | } | ||
| 579 | |||
| 580 | 2 | void Player::GetFilenamesForPos(int pos, std::string& seq, std::string& med) { | |
| 581 |
2/4✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
2 | PlaylistSnapshot()->GetFilenamesForPos(pos, seq, med); |
| 582 | 2 | } | |
| 583 | |||
| 584 | 2 | int Player::Load(const std::string filename) { | |
| 585 |
3/6✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
|
6 | return PlaylistSnapshot()->Load(filename.c_str()); |
| 586 | } | ||
| 587 | |||
| 588 | ✗ | int Player::Start() { | |
| 589 | ✗ | return PlaylistSnapshot()->Start(); | |
| 590 | } | ||
| 591 | |||
| 592 | 9050 | void Player::RestartItem() { | |
| 593 |
2/4✓ Branch 0 taken 9050 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9050 times.
✗ Branch 3 not taken.
|
9050 | PlaylistSnapshot()->RestartItem(); |
| 594 | 9050 | } | |
| 595 | |||
| 596 | 9112 | void Player::NextItem() { | |
| 597 |
2/4✓ Branch 0 taken 9112 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9112 times.
✗ Branch 3 not taken.
|
9112 | PlaylistSnapshot()->NextItem(); |
| 598 | 9112 | } | |
| 599 | |||
| 600 | 9080 | void Player::PrevItem() { | |
| 601 |
2/4✓ Branch 0 taken 9080 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9080 times.
✗ Branch 3 not taken.
|
9080 | PlaylistSnapshot()->PrevItem(); |
| 602 | 9080 | } | |
| 603 | |||
| 604 | 15 | void Player::Pause() { | |
| 605 |
2/4✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
|
15 | PlaylistSnapshot()->Pause(); |
| 606 | 15 | } | |
| 607 | |||
| 608 | 13 | void Player::Resume() { | |
| 609 |
2/4✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 13 times.
✗ Branch 3 not taken.
|
13 | PlaylistSnapshot()->Resume(); |
| 610 | 13 | } | |
| 611 | |||
| 612 | 560 | int Player::Cleanup(void) { | |
| 613 | // The main loop calls this after an unlocked "now idle" read. A start from | ||
| 614 | // another thread can land between that read and here, so only clean up a | ||
| 615 | // playlist that is still idle under its lock; SetIdle() has already run | ||
| 616 | // Cleanup() on every transition to idle. | ||
| 617 |
2/4✓ Branch 0 taken 560 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 560 times.
✗ Branch 3 not taken.
|
560 | return PlaylistSnapshot()->CleanupIfIdle(); |
| 618 | } | ||
| 619 | |||
| 620 | 238337 | void Player::GetCurrentStatus(Json::Value& result) { | |
| 621 |
2/4✓ Branch 0 taken 238337 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 238337 times.
✗ Branch 3 not taken.
|
238337 | PlaylistSnapshot()->GetCurrentStatus(result); |
| 622 | 238337 | } | |
| 623 | |||
| 624 | 25358 | Json::Value Player::GetStatusJSON() { | |
| 625 |
1/2✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
|
25358 | Json::Value result; |
| 626 |
1/2✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
|
25358 | Json::Value playlists(Json::arrayValue); |
| 627 |
1/2✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
|
25358 | Json::Value pl; |
| 628 | |||
| 629 | // One snapshot for the whole object: six separate loads could straddle a | ||
| 630 | // playlist switch and report a mix of two playlists in one status blob. | ||
| 631 |
1/2✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
|
25358 | std::shared_ptr<Playlist> cur = PlaylistSnapshot(); |
| 632 |
1/2✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
|
25358 | pl = cur->GetInfo(); |
| 633 |
2/4✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25358 times.
✗ Branch 3 not taken.
|
25358 | pl["details"] = cur->GetConfig(); |
| 634 |
3/6✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25358 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 25358 times.
✗ Branch 5 not taken.
|
25358 | pl["status"] = (int)cur->getPlaylistStatus(); |
| 635 |
3/6✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25358 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 25358 times.
✗ Branch 5 not taken.
|
25358 | pl["scheduled"] = cur->WasScheduled() ? true : false; |
| 636 |
3/6✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25358 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 25358 times.
✗ Branch 5 not taken.
|
25358 | pl["position"] = cur->GetPosition(); |
| 637 |
2/4✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25358 times.
✗ Branch 3 not taken.
|
25358 | pl["lastModified"] = (Json::UInt64)cur->GetFileTime(); |
| 638 | |||
| 639 | // things we store locally that would need to be in an array if we can | ||
| 640 | // play multiple playlists concurrently | ||
| 641 |
1/2✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
|
25358 | std::lock_guard<std::mutex> lk(m_stateLock); |
| 642 |
2/4✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25358 times.
✗ Branch 3 not taken.
|
25358 | pl["origStartTime"] = (Json::UInt64)origStartTime; |
| 643 |
2/4✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25358 times.
✗ Branch 3 not taken.
|
25358 | pl["origStopTime"] = (Json::UInt64)origStopTime; |
| 644 |
2/4✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25358 times.
✗ Branch 3 not taken.
|
25358 | pl["startTime"] = (Json::UInt64)startTime; |
| 645 |
2/4✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25358 times.
✗ Branch 3 not taken.
|
25358 | pl["stopTime"] = (Json::UInt64)stopTime; |
| 646 |
2/4✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25358 times.
✗ Branch 3 not taken.
|
25358 | pl["stopMethod"] = stopMethod; |
| 647 |
2/4✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25358 times.
✗ Branch 3 not taken.
|
25358 | pl["priority"] = priority; |
| 648 | |||
| 649 |
1/2✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
|
25358 | playlists.append(pl); |
| 650 | |||
| 651 |
2/4✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25358 times.
✗ Branch 3 not taken.
|
25358 | result["playlists"] = playlists; |
| 652 | |||
| 653 | 25358 | return result; | |
| 654 | 25358 | } | |
| 655 | |||
| 656 | // -------------------------------------------------------------------------- | ||
| 657 | // OpenAPI docs for the /player/* endpoints handled below. | ||
| 658 | // -------------------------------------------------------------------------- | ||
| 659 | |||
| 660 | /** | ||
| 661 | * Get the player status. Equivalent to /api/player/status. | ||
| 662 | * | ||
| 663 | * @route GET /api/player | ||
| 664 | * @response 200 Player status JSON. | ||
| 665 | */ | ||
| 666 | |||
| 667 | /** | ||
| 668 | * Get the player status (playlist, sequence, timing, mode, etc.). | ||
| 669 | * | ||
| 670 | * @route GET /api/player/status | ||
| 671 | * @response 200 Player status JSON. | ||
| 672 | */ | ||
| 673 | |||
| 674 | /** | ||
| 675 | * Get information about the currently playing playlist. | ||
| 676 | * | ||
| 677 | * @route GET /api/player/current | ||
| 678 | * @response 200 Object with a `playlist` member describing the current playlist. | ||
| 679 | */ | ||
| 680 | 39566 | HttpResponsePtr Player::render_GET(const HttpRequestPtr& req) { | |
| 681 |
2/4✓ Branch 0 taken 39566 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 39566 times.
✗ Branch 3 not taken.
|
39566 | auto pieces = getPathPieces(req->path()); |
| 682 | 39566 | int plen = pieces.size(); | |
| 683 | |||
| 684 |
1/2✓ Branch 0 taken 39566 times.
✗ Branch 1 not taken.
|
39566 | Json::Value result; |
| 685 | |||
| 686 |
8/10✓ Branch 0 taken 39561 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 39561 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 39561 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 25353 times.
✓ Branch 7 taken 14208 times.
✓ Branch 8 taken 25358 times.
✓ Branch 9 taken 14208 times.
|
39566 | if ((plen == 1) || ((plen == 2) && (pieces[1] == "status"))) { |
| 687 |
1/2✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
|
25358 | result = GetStatusJSON(); |
| 688 |
4/8✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25358 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 25358 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 25358 times.
✗ Branch 7 not taken.
|
101432 | return makeStringResponse(SaveJsonToString(result, " "), 200, "application/json"); |
| 689 |
4/8✓ Branch 0 taken 14208 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14208 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 14208 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 14208 times.
✗ Branch 7 not taken.
|
14208 | } else if ((plen == 2) && (pieces[1] == "current")) { |
| 690 |
1/2✓ Branch 0 taken 14208 times.
✗ Branch 1 not taken.
|
14208 | Json::Value result; |
| 691 |
1/2✓ Branch 0 taken 14208 times.
✗ Branch 1 not taken.
|
14208 | Json::Value pl; |
| 692 |
2/4✓ Branch 0 taken 14208 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14208 times.
✗ Branch 3 not taken.
|
14208 | pl = PlaylistSnapshot()->GetInfo(); |
| 693 |
2/4✓ Branch 0 taken 14208 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14208 times.
✗ Branch 3 not taken.
|
14208 | result["playlist"] = pl; |
| 694 |
4/8✓ Branch 0 taken 14208 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14208 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 14208 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 14208 times.
✗ Branch 7 not taken.
|
42624 | return makeStringResponse(SaveJsonToString(result, " "), 200, "application/json"); |
| 695 | 14208 | } | |
| 696 | |||
| 697 | ✗ | return makeStringResponse("Not Found", 404, "text/plain"); | |
| 698 | 39566 | } | |
| 699 | |||
| 700 | ✗ | HttpResponsePtr Player::render_POST(const HttpRequestPtr& req) { | |
| 701 | ✗ | auto pieces = getPathPieces(req->path()); | |
| 702 | ✗ | int plen = pieces.size(); | |
| 703 | |||
| 704 | ✗ | return makeStringResponse("Not Found", 404, "text/plain"); | |
| 705 | ✗ | } | |
| 706 | |||
| 707 | ✗ | HttpResponsePtr Player::render_PUT(const HttpRequestPtr& req) { | |
| 708 | ✗ | auto pieces = getPathPieces(req->path()); | |
| 709 | ✗ | int plen = pieces.size(); | |
| 710 | |||
| 711 | ✗ | return makeStringResponse("Not Found", 404, "text/plain"); | |
| 712 | ✗ | } | |
| 713 |