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 ULONG VompClientRRProc::VOMP_PROTOCOL_VERSION = 0x00000200;
47 // cc is release protocol version, increase with every release, that changes protocol
48 // dd is development protocol version, set to zero at every release,
49 // increase for every protocol change in git
50 // bb not equal zero should indicate a non loggytronic protocol
51 // aa is reserved for future use
53 ULONG VompClientRRProc::getProtocolVersion()
55 return VOMP_PROTOCOL_VERSION;
58 VompClientRRProc::VompClientRRProc(VompClient& x)
61 log = Log::getInstance();
66 VompClientRRProc::~VompClientRRProc()
71 bool VompClientRRProc::init()
73 int a = threadStart();
78 bool VompClientRRProc::recvRequest(RequestPacket* newRequest)
82 Now we have a queue system is used,
83 since on rare occasion the client fire two request at once
84 e.g. heavily channel switching
85 then processing only a single request would cause a deadlock in the client
89 log->log("RRProc", Log::DEBUG, "recvReq");
91 req_queue.push(newRequest);
93 log->log("RRProc", Log::DEBUG, "recvReq set req and signalled");
99 void VompClientRRProc::threadMethod()
102 log->log("RRProc", Log::DEBUG, "threadMethod startup");
104 if (req_queue.size() != 0)
107 - log->log("RRProc", Log::ERR, "threadMethod err 1");
111 That was how the code used to be.
113 TODO: Work out why this happens.
116 log->log("RRProc", Log::ERR, "threadMethod startup with already queued packets");
117 while (req_queue.size())
119 //log->log("RRProc", Log::DEBUG, "thread while");
120 req = req_queue.front();
123 threadUnlock(); // allow recvRequest to be queuing packets while we are working on this one
125 if (!processPacket())
127 log->log("RRProc", Log::ERR, "processPacket exited with fail");
133 log->log("RRProc", Log::ERR, "threadMethod startup with already queued packets done.");
139 log->log("RRProc", Log::DEBUG, "threadMethod waiting");
140 threadWaitForSignal(); // unlocks, waits, relocks
141 if (req_queue.size() == 0)
143 log->log("RRProc", Log::INFO, "threadMethod err 2 or quit");
148 // signalled with something in queue
150 log->log("RRProc", Log::DEBUG, "thread woken with req, queue size: %i", req_queue.size());
152 while (req_queue.size())
154 //log->log("RRProc", Log::DEBUG, "thread while");
155 req = req_queue.front();
158 threadUnlock(); // allow recvRequest to be queuing packets while we are working on this one
160 if (!processPacket())
162 log->log("RRProc", Log::ERR, "processPacket exited with fail");
169 // locked and run out of packets to process
173 bool VompClientRRProc::processPacket()
175 resp = new ResponsePacket();
176 if (!resp->init(req->requestID))
178 log->log("RRProc", Log::ERR, "response packet init fail");
181 if (req->data) free(req->data);
193 result = processLogin();
195 #ifndef VOMPSTANDALONE
197 result = processGetRecordingsList();
200 result = processDeleteRecording();
203 result = processGetChannelsList();
206 result = processStartStreamingChannel();
209 result = processGetBlock();
212 result = processStopStreaming();
215 result = processStartStreamingRecording();
218 result = processGetChannelSchedule();
222 result = processConfigSave();
225 result = processConfigLoad();
227 #ifndef VOMPSTANDALONE
229 result = processReScanRecording(); // FIXME obselete
232 result = processGetTimers();
235 result = processSetTimer();
238 result = processPositionFromFrameNumber();
241 result = processFrameNumberFromPosition();
244 result = processMoveRecording();
247 result = processGetIFrame();
250 result = processGetRecInfo();
253 result = processGetMarks();
256 result = processGetChannelPids();
259 result = processDeleteTimer();
262 case VDR_GETMEDIALIST:
263 result = processGetMediaList();
266 result = processOpenMedia();
268 case VDR_GETMEDIABLOCK:
269 result = processGetMediaBlock();
272 result = processGetLanguageList();
275 result = processGetLanguageContent();
277 case VDR_GETMEDIAINFO:
278 result = processGetMediaInfo();
280 case VDR_CLOSECHANNEL:
281 result = processCloseMediaChannel();
284 result = processSetCharset();
291 if (req->data) free(req->data);
295 if (result) return true;
299 int VompClientRRProc::processLogin()
301 if (req->dataLength != 6) return 0;
305 char configFileName[PATH_MAX];
306 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]);
307 x.config.init(configFileName);
309 // Send the login reply
311 time_t timeNow = time(NULL);
312 struct tm* timeStruct = localtime(&timeNow);
313 int timeOffset = timeStruct->tm_gmtoff;
315 resp->addULONG(timeNow);
316 resp->addLONG(timeOffset);
317 resp->addULONG(VOMP_PROTOCOL_VERSION);
319 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
320 log->log("RRProc", Log::DEBUG, "written login reply len %lu", resp->getLen());
323 x.netLog(); // safe to run here since the client won't start net logging for a while yet
328 int VompClientRRProc::processSetCharset()
330 int charset = ntohl(*(ULONG*)req->data);
331 if (charset>0 && charset<3)
333 log->log("RRProc", Log::DEBUG, "Set charset to %d", charset);
334 x.setCharset(charset);
339 log->log("RRProc", Log::DEBUG, "Invalid charset %d", charset);
343 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
347 int VompClientRRProc::processConfigSave()
349 char* section = (char*)req->data;
353 for (UINT k = 0; k < req->dataLength; k++)
355 if (req->data[k] == '\0')
359 key = (char*)&req->data[k+1];
363 value = (char*)&req->data[k+1];
369 // if the last string (value) doesnt have null terminator, give up
370 if (req->data[req->dataLength - 1] != '\0') return 0;
372 log->log("RRProc", Log::DEBUG, "Config save: %s %s %s", section, key, value);
373 if (x.config.setValueString(section, key, value))
383 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
388 int VompClientRRProc::processConfigLoad()
390 char* section = (char*)req->data;
393 for (UINT k = 0; k < req->dataLength; k++)
395 if (req->data[k] == '\0')
397 key = (char*)&req->data[k+1];
402 char* value = x.config.getValueString(section, key);
406 resp->addString(value);//client coding, do not touch
407 log->log("RRProc", Log::DEBUG, "Written config load packet");
413 log->log("RRProc", Log::DEBUG, "Written config load failed packet");
417 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
423 //helper for sending from a serialize buffer
424 //insert the used len into the first 4 Bytes of the buffer
425 void VompClientRRProc::sendPacket(SerializeBuffer *b) {
426 resp->copyin(b->getStart(),b->getCurrent()-b->getStart());
428 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
432 * media List Request:
433 * Media List response:
436 #define MLISTBUF 500000
437 int VompClientRRProc::processGetMediaList()
439 SerializeBuffer buffer(req->data,req->dataLength);
440 MediaURI uri(0,NULL,NULL);
441 VDR_GetMediaListRequest request(&uri);
442 if (request.deserialize(&buffer) != 0) {
443 log->log("Client", Log::ERR, "getMediaList unable to deserialize");
446 const char *dirname=uri.getName();
447 log->log("Client", Log::DEBUG, "getMediaList for %s", dirname);
450 if (dirname == NULL) {
451 ml=x.media->getRootList();
453 ml=x.media->getMediaList(&uri);
456 log->log("Client", Log::ERR, "getMediaList returned NULL");
459 SerializeBuffer rbuf(MLISTBUF,false,true);
460 ULONG flags=0; //TODO: real error handling by setting flags
461 VDR_GetMediaListResponse response(&flags,ml);
462 if (response.serialize(&rbuf) != 0) {
463 log->log("Client", Log::ERR, "getMediaList returned NULL");
467 log->log("Client", Log::DEBUG, "getMediaList size %u", ml->size());
472 log->log("Client", Log::DEBUG, "Written Media list");
477 * openMedia response:
479 int VompClientRRProc::processOpenMedia()
481 SerializeBuffer buffer(req->data,req->dataLength);
482 MediaURI uri(0,NULL,NULL);
486 VDR_OpenMediumRequest request(&channel,&uri,&xs,&ys);
487 if (request.deserialize(&buffer) != 0) {
488 log->log("Client", Log::ERR, "openMediaRequest unable to deserialize");
491 const char *name=uri.getName();
492 log->log("Client", Log::DEBUG, "openMediaRequest for %s", name);
494 int rt=x.media->openMedium(channel,&uri,&size,xs,ys);
499 log->log("Client", Log::ERR, "openMediaRequest unable to open");
501 VDR_OpenMediumResponse response(&flags,&size);
502 SerializeBuffer rbuf(response.getSerializedLen()+4,false,true);
503 if (response.serialize(&rbuf) != 0) {
504 log->log("Client", Log::ERR, "openMediaRequest cannot serialize");
507 log->log("Client", Log::DEBUG, "openMediaRequest size %llu", size);
514 * packet - no serialized response!
516 int VompClientRRProc::processGetMediaBlock()
518 SerializeBuffer buffer(req->data,req->dataLength);
522 VDR_GetMediaBlockRequest request(&channel,&position,&amount);
523 if (request.deserialize(&buffer) != 0) {
524 log->log("Client", Log::ERR, "getMediaBlock unable to deserialize");
527 log->log("Client", Log::DEBUG, "getMediaBlock pos = %llu length = %lu,chan=%lu", position, amount,channel);
529 UCHAR sendBuffer[amount ];
530 ULONG amountReceived = 0;
531 UCHAR *rbuf=sendBuffer;
532 int rt=x.media->getMediaBlock(channel,position,amount,&amountReceived,&rbuf);
533 if (!amountReceived || rt != 0)
535 log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0");
539 if (rbuf != sendBuffer) {
540 //the provider did not use the optimized handling with using my buffer
541 resp->copyin(rbuf,amountReceived);
544 // the provider did not allocate a new buffer
545 resp->copyin(sendBuffer,amountReceived);
549 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
550 log->log("Client", Log::DEBUG, "written ok %lu", amountReceived);
557 int VompClientRRProc::processGetMediaInfo()
559 SerializeBuffer buffer(req->data,req->dataLength);
561 VDR_GetMediaInfoRequest request(&channel);
562 if (request.deserialize(&buffer) != 0) {
563 log->log("Client", Log::ERR, "getMediaInfo unable to deserialize");
566 log->log("Client", Log::DEBUG, "getMediaInfo chan=%lu", channel);
569 int rt=x.media->getMediaInfo(channel,&mi);
572 log->log("Client", Log::ERR, "getMediaInfo unable to get");
574 VDR_GetMediaInfoResponse response(&flags,&mi);
575 SerializeBuffer rbuf(response.getSerializedLen()+4,false,true);
576 if (response.serialize(&rbuf) != 0) {
577 log->log("Client", Log::ERR, "getMediaInfo cannot serialize");
589 int VompClientRRProc::processCloseMediaChannel()
591 SerializeBuffer buffer(req->data,req->dataLength);
593 VDR_CloseMediaChannelRequest request(&channel);
594 if (request.deserialize(&buffer) != 0) {
595 log->log("Client", Log::ERR, "closeMediaChannel unable to deserialize");
599 log->log("Client", Log::DEBUG, "closeMediaChannel chan=%lu", channel);
600 int rt=x.media->closeMediaChannel(channel);
603 log->log("Client", Log::ERR, "closeMediaChannel unable to get");
605 VDR_CloseMediaChannelResponse response(&flags);
606 SerializeBuffer rbuf(response.getSerializedLen()+4,false,true);
607 if (response.serialize(&rbuf) != 0) {
608 log->log("Client", Log::ERR, "closeMediaChannel cannot serialize");
617 int VompClientRRProc::processGetLanguageList()
619 x.i18n.findLanguages();
620 const I18n::lang_code_list& languages = x.i18n.getLanguageList();
622 I18n::lang_code_list::const_iterator iter;
623 for (iter = languages.begin(); iter != languages.end(); ++iter)
625 resp->addString(iter->first.c_str()); // Source code is acsii
626 resp->addString(x.charconvutf8->Convert(iter->second.c_str())); //translate string can be any utf-8 character
629 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
633 int VompClientRRProc::processGetLanguageContent()
635 if (req->dataLength <= 0) return 0;
636 std::string code, result;
637 code.assign((char*)req->data, req->dataLength - 1);
638 x.i18n.findLanguages();
639 I18n::trans_table texts = x.i18n.getLanguageContent(code);
640 I18n::trans_table::const_iterator iter;
641 for (iter = texts.begin(); iter != texts.end(); ++iter)
643 resp->addString(iter->first.c_str());// source code is acsii since it is english
644 resp->addString(x.charconvutf8->Convert(iter->second.c_str())); // translate text can be any unicode string, it is stored as UTF-8
647 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
651 #ifndef VOMPSTANDALONE
653 int VompClientRRProc::processGetRecordingsList()
656 int Percent = VideoDiskSpace(&FreeMB);
657 int Total = (FreeMB / (100 - Percent)) * 100;
659 resp->addULONG(Total);
660 resp->addULONG(FreeMB);
661 resp->addULONG(Percent);
663 cRecordings Recordings;
666 for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
668 #if VDRVERSNUM < 10721
669 resp->addULONG(recording->start);
671 resp->addULONG(recording->Start());
673 resp->addUCHAR(recording->IsNew() ? 1 : 0);
674 resp->addString(x.charconvsys->Convert(recording->Name())); //coding of recording name is system dependent
675 resp->addString(recording->FileName());//file name are not visible by user do not touch
679 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
681 log->log("RRProc", Log::DEBUG, "Written recordings list");
686 int VompClientRRProc::processDeleteRecording()
688 // data is a pointer to the fileName string
690 cRecordings Recordings;
691 Recordings.Load(); // probably have to do this
693 cRecording* recording = Recordings.GetByName((char*)req->data);
695 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
699 log->log("RRProc", Log::DEBUG, "deleting recording: %s", recording->Name());
701 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
704 if (recording->Delete())
706 // Copy svdrp's way of doing this, see if it works
707 #if VDRVERSNUM > 10300
708 ::Recordings.DelByName(recording->FileName());
728 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
733 int VompClientRRProc::processMoveRecording()
735 log->log("RRProc", Log::DEBUG, "Process move recording");
736 char* fileName = (char*)req->data;
737 char* newPath = NULL;
739 for (UINT k = 0; k < req->dataLength; k++)
741 if (req->data[k] == '\0')
743 newPath = (char*)&req->data[k+1];
747 if (!newPath) return 0;
749 cRecordings Recordings;
750 Recordings.Load(); // probably have to do this
752 cRecording* recording = Recordings.GetByName((char*)fileName);
754 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
758 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
761 log->log("RRProc", Log::DEBUG, "moving recording: %s", recording->Name());
762 log->log("RRProc", Log::DEBUG, "moving recording: %s", recording->FileName());
763 log->log("RRProc", Log::DEBUG, "to: %s", newPath);
765 const char* t = recording->FileName();
767 char* dateDirName = NULL; int k;
768 char* titleDirName = NULL; int j;
770 // Find the datedirname
771 for(k = strlen(t) - 1; k >= 0; k--)
775 log->log("RRProc", Log::DEBUG, "l1: %i", strlen(&t[k+1]) + 1);
776 dateDirName = new char[strlen(&t[k+1]) + 1];
777 strcpy(dateDirName, &t[k+1]);
782 // Find the titledirname
784 for(j = k-1; j >= 0; j--)
788 log->log("RRProc", Log::DEBUG, "l2: %i", (k - j - 1) + 1);
789 titleDirName = new char[(k - j - 1) + 1];
790 memcpy(titleDirName, &t[j+1], k - j - 1);
791 titleDirName[k - j - 1] = '\0';
796 log->log("RRProc", Log::DEBUG, "datedirname: %s", dateDirName);
797 log->log("RRProc", Log::DEBUG, "titledirname: %s", titleDirName);
798 log->log("RRProc", Log::DEBUG, "viddir: %s", VideoDirectory);
800 char* newPathConv = new char[strlen(newPath)+1];
801 strcpy(newPathConv, newPath);
802 ExchangeChars(newPathConv, true);
803 log->log("RRProc", Log::DEBUG, "EC: %s", newPathConv);
805 char* newContainer = new char[strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1];
806 log->log("RRProc", Log::DEBUG, "l10: %i", strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1);
807 sprintf(newContainer, "%s%s%s", VideoDirectory, newPathConv, titleDirName);
808 delete[] newPathConv;
810 log->log("RRProc", Log::DEBUG, "%s", newContainer);
813 int statret = stat(newContainer, &dstat);
814 if ((statret == -1) && (errno == ENOENT)) // Dir does not exist
816 log->log("RRProc", Log::DEBUG, "new dir does not exist");
817 int mkdirret = mkdir(newContainer, 0755);
820 delete[] dateDirName;
821 delete[] titleDirName;
822 delete[] newContainer;
826 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
830 else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR))) // Something exists but it's not a dir
832 delete[] dateDirName;
833 delete[] titleDirName;
834 delete[] newContainer;
838 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
842 // Ok, the directory container has been made, or it pre-existed.
844 char* newDir = new char[strlen(newContainer) + 1 + strlen(dateDirName) + 1];
845 sprintf(newDir, "%s/%s", newContainer, dateDirName);
847 log->log("RRProc", Log::DEBUG, "doing rename '%s' '%s'", t, newDir);
848 int renameret = rename(t, newDir);
851 // Success. Test for remove old dir containter
852 char* oldTitleDir = new char[k+1];
853 memcpy(oldTitleDir, t, k);
854 oldTitleDir[k] = '\0';
855 log->log("RRProc", Log::DEBUG, "len: %i, cp: %i, strlen: %i, oldtitledir: %s", k+1, k, strlen(oldTitleDir), oldTitleDir);
856 rmdir(oldTitleDir); // can't do anything about a fail result at this point.
857 delete[] oldTitleDir;
862 #if VDRVERSNUM > 10311
864 ::Recordings.Update();
866 // Success. Send a different packet from just a ulong
867 resp->addULONG(1); // success
868 resp->addString(newDir); //system depent do not convert
876 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
878 delete[] dateDirName;
879 delete[] titleDirName;
880 delete[] newContainer;
887 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
894 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
900 int VompClientRRProc::processGetChannelsList()
904 char* chanConfig = x.config.getValueString("General", "Channels");
906 if (chanConfig) allChans = strcasecmp(chanConfig, "FTA only");
908 for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
910 #if VDRVERSNUM < 10300
911 if (!channel->GroupSep() && (!channel->Ca() || allChans))
913 if (!channel->GroupSep() && (!channel->Ca(0) || allChans))
916 log->log("RRProc", Log::DEBUG, "name: '%s'", channel->Name());
918 if (channel->Vpid()) type = 1;
919 #if VDRVERSNUM < 10300
922 else if (channel->Apid(0)) type = 2;
926 resp->addULONG(channel->Number());
927 resp->addULONG(type);
928 resp->addString(x.charconvsys->Convert(channel->Name()));
929 #if VDRVERSNUM < 10703
932 resp->addULONG(channel->Vtype());
938 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
940 log->log("RRProc", Log::DEBUG, "Written channels list");
945 int VompClientRRProc::processGetChannelPids()
947 ULONG channelNumber = ntohl(*(ULONG*)req->data);
949 cChannel* channel = x.channelFromNumber(channelNumber);
954 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
963 #if VDRVERSNUM < 10300
965 log->log("RRProc", Log::DEBUG, "Apid1: %i", channel->Apid1());
966 log->log("RRProc", Log::DEBUG, "Apid2: %i", channel->Apid2());
968 if (channel->Apid2())
970 else if (channel->Apid1())
977 for (const int *Apid = channel->Apids(); *Apid; Apid++)
981 for (const int *Dpid = channel->Dpids(); *Dpid; Dpid++)
985 for (const int *Spid = channel->Spids(); *Spid; Spid++)
992 // Format of response
1011 resp->addULONG(channel->Vpid());
1012 #if VDRVERSNUM < 10703
1015 resp->addULONG(channel->Vtype());
1017 resp->addULONG(numApids);
1019 #if VDRVERSNUM < 10300
1022 resp->addULONG(channel->Apid1());
1023 resp->addString("");
1027 resp->addULONG(channel->Apid2());
1028 resp->addString("");
1033 for (ULONG i = 0; i < numApids; i++)
1035 resp->addULONG(channel->Apid(i));
1036 resp->addString(x.charconvsys->Convert(channel->Alang(i)));
1038 resp->addULONG(numDpids);
1039 for (ULONG i = 0; i < numDpids; i++)
1041 resp->addULONG(channel->Dpid(i));
1042 resp->addString(x.charconvsys->Convert(channel->Dlang(i)));
1044 resp->addULONG(numSpids);
1045 for (ULONG i = 0; i < numSpids; i++)
1047 resp->addULONG(channel->Spid(i));
1048 resp->addString(x.charconvsys->Convert(channel->Slang(i)));
1051 resp->addULONG(channel->Tpid());
1052 // Format of extended response, for compatibility with older client at the end
1064 #if VDRVERSNUM < 10300
1074 for (ULONG i = 0; i < numApids; i++)
1076 #if VDRVERSNUM < 10715
1079 resp->addULONG(channel->Atype(i));
1082 for (ULONG i = 0; i < numDpids; i++)
1084 #if VDRVERSNUM < 10715
1085 resp->addULONG(0x6A /*AC3*/);
1087 resp->addULONG(channel->Dtype(i));
1090 for (ULONG i = 0; i < numSpids; i++)
1092 #if VDRVERSNUM < 10715
1097 resp->addULONG(channel->SubtitlingType(i));
1098 resp->addULONG(channel->CompositionPageId(i));
1099 resp->addULONG(channel->AncillaryPageId(i));
1106 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1108 log->log("RRProc", Log::DEBUG, "Written channels pids");
1113 int VompClientRRProc::processStartStreamingChannel()
1117 log->log("RRProc", Log::ERR, "Client called start streaming twice");
1121 log->log("RRProc", Log::DEBUG, "req->dataLength = %i", req->dataLength);
1122 ULONG channelNumber = ntohl(*(ULONG*)req->data);
1124 cChannel* channel = x.channelFromNumber(channelNumber);
1129 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1133 // get the priority we should use
1135 int priority = x.config.getValueLong("General", "Live priority", &fail);
1138 log->log("RRProc", Log::DEBUG, "Config: Live TV priority: %i", priority);
1142 log->log("RRProc", Log::DEBUG, "Config: Live TV priority config fail");
1146 // a bit of sanity..
1147 #if VDRVERSNUM < 10725
1148 if (priority < 0) priority = 0;
1150 if (priority < -99) priority = -99;
1152 if (priority > 99) priority = 99;
1154 log->log("RRProc", Log::DEBUG, "Using live TV priority %i", priority);
1155 x.lp = MVPReceiver::create(channel, priority);
1161 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1165 if (!x.lp->init(&x.tcp, req->requestID))
1171 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1177 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1181 int VompClientRRProc::processStopStreaming()
1183 log->log("RRProc", Log::DEBUG, "STOP STREAMING RECEIVED");
1186 x.lp->detachMVPReceiver();
1190 else if (x.recplayer)
1192 x.writeResumeData();
1195 delete x.recordingManager;
1197 x.recordingManager = NULL;
1202 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1206 int VompClientRRProc::processGetBlock()
1210 log->log("RRProc", Log::ERR, "Get block called during live streaming");
1216 log->log("RRProc", Log::ERR, "Get block called when no recording open");
1220 UCHAR* data = req->data;
1222 ULLONG position = x.ntohll(*(ULLONG*)data);
1223 data += sizeof(ULLONG);
1224 ULONG amount = ntohl(*(ULONG*)data);
1226 log->log("RRProc", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount);
1228 UCHAR sendBuffer[amount];
1229 ULONG amountReceived = x.recplayer->getBlock(&sendBuffer[0], position, amount);
1231 if (!amountReceived)
1234 log->log("RRProc", Log::DEBUG, "written 4(0) as getblock got 0");
1238 resp->copyin(sendBuffer, amountReceived);
1239 log->log("RRProc", Log::DEBUG, "written %lu", amountReceived);
1243 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1244 log->log("RRProc", Log::DEBUG, "Finished getblock, have sent %lu", resp->getLen());
1248 int VompClientRRProc::processStartStreamingRecording()
1250 // data is a pointer to the fileName string
1252 x.recordingManager = new cRecordings;
1253 x.recordingManager->Load();
1255 cRecording* recording = x.recordingManager->GetByName((char*)req->data);
1257 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
1261 x.recplayer = new RecPlayer(recording);
1263 resp->addULLONG(x.recplayer->getLengthBytes());
1264 resp->addULONG(x.recplayer->getLengthFrames());
1266 #if VDRVERSNUM < 10703
1267 resp->addUCHAR(true);//added for TS
1269 resp->addUCHAR(recording->IsPesRecording());//added for TS
1273 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1275 log->log("RRProc", Log::DEBUG, "written totalLength");
1279 delete x.recordingManager;
1280 x.recordingManager = NULL;
1285 int VompClientRRProc::processPositionFromFrameNumber()
1289 ULONG frameNumber = ntohl(*(ULONG*)req->data);
1293 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1297 retval = x.recplayer->positionFromFrameNumber(frameNumber);
1300 resp->addULLONG(retval);
1302 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1304 log->log("RRProc", Log::DEBUG, "Wrote posFromFrameNum reply to client");
1308 int VompClientRRProc::processFrameNumberFromPosition()
1312 ULLONG position = x.ntohll(*(ULLONG*)req->data);
1316 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1320 retval = x.recplayer->frameNumberFromPosition(position);
1323 resp->addULONG(retval);
1325 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1327 log->log("RRProc", Log::DEBUG, "Wrote frameNumFromPos reply to client");
1331 int VompClientRRProc::processGetIFrame()
1333 bool success = false;
1335 ULONG* data = (ULONG*)req->data;
1337 ULONG frameNumber = ntohl(*data);
1339 ULONG direction = ntohl(*data);
1341 ULLONG rfilePosition = 0;
1342 ULONG rframeNumber = 0;
1343 ULONG rframeLength = 0;
1347 log->log("RRProc", Log::DEBUG, "GetIFrame recording called when no recording being played!");
1351 success = x.recplayer->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength);
1354 // returns file position, frame number, length
1358 resp->addULLONG(rfilePosition);
1359 resp->addULONG(rframeNumber);
1360 resp->addULONG(rframeLength);
1368 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1370 log->log("RRProc", Log::DEBUG, "Wrote GNIF reply to client %llu %lu %lu", rfilePosition, rframeNumber, rframeLength);
1374 int VompClientRRProc::processGetChannelSchedule()
1376 ULONG* data = (ULONG*)req->data;
1378 ULONG channelNumber = ntohl(*data);
1380 ULONG startTime = ntohl(*data);
1382 ULONG duration = ntohl(*data);
1384 log->log("RRProc", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
1386 cChannel* channel = x.channelFromNumber(channelNumber);
1391 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1393 log->log("RRProc", Log::DEBUG, "written 0 because channel = NULL");
1397 log->log("RRProc", Log::DEBUG, "Got channel");
1399 #if VDRVERSNUM < 10300
1400 cMutexLock MutexLock;
1401 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1403 cSchedulesLock MutexLock;
1404 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1410 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1412 log->log("RRProc", Log::DEBUG, "written 0 because Schedule!s! = NULL");
1416 log->log("RRProc", Log::DEBUG, "Got schedule!s! object");
1418 const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
1423 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1425 log->log("RRProc", Log::DEBUG, "written 0 because Schedule = NULL");
1429 log->log("RRProc", Log::DEBUG, "Got schedule object");
1431 const char* empty = "";
1432 bool atLeastOneEvent = false;
1435 ULONG thisEventTime;
1436 ULONG thisEventDuration;
1437 const char* thisEventTitle;
1438 const char* thisEventSubTitle;
1439 const char* thisEventDescription;
1441 #if VDRVERSNUM < 10300
1443 const cEventInfo *event;
1444 for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1446 event = Schedule->GetEventNumber(eventNumber);
1448 thisEventID = event->GetEventID();
1449 thisEventTime = event->GetTime();
1450 thisEventDuration = event->GetDuration();
1451 thisEventTitle = event->GetTitle();
1452 thisEventSubTitle = event->GetSubtitle();
1453 thisEventDescription = event->GetExtendedDescription();
1457 for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
1459 thisEventID = event->EventID();
1460 thisEventTime = event->StartTime();
1461 thisEventDuration = event->Duration();
1462 thisEventTitle = event->Title();
1463 thisEventSubTitle = NULL;
1464 thisEventDescription = event->Description();
1468 //in the past filter
1469 if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue;
1472 if ((thisEventTime + thisEventDuration) <= startTime) continue;
1475 if (thisEventTime >= (startTime + duration)) continue;
1477 if (!thisEventTitle) thisEventTitle = empty;
1478 if (!thisEventSubTitle) thisEventSubTitle = empty;
1479 if (!thisEventDescription) thisEventDescription = empty;
1481 resp->addULONG(thisEventID);
1482 resp->addULONG(thisEventTime);
1483 resp->addULONG(thisEventDuration);
1485 resp->addString(x.charconvsys->Convert(thisEventTitle));
1486 resp->addString(x.charconvsys->Convert(thisEventSubTitle));
1487 resp->addString(x.charconvsys->Convert(thisEventDescription));
1489 atLeastOneEvent = true;
1492 log->log("RRProc", Log::DEBUG, "Got all event data");
1494 if (!atLeastOneEvent)
1497 log->log("RRProc", Log::DEBUG, "Written 0 because no data");
1501 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1503 log->log("RRProc", Log::DEBUG, "written schedules packet");
1508 int VompClientRRProc::processGetTimers()
1511 int numTimers = Timers.Count();
1513 resp->addULONG(numTimers);
1515 for (int i = 0; i < numTimers; i++)
1517 timer = Timers.Get(i);
1519 #if VDRVERSNUM < 10300
1520 resp->addULONG(timer->Active());
1522 resp->addULONG(timer->HasFlags(tfActive));
1524 resp->addULONG(timer->Recording());
1525 resp->addULONG(timer->Pending());
1526 resp->addULONG(timer->Priority());
1527 resp->addULONG(timer->Lifetime());
1528 resp->addULONG(timer->Channel()->Number());
1529 resp->addULONG(timer->StartTime());
1530 resp->addULONG(timer->StopTime());
1531 resp->addULONG(timer->Day());
1532 resp->addULONG(timer->WeekDays());
1533 resp->addString(timer->File()); //Filename is system specific and not visible by user
1537 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1539 log->log("RRProc", Log::DEBUG, "Written timers list");
1544 int VompClientRRProc::processSetTimer()
1546 char* timerString = new char[strlen((char*)req->data) + 1];
1547 strcpy(timerString, (char*)req->data);
1549 #if VDRVERSNUM < 10300
1551 // If this is VDR 1.2 the date part of the timer string must be reduced
1552 // to just DD rather than YYYY-MM-DD
1554 int s = 0; // source
1555 int d = 0; // destination
1557 while(c != 2) // copy up to date section, including the second ':'
1559 timerString[d] = req->data[s];
1560 if (req->data[s] == ':') c++;
1564 // now it has copied up to the date section
1566 while(c != 2) // waste YYYY-MM-
1568 if (req->data[s] == '-') c++;
1571 // now source is at the DD
1572 memcpy(&timerString[d], &req->data[s], req->dataLength - s);
1573 d += req->dataLength - s;
1574 timerString[d] = '\0';
1576 log->log("RRProc", Log::DEBUG, "Timer string after 1.2 conversion:");
1579 log->log("RRProc", Log::DEBUG, "%s", timerString);
1581 cTimer *timer = new cTimer;
1582 if (timer->Parse((char*)timerString))
1584 cTimer *t = Timers.GetTimer(timer);
1588 #if VDRVERSNUM < 10300
1591 Timers.SetModified();
1595 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1596 return 1; // FIXME - cTimer* timer is leaked here!
1602 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1609 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1615 int VompClientRRProc::processDeleteTimer()
1617 log->log("RRProc", Log::DEBUG, "Delete timer called");
1622 INT delChannel = ntohl(*(ULONG*)&req->data[position]); position += 4;
1623 INT delWeekdays = ntohl(*(ULONG*)&req->data[position]); position += 4;
1624 INT delDay = ntohl(*(ULONG*)&req->data[position]); position += 4;
1625 INT delStart = ntohl(*(ULONG*)&req->data[position]); position += 4;
1626 INT delStop = ntohl(*(ULONG*)&req->data[position]); position += 4;
1629 for (ti = Timers.First(); ti; ti = Timers.Next(ti))
1631 if ( (ti->Channel()->Number() == delChannel)
1632 && ((ti->WeekDays() && (ti->WeekDays() == delWeekdays)) || (!ti->WeekDays() && (ti->Day() == delDay)))
1633 && (ti->StartTime() == delStart)
1634 && (ti->StopTime() == delStop) )
1642 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1646 if (!Timers.BeingEdited())
1648 if (!ti->Recording())
1651 Timers.SetModified();
1654 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1659 log->log("RRProc", Log::ERR, "Unable to delete timer - timer is running");
1662 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1668 log->log("RRProc", Log::ERR, "Unable to delete timer - timers being edited at VDR");
1671 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1676 int VompClientRRProc::processGetRecInfo()
1678 // data is a pointer to the fileName string
1680 cRecordings Recordings;
1681 Recordings.Load(); // probably have to do this
1683 cRecording *recording = Recordings.GetByName((char*)req->data);
1685 time_t timerStart = 0;
1686 time_t timerStop = 0;
1687 char* summary = NULL;
1688 char* shorttext = NULL;
1689 char* description = NULL;
1690 bool newsummary=false;
1691 ULONG resumePoint = 0;
1695 log->log("RRProc", Log::ERR, "GetRecInfo found no recording");
1698 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1703 4 bytes: start time for timer
1704 4 bytes: end time for timer
1705 4 bytes: resume point
1707 4 bytes: num components
1714 8 bytes: frames per second
1717 // Get current timer
1719 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
1722 timerStart = rc->Timer()->StartTime();
1723 timerStop = rc->Timer()->StopTime();
1724 log->log("RRProc", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop);
1727 resp->addULONG(timerStart);
1728 resp->addULONG(timerStop);
1732 /* char* value = x.config.getValueString("ResumeData", (char*)req->data);
1735 resumePoint = strtoul(value, NULL, 10);
1739 char* ResumeIdC = x.config.getValueString("General", "ResumeId");
1742 ResumeId = atoi(ResumeIdC);
1746 ResumeId = 0; //default if not defined in vomp-MAC.conf
1748 while (ResumeIDLock)
1749 cCondWait::SleepMs(100);
1750 ResumeIDLock = true;
1751 int OldSetupResumeID = Setup.ResumeID;
1752 Setup.ResumeID = ResumeId; //UGLY: quickly change resumeid
1753 #if VDRVERSNUM < 10703
1754 cResumeFile ResumeFile(recording->FileName()); //get corresponding resume file
1756 cResumeFile ResumeFile(recording->FileName(), recording->IsPesRecording()); //get corresponding resume file
1758 Setup.ResumeID = OldSetupResumeID; //and restore it back
1759 ResumeIDLock = false;
1761 int resume = ResumeFile.Read();
1762 //isyslog("VOMPDEBUG: resumePoint = %i, resume = %i, ResumeId = %i",resumePoint, resume, ResumeId);
1764 resumePoint = ResumeFile.Read();
1766 log->log("RRProc", Log::DEBUG, "GRI: RP: %lu", resumePoint);
1768 resp->addULONG(resumePoint);
1772 #if VDRVERSNUM < 10300
1773 summary = (char*)recording->Summary();
1775 const cRecordingInfo *Info = recording->Info();
1776 shorttext = (char*)Info->ShortText();
1777 description = (char*) (char*)Info->Description();
1778 if (isempty(shorttext)) summary=description;
1779 else if (isempty(description)) summary=shorttext;
1781 int length=strlen(description)+strlen(shorttext)+4;
1782 summary=new char[length];
1783 snprintf(summary,length,"%s\n\n%s",shorttext,description);
1787 if (isempty(summary)) summary = (char*)Info->Description();
1789 log->log("RRProc", Log::DEBUG, "GRI: S: %s", summary);
1792 resp->addString(x.charconvsys->Convert(summary));
1793 if (newsummary) delete [] summary;
1797 resp->addString("");
1802 #if VDRVERSNUM < 10300
1804 // Send 0 for numchannels - this signals the client this info is not available
1808 const cComponents* components = Info->Components();
1810 log->log("RRProc", Log::DEBUG, "GRI: D1: %p", components);
1818 resp->addULONG(components->NumComponents());
1820 tComponent* component;
1821 for (int i = 0; i < components->NumComponents(); i++)
1823 component = components->Component(i);
1825 log->log("RRProc", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
1827 resp->addUCHAR(component->stream);
1828 resp->addUCHAR(component->type);
1830 if (component->language)
1832 resp->addString(x.charconvsys->Convert(component->language));
1836 resp->addString("");
1838 if (component->description)
1840 resp->addString(x.charconvsys->Convert(component->description));
1844 resp->addString("");
1850 double framespersec;
1851 #if VDRVERSNUM < 10703
1852 framespersec = FRAMESPERSEC;
1854 framespersec = Info->FramesPerSecond();
1856 resp->adddouble(framespersec);
1861 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1863 log->log("RRProc", Log::DEBUG, "Written getrecinfo");
1873 int VompClientRRProc::processReScanRecording()
1877 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1881 x.recplayer->scan();
1883 resp->addULLONG(x.recplayer->getLengthBytes());
1884 resp->addULONG(x.recplayer->getLengthFrames());
1886 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1887 log->log("RRProc", Log::DEBUG, "Rescan recording, wrote new length to client");
1891 // FIXME without client calling rescan, getblock wont work even tho more data is avail
1893 int VompClientRRProc::processGetMarks()
1895 // data is a pointer to the fileName string
1898 cRecordings Recordings;
1899 Recordings.Load(); // probably have to do this
1901 cRecording *recording = Recordings.GetByName((char*)req->data);
1903 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
1907 #if VDRVERSNUM < 10703
1908 Marks.Load(recording->FileName());
1910 Marks.Load(recording->FileName(), recording->FramesPerSecond(), recording->IsPesRecording());
1914 for (const cMark *m = Marks.First(); m; m = Marks.Next(m))
1916 #if VDRVERSNUM < 10721
1917 ULLONG mposition = m->position;
1919 ULLONG mposition = m->Position();
1921 log->log("RRProc", Log::DEBUG, "found Mark %i", mposition);
1923 resp->addULONG(mposition);
1928 log->log("RRProc", Log::DEBUG, "no marks found, sending 0-mark");
1934 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1936 log->log("RRProc", Log::DEBUG, "Written Marks list");
1941 #endif // !VOMPSTANDALONE