]> git.vomp.tv Git - vompserver.git/blob - vompclient.c
Vogel Media Player 2008-11-28
[vompserver.git] / vompclient.c
1 /*
2     Copyright 2004-2008 Chris Tallon
3
4     This file is part of VOMP.
5
6     VOMP is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     VOMP is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with VOMP; if not, write to the Free Software
18     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 */
20
21 #include "mediaplayer.h"
22 #include "media.h"
23 #include "servermediafile.h"
24 #include "vdrcommand.h"
25 #include "vompclient.h"
26
27 #include "responsepacket.h"
28
29 #ifndef VOMPSTANDALONE
30 #include <vdr/channels.h>
31 #include <vdr/recording.h>
32 #include "recplayer.h"
33 #include "mvpreceiver.h"
34 #endif
35
36
37
38 pthread_mutex_t threadClientMutex;
39 int VompClient::nr_clients = 0;
40
41
42 VompClient::VompClient(Config* cfgBase, char* tconfigDir, int tsocket)
43  : rrproc(*this), tcp(tsocket), i18n(tconfigDir)
44 {
45 #ifndef VOMPSTANDALONE
46   lp = NULL;
47   recplayer = NULL;
48   recordingManager = NULL;
49 #endif
50   log = Log::getInstance();
51   loggedIn = false;
52   configDir = tconfigDir;
53   log->log("Client", Log::DEBUG, "Config dir: %s", configDir);
54   baseConfig = cfgBase;
55   incClients();
56   media=new MediaPlayer();
57   mediaprovider=new ServerMediaFile(cfgBase,media);
58   
59   rrproc.init();
60 }
61
62 VompClient::~VompClient()
63 {
64   log->log("Client", Log::DEBUG, "Vomp client destructor");
65 #ifndef VOMPSTANDALONE  
66   if (lp)
67   {
68     delete lp;
69     lp = NULL;
70   }
71   else if (recplayer)
72   {
73     writeResumeData();
74
75     delete recplayer;
76     delete recordingManager;
77     recplayer = NULL;
78     recordingManager = NULL;
79   }
80 #endif
81   if (loggedIn) cleanConfig();
82   decClients();
83 }
84
85 void VompClient::incClients()
86 {
87   pthread_mutex_lock(&threadClientMutex);
88   VompClient::nr_clients++;
89   pthread_mutex_unlock(&threadClientMutex);
90 }
91
92 void VompClient::decClients()
93 {
94   pthread_mutex_lock(&threadClientMutex);
95   VompClient::nr_clients--;
96   pthread_mutex_unlock(&threadClientMutex);
97 }
98
99 int VompClient::getNrClients()
100 {
101   int nrClients;
102   pthread_mutex_lock(&threadClientMutex);
103   nrClients = VompClient::nr_clients;
104   pthread_mutex_unlock(&threadClientMutex);
105   return nrClients;
106 }
107
108
109 void VompClient::cleanConfig()
110 {
111   log->log("Client", Log::DEBUG, "Clean config");
112
113 #ifndef VOMPSTANDALONE
114
115   cRecordings Recordings;
116   Recordings.Load();
117
118   int numReturns;
119   int length;
120   char* resumes = config.getSectionKeyNames("ResumeData", numReturns, length);
121   char* position = resumes;
122   for(int k = 0; k < numReturns; k++)
123   {
124     log->log("Client", Log::DEBUG, "EXAMINING: %i %i %p %s", k, numReturns, position, position);
125
126     cRecording* recording = Recordings.GetByName(position);
127     if (!recording)
128     {
129       // doesn't exist anymore
130       log->log("Client", Log::DEBUG, "Found a recording that doesn't exist anymore");
131       config.deleteValue("ResumeData", position);
132     }
133     else
134     {
135       log->log("Client", Log::DEBUG, "This recording still exists");
136     }
137
138     position += strlen(position) + 1;
139   }
140
141   delete[] resumes;
142 #endif
143 }
144
145 void VompClientStartThread(void* arg)
146 {
147   VompClient* m = (VompClient*)arg;
148   m->run2();
149   // Nothing external to this class has a reference to it
150   // This is the end of the thread.. so delete m
151   delete m;
152   pthread_exit(NULL);
153 }
154
155 int VompClient::run()
156 {
157   if (pthread_create(&runThread, NULL, (void*(*)(void*))VompClientStartThread, (void *)this) == -1) return 0;
158   log->log("Client", Log::DEBUG, "VompClient run success");
159   return 1;
160 }
161
162 void VompClient::run2()
163 {
164   // Thread stuff
165   sigset_t sigset;
166   sigfillset(&sigset);
167   pthread_sigmask(SIG_BLOCK, &sigset, NULL);
168   pthread_detach(runThread);  // Detach
169
170 //  tcp.disableReadTimeout();
171
172 //  tcp.setSoKeepTime(3);
173   tcp.setNonBlocking();
174
175   ULONG channelID;
176   ULONG requestID;
177   ULONG opcode;
178   ULONG extraDataLength;
179   UCHAR* data;
180   
181   ULONG kaTimeStamp;
182
183   while(1)
184   {
185     log->log("Client", Log::DEBUG, "Waiting");
186     
187     if (!tcp.readData((UCHAR*)&channelID, sizeof(ULONG)))
188     {
189       // If this read fails then the client just hasn't sent anything.
190       // If any of the lower reads fail, then break, the connection is probably dead
191
192       // check connection is ok
193 //      if (tcp.isConnected()) continue;
194       
195       log->log("Client", Log::DEBUG, "Disconnection detected");
196       break;
197     }     
198     
199     channelID = ntohl(channelID);
200     if (channelID == 1)
201     {
202       if (!tcp.readData((UCHAR*)&requestID, sizeof(ULONG))) break;
203       requestID = ntohl(requestID);
204
205       if (!tcp.readData((UCHAR*)&opcode, sizeof(ULONG))) break;
206       opcode = ntohl(opcode);
207
208       if (!tcp.readData((UCHAR*)&extraDataLength, sizeof(ULONG))) break;
209       extraDataLength = ntohl(extraDataLength);
210       if (extraDataLength > 200000) // a random sanity limit
211       {
212         log->log("Client", Log::ERR, "ExtraDataLength > 200000!");
213         break;
214       }
215
216       if (extraDataLength)
217       {
218         data = (UCHAR*)malloc(extraDataLength);
219         if (!data)
220         {
221           log->log("Client", Log::ERR, "Extra data buffer malloc error");
222           break;
223         }
224         
225         if (!tcp.readData(data, extraDataLength))
226         {
227           log->log("Client", Log::ERR, "Could not read extradata");
228           free(data);
229           break;
230         }      
231       }
232       else
233       {
234         data = NULL;
235       }
236
237       log->log("Client", Log::DEBUG, "Received chan=%lu, ser=%lu, op=%lu, edl=%lu", channelID, requestID, opcode, extraDataLength);
238
239       if (!loggedIn && (opcode != 1))
240       {
241         log->log("Client", Log::ERR, "Not logged in and opcode != 1");
242         if (data) free(data);
243         break;
244       }
245
246       RequestPacket* req = new RequestPacket(requestID, opcode, data, extraDataLength);
247       rrproc.recvRequest(req);
248     }
249     else if (channelID == 3)
250     {
251       if (!tcp.readData((UCHAR*)&kaTimeStamp, sizeof(ULONG))) break;
252       kaTimeStamp = ntohl(kaTimeStamp);
253
254       log->log("Client", Log::DEBUG, "Received chan=%lu kats=%lu", channelID, kaTimeStamp);    
255
256       UCHAR buffer[8];
257       *(ULONG*)&buffer[0] = htonl(3); // KA CHANNEL
258       *(ULONG*)&buffer[4] = htonl(kaTimeStamp);
259       if (!tcp.sendPacket(buffer, 8))
260       {
261         log->log("Client", Log::ERR, "Could not send back KA reply");
262         break;
263       }      
264     }
265     else
266     {
267       log->log("Client", Log::ERR, "Incoming channel number unknown");
268       break;
269     }
270   }
271 }
272
273 ULLONG VompClient::ntohll(ULLONG a)
274 {
275   return htonll(a);
276 }
277
278 ULLONG VompClient::htonll(ULLONG a)
279 {
280   #if BYTE_ORDER == BIG_ENDIAN
281     return a;
282   #else
283     ULLONG b = 0;
284
285     b = ((a << 56) & 0xFF00000000000000ULL)
286       | ((a << 40) & 0x00FF000000000000ULL)
287       | ((a << 24) & 0x0000FF0000000000ULL)
288       | ((a <<  8) & 0x000000FF00000000ULL)
289       | ((a >>  8) & 0x00000000FF000000ULL)
290       | ((a >> 24) & 0x0000000000FF0000ULL)
291       | ((a >> 40) & 0x000000000000FF00ULL)
292       | ((a >> 56) & 0x00000000000000FFULL) ;
293
294     return b;
295   #endif
296 }
297
298 #ifndef VOMPSTANDALONE
299
300 cChannel* VompClient::channelFromNumber(ULONG channelNumber)
301 {
302   cChannel* channel = NULL;
303
304   for (channel = Channels.First(); channel; channel = Channels.Next(channel))
305   {
306     if (!channel->GroupSep())
307     {
308       log->log("Client", Log::DEBUG, "Looking for channel %lu::: number: %i name: '%s'", channelNumber, channel->Number(), channel->Name());
309
310       if (channel->Number() == (int)channelNumber)
311       {
312         int vpid = channel->Vpid();
313 #if VDRVERSNUM < 10300
314         int apid1 = channel->Apid1();
315 #else
316         int apid1 = channel->Apid(0);
317 #endif
318         log->log("Client", Log::DEBUG, "Found channel number %lu, vpid = %i, apid1 = %i", channelNumber, vpid, apid1);
319         return channel;
320       }
321     }
322   }
323
324   if (!channel)
325   {
326     log->log("Client", Log::DEBUG, "Channel not found");
327   }
328
329   return channel;
330 }
331
332 void VompClient::writeResumeData()
333 {
334   config.setValueLong("ResumeData",
335                           (char*)recplayer->getCurrentRecording()->FileName(),
336                           recplayer->frameNumberFromPosition(recplayer->getLastPosition()) );
337 }
338
339 #endif
340