1 /*
2 * Copyright (C) 2005-2021 Team Kodi (https://kodi.tv)
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 * See LICENSE.md for more information.
6 */
7
8 #include "Subscription.h"
9
10 #include "HTSPConnection.h"
11 #include "utilities/Logger.h"
12
13 #include "kodi/General.h"
14
15 #include <cstring>
16
17 using namespace tvheadend;
18 using namespace tvheadend::utilities;
19
Subscription(HTSPConnection & conn)20 Subscription::Subscription(HTSPConnection& conn)
21 : m_id(0),
22 m_channelId(0),
23 m_weight(SUBSCRIPTION_WEIGHT_NORMAL),
24 m_speed(1000),
25 m_state(SUBSCRIPTION_STOPPED),
26 m_conn(conn)
27 {
28 }
29
IsActive() const30 bool Subscription::IsActive() const
31 {
32 std::lock_guard<std::recursive_mutex> lock(m_mutex);
33 return (GetState() != SUBSCRIPTION_STOPPED);
34 }
35
GetId() const36 uint32_t Subscription::GetId() const
37 {
38 std::lock_guard<std::recursive_mutex> lock(m_mutex);
39 return m_id;
40 }
41
SetId(uint32_t id)42 void Subscription::SetId(uint32_t id)
43 {
44 std::lock_guard<std::recursive_mutex> lock(m_mutex);
45 m_id = id;
46 }
47
GetChannelId() const48 uint32_t Subscription::GetChannelId() const
49 {
50 std::lock_guard<std::recursive_mutex> lock(m_mutex);
51 return m_channelId;
52 }
53
SetChannelId(uint32_t id)54 void Subscription::SetChannelId(uint32_t id)
55 {
56 std::lock_guard<std::recursive_mutex> lock(m_mutex);
57 m_channelId = id;
58 }
59
GetWeight() const60 uint32_t Subscription::GetWeight() const
61 {
62 std::lock_guard<std::recursive_mutex> lock(m_mutex);
63 return m_weight;
64 }
65
SetWeight(uint32_t weight)66 void Subscription::SetWeight(uint32_t weight)
67 {
68 std::lock_guard<std::recursive_mutex> lock(m_mutex);
69 m_weight = weight;
70 }
71
GetSpeed() const72 int32_t Subscription::GetSpeed() const
73 {
74 std::lock_guard<std::recursive_mutex> lock(m_mutex);
75 return m_speed;
76 }
77
SetSpeed(int32_t speed)78 void Subscription::SetSpeed(int32_t speed)
79 {
80 std::lock_guard<std::recursive_mutex> lock(m_mutex);
81 m_speed = speed;
82 }
83
GetState() const84 eSubsriptionState Subscription::GetState() const
85 {
86 std::lock_guard<std::recursive_mutex> lock(m_mutex);
87 return m_state;
88 }
89
SetState(eSubsriptionState state)90 void Subscription::SetState(eSubsriptionState state)
91 {
92 std::lock_guard<std::recursive_mutex> lock(m_mutex);
93 m_state = state;
94 }
95
GetProfile() const96 std::string Subscription::GetProfile() const
97 {
98 std::lock_guard<std::recursive_mutex> lock(m_mutex);
99 return m_profile;
100 }
101
SetProfile(const std::string & profile)102 void Subscription::SetProfile(const std::string& profile)
103 {
104 std::lock_guard<std::recursive_mutex> lock(m_mutex);
105 m_profile = profile;
106 }
107
SendSubscribe(std::unique_lock<std::recursive_mutex> & lock,uint32_t channelId,uint32_t weight,bool restart)108 void Subscription::SendSubscribe(std::unique_lock<std::recursive_mutex>& lock,
109 uint32_t channelId,
110 uint32_t weight,
111 bool restart)
112 {
113 /* We don't want to change anything when restarting a subscription */
114 if (!restart)
115 {
116 SetChannelId(channelId);
117 SetWeight(weight);
118 SetId(GetNextId());
119 SetSpeed(1000); //set back to normal
120 }
121
122 /* Build message */
123 htsmsg_t* m = htsmsg_create_map();
124 htsmsg_add_s32(m, "channelId", GetChannelId());
125 htsmsg_add_u32(m, "subscriptionId", GetId());
126 htsmsg_add_u32(m, "weight", GetWeight());
127 htsmsg_add_u32(m, "timeshiftPeriod", static_cast<uint32_t>(~0));
128 htsmsg_add_u32(m, "normts", 1);
129 htsmsg_add_u32(m, "queueDepth", PACKET_QUEUE_DEPTH);
130
131 /* Use the specified profile if it has been set */
132 if (!GetProfile().empty())
133 htsmsg_add_str(m, "profile", GetProfile().c_str());
134
135 Logger::Log(LogLevel::LEVEL_DEBUG, "demux subscribe to %d", GetChannelId());
136
137 /* Send and Wait for response */
138 if (restart)
139 m = m_conn.SendAndWait0(lock, "subscribe", m);
140 else
141 m = m_conn.SendAndWait(lock, "subscribe", m);
142 if (!m)
143 return;
144
145 htsmsg_destroy(m);
146
147 SetState(SUBSCRIPTION_STARTING);
148 Logger::Log(LogLevel::LEVEL_DEBUG,
149 "demux successfully subscribed to channel id %d, subscription id %d", GetChannelId(),
150 GetId());
151 }
152
SendUnsubscribe(std::unique_lock<std::recursive_mutex> & lock)153 void Subscription::SendUnsubscribe(std::unique_lock<std::recursive_mutex>& lock)
154 {
155 /* Build message */
156 htsmsg_t* m = htsmsg_create_map();
157 htsmsg_add_u32(m, "subscriptionId", GetId());
158 Logger::Log(LogLevel::LEVEL_DEBUG, "demux unsubscribe from %d", GetChannelId());
159
160 /* Mark subscription as inactive immediately in case this command fails */
161 SetState(SUBSCRIPTION_STOPPED);
162
163 /* Send and Wait */
164 m = m_conn.SendAndWait(lock, "unsubscribe", m);
165 if (!m)
166 return;
167
168 htsmsg_destroy(m);
169 Logger::Log(LogLevel::LEVEL_DEBUG,
170 "demux successfully unsubscribed from channel id %d, subscription id %d",
171 GetChannelId(), GetId());
172 }
173
SendSeek(std::unique_lock<std::recursive_mutex> & lock,double time)174 bool Subscription::SendSeek(std::unique_lock<std::recursive_mutex>& lock, double time)
175 {
176 /* Build message */
177 htsmsg_t* m = htsmsg_create_map();
178 htsmsg_add_u32(m, "subscriptionId", GetId());
179 htsmsg_add_s64(m, "time", static_cast<int64_t>(time * 1000LL));
180 htsmsg_add_u32(m, "absolute", 1);
181 Logger::Log(LogLevel::LEVEL_DEBUG, "demux send seek %d", time);
182
183 /* Send and Wait */
184 m = m_conn.SendAndWait(lock, "subscriptionSeek", m);
185 if (!m)
186 return false;
187
188 htsmsg_destroy(m);
189 return true;
190 }
191
SendSpeed(std::unique_lock<std::recursive_mutex> & lock,int32_t speed,bool restart)192 void Subscription::SendSpeed(std::unique_lock<std::recursive_mutex>& lock,
193 int32_t speed,
194 bool restart)
195 {
196 /* We don't want to change the speed when restarting a subscription */
197 if (!restart)
198 SetSpeed(speed);
199
200 /* Build message */
201 htsmsg_t* m = htsmsg_create_map();
202 htsmsg_add_u32(m, "subscriptionId", GetId());
203 htsmsg_add_s32(m, "speed",
204 GetSpeed() / 10); // Kodi uses values an order of magnitude larger than tvheadend
205 Logger::Log(LogLevel::LEVEL_DEBUG, "demux send speed %d", GetSpeed() / 10);
206
207 if (restart)
208 m = m_conn.SendAndWait0(lock, "subscriptionSpeed", m);
209 else
210 m = m_conn.SendAndWait(lock, "subscriptionSpeed", m);
211
212 if (m)
213 htsmsg_destroy(m);
214 }
215
SendWeight(std::unique_lock<std::recursive_mutex> & lock,uint32_t weight)216 void Subscription::SendWeight(std::unique_lock<std::recursive_mutex>& lock, uint32_t weight)
217 {
218 SetWeight(weight);
219
220 /* Build message */
221 htsmsg_t* m = htsmsg_create_map();
222 htsmsg_add_u32(m, "subscriptionId", GetId());
223 htsmsg_add_s32(m, "weight", GetWeight());
224 Logger::Log(LogLevel::LEVEL_DEBUG, "demux send weight %u", GetWeight());
225
226 /* Send and Wait */
227 m = m_conn.SendAndWait(lock, "subscriptionChangeWeight", m);
228 if (m)
229 htsmsg_destroy(m);
230 }
231
ParseSubscriptionStatus(htsmsg_t * m)232 void Subscription::ParseSubscriptionStatus(htsmsg_t* m)
233 {
234 /* Not for preTuning and postTuning subscriptions */
235 if (GetWeight() == static_cast<uint32_t>(SUBSCRIPTION_WEIGHT_PRETUNING) ||
236 GetWeight() == static_cast<uint32_t>(SUBSCRIPTION_WEIGHT_POSTTUNING))
237 {
238 SetState(SUBSCRIPTION_PREPOSTTUNING);
239 return;
240 }
241
242 const char* status = htsmsg_get_str(m, "status");
243
244 /* 'subscriptionErrors' was added in htsp v20, use 'status' for older backends */
245 if (m_conn.GetProtocol() >= 20)
246 {
247 const char* error = htsmsg_get_str(m, "subscriptionError");
248
249 /* This field is absent when everything is fine */
250 if (error)
251 {
252 if (!std::strcmp("badSignal", error))
253 SetState(SUBSCRIPTION_NOSIGNAL);
254 else if (!std::strcmp("scrambled", error))
255 SetState(SUBSCRIPTION_SCRAMBLED);
256 else if (!std::strcmp("userLimit", error))
257 SetState(SUBSCRIPTION_USERLIMIT);
258 else if (!std::strcmp("noFreeAdapter", error))
259 SetState(SUBSCRIPTION_NOFREEADAPTER);
260 else if (!std::strcmp("tuningFailed", error))
261 SetState(SUBSCRIPTION_TUNINGFAILED);
262 else if (!std::strcmp("userAccess", error))
263 SetState(SUBSCRIPTION_NOACCESS);
264 else
265 SetState(SUBSCRIPTION_UNKNOWN);
266
267 /* Show an OSD message */
268 ShowStateNotification();
269 }
270 else
271 SetState(SUBSCRIPTION_RUNNING);
272 }
273 else
274 {
275 /* This field is absent when everything is fine */
276 if (status)
277 {
278 SetState(SUBSCRIPTION_UNKNOWN);
279
280 /* Show an OSD message */
281 kodi::QueueNotification(QUEUE_INFO, "", status);
282 }
283 else
284 SetState(SUBSCRIPTION_RUNNING);
285 }
286 }
287
ShowStateNotification()288 void Subscription::ShowStateNotification()
289 {
290 if (GetState() == SUBSCRIPTION_NOFREEADAPTER)
291 kodi::QueueNotification(QUEUE_WARNING, "", kodi::GetLocalizedString(30450));
292 else if (GetState() == SUBSCRIPTION_SCRAMBLED)
293 kodi::QueueNotification(QUEUE_WARNING, "", kodi::GetLocalizedString(30451));
294 else if (GetState() == SUBSCRIPTION_NOSIGNAL)
295 kodi::QueueNotification(QUEUE_WARNING, "", kodi::GetLocalizedString(30452));
296 else if (GetState() == SUBSCRIPTION_TUNINGFAILED)
297 kodi::QueueNotification(QUEUE_WARNING, "", kodi::GetLocalizedString(30453));
298 else if (GetState() == SUBSCRIPTION_USERLIMIT)
299 kodi::QueueNotification(QUEUE_WARNING, "", kodi::GetLocalizedString(30454));
300 else if (GetState() == SUBSCRIPTION_NOACCESS)
301 kodi::QueueNotification(QUEUE_WARNING, "", kodi::GetLocalizedString(30455));
302 else if (GetState() == SUBSCRIPTION_UNKNOWN)
303 kodi::QueueNotification(QUEUE_WARNING, "", kodi::GetLocalizedString(30456));
304 }
305
GetNextId()306 uint32_t Subscription::GetNextId()
307 {
308 static uint32_t id = 0;
309 return ++id;
310 }
311