changed: upgraded libhts (tvheadend message library) to latest version
[xbmc:xbmc-antiquated.git] / XBMC / xbmc / cores / dvdplayer / DVDInputStreams / DVDInputStreamHTSP.cpp
1 /*
2  *      Copyright (C) 2005-2008 Team XBMC
3  *      http://www.xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21
22 #include "stdafx.h"
23 #include "DVDInputStreamHTSP.h"
24 #include "URL.h"
25 #include "utils/log.h"
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <netinet/in.h>
29 #include <netinet/tcp.h>
30
31 extern "C" {
32 #include "lib/libhts/net.h"
33 #include "lib/libhts/htsmsg.h"
34 #include "lib/libhts/htsmsg_binary.h"
35 }
36
37 htsmsg_t* CDVDInputStreamHTSP::ReadMessage()
38 {
39   void*    buf;
40   uint32_t l;
41
42   if(htsp_tcp_read_timeout(m_fd, &l, 4, 10000))
43   {
44     printf("Failed to read packet size\n");
45     return NULL;
46   }
47
48   l   = ntohl(l);
49   buf = malloc(l);
50
51   if(htsp_tcp_read_timeout(m_fd, buf, l, 10000))
52   {
53     printf("Failed to read packet\n");
54     free(buf);
55     return NULL;
56   }
57
58   return htsmsg_binary_deserialize(buf, l, buf); /* consumes 'buf' */
59 }
60
61 bool CDVDInputStreamHTSP::SendMessage(htsmsg_t* m)
62 {
63   void*  buf;
64   size_t len;
65
66   if(htsmsg_binary_serialize(m, &buf, &len, -1) < 0)
67   {
68     htsmsg_destroy(m);
69     return false;
70   }
71   htsmsg_destroy(m);
72
73   if(send(m_fd, buf, len, 0) < 0)
74   {
75     free(buf);
76     return false;
77   }
78   free(buf);
79   return true;
80 }
81
82 htsmsg_t* CDVDInputStreamHTSP::ReadResult(htsmsg_t* m, bool sequence)
83 {
84   if(sequence)
85     htsmsg_add_u32(m, "seq", ++m_seq);
86
87   if(!SendMessage(m))
88     return NULL;
89
90   while((m = ReadMessage()))
91   {
92     uint32_t seq;
93     if(!sequence)
94       break;
95     if(!htsmsg_get_u32(m, "seq", &seq) && seq == m_seq)
96       break;
97
98     CLog::Log(LOGERROR, "CDVDInputStreamHTSP::ReadResult - discarded message with invalid sequence number");
99     htsmsg_print(m);
100     htsmsg_destroy(m);
101   }
102
103   const char* error;
104   if(m && (error = htsmsg_get_str(m, "error")))
105   {
106     CLog::Log(LOGERROR, "CDVDInputStreamHTSP::ReadResult - error (%s)", error);
107     htsmsg_destroy(m);
108     return NULL;
109   }
110
111   return m;
112 }
113
114 bool CDVDInputStreamHTSP::ReadSuccess(htsmsg_t* m, bool sequence, std::string action)
115 {
116   if((m = ReadResult(m, sequence)) == NULL)
117   {
118     CLog::Log(LOGDEBUG, "CDVDInputStreamHTSP::ReadSuccess - failed to %s", action.c_str());
119     return false;
120   }
121   htsmsg_destroy(m);
122   return true;
123 }
124
125 htsmsg_t* CDVDInputStreamHTSP::ReadStream()
126 {
127   htsmsg_t* msg;
128
129   while((msg = ReadMessage()))
130   {
131     uint32_t subs;
132     if(htsmsg_get_u32(msg, "subscriptionId", &subs) || subs != m_subs)
133     {
134       htsmsg_destroy(msg);
135       continue;
136     }
137     m_startup = false;
138     return msg;
139   }
140   return NULL;
141 }
142
143 CDVDInputStreamHTSP::CDVDInputStreamHTSP() 
144   : CDVDInputStream(DVDSTREAM_TYPE_HTSP)
145   , m_fd(INVALID_SOCKET)
146   , m_seq(0)
147   , m_subs(0)
148   , m_channel(0)
149   , m_startup(false)
150 {
151 }
152
153 CDVDInputStreamHTSP::~CDVDInputStreamHTSP()
154 {
155 }
156
157 bool CDVDInputStreamHTSP::Open(const char* file, const std::string& content)
158 {
159   if (!CDVDInputStream::Open(file, content)) 
160     return false;
161
162   CURL url(file);
163
164   char errbuf[1024];
165   int  errlen = sizeof(errbuf);
166   htsmsg_t *m;
167   const char *method, *server, *version;
168   int32_t proto = 0;
169
170   if(url.GetPort() == 0)
171     url.SetPort(9982);
172
173   if(sscanf(url.GetFileName().c_str(), "channels/%d", &m_channel) != 1)
174   {
175     CLog::Log(LOGERROR, "CDVDInputStreamHTSP::Open - invalid url (%s)\n", url.GetFileName().c_str());
176     return false;
177   }
178
179   m_fd = htsp_tcp_connect(url.GetHostName().c_str()
180                         , url.GetPort()
181                         , errbuf, errlen, 3000);
182   if(m_fd == INVALID_SOCKET)
183   {
184     CLog::Log(LOGERROR, "CDVDInputStreamHTSP::Open - failed to connect to server (%s)\n", errbuf);
185     return false;
186   }
187
188   // read welcome
189   if((m = ReadMessage()) == NULL)
190   {
191     CLog::Log(LOGERROR, "CDVDInputStreamHTSP::Open - failed to read greeting from server");
192     return false;
193   }
194   method  = htsmsg_get_str(m, "method");
195             htsmsg_get_s32(m, "htspversion", &proto);
196   server  = htsmsg_get_str(m, "servername");
197   version = htsmsg_get_str(m, "serverversion");
198
199   CLog::Log(LOGDEBUG, "CDVDInputStreamHTSP::Open - connected to server: [%s], version: [%s], proto: %d"
200                     , server ? server : "", version ? version : "", proto);
201
202   htsmsg_destroy(m);
203
204   m = htsmsg_create_map();
205   htsmsg_add_str(m, "method"        , "login");
206   htsmsg_add_s32(m, "htspversion"   , proto);
207
208   if(!ReadSuccess(m, false, "get reply from authentication with server"))
209     return false;
210
211   if(!SendSubscribe(m_subs, m_channel))
212     return false;
213
214   m_startup = true;
215   return true;
216 }
217
218 bool CDVDInputStreamHTSP::IsEOF()
219 {
220   return false;
221 }
222
223 void CDVDInputStreamHTSP::Close()
224 {
225   CDVDInputStream::Close();
226   if(m_fd != INVALID_SOCKET)
227     closesocket(m_fd);
228   m_fd = INVALID_SOCKET;
229 }
230
231 int CDVDInputStreamHTSP::Read(BYTE* buf, int buf_size)
232 {
233   return -1;
234 }
235
236 bool CDVDInputStreamHTSP::SendSubscribe(int subscription, int channel)
237 {
238   htsmsg_t *m = htsmsg_create_map();
239   htsmsg_add_str(m, "method"        , "subscribe");
240   htsmsg_add_s32(m, "channelId"     , channel);
241   htsmsg_add_s32(m, "subscriptionId", subscription);
242   return ReadSuccess(m, true, "subscribe to channel");
243 }
244
245 bool CDVDInputStreamHTSP::SendUnsubscribe(int subscription)
246 {
247   htsmsg_t *m = htsmsg_create_map();
248   htsmsg_add_str(m, "method"        , "unsubscribe");
249   htsmsg_add_s32(m, "subscriptionId", subscription);
250   return ReadSuccess(m, true, "unsubscribe from channel");
251 }
252
253 bool CDVDInputStreamHTSP::SetChannel(int channel)
254 {
255
256   if(!SendUnsubscribe(m_subs))
257     return false;
258
259   if(!SendSubscribe(m_subs+1, channel))
260   {
261     CLog::Log(LOGERROR, "CDVDInputStreamHTSP::SetChannel - failed to set channel, trying to restore...");
262     SendSubscribe(m_subs, m_channel);
263     return false;
264   }
265   m_channel = channel;
266   m_subs    = m_subs+1;
267   m_startup = true;
268   return true;
269 }
270
271 bool CDVDInputStreamHTSP::NextChannel()
272 {
273   return SetChannel(m_channel + 1);
274 }
275
276 bool CDVDInputStreamHTSP::PrevChannel()
277 {
278   return SetChannel(m_channel - 1);
279 }