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 <vdr/remote.h>
31 #include "recplayer.h"
32 #include "mvpreceiver.h"
35 #include "vompclientrrproc.h"
36 #include "vompclient.h"
39 #include "mediaplayer.h"
40 #include "servermediafile.h"
42 #include "vdrcommand.h"
46 ULONG VompClientRRProc::VOMP_PROTOCOL_VERSION = 0x00000300;
48 // cc is release protocol version, increase with every release, that changes protocol
49 // dd is development protocol version, set to zero at every release,
50 // increase for every protocol change in git
51 // bb not equal zero should indicate a non loggytronic protocol
52 // aa is reserved for future use
54 ULONG VompClientRRProc::getProtocolVersion()
56 return VOMP_PROTOCOL_VERSION;
59 VompClientRRProc::VompClientRRProc(VompClient& x)
62 log = Log::getInstance();
67 VompClientRRProc::~VompClientRRProc()
72 bool VompClientRRProc::init()
74 int a = threadStart();
79 bool VompClientRRProc::recvRequest(RequestPacket* newRequest)
83 Now we have a queue system is used,
84 since on rare occasion the client fire two request at once
85 e.g. heavily channel switching
86 then processing only a single request would cause a deadlock in the client
90 log->log("RRProc", Log::DEBUG, "recvReq");
92 req_queue.push(newRequest);
94 log->log("RRProc", Log::DEBUG, "recvReq set req and signalled");
100 void VompClientRRProc::threadMethod()
103 log->log("RRProc", Log::DEBUG, "threadMethod startup");
105 if (req_queue.size() != 0)
108 - log->log("RRProc", Log::ERR, "threadMethod err 1");
112 That was how the code used to be.
114 TODO: Work out why this happens.
117 log->log("RRProc", Log::ERR, "threadMethod startup with already queued packets");
118 while (req_queue.size())
120 //log->log("RRProc", Log::DEBUG, "thread while");
121 req = req_queue.front();
124 threadUnlock(); // allow recvRequest to be queuing packets while we are working on this one
126 if (!processPacket())
128 log->log("RRProc", Log::ERR, "processPacket exited with fail");
134 log->log("RRProc", Log::ERR, "threadMethod startup with already queued packets done.");
140 log->log("RRProc", Log::DEBUG, "threadMethod waiting");
141 threadWaitForSignal(); // unlocks, waits, relocks
142 if (req_queue.size() == 0)
144 log->log("RRProc", Log::INFO, "threadMethod err 2 or quit");
149 // signalled with something in queue
151 log->log("RRProc", Log::DEBUG, "thread woken with req, queue size: %i", req_queue.size());
153 while (req_queue.size())
155 //log->log("RRProc", Log::DEBUG, "thread while");
156 req = req_queue.front();
159 threadUnlock(); // allow recvRequest to be queuing packets while we are working on this one
161 if (!processPacket())
163 log->log("RRProc", Log::ERR, "processPacket exited with fail");
170 // locked and run out of packets to process
174 bool VompClientRRProc::processPacket()
176 resp = new ResponsePacket();
177 if (!resp->init(req->requestID))
179 log->log("RRProc", Log::ERR, "response packet init fail");
182 if (req->data) free(req->data);
194 result = processLogin();
196 #ifndef VOMPSTANDALONE
198 result = processGetRecordingsList();
201 result = processDeleteRecording();
204 result = processGetChannelsList();
207 result = processStartStreamingChannel();
210 result = processGetBlock();
213 result = processStopStreaming();
216 result = processStartStreamingRecording();
219 result = processGetChannelSchedule();
223 result = processConfigSave();
226 result = processConfigLoad();
228 #ifndef VOMPSTANDALONE
230 result = processReScanRecording(); // FIXME obselete
233 result = processGetTimers();
236 result = processSetTimer();
239 result = processPositionFromFrameNumber();
242 result = processFrameNumberFromPosition();
245 result = processMoveRecording();
248 result = processGetIFrame();
251 result = processGetRecInfo();
254 result = processGetMarks();
257 result = processGetChannelPids();
260 result = processDeleteTimer();
263 result = processVDRShutdown();
266 case VDR_GETMEDIALIST:
267 result = processGetMediaList();
270 result = processOpenMedia();
272 case VDR_GETMEDIABLOCK:
273 result = processGetMediaBlock();
276 result = processGetLanguageList();
279 result = processGetLanguageContent();
281 case VDR_GETMEDIAINFO:
282 result = processGetMediaInfo();
284 case VDR_CLOSECHANNEL:
285 result = processCloseMediaChannel();
288 result = processSetCharset();
295 if (req->data) free(req->data);
299 if (result) return true;
303 int VompClientRRProc::processLogin()
305 if (req->dataLength != 6) return 0;
309 char configFileName[PATH_MAX];
310 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]);
311 x.config.init(configFileName);
313 // Send the login reply
315 time_t timeNow = time(NULL);
316 struct tm* timeStruct = localtime(&timeNow);
317 int timeOffset = timeStruct->tm_gmtoff;
319 resp->addULONG(timeNow);
320 resp->addLONG(timeOffset);
321 resp->addULONG(VOMP_PROTOCOL_VERSION);
323 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
324 log->log("RRProc", Log::DEBUG, "written login reply len %lu", resp->getLen());
327 x.netLog(); // safe to run here since the client won't start net logging for a while yet
332 int VompClientRRProc::processSetCharset()
334 int charset = ntohl(*(ULONG*)req->data);
335 if (charset>0 && charset<3)
337 log->log("RRProc", Log::DEBUG, "Set charset to %d", charset);
338 x.setCharset(charset);
343 log->log("RRProc", Log::DEBUG, "Invalid charset %d", charset);
347 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
351 int VompClientRRProc::processConfigSave()
353 char* section = (char*)req->data;
357 for (UINT k = 0; k < req->dataLength; k++)
359 if (req->data[k] == '\0')
363 key = (char*)&req->data[k+1];
367 value = (char*)&req->data[k+1];
373 // if the last string (value) doesnt have null terminator, give up
374 if (req->data[req->dataLength - 1] != '\0') return 0;
376 log->log("RRProc", Log::DEBUG, "Config save: %s %s %s", section, key, value);
377 if (x.config.setValueString(section, key, value))
387 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
392 int VompClientRRProc::processConfigLoad()
394 char* section = (char*)req->data;
397 for (UINT k = 0; k < req->dataLength; k++)
399 if (req->data[k] == '\0')
401 key = (char*)&req->data[k+1];
406 char* value = x.config.getValueString(section, key);
410 resp->addString(value);//client coding, do not touch
411 log->log("RRProc", Log::DEBUG, "Written config load packet");
417 log->log("RRProc", Log::DEBUG, "Written config load failed packet");
421 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
427 //helper for sending from a serialize buffer
428 //insert the used len into the first 4 Bytes of the buffer
429 void VompClientRRProc::sendPacket(SerializeBuffer *b) {
430 resp->copyin(b->getStart(),b->getCurrent()-b->getStart());
432 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
436 * media List Request:
437 * Media List response:
440 #define MLISTBUF 500000
441 int VompClientRRProc::processGetMediaList()
443 SerializeBuffer buffer(req->data,req->dataLength);
444 MediaURI uri(0,NULL,NULL);
445 VDR_GetMediaListRequest request(&uri);
446 if (request.deserialize(&buffer) != 0) {
447 log->log("Client", Log::ERR, "getMediaList unable to deserialize");
450 const char *dirname=uri.getName();
451 log->log("Client", Log::DEBUG, "getMediaList for %s", dirname);
454 if (dirname == NULL) {
455 ml=x.media->getRootList();
457 ml=x.media->getMediaList(&uri);
460 log->log("Client", Log::ERR, "getMediaList returned NULL");
463 SerializeBuffer rbuf(MLISTBUF,false,true);
464 ULONG flags=0; //TODO: real error handling by setting flags
465 VDR_GetMediaListResponse response(&flags,ml);
466 if (response.serialize(&rbuf) != 0) {
467 log->log("Client", Log::ERR, "getMediaList returned NULL");
471 log->log("Client", Log::DEBUG, "getMediaList size %u", ml->size());
476 log->log("Client", Log::DEBUG, "Written Media list");
481 * openMedia response:
483 int VompClientRRProc::processOpenMedia()
485 SerializeBuffer buffer(req->data,req->dataLength);
486 MediaURI uri(0,NULL,NULL);
490 VDR_OpenMediumRequest request(&channel,&uri,&xs,&ys);
491 if (request.deserialize(&buffer) != 0) {
492 log->log("Client", Log::ERR, "openMediaRequest unable to deserialize");
495 const char *name=uri.getName();
496 log->log("Client", Log::DEBUG, "openMediaRequest for %s", name);
498 int rt=x.media->openMedium(channel,&uri,&size,xs,ys);
503 log->log("Client", Log::ERR, "openMediaRequest unable to open");
505 VDR_OpenMediumResponse response(&flags,&size);
506 SerializeBuffer rbuf(response.getSerializedLen()+4,false,true);
507 if (response.serialize(&rbuf) != 0) {
508 log->log("Client", Log::ERR, "openMediaRequest cannot serialize");
511 log->log("Client", Log::DEBUG, "openMediaRequest size %llu", size);
518 * packet - no serialized response!
520 int VompClientRRProc::processGetMediaBlock()
522 SerializeBuffer buffer(req->data,req->dataLength);
526 VDR_GetMediaBlockRequest request(&channel,&position,&amount);
527 if (request.deserialize(&buffer) != 0) {
528 log->log("Client", Log::ERR, "getMediaBlock unable to deserialize");
531 log->log("Client", Log::DEBUG, "getMediaBlock pos = %llu length = %lu,chan=%lu", position, amount,channel);
533 UCHAR sendBuffer[amount ];
534 ULONG amountReceived = 0;
535 UCHAR *rbuf=sendBuffer;
536 int rt=x.media->getMediaBlock(channel,position,amount,&amountReceived,&rbuf);
537 if (!amountReceived || rt != 0)
539 log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0");
543 if (rbuf != sendBuffer) {
544 //the provider did not use the optimized handling with using my buffer
545 resp->copyin(rbuf,amountReceived);
548 // the provider did not allocate a new buffer
549 resp->copyin(sendBuffer,amountReceived);
553 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
554 log->log("Client", Log::DEBUG, "written ok %lu", amountReceived);
561 int VompClientRRProc::processGetMediaInfo()
563 SerializeBuffer buffer(req->data,req->dataLength);
565 VDR_GetMediaInfoRequest request(&channel);
566 if (request.deserialize(&buffer) != 0) {
567 log->log("Client", Log::ERR, "getMediaInfo unable to deserialize");
570 log->log("Client", Log::DEBUG, "getMediaInfo chan=%lu", channel);
573 int rt=x.media->getMediaInfo(channel,&mi);
576 log->log("Client", Log::ERR, "getMediaInfo unable to get");
578 VDR_GetMediaInfoResponse response(&flags,&mi);
579 SerializeBuffer rbuf(response.getSerializedLen()+4,false,true);
580 if (response.serialize(&rbuf) != 0) {
581 log->log("Client", Log::ERR, "getMediaInfo cannot serialize");
593 int VompClientRRProc::processCloseMediaChannel()
595 SerializeBuffer buffer(req->data,req->dataLength);
597 VDR_CloseMediaChannelRequest request(&channel);
598 if (request.deserialize(&buffer) != 0) {
599 log->log("Client", Log::ERR, "closeMediaChannel unable to deserialize");
603 log->log("Client", Log::DEBUG, "closeMediaChannel chan=%lu", channel);
604 int rt=x.media->closeMediaChannel(channel);
607 log->log("Client", Log::ERR, "closeMediaChannel unable to get");
609 VDR_CloseMediaChannelResponse response(&flags);
610 SerializeBuffer rbuf(response.getSerializedLen()+4,false,true);
611 if (response.serialize(&rbuf) != 0) {
612 log->log("Client", Log::ERR, "closeMediaChannel cannot serialize");
621 int VompClientRRProc::processGetLanguageList()
623 x.i18n.findLanguages();
624 const I18n::lang_code_list& languages = x.i18n.getLanguageList();
626 I18n::lang_code_list::const_iterator iter;
627 for (iter = languages.begin(); iter != languages.end(); ++iter)
629 resp->addString(iter->first.c_str()); // Source code is acsii
630 resp->addString(x.charconvutf8->Convert(iter->second.c_str())); //translate string can be any utf-8 character
633 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
637 int VompClientRRProc::processGetLanguageContent()
639 if (req->dataLength <= 0) return 0;
640 std::string code, result;
641 code.assign((char*)req->data, req->dataLength - 1);
642 x.i18n.findLanguages();
643 I18n::trans_table texts = x.i18n.getLanguageContent(code);
644 I18n::trans_table::const_iterator iter;
645 for (iter = texts.begin(); iter != texts.end(); ++iter)
647 resp->addString(iter->first.c_str());// source code is acsii since it is english
648 resp->addString(x.charconvutf8->Convert(iter->second.c_str())); // translate text can be any unicode string, it is stored as UTF-8
651 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
655 #ifndef VOMPSTANDALONE
657 int VompClientRRProc::processGetRecordingsList()
660 #if APIVERSNUM > 20101
661 int Percent = cVideoDirectory::VideoDiskSpace(&FreeMB);
663 int Percent = VideoDiskSpace(&FreeMB);
665 int Total = (FreeMB / (100 - Percent)) * 100;
667 resp->addULONG(Total);
668 resp->addULONG(FreeMB);
669 resp->addULONG(Percent);
671 cRecordings Recordings;
674 for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
676 #if VDRVERSNUM < 10721
677 resp->addULONG(recording->start);
679 resp->addULONG(recording->Start());
681 resp->addUCHAR(recording->IsNew() ? 1 : 0);
682 resp->addString(x.charconvsys->Convert(recording->Name())); //coding of recording name is system dependent
683 resp->addString(recording->FileName());//file name are not visible by user do not touch
687 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
689 log->log("RRProc", Log::DEBUG, "Written recordings list");
694 int VompClientRRProc::processDeleteRecording()
696 // data is a pointer to the fileName string
698 cRecordings Recordings;
699 Recordings.Load(); // probably have to do this
701 cRecording* recording = Recordings.GetByName((char*)req->data);
703 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
707 log->log("RRProc", Log::DEBUG, "deleting recording: %s", recording->Name());
709 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
712 if (recording->Delete())
714 // Copy svdrp's way of doing this, see if it works
715 #if VDRVERSNUM > 10300
716 ::Recordings.DelByName(recording->FileName());
736 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
741 int VompClientRRProc::processMoveRecording()
743 log->log("RRProc", Log::DEBUG, "Process move recording");
744 char* fileName = (char*)req->data;
745 char* newPath = NULL;
747 for (UINT k = 0; k < req->dataLength; k++)
749 if (req->data[k] == '\0')
751 newPath = (char*)&req->data[k+1];
755 if (!newPath) return 0;
757 cRecordings Recordings;
758 Recordings.Load(); // probably have to do this
760 cRecording* recording = Recordings.GetByName((char*)fileName);
762 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
766 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
769 log->log("RRProc", Log::DEBUG, "moving recording: %s", recording->Name());
770 log->log("RRProc", Log::DEBUG, "moving recording: %s", recording->FileName());
771 log->log("RRProc", Log::DEBUG, "to: %s", newPath);
773 const char* t = recording->FileName();
775 char* dateDirName = NULL; int k;
776 char* titleDirName = NULL; int j;
778 // Find the datedirname
779 for(k = strlen(t) - 1; k >= 0; k--)
783 log->log("RRProc", Log::DEBUG, "l1: %i", strlen(&t[k+1]) + 1);
784 dateDirName = new char[strlen(&t[k+1]) + 1];
785 strcpy(dateDirName, &t[k+1]);
790 // Find the titledirname
792 for(j = k-1; j >= 0; j--)
796 log->log("RRProc", Log::DEBUG, "l2: %i", (k - j - 1) + 1);
797 titleDirName = new char[(k - j - 1) + 1];
798 memcpy(titleDirName, &t[j+1], k - j - 1);
799 titleDirName[k - j - 1] = '\0';
804 log->log("RRProc", Log::DEBUG, "datedirname: %s", dateDirName);
805 log->log("RRProc", Log::DEBUG, "titledirname: %s", titleDirName);
806 #if APIVERSNUM > 20101
807 log->log("RRProc", Log::DEBUG, "viddir: %s", cVideoDirectory::Name());
809 log->log("RRProc", Log::DEBUG, "viddir: %s", VideoDirectory);
812 char* newPathConv = new char[strlen(newPath)+1];
813 strcpy(newPathConv, newPath);
814 ExchangeChars(newPathConv, true);
815 log->log("RRProc", Log::DEBUG, "EC: %s", newPathConv);
817 #if APIVERSNUM > 20101
818 char* newContainer = new char[strlen(cVideoDirectory::Name()) + strlen(newPathConv) + strlen(titleDirName) + 1];
819 log->log("RRProc", Log::DEBUG, "l10: %i", strlen(cVideoDirectory::Name()) + strlen(newPathConv) + strlen(titleDirName) + 1);
820 sprintf(newContainer, "%s%s%s", cVideoDirectory::Name(), newPathConv, titleDirName);
822 char* newContainer = new char[strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1];
823 log->log("RRProc", Log::DEBUG, "l10: %i", strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1);
824 sprintf(newContainer, "%s%s%s", VideoDirectory, newPathConv, titleDirName);
826 delete[] newPathConv;
828 log->log("RRProc", Log::DEBUG, "%s", newContainer);
831 int statret = stat(newContainer, &dstat);
832 if ((statret == -1) && (errno == ENOENT)) // Dir does not exist
834 log->log("RRProc", Log::DEBUG, "new dir does not exist");
835 int mkdirret = mkdir(newContainer, 0755);
838 delete[] dateDirName;
839 delete[] titleDirName;
840 delete[] newContainer;
844 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
848 else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR))) // Something exists but it's not a dir
850 delete[] dateDirName;
851 delete[] titleDirName;
852 delete[] newContainer;
856 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
860 // Ok, the directory container has been made, or it pre-existed.
862 char* newDir = new char[strlen(newContainer) + 1 + strlen(dateDirName) + 1];
863 sprintf(newDir, "%s/%s", newContainer, dateDirName);
865 log->log("RRProc", Log::DEBUG, "doing rename '%s' '%s'", t, newDir);
866 int renameret = rename(t, newDir);
869 // Success. Test for remove old dir containter
870 char* oldTitleDir = new char[k+1];
871 memcpy(oldTitleDir, t, k);
872 oldTitleDir[k] = '\0';
873 log->log("RRProc", Log::DEBUG, "len: %i, cp: %i, strlen: %i, oldtitledir: %s", k+1, k, strlen(oldTitleDir), oldTitleDir);
874 rmdir(oldTitleDir); // can't do anything about a fail result at this point.
875 delete[] oldTitleDir;
880 #if VDRVERSNUM > 10311
882 ::Recordings.Update();
884 // Success. Send a different packet from just a ulong
885 resp->addULONG(1); // success
886 resp->addString(newDir); //system depent do not convert
894 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
896 delete[] dateDirName;
897 delete[] titleDirName;
898 delete[] newContainer;
905 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
912 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
918 int VompClientRRProc::processGetChannelsList()
922 char* chanConfig = x.config.getValueString("General", "Channels");
924 if (chanConfig) allChans = strcasecmp(chanConfig, "FTA only");
926 for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
928 #if VDRVERSNUM < 10300
929 if (!channel->GroupSep() && (!channel->Ca() || allChans))
931 if (!channel->GroupSep() && (!channel->Ca(0) || allChans))
934 log->log("RRProc", Log::DEBUG, "name: '%s'", channel->Name());
936 if (channel->Vpid()) type = 1;
937 #if VDRVERSNUM < 10300
940 else if (channel->Apid(0)) type = 2;
944 resp->addULONG(channel->Number());
945 resp->addULONG(type);
946 resp->addString(x.charconvsys->Convert(channel->Name()));
947 #if VDRVERSNUM < 10703
950 resp->addULONG(channel->Vtype());
956 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
958 log->log("RRProc", Log::DEBUG, "Written channels list");
963 int VompClientRRProc::processGetChannelPids()
965 ULONG channelNumber = ntohl(*(ULONG*)req->data);
967 cChannel* channel = x.channelFromNumber(channelNumber);
972 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
981 #if VDRVERSNUM < 10300
983 log->log("RRProc", Log::DEBUG, "Apid1: %i", channel->Apid1());
984 log->log("RRProc", Log::DEBUG, "Apid2: %i", channel->Apid2());
986 if (channel->Apid2())
988 else if (channel->Apid1())
995 for (const int *Apid = channel->Apids(); *Apid; Apid++)
999 for (const int *Dpid = channel->Dpids(); *Dpid; Dpid++)
1003 for (const int *Spid = channel->Spids(); *Spid; Spid++)
1010 // Format of response
1029 resp->addULONG(channel->Vpid());
1030 #if VDRVERSNUM < 10703
1033 resp->addULONG(channel->Vtype());
1035 resp->addULONG(numApids);
1037 #if VDRVERSNUM < 10300
1040 resp->addULONG(channel->Apid1());
1041 resp->addString("");
1045 resp->addULONG(channel->Apid2());
1046 resp->addString("");
1051 for (ULONG i = 0; i < numApids; i++)
1053 resp->addULONG(channel->Apid(i));
1054 resp->addString(x.charconvsys->Convert(channel->Alang(i)));
1056 resp->addULONG(numDpids);
1057 for (ULONG i = 0; i < numDpids; i++)
1059 resp->addULONG(channel->Dpid(i));
1060 resp->addString(x.charconvsys->Convert(channel->Dlang(i)));
1062 resp->addULONG(numSpids);
1063 for (ULONG i = 0; i < numSpids; i++)
1065 resp->addULONG(channel->Spid(i));
1066 resp->addString(x.charconvsys->Convert(channel->Slang(i)));
1069 resp->addULONG(channel->Tpid());
1070 // Format of extended response, for compatibility with older client at the end
1082 #if VDRVERSNUM < 10300
1092 for (ULONG i = 0; i < numApids; i++)
1094 #if VDRVERSNUM < 10715
1097 resp->addULONG(channel->Atype(i));
1100 for (ULONG i = 0; i < numDpids; i++)
1102 #if VDRVERSNUM < 10715
1103 resp->addULONG(0x6A /*AC3*/);
1105 resp->addULONG(channel->Dtype(i));
1108 for (ULONG i = 0; i < numSpids; i++)
1110 #if VDRVERSNUM < 10715
1115 resp->addULONG(channel->SubtitlingType(i));
1116 resp->addULONG(channel->CompositionPageId(i));
1117 resp->addULONG(channel->AncillaryPageId(i));
1124 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1126 log->log("RRProc", Log::DEBUG, "Written channels pids");
1131 int VompClientRRProc::processStartStreamingChannel()
1135 log->log("RRProc", Log::ERR, "Client called start streaming twice");
1139 log->log("RRProc", Log::DEBUG, "req->dataLength = %i", req->dataLength);
1140 ULONG channelNumber = ntohl(*(ULONG*)req->data);
1142 cChannel* channel = x.channelFromNumber(channelNumber);
1147 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1151 // get the priority we should use
1153 int priority = x.config.getValueLong("General", "Live priority", &fail);
1156 log->log("RRProc", Log::DEBUG, "Config: Live TV priority: %i", priority);
1160 log->log("RRProc", Log::DEBUG, "Config: Live TV priority config fail");
1164 // a bit of sanity..
1165 #if VDRVERSNUM < 10725
1166 if (priority < 0) priority = 0;
1168 if (priority < -99) priority = -99;
1170 if (priority > 99) priority = 99;
1172 log->log("RRProc", Log::DEBUG, "Using live TV priority %i", priority);
1173 x.lp = MVPReceiver::create(channel, priority);
1179 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1183 if (!x.lp->init(&x.tcp, req->requestID))
1189 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1195 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1199 int VompClientRRProc::processStopStreaming()
1201 log->log("RRProc", Log::DEBUG, "STOP STREAMING RECEIVED");
1204 x.lp->detachMVPReceiver();
1208 else if (x.recplayer)
1210 x.writeResumeData();
1213 delete x.recordingManager;
1215 x.recordingManager = NULL;
1220 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1224 int VompClientRRProc::processGetBlock()
1228 log->log("RRProc", Log::ERR, "Get block called during live streaming");
1234 log->log("RRProc", Log::ERR, "Get block called when no recording open");
1238 UCHAR* data = req->data;
1240 ULLONG position = x.ntohll(*(ULLONG*)data);
1241 data += sizeof(ULLONG);
1242 ULONG amount = ntohl(*(ULONG*)data);
1244 log->log("RRProc", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount);
1246 UCHAR sendBuffer[amount];
1247 ULONG amountReceived = x.recplayer->getBlock(&sendBuffer[0], position, amount);
1249 if (!amountReceived)
1252 log->log("RRProc", Log::DEBUG, "written 4(0) as getblock got 0");
1256 resp->copyin(sendBuffer, amountReceived);
1257 log->log("RRProc", Log::DEBUG, "written %lu", amountReceived);
1261 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1262 log->log("RRProc", Log::DEBUG, "Finished getblock, have sent %lu", resp->getLen());
1266 int VompClientRRProc::processStartStreamingRecording()
1268 // data is a pointer to the fileName string
1270 x.recordingManager = new cRecordings;
1271 x.recordingManager->Load();
1273 cRecording* recording = x.recordingManager->GetByName((char*)req->data);
1275 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
1279 x.recplayer = new RecPlayer(recording);
1281 resp->addULLONG(x.recplayer->getLengthBytes());
1282 resp->addULONG(x.recplayer->getLengthFrames());
1284 #if VDRVERSNUM < 10703
1285 resp->addUCHAR(true);//added for TS
1287 resp->addUCHAR(recording->IsPesRecording());//added for TS
1291 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1293 log->log("RRProc", Log::DEBUG, "written totalLength");
1297 delete x.recordingManager;
1298 x.recordingManager = NULL;
1303 int VompClientRRProc::processPositionFromFrameNumber()
1307 ULONG frameNumber = ntohl(*(ULONG*)req->data);
1311 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1315 retval = x.recplayer->positionFromFrameNumber(frameNumber);
1318 resp->addULLONG(retval);
1320 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1322 log->log("RRProc", Log::DEBUG, "Wrote posFromFrameNum reply to client");
1326 int VompClientRRProc::processFrameNumberFromPosition()
1330 ULLONG position = x.ntohll(*(ULLONG*)req->data);
1334 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1338 retval = x.recplayer->frameNumberFromPosition(position);
1341 resp->addULONG(retval);
1343 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1345 log->log("RRProc", Log::DEBUG, "Wrote frameNumFromPos reply to client");
1349 int VompClientRRProc::processGetIFrame()
1351 bool success = false;
1353 ULONG* data = (ULONG*)req->data;
1355 ULONG frameNumber = ntohl(*data);
1357 ULONG direction = ntohl(*data);
1359 ULLONG rfilePosition = 0;
1360 ULONG rframeNumber = 0;
1361 ULONG rframeLength = 0;
1365 log->log("RRProc", Log::DEBUG, "GetIFrame recording called when no recording being played!");
1369 success = x.recplayer->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength);
1372 // returns file position, frame number, length
1376 resp->addULLONG(rfilePosition);
1377 resp->addULONG(rframeNumber);
1378 resp->addULONG(rframeLength);
1386 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1388 log->log("RRProc", Log::DEBUG, "Wrote GNIF reply to client %llu %lu %lu", rfilePosition, rframeNumber, rframeLength);
1392 int VompClientRRProc::processGetChannelSchedule()
1394 ULONG* data = (ULONG*)req->data;
1396 ULONG channelNumber = ntohl(*data);
1398 ULONG startTime = ntohl(*data);
1400 ULONG duration = ntohl(*data);
1402 log->log("RRProc", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
1404 cChannel* channel = x.channelFromNumber(channelNumber);
1409 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1411 log->log("RRProc", Log::DEBUG, "written 0 because channel = NULL");
1415 log->log("RRProc", Log::DEBUG, "Got channel");
1417 #if VDRVERSNUM < 10300
1418 cMutexLock MutexLock;
1419 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1421 cSchedulesLock MutexLock;
1422 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1428 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1430 log->log("RRProc", Log::DEBUG, "written 0 because Schedule!s! = NULL");
1434 log->log("RRProc", Log::DEBUG, "Got schedule!s! object");
1436 const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
1441 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1443 log->log("RRProc", Log::DEBUG, "written 0 because Schedule = NULL");
1447 log->log("RRProc", Log::DEBUG, "Got schedule object");
1449 const char* empty = "";
1450 bool atLeastOneEvent = false;
1453 ULONG thisEventTime;
1454 ULONG thisEventDuration;
1455 const char* thisEventTitle;
1456 const char* thisEventSubTitle;
1457 const char* thisEventDescription;
1459 #if VDRVERSNUM < 10300
1461 const cEventInfo *event;
1462 for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1464 event = Schedule->GetEventNumber(eventNumber);
1466 thisEventID = event->GetEventID();
1467 thisEventTime = event->GetTime();
1468 thisEventDuration = event->GetDuration();
1469 thisEventTitle = event->GetTitle();
1470 thisEventSubTitle = event->GetSubtitle();
1471 thisEventDescription = event->GetExtendedDescription();
1475 for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
1477 thisEventID = event->EventID();
1478 thisEventTime = event->StartTime();
1479 thisEventDuration = event->Duration();
1480 thisEventTitle = event->Title();
1481 thisEventSubTitle = NULL;
1482 thisEventDescription = event->Description();
1486 //in the past filter
1487 if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue;
1490 if ((thisEventTime + thisEventDuration) <= startTime) continue;
1493 if (thisEventTime >= (startTime + duration)) continue;
1495 if (!thisEventTitle) thisEventTitle = empty;
1496 if (!thisEventSubTitle) thisEventSubTitle = empty;
1497 if (!thisEventDescription) thisEventDescription = empty;
1499 resp->addULONG(thisEventID);
1500 resp->addULONG(thisEventTime);
1501 resp->addULONG(thisEventDuration);
1503 resp->addString(x.charconvsys->Convert(thisEventTitle));
1504 resp->addString(x.charconvsys->Convert(thisEventSubTitle));
1505 resp->addString(x.charconvsys->Convert(thisEventDescription));
1507 atLeastOneEvent = true;
1510 log->log("RRProc", Log::DEBUG, "Got all event data");
1512 if (!atLeastOneEvent)
1515 log->log("RRProc", Log::DEBUG, "Written 0 because no data");
1519 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1521 log->log("RRProc", Log::DEBUG, "written schedules packet");
1526 int VompClientRRProc::processGetTimers()
1529 int numTimers = Timers.Count();
1531 resp->addULONG(numTimers);
1533 for (int i = 0; i < numTimers; i++)
1535 timer = Timers.Get(i);
1537 #if VDRVERSNUM < 10300
1538 resp->addULONG(timer->Active());
1540 resp->addULONG(timer->HasFlags(tfActive));
1542 resp->addULONG(timer->Recording());
1543 resp->addULONG(timer->Pending());
1544 resp->addULONG(timer->Priority());
1545 resp->addULONG(timer->Lifetime());
1546 resp->addULONG(timer->Channel()->Number());
1547 resp->addULONG(timer->StartTime());
1548 resp->addULONG(timer->StopTime());
1549 resp->addULONG(timer->Day());
1550 resp->addULONG(timer->WeekDays());
1551 resp->addString(timer->File()); //Filename is system specific and not visible by user
1555 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1557 log->log("RRProc", Log::DEBUG, "Written timers list");
1562 int VompClientRRProc::processSetTimer()
1564 char* timerString = new char[strlen((char*)req->data) + 1];
1565 strcpy(timerString, (char*)req->data);
1567 #if VDRVERSNUM < 10300
1569 // If this is VDR 1.2 the date part of the timer string must be reduced
1570 // to just DD rather than YYYY-MM-DD
1572 int s = 0; // source
1573 int d = 0; // destination
1575 while(c != 2) // copy up to date section, including the second ':'
1577 timerString[d] = req->data[s];
1578 if (req->data[s] == ':') c++;
1582 // now it has copied up to the date section
1584 while(c != 2) // waste YYYY-MM-
1586 if (req->data[s] == '-') c++;
1589 // now source is at the DD
1590 memcpy(&timerString[d], &req->data[s], req->dataLength - s);
1591 d += req->dataLength - s;
1592 timerString[d] = '\0';
1594 log->log("RRProc", Log::DEBUG, "Timer string after 1.2 conversion:");
1597 log->log("RRProc", Log::DEBUG, "%s", timerString);
1599 cTimer *timer = new cTimer;
1600 if (timer->Parse((char*)timerString))
1602 cTimer *t = Timers.GetTimer(timer);
1606 #if VDRVERSNUM < 10300
1609 Timers.SetModified();
1613 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1614 return 1; // FIXME - cTimer* timer is leaked here!
1620 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1627 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1633 int VompClientRRProc::processDeleteTimer()
1635 log->log("RRProc", Log::DEBUG, "Delete timer called");
1640 INT delChannel = ntohl(*(ULONG*)&req->data[position]); position += 4;
1641 INT delWeekdays = ntohl(*(ULONG*)&req->data[position]); position += 4;
1642 INT delDay = ntohl(*(ULONG*)&req->data[position]); position += 4;
1643 INT delStart = ntohl(*(ULONG*)&req->data[position]); position += 4;
1644 INT delStop = ntohl(*(ULONG*)&req->data[position]); position += 4;
1647 for (ti = Timers.First(); ti; ti = Timers.Next(ti))
1649 if ( (ti->Channel()->Number() == delChannel)
1650 && ((ti->WeekDays() && (ti->WeekDays() == delWeekdays)) || (!ti->WeekDays() && (ti->Day() == delDay)))
1651 && (ti->StartTime() == delStart)
1652 && (ti->StopTime() == delStop) )
1660 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1664 if (!Timers.BeingEdited())
1666 if (!ti->Recording())
1669 Timers.SetModified();
1672 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1677 log->log("RRProc", Log::ERR, "Unable to delete timer - timer is running");
1680 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1686 log->log("RRProc", Log::ERR, "Unable to delete timer - timers being edited at VDR");
1689 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1694 int VompClientRRProc::processGetRecInfo()
1696 // data is a pointer to the fileName string
1698 cRecordings Recordings;
1699 Recordings.Load(); // probably have to do this
1701 cRecording *recording = Recordings.GetByName((char*)req->data);
1703 time_t timerStart = 0;
1704 time_t timerStop = 0;
1705 char* summary = NULL;
1706 char* shorttext = NULL;
1707 char* description = NULL;
1708 bool newsummary=false;
1709 ULONG resumePoint = 0;
1713 log->log("RRProc", Log::ERR, "GetRecInfo found no recording");
1716 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1721 4 bytes: start time for timer
1722 4 bytes: end time for timer
1723 4 bytes: resume point
1725 4 bytes: num components
1732 8 bytes: frames per second
1735 // Get current timer
1737 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
1740 timerStart = rc->Timer()->StartTime();
1741 timerStop = rc->Timer()->StopTime();
1742 log->log("RRProc", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop);
1745 resp->addULONG(timerStart);
1746 resp->addULONG(timerStop);
1750 /* char* value = x.config.getValueString("ResumeData", (char*)req->data);
1753 resumePoint = strtoul(value, NULL, 10);
1757 char* ResumeIdC = x.config.getValueString("General", "ResumeId");
1760 ResumeId = atoi(ResumeIdC);
1764 ResumeId = 0; //default if not defined in vomp-MAC.conf
1766 while (ResumeIDLock)
1767 cCondWait::SleepMs(100);
1768 ResumeIDLock = true;
1769 int OldSetupResumeID = Setup.ResumeID;
1770 Setup.ResumeID = ResumeId; //UGLY: quickly change resumeid
1771 #if VDRVERSNUM < 10703
1772 cResumeFile ResumeFile(recording->FileName()); //get corresponding resume file
1774 cResumeFile ResumeFile(recording->FileName(), recording->IsPesRecording()); //get corresponding resume file
1776 Setup.ResumeID = OldSetupResumeID; //and restore it back
1777 ResumeIDLock = false;
1779 int resume = ResumeFile.Read();
1780 //isyslog("VOMPDEBUG: resumePoint = %i, resume = %i, ResumeId = %i",resumePoint, resume, ResumeId);
1782 resumePoint = ResumeFile.Read();
1784 log->log("RRProc", Log::DEBUG, "GRI: RP: %lu", resumePoint);
1786 resp->addULONG(resumePoint);
1790 #if VDRVERSNUM < 10300
1791 summary = (char*)recording->Summary();
1793 const cRecordingInfo *Info = recording->Info();
1794 shorttext = (char*)Info->ShortText();
1795 description = (char*) (char*)Info->Description();
1796 if (isempty(shorttext)) summary=description;
1797 else if (isempty(description)) summary=shorttext;
1799 int length=strlen(description)+strlen(shorttext)+4;
1800 summary=new char[length];
1801 snprintf(summary,length,"%s\n\n%s",shorttext,description);
1805 if (isempty(summary)) summary = (char*)Info->Description();
1807 log->log("RRProc", Log::DEBUG, "GRI: S: %s", summary);
1810 resp->addString(x.charconvsys->Convert(summary));
1811 if (newsummary) delete [] summary;
1815 resp->addString("");
1820 #if VDRVERSNUM < 10300
1822 // Send 0 for numchannels - this signals the client this info is not available
1826 const cComponents* components = Info->Components();
1828 log->log("RRProc", Log::DEBUG, "GRI: D1: %p", components);
1836 resp->addULONG(components->NumComponents());
1838 tComponent* component;
1839 for (int i = 0; i < components->NumComponents(); i++)
1841 component = components->Component(i);
1843 log->log("RRProc", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
1845 resp->addUCHAR(component->stream);
1846 resp->addUCHAR(component->type);
1848 if (component->language)
1850 resp->addString(x.charconvsys->Convert(component->language));
1854 resp->addString("");
1856 if (component->description)
1858 resp->addString(x.charconvsys->Convert(component->description));
1862 resp->addString("");
1868 double framespersec;
1869 #if VDRVERSNUM < 10703
1870 framespersec = FRAMESPERSEC;
1872 framespersec = Info->FramesPerSecond();
1874 resp->adddouble(framespersec);
1879 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1881 log->log("RRProc", Log::DEBUG, "Written getrecinfo");
1891 int VompClientRRProc::processReScanRecording()
1895 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1899 x.recplayer->scan();
1901 resp->addULLONG(x.recplayer->getLengthBytes());
1902 resp->addULONG(x.recplayer->getLengthFrames());
1904 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1905 log->log("RRProc", Log::DEBUG, "Rescan recording, wrote new length to client");
1909 // FIXME without client calling rescan, getblock wont work even tho more data is avail
1911 int VompClientRRProc::processGetMarks()
1913 // data is a pointer to the fileName string
1916 cRecordings Recordings;
1917 Recordings.Load(); // probably have to do this
1919 cRecording *recording = Recordings.GetByName((char*)req->data);
1921 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
1925 #if VDRVERSNUM < 10703
1926 Marks.Load(recording->FileName());
1928 Marks.Load(recording->FileName(), recording->FramesPerSecond(), recording->IsPesRecording());
1932 for (const cMark *m = Marks.First(); m; m = Marks.Next(m))
1934 #if VDRVERSNUM < 10721
1935 ULLONG mposition = m->position;
1937 ULLONG mposition = m->Position();
1939 log->log("RRProc", Log::DEBUG, "found Mark %i", mposition);
1941 resp->addULONG(mposition);
1946 log->log("RRProc", Log::DEBUG, "no marks found, sending 0-mark");
1952 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1954 log->log("RRProc", Log::DEBUG, "Written Marks list");
1959 int VompClientRRProc::processVDRShutdown()
1961 log->log("RRProc", Log::DEBUG, "Trying to shutdown VDR");
1962 VompClient::decClients(); // Temporarily make this client disappear
1963 cRemote::Put(kPower);
1964 VompClient::incClients();
1966 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1970 #endif // !VOMPSTANDALONE