2 Copyright 2008 Chris Tallon
4 This file is part of VOMP.
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.
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.
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.
23 #ifndef VOMPSTANDALONE
24 #include <vdr/recording.h>
25 #include <vdr/channels.h>
26 #include <vdr/videodir.h>
27 #include <vdr/plugin.h>
28 #include <vdr/timers.h>
30 #include "recplayer.h"
31 #include "mvpreceiver.h"
34 #include "vompclientrrproc.h"
35 #include "vompclient.h"
38 #include "mediaplayer.h"
39 #include "servermediafile.h"
41 #include "vdrcommand.h"
45 VompClientRRProc::VompClientRRProc(VompClient& x)
48 log = Log::getInstance();
53 VompClientRRProc::~VompClientRRProc()
58 bool VompClientRRProc::init()
60 int a = threadStart();
65 bool VompClientRRProc::recvRequest(RequestPacket* newRequest)
69 Now we have a queue system is used,
70 since on rare occasion the client fire two request at once
71 e.g. heavily channel switching
72 then processing only a single request would cause a deadlock in the client
76 log->log("RRProc", Log::DEBUG, "recvReq");
78 req_queue.push(newRequest);
80 log->log("RRProc", Log::DEBUG, "recvReq set req and signalled");
86 void VompClientRRProc::threadMethod()
89 log->log("RRProc", Log::DEBUG, "threadMethod startup");
91 if (req_queue.size() != 0)
94 - log->log("RRProc", Log::ERR, "threadMethod err 1");
98 That was how the code used to be.
100 TODO: Work out why this happens.
103 log->log("RRProc", Log::ERR, "threadMethod startup with already queued packets");
104 while (req_queue.size())
106 //log->log("RRProc", Log::DEBUG, "thread while");
107 req = req_queue.front();
110 threadUnlock(); // allow recvRequest to be queuing packets while we are working on this one
112 if (!processPacket())
114 log->log("RRProc", Log::ERR, "processPacket exited with fail");
120 log->log("RRProc", Log::ERR, "threadMethod startup with already queued packets done.");
126 log->log("RRProc", Log::DEBUG, "threadMethod waiting");
127 threadWaitForSignal(); // unlocks, waits, relocks
128 if (req_queue.size() == 0)
130 log->log("RRProc", Log::INFO, "threadMethod err 2 or quit");
135 // signalled with something in queue
137 log->log("RRProc", Log::DEBUG, "thread woken with req, queue size: %i", req_queue.size());
139 while (req_queue.size())
141 //log->log("RRProc", Log::DEBUG, "thread while");
142 req = req_queue.front();
145 threadUnlock(); // allow recvRequest to be queuing packets while we are working on this one
147 if (!processPacket())
149 log->log("RRProc", Log::ERR, "processPacket exited with fail");
156 // locked and run out of packets to process
160 bool VompClientRRProc::processPacket()
162 resp = new ResponsePacket();
163 if (!resp->init(req->requestID))
165 log->log("RRProc", Log::ERR, "response packet init fail");
168 if (req->data) free(req->data);
180 result = processLogin();
182 #ifndef VOMPSTANDALONE
184 result = processGetRecordingsList();
187 result = processDeleteRecording();
190 result = processGetChannelsList();
193 result = processStartStreamingChannel();
196 result = processGetBlock();
199 result = processStopStreaming();
202 result = processStartStreamingRecording();
205 result = processGetChannelSchedule();
209 result = processConfigSave();
212 result = processConfigLoad();
214 #ifndef VOMPSTANDALONE
216 result = processReScanRecording(); // FIXME obselete
219 result = processGetTimers();
222 result = processSetTimer();
225 result = processPositionFromFrameNumber();
228 result = processFrameNumberFromPosition();
231 result = processMoveRecording();
234 result = processGetIFrame();
237 result = processGetRecInfo();
240 result = processGetMarks();
243 result = processGetChannelPids();
246 result = processDeleteTimer();
249 case VDR_GETMEDIALIST:
250 result = processGetMediaList();
253 result = processOpenMedia();
255 case VDR_GETMEDIABLOCK:
256 result = processGetMediaBlock();
259 result = processGetLanguageList();
262 result = processGetLanguageContent();
264 case VDR_GETMEDIAINFO:
265 result= processGetMediaInfo();
267 case VDR_CLOSECHANNEL:
268 result= processCloseMediaChannel();
271 result = processSetCharset();
279 if (req->data) free(req->data);
283 if (result) return true;
287 int VompClientRRProc::processLogin()
289 if (req->dataLength != 6) return 0;
293 char configFileName[PATH_MAX];
294 snprintf(configFileName, PATH_MAX, "%s/vomp-%02X-%02X-%02X-%02X-%02X-%02X.conf", x.configDir, req->data[0], req->data[1], req->data[2], req->data[3], req->data[4], req->data[5]);
295 x.config.init(configFileName);
297 // Send the login reply
299 time_t timeNow = time(NULL);
300 struct tm* timeStruct = localtime(&timeNow);
301 int timeOffset = timeStruct->tm_gmtoff;
303 resp->addULONG(timeNow);
304 resp->addLONG(timeOffset);
306 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
307 log->log("RRProc", Log::DEBUG, "written login reply len %lu", resp->getLen());
310 x.netLog(); // safe to run here since the client won't start net logging for a while yet
315 int VompClientRRProc::processSetCharset()
317 int charset = ntohl(*(ULONG*)req->data);
318 if (charset>0 && charset<3) {
319 log->log("RRProc", Log::DEBUG, "Set charset to %d", charset);
320 x.setCharset(charset);
323 log->log("RRProc", Log::DEBUG, "Invalid charset %d", charset);
327 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
334 int VompClientRRProc::processConfigSave()
336 char* section = (char*)req->data;
340 for (UINT k = 0; k < req->dataLength; k++)
342 if (req->data[k] == '\0')
346 key = (char*)&req->data[k+1];
350 value = (char*)&req->data[k+1];
356 // if the last string (value) doesnt have null terminator, give up
357 if (req->data[req->dataLength - 1] != '\0') return 0;
359 log->log("RRProc", Log::DEBUG, "Config save: %s %s %s", section, key, value);
360 if (x.config.setValueString(section, key, value))
370 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
375 int VompClientRRProc::processConfigLoad()
377 char* section = (char*)req->data;
380 for (UINT k = 0; k < req->dataLength; k++)
382 if (req->data[k] == '\0')
384 key = (char*)&req->data[k+1];
389 char* value = x.config.getValueString(section, key);
393 resp->addString(value);//client coding, do not touch
394 log->log("RRProc", Log::DEBUG, "Written config load packet");
400 log->log("RRProc", Log::DEBUG, "Written config load failed packet");
404 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
410 //helper for sending from a serialize buffer
411 //insert the used len into the first 4 Bytes of the buffer
412 void VompClientRRProc::sendPacket(SerializeBuffer *b) {
413 resp->copyin(b->getStart(),b->getCurrent()-b->getStart());
415 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
419 * media List Request:
420 * Media List response:
423 #define MLISTBUF 500000
424 int VompClientRRProc::processGetMediaList()
426 SerializeBuffer buffer(req->data,req->dataLength);
427 MediaURI uri(0,NULL,NULL);
428 VDR_GetMediaListRequest request(&uri);
429 if (request.deserialize(&buffer) != 0) {
430 log->log("Client", Log::ERR, "getMediaList unable to deserialize");
433 const char *dirname=uri.getName();
434 log->log("Client", Log::DEBUG, "getMediaList for %s", dirname);
437 if (dirname == NULL) {
438 ml=x.media->getRootList();
440 ml=x.media->getMediaList(&uri);
443 log->log("Client", Log::ERR, "getMediaList returned NULL");
446 SerializeBuffer rbuf(MLISTBUF,false,true);
447 ULONG flags=0; //TODO: real error handling by setting flags
448 VDR_GetMediaListResponse response(&flags,ml);
449 if (response.serialize(&rbuf) != 0) {
450 log->log("Client", Log::ERR, "getMediaList returned NULL");
454 log->log("Client", Log::DEBUG, "getMediaList size %u", ml->size());
459 log->log("Client", Log::DEBUG, "Written Media list");
464 * openMedia response:
466 int VompClientRRProc::processOpenMedia()
468 SerializeBuffer buffer(req->data,req->dataLength);
469 MediaURI uri(0,NULL,NULL);
473 VDR_OpenMediumRequest request(&channel,&uri,&xs,&ys);
474 if (request.deserialize(&buffer) != 0) {
475 log->log("Client", Log::ERR, "openMediaRequest unable to deserialize");
478 const char *name=uri.getName();
479 log->log("Client", Log::DEBUG, "openMediaRequest for %s", name);
481 int rt=x.media->openMedium(channel,&uri,&size,xs,ys);
486 log->log("Client", Log::ERR, "openMediaRequest unable to open");
488 VDR_OpenMediumResponse response(&flags,&size);
489 SerializeBuffer rbuf(response.getSerializedLen()+4,false,true);
490 if (response.serialize(&rbuf) != 0) {
491 log->log("Client", Log::ERR, "openMediaRequest cannot serialize");
494 log->log("Client", Log::DEBUG, "openMediaRequest size %llu", size);
501 * packet - no serialized response!
503 int VompClientRRProc::processGetMediaBlock()
505 SerializeBuffer buffer(req->data,req->dataLength);
509 VDR_GetMediaBlockRequest request(&channel,&position,&amount);
510 if (request.deserialize(&buffer) != 0) {
511 log->log("Client", Log::ERR, "getMediaBlock unable to deserialize");
514 log->log("Client", Log::DEBUG, "getMediaBlock pos = %llu length = %lu,chan=%lu", position, amount,channel);
516 UCHAR sendBuffer[amount ];
517 ULONG amountReceived = 0;
518 UCHAR *rbuf=sendBuffer;
519 int rt=x.media->getMediaBlock(channel,position,amount,&amountReceived,&rbuf);
520 if (!amountReceived || rt != 0)
522 log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0");
526 if (rbuf != sendBuffer) {
527 //the provider did not use the optimized handling with using my buffer
528 resp->copyin(rbuf,amountReceived);
531 // the provider did not allocate a new buffer
532 resp->copyin(sendBuffer,amountReceived);
536 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
537 log->log("Client", Log::DEBUG, "written ok %lu", amountReceived);
544 int VompClientRRProc::processGetMediaInfo()
546 SerializeBuffer buffer(req->data,req->dataLength);
548 VDR_GetMediaInfoRequest request(&channel);
549 if (request.deserialize(&buffer) != 0) {
550 log->log("Client", Log::ERR, "getMediaInfo unable to deserialize");
553 log->log("Client", Log::DEBUG, "getMediaInfo chan=%lu", channel);
556 int rt=x.media->getMediaInfo(channel,&mi);
559 log->log("Client", Log::ERR, "getMediaInfo unable to get");
561 VDR_GetMediaInfoResponse response(&flags,&mi);
562 SerializeBuffer rbuf(response.getSerializedLen()+4,false,true);
563 if (response.serialize(&rbuf) != 0) {
564 log->log("Client", Log::ERR, "getMediaInfo cannot serialize");
576 int VompClientRRProc::processCloseMediaChannel()
578 SerializeBuffer buffer(req->data,req->dataLength);
580 VDR_CloseMediaChannelRequest request(&channel);
581 if (request.deserialize(&buffer) != 0) {
582 log->log("Client", Log::ERR, "closeMediaChannel unable to deserialize");
586 log->log("Client", Log::DEBUG, "closeMediaChannel chan=%lu", channel);
587 int rt=x.media->closeMediaChannel(channel);
590 log->log("Client", Log::ERR, "closeMediaChannel unable to get");
592 VDR_CloseMediaChannelResponse response(&flags);
593 SerializeBuffer rbuf(response.getSerializedLen()+4,false,true);
594 if (response.serialize(&rbuf) != 0) {
595 log->log("Client", Log::ERR, "closeMediaChannel cannot serialize");
604 int VompClientRRProc::processGetLanguageList()
606 x.i18n.findLanguages();
607 const I18n::lang_code_list& languages = x.i18n.getLanguageList();
609 I18n::lang_code_list::const_iterator iter;
610 for (iter = languages.begin(); iter != languages.end(); ++iter)
612 resp->addString(iter->first.c_str()); // Source code is acsii
613 resp->addString(x.charconvutf8->Convert(iter->second.c_str())); //translate string can be any utf-8 character
616 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
620 int VompClientRRProc::processGetLanguageContent()
622 if (req->dataLength <= 0) return 0;
623 std::string code, result;
624 code.assign((char*)req->data, req->dataLength - 1);
625 x.i18n.findLanguages();
626 I18n::trans_table texts = x.i18n.getLanguageContent(code);
627 I18n::trans_table::const_iterator iter;
628 for (iter = texts.begin(); iter != texts.end(); ++iter)
630 resp->addString(iter->first.c_str());// source code is acsii since it is english
631 resp->addString(x.charconvutf8->Convert(iter->second.c_str())); // translate text can be any unicode string, it is stored as UTF-8
634 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
638 #ifndef VOMPSTANDALONE
640 int VompClientRRProc::processGetRecordingsList()
643 int Percent = VideoDiskSpace(&FreeMB);
644 int Total = (FreeMB / (100 - Percent)) * 100;
646 resp->addULONG(Total);
647 resp->addULONG(FreeMB);
648 resp->addULONG(Percent);
650 cRecordings Recordings;
653 for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
655 #if VDRVERSNUM < 10721
656 resp->addULONG(recording->start);
658 resp->addULONG(recording->Start());
660 resp->addString(x.charconvsys->Convert(recording->Name())); //coding of recording name is system dependent
661 resp->addString(recording->FileName());//file name are not visible by user do not touch
665 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
667 log->log("RRProc", Log::DEBUG, "Written recordings list");
672 int VompClientRRProc::processDeleteRecording()
674 // data is a pointer to the fileName string
676 cRecordings Recordings;
677 Recordings.Load(); // probably have to do this
679 cRecording* recording = Recordings.GetByName((char*)req->data);
681 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
685 log->log("RRProc", Log::DEBUG, "deleting recording: %s", recording->Name());
687 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
690 if (recording->Delete())
692 // Copy svdrp's way of doing this, see if it works
693 #if VDRVERSNUM > 10300
694 ::Recordings.DelByName(recording->FileName());
714 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
719 int VompClientRRProc::processMoveRecording()
721 log->log("RRProc", Log::DEBUG, "Process move recording");
722 char* fileName = (char*)req->data;
723 char* newPath = NULL;
725 for (UINT k = 0; k < req->dataLength; k++)
727 if (req->data[k] == '\0')
729 newPath = (char*)&req->data[k+1];
733 if (!newPath) return 0;
735 cRecordings Recordings;
736 Recordings.Load(); // probably have to do this
738 cRecording* recording = Recordings.GetByName((char*)fileName);
740 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
744 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
747 log->log("RRProc", Log::DEBUG, "moving recording: %s", recording->Name());
748 log->log("RRProc", Log::DEBUG, "moving recording: %s", recording->FileName());
749 log->log("RRProc", Log::DEBUG, "to: %s", newPath);
751 const char* t = recording->FileName();
753 char* dateDirName = NULL; int k;
754 char* titleDirName = NULL; int j;
756 // Find the datedirname
757 for(k = strlen(t) - 1; k >= 0; k--)
761 log->log("RRProc", Log::DEBUG, "l1: %i", strlen(&t[k+1]) + 1);
762 dateDirName = new char[strlen(&t[k+1]) + 1];
763 strcpy(dateDirName, &t[k+1]);
768 // Find the titledirname
770 for(j = k-1; j >= 0; j--)
774 log->log("RRProc", Log::DEBUG, "l2: %i", (k - j - 1) + 1);
775 titleDirName = new char[(k - j - 1) + 1];
776 memcpy(titleDirName, &t[j+1], k - j - 1);
777 titleDirName[k - j - 1] = '\0';
782 log->log("RRProc", Log::DEBUG, "datedirname: %s", dateDirName);
783 log->log("RRProc", Log::DEBUG, "titledirname: %s", titleDirName);
784 log->log("RRProc", Log::DEBUG, "viddir: %s", VideoDirectory);
786 char* newPathConv = new char[strlen(newPath)+1];
787 strcpy(newPathConv, newPath);
788 ExchangeChars(newPathConv, true);
789 log->log("RRProc", Log::DEBUG, "EC: %s", newPathConv);
791 char* newContainer = new char[strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1];
792 log->log("RRProc", Log::DEBUG, "l10: %i", strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1);
793 sprintf(newContainer, "%s%s%s", VideoDirectory, newPathConv, titleDirName);
794 delete[] newPathConv;
796 log->log("RRProc", Log::DEBUG, "%s", newContainer);
799 int statret = stat(newContainer, &dstat);
800 if ((statret == -1) && (errno == ENOENT)) // Dir does not exist
802 log->log("RRProc", Log::DEBUG, "new dir does not exist");
803 int mkdirret = mkdir(newContainer, 0755);
806 delete[] dateDirName;
807 delete[] titleDirName;
808 delete[] newContainer;
812 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
816 else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR))) // Something exists but it's not a dir
818 delete[] dateDirName;
819 delete[] titleDirName;
820 delete[] newContainer;
824 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
828 // Ok, the directory container has been made, or it pre-existed.
830 char* newDir = new char[strlen(newContainer) + 1 + strlen(dateDirName) + 1];
831 sprintf(newDir, "%s/%s", newContainer, dateDirName);
833 log->log("RRProc", Log::DEBUG, "doing rename '%s' '%s'", t, newDir);
834 int renameret = rename(t, newDir);
837 // Success. Test for remove old dir containter
838 char* oldTitleDir = new char[k+1];
839 memcpy(oldTitleDir, t, k);
840 oldTitleDir[k] = '\0';
841 log->log("RRProc", Log::DEBUG, "len: %i, cp: %i, strlen: %i, oldtitledir: %s", k+1, k, strlen(oldTitleDir), oldTitleDir);
842 rmdir(oldTitleDir); // can't do anything about a fail result at this point.
843 delete[] oldTitleDir;
848 #if VDRVERSNUM > 10311
850 ::Recordings.Update();
852 // Success. Send a different packet from just a ulong
853 resp->addULONG(1); // success
854 resp->addString(newDir); //system depent do not convert
862 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
864 delete[] dateDirName;
865 delete[] titleDirName;
866 delete[] newContainer;
873 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
880 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
886 int VompClientRRProc::processGetChannelsList()
890 char* chanConfig = x.config.getValueString("General", "Channels");
892 if (chanConfig) allChans = strcasecmp(chanConfig, "FTA only");
894 for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
896 #if VDRVERSNUM < 10300
897 if (!channel->GroupSep() && (!channel->Ca() || allChans))
899 if (!channel->GroupSep() && (!channel->Ca(0) || allChans))
902 log->log("RRProc", Log::DEBUG, "name: '%s'", channel->Name());
904 if (channel->Vpid()) type = 1;
905 #if VDRVERSNUM < 10300
908 else if (channel->Apid(0)) type = 2;
912 resp->addULONG(channel->Number());
913 resp->addULONG(type);
914 resp->addString(x.charconvsys->Convert(channel->Name()));
915 #if VDRVERSNUM < 10703
918 resp->addULONG(channel->Vtype());
924 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
926 log->log("RRProc", Log::DEBUG, "Written channels list");
931 int VompClientRRProc::processGetChannelPids()
933 ULONG channelNumber = ntohl(*(ULONG*)req->data);
935 cChannel* channel = x.channelFromNumber(channelNumber);
940 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
949 #if VDRVERSNUM < 10300
951 log->log("RRProc", Log::DEBUG, "Apid1: %i", channel->Apid1());
952 log->log("RRProc", Log::DEBUG, "Apid2: %i", channel->Apid2());
954 if (channel->Apid2())
956 else if (channel->Apid1())
963 for (const int *Apid = channel->Apids(); *Apid; Apid++)
967 for (const int *Dpid = channel->Dpids(); *Dpid; Dpid++)
971 for (const int *Spid = channel->Spids(); *Spid; Spid++)
978 // Format of response
997 resp->addULONG(channel->Vpid());
998 #if VDRVERSNUM < 10703
1001 resp->addULONG(channel->Vtype());
1003 resp->addULONG(numApids);
1005 #if VDRVERSNUM < 10300
1008 resp->addULONG(channel->Apid1());
1009 resp->addString("");
1013 resp->addULONG(channel->Apid2());
1014 resp->addString("");
1019 for (ULONG i = 0; i < numApids; i++)
1021 resp->addULONG(channel->Apid(i));
1022 resp->addString(x.charconvsys->Convert(channel->Alang(i)));
1024 resp->addULONG(numDpids);
1025 for (ULONG i = 0; i < numDpids; i++)
1027 resp->addULONG(channel->Dpid(i));
1028 resp->addString(x.charconvsys->Convert(channel->Dlang(i)));
1030 resp->addULONG(numSpids);
1031 for (ULONG i = 0; i < numSpids; i++)
1033 resp->addULONG(channel->Spid(i));
1034 resp->addString(x.charconvsys->Convert(channel->Slang(i)));
1037 resp->addULONG(channel->Tpid());
1041 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1043 log->log("RRProc", Log::DEBUG, "Written channels pids");
1048 int VompClientRRProc::processStartStreamingChannel()
1052 log->log("RRProc", Log::ERR, "Client called start streaming twice");
1056 log->log("RRProc", Log::DEBUG, "req->dataLength = %i", req->dataLength);
1057 ULONG channelNumber = ntohl(*(ULONG*)req->data);
1059 cChannel* channel = x.channelFromNumber(channelNumber);
1064 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1068 // get the priority we should use
1070 int priority = x.config.getValueLong("General", "Live priority", &fail);
1073 log->log("RRProc", Log::DEBUG, "Config: Live TV priority: %i", priority);
1077 log->log("RRProc", Log::DEBUG, "Config: Live TV priority config fail");
1081 // a bit of sanity..
1082 if (priority < 0) priority = 0;
1083 if (priority > 99) priority = 99;
1085 log->log("RRProc", Log::DEBUG, "Using live TV priority %i", priority);
1086 x.lp = MVPReceiver::create(channel, priority);
1092 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1096 if (!x.lp->init(&x.tcp, req->requestID))
1102 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1108 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1112 int VompClientRRProc::processStopStreaming()
1114 log->log("RRProc", Log::DEBUG, "STOP STREAMING RECEIVED");
1120 else if (x.recplayer)
1122 x.writeResumeData();
1125 delete x.recordingManager;
1127 x.recordingManager = NULL;
1132 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1136 int VompClientRRProc::processGetBlock()
1140 log->log("RRProc", Log::ERR, "Get block called during live streaming");
1146 log->log("RRProc", Log::ERR, "Get block called when no recording open");
1150 UCHAR* data = req->data;
1152 ULLONG position = x.ntohll(*(ULLONG*)data);
1153 data += sizeof(ULLONG);
1154 ULONG amount = ntohl(*(ULONG*)data);
1156 log->log("RRProc", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount);
1158 UCHAR sendBuffer[amount];
1159 ULONG amountReceived = x.recplayer->getBlock(&sendBuffer[0], position, amount);
1161 if (!amountReceived)
1164 log->log("RRProc", Log::DEBUG, "written 4(0) as getblock got 0");
1168 resp->copyin(sendBuffer, amountReceived);
1169 log->log("RRProc", Log::DEBUG, "written %lu", amountReceived);
1173 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1174 log->log("RRProc", Log::DEBUG, "Finished getblock, have sent %lu", resp->getLen());
1178 int VompClientRRProc::processStartStreamingRecording()
1180 // data is a pointer to the fileName string
1182 x.recordingManager = new cRecordings;
1183 x.recordingManager->Load();
1185 cRecording* recording = x.recordingManager->GetByName((char*)req->data);
1187 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
1191 x.recplayer = new RecPlayer(recording);
1193 resp->addULLONG(x.recplayer->getLengthBytes());
1194 resp->addULONG(x.recplayer->getLengthFrames());
1196 #if VDRVERSNUM < 10703
1197 resp->addUCHAR(true);//added for TS
1199 resp->addUCHAR(recording->IsPesRecording());//added for TS
1203 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1205 log->log("RRProc", Log::DEBUG, "written totalLength");
1209 delete x.recordingManager;
1210 x.recordingManager = NULL;
1215 int VompClientRRProc::processPositionFromFrameNumber()
1219 ULONG frameNumber = ntohl(*(ULONG*)req->data);
1223 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1227 retval = x.recplayer->positionFromFrameNumber(frameNumber);
1230 resp->addULLONG(retval);
1232 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1234 log->log("RRProc", Log::DEBUG, "Wrote posFromFrameNum reply to client");
1238 int VompClientRRProc::processFrameNumberFromPosition()
1242 ULLONG position = x.ntohll(*(ULLONG*)req->data);
1246 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1250 retval = x.recplayer->frameNumberFromPosition(position);
1253 resp->addULONG(retval);
1255 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1257 log->log("RRProc", Log::DEBUG, "Wrote frameNumFromPos reply to client");
1261 int VompClientRRProc::processGetIFrame()
1263 bool success = false;
1265 ULONG* data = (ULONG*)req->data;
1267 ULONG frameNumber = ntohl(*data);
1269 ULONG direction = ntohl(*data);
1271 ULLONG rfilePosition = 0;
1272 ULONG rframeNumber = 0;
1273 ULONG rframeLength = 0;
1277 log->log("RRProc", Log::DEBUG, "GetIFrame recording called when no recording being played!");
1281 success = x.recplayer->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength);
1284 // returns file position, frame number, length
1288 resp->addULLONG(rfilePosition);
1289 resp->addULONG(rframeNumber);
1290 resp->addULONG(rframeLength);
1298 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1300 log->log("RRProc", Log::DEBUG, "Wrote GNIF reply to client %llu %lu %lu", rfilePosition, rframeNumber, rframeLength);
1304 int VompClientRRProc::processGetChannelSchedule()
1306 ULONG* data = (ULONG*)req->data;
1308 ULONG channelNumber = ntohl(*data);
1310 ULONG startTime = ntohl(*data);
1312 ULONG duration = ntohl(*data);
1314 log->log("RRProc", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
1316 cChannel* channel = x.channelFromNumber(channelNumber);
1321 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1323 log->log("RRProc", Log::DEBUG, "written 0 because channel = NULL");
1327 log->log("RRProc", Log::DEBUG, "Got channel");
1329 #if VDRVERSNUM < 10300
1330 cMutexLock MutexLock;
1331 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1333 cSchedulesLock MutexLock;
1334 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1340 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1342 log->log("RRProc", Log::DEBUG, "written 0 because Schedule!s! = NULL");
1346 log->log("RRProc", Log::DEBUG, "Got schedule!s! object");
1348 const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
1353 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1355 log->log("RRProc", Log::DEBUG, "written 0 because Schedule = NULL");
1359 log->log("RRProc", Log::DEBUG, "Got schedule object");
1361 const char* empty = "";
1362 bool atLeastOneEvent = false;
1365 ULONG thisEventTime;
1366 ULONG thisEventDuration;
1367 const char* thisEventTitle;
1368 const char* thisEventSubTitle;
1369 const char* thisEventDescription;
1371 #if VDRVERSNUM < 10300
1373 const cEventInfo *event;
1374 for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1376 event = Schedule->GetEventNumber(eventNumber);
1378 thisEventID = event->GetEventID();
1379 thisEventTime = event->GetTime();
1380 thisEventDuration = event->GetDuration();
1381 thisEventTitle = event->GetTitle();
1382 thisEventSubTitle = event->GetSubtitle();
1383 thisEventDescription = event->GetExtendedDescription();
1387 for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
1389 thisEventID = event->EventID();
1390 thisEventTime = event->StartTime();
1391 thisEventDuration = event->Duration();
1392 thisEventTitle = event->Title();
1393 thisEventSubTitle = NULL;
1394 thisEventDescription = event->Description();
1398 //in the past filter
1399 if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue;
1402 if ((thisEventTime + thisEventDuration) <= startTime) continue;
1405 if (thisEventTime >= (startTime + duration)) continue;
1407 if (!thisEventTitle) thisEventTitle = empty;
1408 if (!thisEventSubTitle) thisEventSubTitle = empty;
1409 if (!thisEventDescription) thisEventDescription = empty;
1411 resp->addULONG(thisEventID);
1412 resp->addULONG(thisEventTime);
1413 resp->addULONG(thisEventDuration);
1415 resp->addString(x.charconvsys->Convert(thisEventTitle));
1416 resp->addString(x.charconvsys->Convert(thisEventSubTitle));
1417 resp->addString(x.charconvsys->Convert(thisEventDescription));
1419 atLeastOneEvent = true;
1422 log->log("RRProc", Log::DEBUG, "Got all event data");
1424 if (!atLeastOneEvent)
1427 log->log("RRProc", Log::DEBUG, "Written 0 because no data");
1431 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1433 log->log("RRProc", Log::DEBUG, "written schedules packet");
1438 int VompClientRRProc::processGetTimers()
1441 int numTimers = Timers.Count();
1443 resp->addULONG(numTimers);
1445 for (int i = 0; i < numTimers; i++)
1447 timer = Timers.Get(i);
1449 #if VDRVERSNUM < 10300
1450 resp->addULONG(timer->Active());
1452 resp->addULONG(timer->HasFlags(tfActive));
1454 resp->addULONG(timer->Recording());
1455 resp->addULONG(timer->Pending());
1456 resp->addULONG(timer->Priority());
1457 resp->addULONG(timer->Lifetime());
1458 resp->addULONG(timer->Channel()->Number());
1459 resp->addULONG(timer->StartTime());
1460 resp->addULONG(timer->StopTime());
1461 resp->addULONG(timer->Day());
1462 resp->addULONG(timer->WeekDays());
1463 resp->addString(timer->File()); //Filename is system specific and not visible by user
1467 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1469 log->log("RRProc", Log::DEBUG, "Written timers list");
1474 int VompClientRRProc::processSetTimer()
1476 char* timerString = new char[strlen((char*)req->data) + 1];
1477 strcpy(timerString, (char*)req->data);
1479 #if VDRVERSNUM < 10300
1481 // If this is VDR 1.2 the date part of the timer string must be reduced
1482 // to just DD rather than YYYY-MM-DD
1484 int s = 0; // source
1485 int d = 0; // destination
1487 while(c != 2) // copy up to date section, including the second ':'
1489 timerString[d] = req->data[s];
1490 if (req->data[s] == ':') c++;
1494 // now it has copied up to the date section
1496 while(c != 2) // waste YYYY-MM-
1498 if (req->data[s] == '-') c++;
1501 // now source is at the DD
1502 memcpy(&timerString[d], &req->data[s], req->dataLength - s);
1503 d += req->dataLength - s;
1504 timerString[d] = '\0';
1506 log->log("RRProc", Log::DEBUG, "Timer string after 1.2 conversion:");
1509 log->log("RRProc", Log::DEBUG, "%s", timerString);
1511 cTimer *timer = new cTimer;
1512 if (timer->Parse((char*)timerString))
1514 cTimer *t = Timers.GetTimer(timer);
1518 #if VDRVERSNUM < 10300
1521 Timers.SetModified();
1525 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1526 return 1; // FIXME - cTimer* timer is leaked here!
1532 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1539 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1545 int VompClientRRProc::processDeleteTimer()
1547 log->log("RRProc", Log::DEBUG, "Delete timer called");
1552 INT delChannel = ntohl(*(ULONG*)&req->data[position]); position += 4;
1553 INT delWeekdays = ntohl(*(ULONG*)&req->data[position]); position += 4;
1554 INT delDay = ntohl(*(ULONG*)&req->data[position]); position += 4;
1555 INT delStart = ntohl(*(ULONG*)&req->data[position]); position += 4;
1556 INT delStop = ntohl(*(ULONG*)&req->data[position]); position += 4;
1559 for (ti = Timers.First(); ti; ti = Timers.Next(ti))
1561 if ( (ti->Channel()->Number() == delChannel)
1562 && ((ti->WeekDays() && (ti->WeekDays() == delWeekdays)) || (!ti->WeekDays() && (ti->Day() == delDay)))
1563 && (ti->StartTime() == delStart)
1564 && (ti->StopTime() == delStop) )
1572 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1576 if (!Timers.BeingEdited())
1578 if (!ti->Recording())
1581 Timers.SetModified();
1584 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1589 log->log("RRProc", Log::ERR, "Unable to delete timer - timer is running");
1592 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1598 log->log("RRProc", Log::ERR, "Unable to delete timer - timers being edited at VDR");
1601 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1606 int VompClientRRProc::processGetRecInfo()
1608 // data is a pointer to the fileName string
1610 cRecordings Recordings;
1611 Recordings.Load(); // probably have to do this
1613 cRecording *recording = Recordings.GetByName((char*)req->data);
1615 time_t timerStart = 0;
1616 time_t timerStop = 0;
1617 char* summary = NULL;
1618 char* shorttext = NULL;
1619 char* description = NULL;
1620 bool newsummary=false;
1621 ULONG resumePoint = 0;
1625 log->log("RRProc", Log::ERR, "GetRecInfo found no recording");
1628 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1633 4 bytes: start time for timer
1634 4 bytes: end time for timer
1635 4 bytes: resume point
1637 4 bytes: num components
1644 8 bytes: frames per second
1647 // Get current timer
1649 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
1652 timerStart = rc->Timer()->StartTime();
1653 timerStop = rc->Timer()->StopTime();
1654 log->log("RRProc", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop);
1657 resp->addULONG(timerStart);
1658 resp->addULONG(timerStop);
1662 /* char* value = x.config.getValueString("ResumeData", (char*)req->data);
1665 resumePoint = strtoul(value, NULL, 10);
1669 char* ResumeIdC = x.config.getValueString("General", "ResumeId");
1672 ResumeId = atoi(ResumeIdC);
1676 ResumeId = 0; //default if not defined in vomp-MAC.conf
1678 while (ResumeIDLock)
1679 cCondWait::SleepMs(100);
1680 ResumeIDLock = true;
1681 int OldSetupResumeID = Setup.ResumeID;
1682 Setup.ResumeID = ResumeId; //UGLY: quickly change resumeid
1683 #if VDRVERSNUM < 10703
1684 cResumeFile ResumeFile(recording->FileName()); //get corresponding resume file
1686 cResumeFile ResumeFile(recording->FileName(), recording->IsPesRecording()); //get corresponding resume file
1688 Setup.ResumeID = OldSetupResumeID; //and restore it back
1689 ResumeIDLock = false;
1691 int resume = ResumeFile.Read();
1692 //isyslog("VOMPDEBUG: resumePoint = %i, resume = %i, ResumeId = %i",resumePoint, resume, ResumeId);
1694 resumePoint = ResumeFile.Read();
1696 log->log("RRProc", Log::DEBUG, "GRI: RP: %lu", resumePoint);
1698 resp->addULONG(resumePoint);
1702 #if VDRVERSNUM < 10300
1703 summary = (char*)recording->Summary();
1705 const cRecordingInfo *Info = recording->Info();
1706 shorttext = (char*)Info->ShortText();
1707 description = (char*) (char*)Info->Description();
1708 if (isempty(shorttext)) summary=description;
1709 else if (isempty(description)) summary=shorttext;
1711 int length=strlen(description)+strlen(shorttext)+4;
1712 summary=new char[length];
1713 snprintf(summary,length,"%s\n\n%s",shorttext,description);
1717 if (isempty(summary)) summary = (char*)Info->Description();
1719 log->log("RRProc", Log::DEBUG, "GRI: S: %s", summary);
1722 resp->addString(x.charconvsys->Convert(summary));
1723 if (newsummary) delete [] summary;
1727 resp->addString("");
1732 #if VDRVERSNUM < 10300
1734 // Send 0 for numchannels - this signals the client this info is not available
1738 const cComponents* components = Info->Components();
1740 log->log("RRProc", Log::DEBUG, "GRI: D1: %p", components);
1748 resp->addULONG(components->NumComponents());
1750 tComponent* component;
1751 for (int i = 0; i < components->NumComponents(); i++)
1753 component = components->Component(i);
1755 log->log("RRProc", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
1757 resp->addUCHAR(component->stream);
1758 resp->addUCHAR(component->type);
1760 if (component->language)
1762 resp->addString(x.charconvsys->Convert(component->language));
1766 resp->addString("");
1768 if (component->description)
1770 resp->addString(x.charconvsys->Convert(component->description));
1774 resp->addString("");
1780 double framespersec;
1781 #if VDRVERSNUM < 10703
1782 framespersec = FRAMESPERSEC;
1784 framespersec = Info->FramesPerSecond();
1786 resp->adddouble(framespersec);
1791 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1793 log->log("RRProc", Log::DEBUG, "Written getrecinfo");
1803 int VompClientRRProc::processReScanRecording()
1807 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1811 x.recplayer->scan();
1813 resp->addULLONG(x.recplayer->getLengthBytes());
1814 resp->addULONG(x.recplayer->getLengthFrames());
1816 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1817 log->log("RRProc", Log::DEBUG, "Rescan recording, wrote new length to client");
1821 // FIXME without client calling rescan, getblock wont work even tho more data is avail
1823 int VompClientRRProc::processGetMarks()
1825 // data is a pointer to the fileName string
1828 cRecordings Recordings;
1829 Recordings.Load(); // probably have to do this
1831 cRecording *recording = Recordings.GetByName((char*)req->data);
1833 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
1837 #if VDRVERSNUM < 10703
1838 Marks.Load(recording->FileName());
1840 Marks.Load(recording->FileName(), recording->FramesPerSecond(), recording->IsPesRecording());
1844 for (const cMark *m = Marks.First(); m; m = Marks.Next(m))
1846 #if VDRVERSNUM < 10721
1847 ULLONG mposition = m->position;
1849 ULLONG mposition = m->Position();
1851 log->log("RRProc", Log::DEBUG, "found Mark %i", mposition);
1853 resp->addULONG(mposition);
1858 log->log("RRProc", Log::DEBUG, "no marks found, sending 0-mark");
1864 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1866 log->log("RRProc", Log::DEBUG, "Written Marks list");
1871 #endif // !VOMPSTANDALONE