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