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 #define VOMP_PROTOCOLL_VERSION 0x00000100
47 // cc is release protocol version, increase with every release, that changes protocoll
48 // dd is development protocol version, set to zero at every release,
49 // increase for every protocoll change in git
50 // bb not equal zero should indicate a non loggytronic protocoll
51 // aa is reserved for future use
53 VompClientRRProc::VompClientRRProc(VompClient& x)
56 log = Log::getInstance();
61 VompClientRRProc::~VompClientRRProc()
66 bool VompClientRRProc::init()
68 int a = threadStart();
73 bool VompClientRRProc::recvRequest(RequestPacket* newRequest)
77 Now we have a queue system is used,
78 since on rare occasion the client fire two request at once
79 e.g. heavily channel switching
80 then processing only a single request would cause a deadlock in the client
84 log->log("RRProc", Log::DEBUG, "recvReq");
86 req_queue.push(newRequest);
88 log->log("RRProc", Log::DEBUG, "recvReq set req and signalled");
94 void VompClientRRProc::threadMethod()
97 log->log("RRProc", Log::DEBUG, "threadMethod startup");
99 if (req_queue.size() != 0)
102 - log->log("RRProc", Log::ERR, "threadMethod err 1");
106 That was how the code used to be.
108 TODO: Work out why this happens.
111 log->log("RRProc", Log::ERR, "threadMethod startup with already queued packets");
112 while (req_queue.size())
114 //log->log("RRProc", Log::DEBUG, "thread while");
115 req = req_queue.front();
118 threadUnlock(); // allow recvRequest to be queuing packets while we are working on this one
120 if (!processPacket())
122 log->log("RRProc", Log::ERR, "processPacket exited with fail");
128 log->log("RRProc", Log::ERR, "threadMethod startup with already queued packets done.");
134 log->log("RRProc", Log::DEBUG, "threadMethod waiting");
135 threadWaitForSignal(); // unlocks, waits, relocks
136 if (req_queue.size() == 0)
138 log->log("RRProc", Log::INFO, "threadMethod err 2 or quit");
143 // signalled with something in queue
145 log->log("RRProc", Log::DEBUG, "thread woken with req, queue size: %i", req_queue.size());
147 while (req_queue.size())
149 //log->log("RRProc", Log::DEBUG, "thread while");
150 req = req_queue.front();
153 threadUnlock(); // allow recvRequest to be queuing packets while we are working on this one
155 if (!processPacket())
157 log->log("RRProc", Log::ERR, "processPacket exited with fail");
164 // locked and run out of packets to process
168 bool VompClientRRProc::processPacket()
170 resp = new ResponsePacket();
171 if (!resp->init(req->requestID))
173 log->log("RRProc", Log::ERR, "response packet init fail");
176 if (req->data) free(req->data);
188 result = processLogin();
190 #ifndef VOMPSTANDALONE
192 result = processGetRecordingsList();
195 result = processDeleteRecording();
198 result = processGetChannelsList();
201 result = processStartStreamingChannel();
204 result = processGetBlock();
207 result = processStopStreaming();
210 result = processStartStreamingRecording();
213 result = processGetChannelSchedule();
217 result = processConfigSave();
220 result = processConfigLoad();
222 #ifndef VOMPSTANDALONE
224 result = processReScanRecording(); // FIXME obselete
227 result = processGetTimers();
230 result = processSetTimer();
233 result = processPositionFromFrameNumber();
236 result = processFrameNumberFromPosition();
239 result = processMoveRecording();
242 result = processGetIFrame();
245 result = processGetRecInfo();
248 result = processGetMarks();
251 result = processGetChannelPids();
254 result = processDeleteTimer();
257 case VDR_GETMEDIALIST:
258 result = processGetMediaList();
261 result = processOpenMedia();
263 case VDR_GETMEDIABLOCK:
264 result = processGetMediaBlock();
267 result = processGetLanguageList();
270 result = processGetLanguageContent();
272 case VDR_GETMEDIAINFO:
273 result = processGetMediaInfo();
275 case VDR_CLOSECHANNEL:
276 result = processCloseMediaChannel();
279 result = processSetCharset();
286 if (req->data) free(req->data);
290 if (result) return true;
294 int VompClientRRProc::processLogin()
296 if (req->dataLength != 6) return 0;
300 char configFileName[PATH_MAX];
301 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]);
302 x.config.init(configFileName);
304 // Send the login reply
306 time_t timeNow = time(NULL);
307 struct tm* timeStruct = localtime(&timeNow);
308 int timeOffset = timeStruct->tm_gmtoff;
310 resp->addULONG(timeNow);
311 resp->addLONG(timeOffset);
312 resp->addULONG(VOMP_PROTOCOLL_VERSION);
314 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
315 log->log("RRProc", Log::DEBUG, "written login reply len %lu", resp->getLen());
318 x.netLog(); // safe to run here since the client won't start net logging for a while yet
323 int VompClientRRProc::processSetCharset()
325 int charset = ntohl(*(ULONG*)req->data);
326 if (charset>0 && charset<3)
328 log->log("RRProc", Log::DEBUG, "Set charset to %d", charset);
329 x.setCharset(charset);
334 log->log("RRProc", Log::DEBUG, "Invalid charset %d", charset);
338 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
342 int VompClientRRProc::processConfigSave()
344 char* section = (char*)req->data;
348 for (UINT k = 0; k < req->dataLength; k++)
350 if (req->data[k] == '\0')
354 key = (char*)&req->data[k+1];
358 value = (char*)&req->data[k+1];
364 // if the last string (value) doesnt have null terminator, give up
365 if (req->data[req->dataLength - 1] != '\0') return 0;
367 log->log("RRProc", Log::DEBUG, "Config save: %s %s %s", section, key, value);
368 if (x.config.setValueString(section, key, value))
378 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
383 int VompClientRRProc::processConfigLoad()
385 char* section = (char*)req->data;
388 for (UINT k = 0; k < req->dataLength; k++)
390 if (req->data[k] == '\0')
392 key = (char*)&req->data[k+1];
397 char* value = x.config.getValueString(section, key);
401 resp->addString(value);//client coding, do not touch
402 log->log("RRProc", Log::DEBUG, "Written config load packet");
408 log->log("RRProc", Log::DEBUG, "Written config load failed packet");
412 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
418 //helper for sending from a serialize buffer
419 //insert the used len into the first 4 Bytes of the buffer
420 void VompClientRRProc::sendPacket(SerializeBuffer *b) {
421 resp->copyin(b->getStart(),b->getCurrent()-b->getStart());
423 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
427 * media List Request:
428 * Media List response:
431 #define MLISTBUF 500000
432 int VompClientRRProc::processGetMediaList()
434 SerializeBuffer buffer(req->data,req->dataLength);
435 MediaURI uri(0,NULL,NULL);
436 VDR_GetMediaListRequest request(&uri);
437 if (request.deserialize(&buffer) != 0) {
438 log->log("Client", Log::ERR, "getMediaList unable to deserialize");
441 const char *dirname=uri.getName();
442 log->log("Client", Log::DEBUG, "getMediaList for %s", dirname);
445 if (dirname == NULL) {
446 ml=x.media->getRootList();
448 ml=x.media->getMediaList(&uri);
451 log->log("Client", Log::ERR, "getMediaList returned NULL");
454 SerializeBuffer rbuf(MLISTBUF,false,true);
455 ULONG flags=0; //TODO: real error handling by setting flags
456 VDR_GetMediaListResponse response(&flags,ml);
457 if (response.serialize(&rbuf) != 0) {
458 log->log("Client", Log::ERR, "getMediaList returned NULL");
462 log->log("Client", Log::DEBUG, "getMediaList size %u", ml->size());
467 log->log("Client", Log::DEBUG, "Written Media list");
472 * openMedia response:
474 int VompClientRRProc::processOpenMedia()
476 SerializeBuffer buffer(req->data,req->dataLength);
477 MediaURI uri(0,NULL,NULL);
481 VDR_OpenMediumRequest request(&channel,&uri,&xs,&ys);
482 if (request.deserialize(&buffer) != 0) {
483 log->log("Client", Log::ERR, "openMediaRequest unable to deserialize");
486 const char *name=uri.getName();
487 log->log("Client", Log::DEBUG, "openMediaRequest for %s", name);
489 int rt=x.media->openMedium(channel,&uri,&size,xs,ys);
494 log->log("Client", Log::ERR, "openMediaRequest unable to open");
496 VDR_OpenMediumResponse response(&flags,&size);
497 SerializeBuffer rbuf(response.getSerializedLen()+4,false,true);
498 if (response.serialize(&rbuf) != 0) {
499 log->log("Client", Log::ERR, "openMediaRequest cannot serialize");
502 log->log("Client", Log::DEBUG, "openMediaRequest size %llu", size);
509 * packet - no serialized response!
511 int VompClientRRProc::processGetMediaBlock()
513 SerializeBuffer buffer(req->data,req->dataLength);
517 VDR_GetMediaBlockRequest request(&channel,&position,&amount);
518 if (request.deserialize(&buffer) != 0) {
519 log->log("Client", Log::ERR, "getMediaBlock unable to deserialize");
522 log->log("Client", Log::DEBUG, "getMediaBlock pos = %llu length = %lu,chan=%lu", position, amount,channel);
524 UCHAR sendBuffer[amount ];
525 ULONG amountReceived = 0;
526 UCHAR *rbuf=sendBuffer;
527 int rt=x.media->getMediaBlock(channel,position,amount,&amountReceived,&rbuf);
528 if (!amountReceived || rt != 0)
530 log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0");
534 if (rbuf != sendBuffer) {
535 //the provider did not use the optimized handling with using my buffer
536 resp->copyin(rbuf,amountReceived);
539 // the provider did not allocate a new buffer
540 resp->copyin(sendBuffer,amountReceived);
544 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
545 log->log("Client", Log::DEBUG, "written ok %lu", amountReceived);
552 int VompClientRRProc::processGetMediaInfo()
554 SerializeBuffer buffer(req->data,req->dataLength);
556 VDR_GetMediaInfoRequest request(&channel);
557 if (request.deserialize(&buffer) != 0) {
558 log->log("Client", Log::ERR, "getMediaInfo unable to deserialize");
561 log->log("Client", Log::DEBUG, "getMediaInfo chan=%lu", channel);
564 int rt=x.media->getMediaInfo(channel,&mi);
567 log->log("Client", Log::ERR, "getMediaInfo unable to get");
569 VDR_GetMediaInfoResponse response(&flags,&mi);
570 SerializeBuffer rbuf(response.getSerializedLen()+4,false,true);
571 if (response.serialize(&rbuf) != 0) {
572 log->log("Client", Log::ERR, "getMediaInfo cannot serialize");
584 int VompClientRRProc::processCloseMediaChannel()
586 SerializeBuffer buffer(req->data,req->dataLength);
588 VDR_CloseMediaChannelRequest request(&channel);
589 if (request.deserialize(&buffer) != 0) {
590 log->log("Client", Log::ERR, "closeMediaChannel unable to deserialize");
594 log->log("Client", Log::DEBUG, "closeMediaChannel chan=%lu", channel);
595 int rt=x.media->closeMediaChannel(channel);
598 log->log("Client", Log::ERR, "closeMediaChannel unable to get");
600 VDR_CloseMediaChannelResponse response(&flags);
601 SerializeBuffer rbuf(response.getSerializedLen()+4,false,true);
602 if (response.serialize(&rbuf) != 0) {
603 log->log("Client", Log::ERR, "closeMediaChannel cannot serialize");
612 int VompClientRRProc::processGetLanguageList()
614 x.i18n.findLanguages();
615 const I18n::lang_code_list& languages = x.i18n.getLanguageList();
617 I18n::lang_code_list::const_iterator iter;
618 for (iter = languages.begin(); iter != languages.end(); ++iter)
620 resp->addString(iter->first.c_str()); // Source code is acsii
621 resp->addString(x.charconvutf8->Convert(iter->second.c_str())); //translate string can be any utf-8 character
624 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
628 int VompClientRRProc::processGetLanguageContent()
630 if (req->dataLength <= 0) return 0;
631 std::string code, result;
632 code.assign((char*)req->data, req->dataLength - 1);
633 x.i18n.findLanguages();
634 I18n::trans_table texts = x.i18n.getLanguageContent(code);
635 I18n::trans_table::const_iterator iter;
636 for (iter = texts.begin(); iter != texts.end(); ++iter)
638 resp->addString(iter->first.c_str());// source code is acsii since it is english
639 resp->addString(x.charconvutf8->Convert(iter->second.c_str())); // translate text can be any unicode string, it is stored as UTF-8
642 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
646 #ifndef VOMPSTANDALONE
648 int VompClientRRProc::processGetRecordingsList()
651 int Percent = VideoDiskSpace(&FreeMB);
652 int Total = (FreeMB / (100 - Percent)) * 100;
654 resp->addULONG(Total);
655 resp->addULONG(FreeMB);
656 resp->addULONG(Percent);
658 cRecordings Recordings;
661 for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
663 #if VDRVERSNUM < 10721
664 resp->addULONG(recording->start);
666 resp->addULONG(recording->Start());
668 resp->addString(x.charconvsys->Convert(recording->Name())); //coding of recording name is system dependent
669 resp->addString(recording->FileName());//file name are not visible by user do not touch
673 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
675 log->log("RRProc", Log::DEBUG, "Written recordings list");
680 int VompClientRRProc::processDeleteRecording()
682 // data is a pointer to the fileName string
684 cRecordings Recordings;
685 Recordings.Load(); // probably have to do this
687 cRecording* recording = Recordings.GetByName((char*)req->data);
689 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
693 log->log("RRProc", Log::DEBUG, "deleting recording: %s", recording->Name());
695 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
698 if (recording->Delete())
700 // Copy svdrp's way of doing this, see if it works
701 #if VDRVERSNUM > 10300
702 ::Recordings.DelByName(recording->FileName());
722 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
727 int VompClientRRProc::processMoveRecording()
729 log->log("RRProc", Log::DEBUG, "Process move recording");
730 char* fileName = (char*)req->data;
731 char* newPath = NULL;
733 for (UINT k = 0; k < req->dataLength; k++)
735 if (req->data[k] == '\0')
737 newPath = (char*)&req->data[k+1];
741 if (!newPath) return 0;
743 cRecordings Recordings;
744 Recordings.Load(); // probably have to do this
746 cRecording* recording = Recordings.GetByName((char*)fileName);
748 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
752 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
755 log->log("RRProc", Log::DEBUG, "moving recording: %s", recording->Name());
756 log->log("RRProc", Log::DEBUG, "moving recording: %s", recording->FileName());
757 log->log("RRProc", Log::DEBUG, "to: %s", newPath);
759 const char* t = recording->FileName();
761 char* dateDirName = NULL; int k;
762 char* titleDirName = NULL; int j;
764 // Find the datedirname
765 for(k = strlen(t) - 1; k >= 0; k--)
769 log->log("RRProc", Log::DEBUG, "l1: %i", strlen(&t[k+1]) + 1);
770 dateDirName = new char[strlen(&t[k+1]) + 1];
771 strcpy(dateDirName, &t[k+1]);
776 // Find the titledirname
778 for(j = k-1; j >= 0; j--)
782 log->log("RRProc", Log::DEBUG, "l2: %i", (k - j - 1) + 1);
783 titleDirName = new char[(k - j - 1) + 1];
784 memcpy(titleDirName, &t[j+1], k - j - 1);
785 titleDirName[k - j - 1] = '\0';
790 log->log("RRProc", Log::DEBUG, "datedirname: %s", dateDirName);
791 log->log("RRProc", Log::DEBUG, "titledirname: %s", titleDirName);
792 log->log("RRProc", Log::DEBUG, "viddir: %s", VideoDirectory);
794 char* newPathConv = new char[strlen(newPath)+1];
795 strcpy(newPathConv, newPath);
796 ExchangeChars(newPathConv, true);
797 log->log("RRProc", Log::DEBUG, "EC: %s", newPathConv);
799 char* newContainer = new char[strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1];
800 log->log("RRProc", Log::DEBUG, "l10: %i", strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1);
801 sprintf(newContainer, "%s%s%s", VideoDirectory, newPathConv, titleDirName);
802 delete[] newPathConv;
804 log->log("RRProc", Log::DEBUG, "%s", newContainer);
807 int statret = stat(newContainer, &dstat);
808 if ((statret == -1) && (errno == ENOENT)) // Dir does not exist
810 log->log("RRProc", Log::DEBUG, "new dir does not exist");
811 int mkdirret = mkdir(newContainer, 0755);
814 delete[] dateDirName;
815 delete[] titleDirName;
816 delete[] newContainer;
820 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
824 else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR))) // Something exists but it's not a dir
826 delete[] dateDirName;
827 delete[] titleDirName;
828 delete[] newContainer;
832 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
836 // Ok, the directory container has been made, or it pre-existed.
838 char* newDir = new char[strlen(newContainer) + 1 + strlen(dateDirName) + 1];
839 sprintf(newDir, "%s/%s", newContainer, dateDirName);
841 log->log("RRProc", Log::DEBUG, "doing rename '%s' '%s'", t, newDir);
842 int renameret = rename(t, newDir);
845 // Success. Test for remove old dir containter
846 char* oldTitleDir = new char[k+1];
847 memcpy(oldTitleDir, t, k);
848 oldTitleDir[k] = '\0';
849 log->log("RRProc", Log::DEBUG, "len: %i, cp: %i, strlen: %i, oldtitledir: %s", k+1, k, strlen(oldTitleDir), oldTitleDir);
850 rmdir(oldTitleDir); // can't do anything about a fail result at this point.
851 delete[] oldTitleDir;
856 #if VDRVERSNUM > 10311
858 ::Recordings.Update();
860 // Success. Send a different packet from just a ulong
861 resp->addULONG(1); // success
862 resp->addString(newDir); //system depent do not convert
870 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
872 delete[] dateDirName;
873 delete[] titleDirName;
874 delete[] newContainer;
881 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
888 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
894 int VompClientRRProc::processGetChannelsList()
898 char* chanConfig = x.config.getValueString("General", "Channels");
900 if (chanConfig) allChans = strcasecmp(chanConfig, "FTA only");
902 for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
904 #if VDRVERSNUM < 10300
905 if (!channel->GroupSep() && (!channel->Ca() || allChans))
907 if (!channel->GroupSep() && (!channel->Ca(0) || allChans))
910 log->log("RRProc", Log::DEBUG, "name: '%s'", channel->Name());
912 if (channel->Vpid()) type = 1;
913 #if VDRVERSNUM < 10300
916 else if (channel->Apid(0)) type = 2;
920 resp->addULONG(channel->Number());
921 resp->addULONG(type);
922 resp->addString(x.charconvsys->Convert(channel->Name()));
923 #if VDRVERSNUM < 10703
926 resp->addULONG(channel->Vtype());
932 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
934 log->log("RRProc", Log::DEBUG, "Written channels list");
939 int VompClientRRProc::processGetChannelPids()
941 ULONG channelNumber = ntohl(*(ULONG*)req->data);
943 cChannel* channel = x.channelFromNumber(channelNumber);
948 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
957 #if VDRVERSNUM < 10300
959 log->log("RRProc", Log::DEBUG, "Apid1: %i", channel->Apid1());
960 log->log("RRProc", Log::DEBUG, "Apid2: %i", channel->Apid2());
962 if (channel->Apid2())
964 else if (channel->Apid1())
971 for (const int *Apid = channel->Apids(); *Apid; Apid++)
975 for (const int *Dpid = channel->Dpids(); *Dpid; Dpid++)
979 for (const int *Spid = channel->Spids(); *Spid; Spid++)
986 // Format of response
1005 resp->addULONG(channel->Vpid());
1006 #if VDRVERSNUM < 10703
1009 resp->addULONG(channel->Vtype());
1011 resp->addULONG(numApids);
1013 #if VDRVERSNUM < 10300
1016 resp->addULONG(channel->Apid1());
1017 resp->addString("");
1021 resp->addULONG(channel->Apid2());
1022 resp->addString("");
1027 for (ULONG i = 0; i < numApids; i++)
1029 resp->addULONG(channel->Apid(i));
1030 resp->addString(x.charconvsys->Convert(channel->Alang(i)));
1032 resp->addULONG(numDpids);
1033 for (ULONG i = 0; i < numDpids; i++)
1035 resp->addULONG(channel->Dpid(i));
1036 resp->addString(x.charconvsys->Convert(channel->Dlang(i)));
1038 resp->addULONG(numSpids);
1039 for (ULONG i = 0; i < numSpids; i++)
1041 resp->addULONG(channel->Spid(i));
1042 resp->addString(x.charconvsys->Convert(channel->Slang(i)));
1045 resp->addULONG(channel->Tpid());
1046 // Format of extended response, for compatibility with older client at the end
1058 #if VDRVERSNUM < 10300
1068 for (ULONG i = 0; i < numApids; i++)
1070 #if VDRVERSNUM < 10715
1073 resp->addULONG(channel->Atype(i));
1076 for (ULONG i = 0; i < numDpids; i++)
1078 #if VDRVERSNUM < 10715
1079 resp->addULONG(0x6A /*AC3*/);
1081 resp->addULONG(channel->Dtype(i));
1084 for (ULONG i = 0; i < numSpids; i++)
1086 #if VDRVERSNUM < 10715
1091 resp->addULONG(channel->SubtitlingType(i));
1092 resp->addULONG(channel->CompositionPageId(i));
1093 resp->addULONG(channel->AncillaryPageId(i));
1100 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1102 log->log("RRProc", Log::DEBUG, "Written channels pids");
1107 int VompClientRRProc::processStartStreamingChannel()
1111 log->log("RRProc", Log::ERR, "Client called start streaming twice");
1115 log->log("RRProc", Log::DEBUG, "req->dataLength = %i", req->dataLength);
1116 ULONG channelNumber = ntohl(*(ULONG*)req->data);
1118 cChannel* channel = x.channelFromNumber(channelNumber);
1123 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1127 // get the priority we should use
1129 int priority = x.config.getValueLong("General", "Live priority", &fail);
1132 log->log("RRProc", Log::DEBUG, "Config: Live TV priority: %i", priority);
1136 log->log("RRProc", Log::DEBUG, "Config: Live TV priority config fail");
1140 // a bit of sanity..
1141 #if VDRVERSNUM < 10725
1142 if (priority < 0) priority = 0;
1144 if (priority < -99) priority = -99;
1146 if (priority > 99) priority = 99;
1148 log->log("RRProc", Log::DEBUG, "Using live TV priority %i", priority);
1149 x.lp = MVPReceiver::create(channel, priority);
1155 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1159 if (!x.lp->init(&x.tcp, req->requestID))
1165 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1171 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1175 int VompClientRRProc::processStopStreaming()
1177 log->log("RRProc", Log::DEBUG, "STOP STREAMING RECEIVED");
1180 x.lp->detachMVPReceiver();
1184 else if (x.recplayer)
1186 x.writeResumeData();
1189 delete x.recordingManager;
1191 x.recordingManager = NULL;
1196 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1200 int VompClientRRProc::processGetBlock()
1204 log->log("RRProc", Log::ERR, "Get block called during live streaming");
1210 log->log("RRProc", Log::ERR, "Get block called when no recording open");
1214 UCHAR* data = req->data;
1216 ULLONG position = x.ntohll(*(ULLONG*)data);
1217 data += sizeof(ULLONG);
1218 ULONG amount = ntohl(*(ULONG*)data);
1220 log->log("RRProc", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount);
1222 UCHAR sendBuffer[amount];
1223 ULONG amountReceived = x.recplayer->getBlock(&sendBuffer[0], position, amount);
1225 if (!amountReceived)
1228 log->log("RRProc", Log::DEBUG, "written 4(0) as getblock got 0");
1232 resp->copyin(sendBuffer, amountReceived);
1233 log->log("RRProc", Log::DEBUG, "written %lu", amountReceived);
1237 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1238 log->log("RRProc", Log::DEBUG, "Finished getblock, have sent %lu", resp->getLen());
1242 int VompClientRRProc::processStartStreamingRecording()
1244 // data is a pointer to the fileName string
1246 x.recordingManager = new cRecordings;
1247 x.recordingManager->Load();
1249 cRecording* recording = x.recordingManager->GetByName((char*)req->data);
1251 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
1255 x.recplayer = new RecPlayer(recording);
1257 resp->addULLONG(x.recplayer->getLengthBytes());
1258 resp->addULONG(x.recplayer->getLengthFrames());
1260 #if VDRVERSNUM < 10703
1261 resp->addUCHAR(true);//added for TS
1263 resp->addUCHAR(recording->IsPesRecording());//added for TS
1267 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1269 log->log("RRProc", Log::DEBUG, "written totalLength");
1273 delete x.recordingManager;
1274 x.recordingManager = NULL;
1279 int VompClientRRProc::processPositionFromFrameNumber()
1283 ULONG frameNumber = ntohl(*(ULONG*)req->data);
1287 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1291 retval = x.recplayer->positionFromFrameNumber(frameNumber);
1294 resp->addULLONG(retval);
1296 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1298 log->log("RRProc", Log::DEBUG, "Wrote posFromFrameNum reply to client");
1302 int VompClientRRProc::processFrameNumberFromPosition()
1306 ULLONG position = x.ntohll(*(ULLONG*)req->data);
1310 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1314 retval = x.recplayer->frameNumberFromPosition(position);
1317 resp->addULONG(retval);
1319 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1321 log->log("RRProc", Log::DEBUG, "Wrote frameNumFromPos reply to client");
1325 int VompClientRRProc::processGetIFrame()
1327 bool success = false;
1329 ULONG* data = (ULONG*)req->data;
1331 ULONG frameNumber = ntohl(*data);
1333 ULONG direction = ntohl(*data);
1335 ULLONG rfilePosition = 0;
1336 ULONG rframeNumber = 0;
1337 ULONG rframeLength = 0;
1341 log->log("RRProc", Log::DEBUG, "GetIFrame recording called when no recording being played!");
1345 success = x.recplayer->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength);
1348 // returns file position, frame number, length
1352 resp->addULLONG(rfilePosition);
1353 resp->addULONG(rframeNumber);
1354 resp->addULONG(rframeLength);
1362 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1364 log->log("RRProc", Log::DEBUG, "Wrote GNIF reply to client %llu %lu %lu", rfilePosition, rframeNumber, rframeLength);
1368 int VompClientRRProc::processGetChannelSchedule()
1370 ULONG* data = (ULONG*)req->data;
1372 ULONG channelNumber = ntohl(*data);
1374 ULONG startTime = ntohl(*data);
1376 ULONG duration = ntohl(*data);
1378 log->log("RRProc", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
1380 cChannel* channel = x.channelFromNumber(channelNumber);
1385 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1387 log->log("RRProc", Log::DEBUG, "written 0 because channel = NULL");
1391 log->log("RRProc", Log::DEBUG, "Got channel");
1393 #if VDRVERSNUM < 10300
1394 cMutexLock MutexLock;
1395 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1397 cSchedulesLock MutexLock;
1398 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1404 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1406 log->log("RRProc", Log::DEBUG, "written 0 because Schedule!s! = NULL");
1410 log->log("RRProc", Log::DEBUG, "Got schedule!s! object");
1412 const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
1417 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1419 log->log("RRProc", Log::DEBUG, "written 0 because Schedule = NULL");
1423 log->log("RRProc", Log::DEBUG, "Got schedule object");
1425 const char* empty = "";
1426 bool atLeastOneEvent = false;
1429 ULONG thisEventTime;
1430 ULONG thisEventDuration;
1431 const char* thisEventTitle;
1432 const char* thisEventSubTitle;
1433 const char* thisEventDescription;
1435 #if VDRVERSNUM < 10300
1437 const cEventInfo *event;
1438 for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1440 event = Schedule->GetEventNumber(eventNumber);
1442 thisEventID = event->GetEventID();
1443 thisEventTime = event->GetTime();
1444 thisEventDuration = event->GetDuration();
1445 thisEventTitle = event->GetTitle();
1446 thisEventSubTitle = event->GetSubtitle();
1447 thisEventDescription = event->GetExtendedDescription();
1451 for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
1453 thisEventID = event->EventID();
1454 thisEventTime = event->StartTime();
1455 thisEventDuration = event->Duration();
1456 thisEventTitle = event->Title();
1457 thisEventSubTitle = NULL;
1458 thisEventDescription = event->Description();
1462 //in the past filter
1463 if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue;
1466 if ((thisEventTime + thisEventDuration) <= startTime) continue;
1469 if (thisEventTime >= (startTime + duration)) continue;
1471 if (!thisEventTitle) thisEventTitle = empty;
1472 if (!thisEventSubTitle) thisEventSubTitle = empty;
1473 if (!thisEventDescription) thisEventDescription = empty;
1475 resp->addULONG(thisEventID);
1476 resp->addULONG(thisEventTime);
1477 resp->addULONG(thisEventDuration);
1479 resp->addString(x.charconvsys->Convert(thisEventTitle));
1480 resp->addString(x.charconvsys->Convert(thisEventSubTitle));
1481 resp->addString(x.charconvsys->Convert(thisEventDescription));
1483 atLeastOneEvent = true;
1486 log->log("RRProc", Log::DEBUG, "Got all event data");
1488 if (!atLeastOneEvent)
1491 log->log("RRProc", Log::DEBUG, "Written 0 because no data");
1495 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1497 log->log("RRProc", Log::DEBUG, "written schedules packet");
1502 int VompClientRRProc::processGetTimers()
1505 int numTimers = Timers.Count();
1507 resp->addULONG(numTimers);
1509 for (int i = 0; i < numTimers; i++)
1511 timer = Timers.Get(i);
1513 #if VDRVERSNUM < 10300
1514 resp->addULONG(timer->Active());
1516 resp->addULONG(timer->HasFlags(tfActive));
1518 resp->addULONG(timer->Recording());
1519 resp->addULONG(timer->Pending());
1520 resp->addULONG(timer->Priority());
1521 resp->addULONG(timer->Lifetime());
1522 resp->addULONG(timer->Channel()->Number());
1523 resp->addULONG(timer->StartTime());
1524 resp->addULONG(timer->StopTime());
1525 resp->addULONG(timer->Day());
1526 resp->addULONG(timer->WeekDays());
1527 resp->addString(timer->File()); //Filename is system specific and not visible by user
1531 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1533 log->log("RRProc", Log::DEBUG, "Written timers list");
1538 int VompClientRRProc::processSetTimer()
1540 char* timerString = new char[strlen((char*)req->data) + 1];
1541 strcpy(timerString, (char*)req->data);
1543 #if VDRVERSNUM < 10300
1545 // If this is VDR 1.2 the date part of the timer string must be reduced
1546 // to just DD rather than YYYY-MM-DD
1548 int s = 0; // source
1549 int d = 0; // destination
1551 while(c != 2) // copy up to date section, including the second ':'
1553 timerString[d] = req->data[s];
1554 if (req->data[s] == ':') c++;
1558 // now it has copied up to the date section
1560 while(c != 2) // waste YYYY-MM-
1562 if (req->data[s] == '-') c++;
1565 // now source is at the DD
1566 memcpy(&timerString[d], &req->data[s], req->dataLength - s);
1567 d += req->dataLength - s;
1568 timerString[d] = '\0';
1570 log->log("RRProc", Log::DEBUG, "Timer string after 1.2 conversion:");
1573 log->log("RRProc", Log::DEBUG, "%s", timerString);
1575 cTimer *timer = new cTimer;
1576 if (timer->Parse((char*)timerString))
1578 cTimer *t = Timers.GetTimer(timer);
1582 #if VDRVERSNUM < 10300
1585 Timers.SetModified();
1589 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1590 return 1; // FIXME - cTimer* timer is leaked here!
1596 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1603 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1609 int VompClientRRProc::processDeleteTimer()
1611 log->log("RRProc", Log::DEBUG, "Delete timer called");
1616 INT delChannel = ntohl(*(ULONG*)&req->data[position]); position += 4;
1617 INT delWeekdays = ntohl(*(ULONG*)&req->data[position]); position += 4;
1618 INT delDay = ntohl(*(ULONG*)&req->data[position]); position += 4;
1619 INT delStart = ntohl(*(ULONG*)&req->data[position]); position += 4;
1620 INT delStop = ntohl(*(ULONG*)&req->data[position]); position += 4;
1623 for (ti = Timers.First(); ti; ti = Timers.Next(ti))
1625 if ( (ti->Channel()->Number() == delChannel)
1626 && ((ti->WeekDays() && (ti->WeekDays() == delWeekdays)) || (!ti->WeekDays() && (ti->Day() == delDay)))
1627 && (ti->StartTime() == delStart)
1628 && (ti->StopTime() == delStop) )
1636 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1640 if (!Timers.BeingEdited())
1642 if (!ti->Recording())
1645 Timers.SetModified();
1648 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1653 log->log("RRProc", Log::ERR, "Unable to delete timer - timer is running");
1656 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1662 log->log("RRProc", Log::ERR, "Unable to delete timer - timers being edited at VDR");
1665 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1670 int VompClientRRProc::processGetRecInfo()
1672 // data is a pointer to the fileName string
1674 cRecordings Recordings;
1675 Recordings.Load(); // probably have to do this
1677 cRecording *recording = Recordings.GetByName((char*)req->data);
1679 time_t timerStart = 0;
1680 time_t timerStop = 0;
1681 char* summary = NULL;
1682 char* shorttext = NULL;
1683 char* description = NULL;
1684 bool newsummary=false;
1685 ULONG resumePoint = 0;
1689 log->log("RRProc", Log::ERR, "GetRecInfo found no recording");
1692 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1697 4 bytes: start time for timer
1698 4 bytes: end time for timer
1699 4 bytes: resume point
1701 4 bytes: num components
1708 8 bytes: frames per second
1711 // Get current timer
1713 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
1716 timerStart = rc->Timer()->StartTime();
1717 timerStop = rc->Timer()->StopTime();
1718 log->log("RRProc", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop);
1721 resp->addULONG(timerStart);
1722 resp->addULONG(timerStop);
1726 /* char* value = x.config.getValueString("ResumeData", (char*)req->data);
1729 resumePoint = strtoul(value, NULL, 10);
1733 char* ResumeIdC = x.config.getValueString("General", "ResumeId");
1736 ResumeId = atoi(ResumeIdC);
1740 ResumeId = 0; //default if not defined in vomp-MAC.conf
1742 while (ResumeIDLock)
1743 cCondWait::SleepMs(100);
1744 ResumeIDLock = true;
1745 int OldSetupResumeID = Setup.ResumeID;
1746 Setup.ResumeID = ResumeId; //UGLY: quickly change resumeid
1747 #if VDRVERSNUM < 10703
1748 cResumeFile ResumeFile(recording->FileName()); //get corresponding resume file
1750 cResumeFile ResumeFile(recording->FileName(), recording->IsPesRecording()); //get corresponding resume file
1752 Setup.ResumeID = OldSetupResumeID; //and restore it back
1753 ResumeIDLock = false;
1755 int resume = ResumeFile.Read();
1756 //isyslog("VOMPDEBUG: resumePoint = %i, resume = %i, ResumeId = %i",resumePoint, resume, ResumeId);
1758 resumePoint = ResumeFile.Read();
1760 log->log("RRProc", Log::DEBUG, "GRI: RP: %lu", resumePoint);
1762 resp->addULONG(resumePoint);
1766 #if VDRVERSNUM < 10300
1767 summary = (char*)recording->Summary();
1769 const cRecordingInfo *Info = recording->Info();
1770 shorttext = (char*)Info->ShortText();
1771 description = (char*) (char*)Info->Description();
1772 if (isempty(shorttext)) summary=description;
1773 else if (isempty(description)) summary=shorttext;
1775 int length=strlen(description)+strlen(shorttext)+4;
1776 summary=new char[length];
1777 snprintf(summary,length,"%s\n\n%s",shorttext,description);
1781 if (isempty(summary)) summary = (char*)Info->Description();
1783 log->log("RRProc", Log::DEBUG, "GRI: S: %s", summary);
1786 resp->addString(x.charconvsys->Convert(summary));
1787 if (newsummary) delete [] summary;
1791 resp->addString("");
1796 #if VDRVERSNUM < 10300
1798 // Send 0 for numchannels - this signals the client this info is not available
1802 const cComponents* components = Info->Components();
1804 log->log("RRProc", Log::DEBUG, "GRI: D1: %p", components);
1812 resp->addULONG(components->NumComponents());
1814 tComponent* component;
1815 for (int i = 0; i < components->NumComponents(); i++)
1817 component = components->Component(i);
1819 log->log("RRProc", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
1821 resp->addUCHAR(component->stream);
1822 resp->addUCHAR(component->type);
1824 if (component->language)
1826 resp->addString(x.charconvsys->Convert(component->language));
1830 resp->addString("");
1832 if (component->description)
1834 resp->addString(x.charconvsys->Convert(component->description));
1838 resp->addString("");
1844 double framespersec;
1845 #if VDRVERSNUM < 10703
1846 framespersec = FRAMESPERSEC;
1848 framespersec = Info->FramesPerSecond();
1850 resp->adddouble(framespersec);
1855 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1857 log->log("RRProc", Log::DEBUG, "Written getrecinfo");
1867 int VompClientRRProc::processReScanRecording()
1871 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1875 x.recplayer->scan();
1877 resp->addULLONG(x.recplayer->getLengthBytes());
1878 resp->addULONG(x.recplayer->getLengthFrames());
1880 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1881 log->log("RRProc", Log::DEBUG, "Rescan recording, wrote new length to client");
1885 // FIXME without client calling rescan, getblock wont work even tho more data is avail
1887 int VompClientRRProc::processGetMarks()
1889 // data is a pointer to the fileName string
1892 cRecordings Recordings;
1893 Recordings.Load(); // probably have to do this
1895 cRecording *recording = Recordings.GetByName((char*)req->data);
1897 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
1901 #if VDRVERSNUM < 10703
1902 Marks.Load(recording->FileName());
1904 Marks.Load(recording->FileName(), recording->FramesPerSecond(), recording->IsPesRecording());
1908 for (const cMark *m = Marks.First(); m; m = Marks.Next(m))
1910 #if VDRVERSNUM < 10721
1911 ULLONG mposition = m->position;
1913 ULLONG mposition = m->Position();
1915 log->log("RRProc", Log::DEBUG, "found Mark %i", mposition);
1917 resp->addULONG(mposition);
1922 log->log("RRProc", Log::DEBUG, "no marks found, sending 0-mark");
1928 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1930 log->log("RRProc", Log::DEBUG, "Written Marks list");
1935 #endif // !VOMPSTANDALONE