/* * markad-standalone.cpp: A program for the Video Disk Recorder * * See the README file for copyright information and how to reach the author. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "markad-standalone.h" #include "version.h" bool SYSLOG=false; bool LOG2REC=false; cMarkAdStandalone *cmasta=NULL; int SysLogLevel=2; static inline int ioprio_set(int which, int who, int ioprio) { #if defined(__i386__) #define __NR_ioprio_set 289 #define __NR_ioprio_get 290 #elif defined(__ppc__) #define __NR_ioprio_set 273 #define __NR_ioprio_get 274 #elif defined(__x86_64__) #define __NR_ioprio_set 251 #define __NR_ioprio_get 252 #elif defined(__ia64__) #define __NR_ioprio_set 1274 #define __NR_ioprio_get 1275 #else return 0; // just do nothing #endif return syscall(__NR_ioprio_set, which, who, ioprio); } void syslog_with_tid(int priority, const char *format, ...) { va_list ap; if ((SYSLOG) && (!LOG2REC)) { char fmt[255]; snprintf(fmt, sizeof(fmt), "[%d] %s", getpid(), format); va_start(ap, format); vsyslog(priority, fmt, ap); va_end(ap); } else { char buf[255]={0}; const time_t now=time(NULL); if (ctime_r(&now,buf)) { buf[strlen(buf)-6]=0; } char fmt[255]; snprintf(fmt, sizeof(fmt), "%s%s [%d] %s", LOG2REC ? "":"markad: ",buf, getpid(), format); va_start(ap, format); vprintf(fmt,ap); va_end(ap); printf("\n"); fflush(stdout); } } cOSDMessage::cOSDMessage(const char *Host, int Port) { tid=0; msg=NULL; host=strdup(Host); port=Port; } cOSDMessage::~cOSDMessage() { if (tid) pthread_join(tid,NULL); if (msg) free(msg); if (host) free((void*) host); } bool cOSDMessage::readreply(int fd) { usleep(400000); char c=' '; do { struct pollfd fds; fds.fd=fd; fds.events=POLLIN; fds.revents=0; int ret=poll(&fds,1,600); if (ret<=0) return false; if (fds.revents!=POLLIN) return false; if (read(fd,&c,1)<0) return false; } while (c!='\n'); return true; } void *cOSDMessage::send(void *posd) { cOSDMessage *osd=(cOSDMessage *) posd; struct hostent *host=gethostbyname(osd->host); if (!host) { osd->tid=0; return NULL; } struct sockaddr_in name; name.sin_family = AF_INET; name.sin_port = htons(osd->port); memcpy(&name.sin_addr.s_addr,host->h_addr,sizeof(host->h_addr)); uint size = sizeof(name); int sock; sock=socket(PF_INET, SOCK_STREAM, 0); if (sock<0) return NULL; if (connect(sock, (struct sockaddr *)&name,size)!=0) { close(sock); return NULL; } if (!osd->readreply(sock)) { close(sock); return NULL; } ssize_t ret; ret=write(sock,"MESG ",5); ret=write(sock,osd->msg,strlen(osd->msg)); ret=write(sock,"\r\n",2); if (!osd->readreply(sock)) { close(sock); return NULL; } ret=write(sock,"QUIT\r\n",6); osd->readreply(sock); close(sock); return NULL; } int cOSDMessage::Send(const char *format, ...) { if (tid) pthread_join(tid,NULL); if (msg) free(msg); va_list ap; va_start(ap, format); if (vasprintf(&msg,format,ap)==-1) return -1; va_end(ap); if (pthread_create(&tid,NULL,(void *(*) (void *))&send, (void *) this)!=0) return -1; return 0; } void cMarkAdStandalone::CalculateCheckPositions(int startframe) { if (!length) return; if (!startframe) return; if (!macontext.Video.Info.FramesPerSecond) return; int delta=macontext.Video.Info.FramesPerSecond*MAXRANGE; int len_in_frames=macontext.Video.Info.FramesPerSecond*length; iStart=-startframe; iStop=-(startframe+len_in_frames); chkSTART=-iStart+delta; chkSTOP=-iStop+(3*delta); } void cMarkAdStandalone::CheckStop() { dsyslog("checking stop"); int delta=macontext.Video.Info.FramesPerSecond*MAXRANGE; clMark *end=marks.GetAround(delta,iStop,MT_STOP,0x0F); if (end) { marks.DelTill(end->position,false); isyslog("using mark on position %i as stop mark",end->position); } else { //fallback if (iStopinBroadCast) { MarkAdMark mark; memset(&mark,0,sizeof(mark)); mark.Position=iStop; mark.Type=MT_ASSUMEDSTOP; AddMark(&mark); } else { isyslog("removing marks from position %i, if any",iStop); } marks.DelTill(iStop,false); } iStop=0; gotendmark=true; } void cMarkAdStandalone::CheckStart() { dsyslog("checking start"); clMark *begin=NULL; if ((macontext.Info.Channels) && (macontext.Audio.Info.Channels) && (macontext.Info.Channels!=macontext.Audio.Info.Channels)) { char as[20]; switch (macontext.Info.Channels) { case 1: strcpy(as,"mono"); break; case 2: strcpy(as,"stereo"); break; case 6: strcpy(as,"dd5.1"); break; default: strcpy(as,"??"); break; } char ad[20]; switch (macontext.Audio.Info.Channels) { case 1: strcpy(ad,"mono"); break; case 2: strcpy(ad,"stereo"); break; case 6: strcpy(ad,"dd5.1"); break; default: strcpy(ad,"??"); break; } isyslog("audio description in info (%s) wrong, we have %s",as,ad); } macontext.Info.Channels=macontext.Audio.Info.Channels; if (macontext.Info.Channels==6) { isyslog("DolbyDigital5.1 audio detected. logo/border/aspect detection disabled"); bDecodeVideo=false; macontext.Video.Options.IgnoreAspectRatio=true; macontext.Video.Options.IgnoreLogoDetection=true; marks.Del(MT_ASPECTSTART); marks.Del(MT_ASPECTSTOP); // start mark must be around istart begin=marks.GetAround(INT_MAX,iStart,MT_CHANNELSTART); } else { if (macontext.Info.DPid.Num) { if (macontext.Info.Channels) isyslog("broadcast with %i audio channels, disabling AC3 decoding",macontext.Info.Channels); macontext.Info.DPid.Num=0; demux->DisableDPid(); } } if ((macontext.Info.AspectRatio.Num) && ((macontext.Info.AspectRatio.Num!= macontext.Video.Info.AspectRatio.Num) || (macontext.Info.AspectRatio.Den!= macontext.Video.Info.AspectRatio.Den))) { isyslog("video aspect description in info (%i:%i) wrong", macontext.Info.AspectRatio.Num, macontext.Info.AspectRatio.Den); } macontext.Info.AspectRatio.Num=macontext.Video.Info.AspectRatio.Num; macontext.Info.AspectRatio.Den=macontext.Video.Info.AspectRatio.Den; isyslog("aspectratio of %i:%i detected. %s", macontext.Video.Info.AspectRatio.Num, macontext.Video.Info.AspectRatio.Den, ((macontext.Video.Info.AspectRatio.Num==4) && (macontext.Video.Info.AspectRatio.Den==3)) ? "logo/border detection disabled" : ""); if ((macontext.Video.Info.AspectRatio.Num==4) && (macontext.Video.Info.AspectRatio.Den==3)) { bDecodeVideo=false; macontext.Video.Options.IgnoreLogoDetection=true; marks.Del(MT_CHANNELSTART); marks.Del(MT_CHANNELSTOP); // start mark must be around iStart begin=marks.GetAround(macontext.Video.Info.FramesPerSecond*(MAXRANGE*4),iStart,MT_ASPECTSTART); } if (!bDecodeVideo) { macontext.Video.Data.Valid=false; marks.Del(MT_LOGOSTART); marks.Del(MT_LOGOSTOP); marks.Del(MT_HBORDERSTART); marks.Del(MT_HBORDERSTOP); marks.Del(MT_VBORDERSTART); marks.Del(MT_VBORDERSTOP); } if (!begin) { begin=marks.GetAround(macontext.Video.Info.FramesPerSecond*(MAXRANGE*2),iStart,MT_START,0x0F); } if (begin) { marks.DelTill(begin->position); CalculateCheckPositions(begin->position); isyslog("using mark on position %i as start mark",begin->position); } else { //fallback marks.DelTill(chkSTART); MarkAdMark mark; memset(&mark,0,sizeof(mark)); mark.Position=iStart; mark.Type=MT_ASSUMEDSTART; AddMark(&mark); CalculateCheckPositions(iStart); } iStart=0; return; } void cMarkAdStandalone::CheckLogoMarks() { clMark *mark=marks.GetFirst(); while (mark) { if ((mark->type==MT_LOGOSTOP) && mark->Next() && mark->Next()->type==MT_LOGOSTART) { int MARKDIFF=(int) (macontext.Video.Info.FramesPerSecond*30); if (abs(mark->Next()->position-mark->position)<=MARKDIFF) { double distance=(mark->Next()->position-mark->position)/macontext.Video.Info.FramesPerSecond; isyslog("mark distance too short (%.1fs), deleting %i,%i",distance, mark->position,mark->Next()->position); clMark *tmp=mark; mark=mark->Next()->Next(); marks.Del(tmp->Next()); marks.Del(tmp); continue; } } mark=mark->Next(); } } void cMarkAdStandalone::AddMark(MarkAdMark *Mark) { if (!Mark) return; if (!Mark->Type) return; if ((macontext.Config) && (macontext.Config->logoExtraction!=-1)) return; if (gotendmark) return; char *comment=NULL; switch (Mark->Type) { case MT_ASSUMEDSTART: if (asprintf(&comment,"assuming start (%i)",Mark->Position)==-1) comment=NULL; break; case MT_ASSUMEDSTOP: if (asprintf(&comment,"assuming stop (%i)",Mark->Position)==-1) comment=NULL; break; case MT_LOGOSTART: if (asprintf(&comment,"detected logo start (%i)*",Mark->Position)==-1) comment=NULL; break; case MT_LOGOSTOP: if (asprintf(&comment,"detected logo stop (%i)",Mark->Position)==-1) comment=NULL; break; case MT_HBORDERSTART: if (asprintf(&comment,"detected start of horiz. borders (%i)*", Mark->Position)==-1) comment=NULL; break; case MT_HBORDERSTOP: if (asprintf(&comment,"detected stop of horiz. borders (%i)", Mark->Position)==-1) comment=NULL; break; case MT_VBORDERSTART: if (asprintf(&comment,"detected start of vert. borders (%i)*", Mark->Position)==-1) comment=NULL; break; case MT_VBORDERSTOP: if (asprintf(&comment,"detected stop of vert. borders (%i)", Mark->Position)==-1) comment=NULL; break; case MT_ASPECTSTART: if (!Mark->AspectRatioBefore.Num) { if (asprintf(&comment,"aspectratio start with %i:%i (%i)*", Mark->AspectRatioAfter.Num,Mark->AspectRatioAfter.Den, Mark->Position)==-1) comment=NULL; } else { if (asprintf(&comment,"aspectratio change from %i:%i to %i:%i (%i)*", Mark->AspectRatioBefore.Num,Mark->AspectRatioBefore.Den, Mark->AspectRatioAfter.Num,Mark->AspectRatioAfter.Den, Mark->Position)==-1) comment=NULL; } break; case MT_ASPECTSTOP: if (asprintf(&comment,"aspectratio change from %i:%i to %i:%i (%i)", Mark->AspectRatioBefore.Num,Mark->AspectRatioBefore.Den, Mark->AspectRatioAfter.Num,Mark->AspectRatioAfter.Den, Mark->Position)==-1) comment=NULL; break; case MT_CHANNELSTART: if (asprintf(&comment,"audio channel change from %i to %i (%i)*", Mark->ChannelsBefore,Mark->ChannelsAfter, Mark->Position)==-1) comment=NULL; break; case MT_CHANNELSTOP: if (asprintf(&comment,"audio channel change from %i to %i (%i)", Mark->ChannelsBefore,Mark->ChannelsAfter, Mark->Position)==-1) comment=NULL; break; case MT_RECORDINGSTART: if (asprintf(&comment,"start of recording (%i)",Mark->Position)==-1) comment=NULL; break; case MT_RECORDINGSTOP: if (asprintf(&comment,"stop of recording (%i)",Mark->Position)==-1) comment=NULL; break; } if (comment) isyslog("%s",comment); if (((Mark->Type & 0x0F)==MT_STOP) && (!iStart) && (Mark->PositionPosition,(Mark->Type & 0xF0)|MT_START); if (prev) { int MARKDIFF; if ((Mark->Type & 0xF0)==MT_LOGOCHANGE) { MARKDIFF=(int) (macontext.Video.Info.FramesPerSecond*240); } else { MARKDIFF=(int) (macontext.Video.Info.FramesPerSecond*10); } if ((Mark->Position-prev->position)Position-prev->position)/macontext.Video.Info.FramesPerSecond; isyslog("mark distance too short (%.1fs), deleting %i,%i",distance, prev->position,Mark->Position); if (!macontext.Video.Options.WeakMarksOk) inBroadCast=false; marks.Del(prev); if (comment) free(comment); return; } } } clMark *prev=marks.GetLast(); if (prev) { if ((prev->type & 0x0F)==(Mark->Type & 0x0F)) { int MARKDIFF=(int) (macontext.Video.Info.FramesPerSecond*30); int diff=abs(Mark->Position-prev->position); if (difftype>Mark->Type) { isyslog("previous mark (%i) stronger than actual mark, deleting %i", prev->position, Mark->Position); if (comment) free(comment); return; } else { isyslog("actual mark stronger then previous mark, deleting %i",prev->position); marks.Del(prev); } } } } if (!macontext.Video.Options.WeakMarksOk) { if ((Mark->Type & 0x0F)==MT_START) { inBroadCast=true; } else { inBroadCast=false; } } marks.Add(Mark->Type,Mark->Position,comment); if (comment) free(comment); } void cMarkAdStandalone::SaveFrame(int frame) { if (!macontext.Video.Info.Width) return; if (!macontext.Video.Data.Valid) return; FILE *pFile; char szFilename[256]; // Open file sprintf(szFilename, "/tmp/frame%06d.pgm", frame); pFile=fopen(szFilename, "wb"); if (pFile==NULL) return; // Write header fprintf(pFile, "P5\n%d %d\n255\n", macontext.Video.Data.PlaneLinesize[0], macontext.Video.Info.Height); // Write pixel data if (fwrite(macontext.Video.Data.Plane[0],1, macontext.Video.Data.PlaneLinesize[0]*macontext.Video.Info.Height,pFile)) {}; // Close file fclose(pFile); } void cMarkAdStandalone::CheckIndexGrowing() { // Here we check if the index is more // advanced than our framecounter. // If not we wait. If we wait too much, // we discard this check... #define WAITTIME 15 if (!indexFile) return; if (macontext.Config->logoExtraction!=-1) return; if (sleepcnt>=2) return; // we already slept too much bool notenough=true; do { struct stat statbuf; if (stat(indexFile,&statbuf)==-1) return; int maxframes=statbuf.st_size/8; if (maxframes<(framecnt+200)) { if ((difftime(time(NULL),statbuf.st_mtime))>=WAITTIME) { if (length && startTime) { if (time(NULL)>(startTime+(time_t) length)) { // "old" recording return; } else { sleepcnt=0; if (!iwaittime) esyslog("recording interrupted, waiting for continuation..."); iwaittime+=WAITTIME; } } else { // "old" recording return; } } marks.Save(directory,macontext.Video.Info.FramesPerSecond,isTS); sleep(WAITTIME); // now we sleep and hopefully the index will grow waittime+=WAITTIME; if (errno==EINTR) return; sleepcnt++; if (sleepcnt>=2) { esyslog("no new data after %is, skipping wait!", waittime); notenough=false; // something went wrong? } } else { if (iwaittime) { esyslog("resuming after %is of interrupted recording, marks can be wrong now!",iwaittime); } iwaittime=0; sleepcnt=0; notenough=false; } } while (notenough); return; } void cMarkAdStandalone::ChangeMarks(clMark **Mark1, clMark **Mark2, MarkAdPos *NewPos) { if (!NewPos) return; if (!Mark1) return; if (!*Mark1) return; bool save=false; if ((*Mark1)->position!=NewPos->FrameNumberBefore) { char *buf=NULL; if (asprintf(&buf,"overlap before %i, moved to %i",(*Mark1)->position, NewPos->FrameNumberBefore)==-1) return; isyslog("%s",buf); marks.Del(*Mark1); *Mark1=marks.Add(MT_MOVED,NewPos->FrameNumberBefore,buf); free(buf); save=true; } if (Mark2 && (*Mark2) && (*Mark2)->position!=NewPos->FrameNumberAfter) { char *buf=NULL; if (asprintf(&buf,"overlap after %i, moved to %i",(*Mark2)->position, NewPos->FrameNumberAfter)==-1) return; isyslog("%s",buf); marks.Del(*Mark2); *Mark2=marks.Add(MT_MOVED,NewPos->FrameNumberAfter,buf); free(buf); save=true; } if (save) marks.Save(directory,macontext.Video.Info.FramesPerSecond,isTS,true); } bool cMarkAdStandalone::ProcessFile2ndPass(clMark **Mark1, clMark **Mark2,int Number, off_t Offset, int Frame, int Frames) { if (!directory) return false; if (!Number) return false; if (!Frames) return false; if (!decoder) return false; if (!Mark1) return false; if (!*Mark1) return false; int pn; // process number 1=start mark, 2=before mark, 3=after mark if (Mark1 && Mark2) { if (!(*Mark1) || !(*Mark2)) return false; if (*Mark1==*Mark2) pn=mSTART; if (*Mark1!=*Mark2) pn=mAFTER; } else { pn=mBEFORE; } if (!Reset(false)) { // reset all, but marks esyslog("failed resetting state"); return false; } iframe=Frame; int actframe=Frame; int framecounter=0; MarkAdPos *pos=NULL; while (framecounterposition); } else { dsyslog("processing file %05i (after mark %i)",Number,(*Mark2)->position); } } if (lseek(f,Offset,SEEK_SET)!=Offset) { close(f); return false; } while ((dataread=read(f,data,datalen))>0) { if (abort) break; if ((demux) && (video) && (decoder) && (streaminfo)) { uchar *tspkt = data; int tslen = dataread; while (tslen>0) { int len=demux->Process(tspkt,tslen,&pkt); if (len<0) { esyslog("error demuxing file"); abort=true; break; } else { if ((pkt.Data) && ((pkt.Type & PACKET_MASK)==PACKET_VIDEO)) { bool dRes=false; if (streaminfo->FindVideoInfos(&macontext,pkt.Data,pkt.Length)) { actframe++; framecnt2++; if (macontext.Video.Info.Pict_Type==MA_I_TYPE) { lastiframe=iframe; if (macontext.Info.VPid.Type==MARKAD_PIDTYPE_VIDEO_H264) { iframe=actframe; } else { iframe=actframe-1; } dRes=true; } } if (pn>mSTART) dRes=decoder->DecodeVideo(&macontext,pkt.Data,pkt.Length); if (dRes) { if ((actframe-iframe)<=3) { if (pn>mSTART) pos=video->ProcessOverlap(lastiframe,Frames,(pn==mBEFORE)); framecounter++; } if ((pos) && (pn==mAFTER)) { // found overlap ChangeMarks(Mark1,Mark2,pos); close(f); return true; } } } tspkt+=len; tslen-=len; } } } if (abort) { close(f); return false; } if (framecounter>Frames) { break; } } close(f); Number++; Offset=0; } return true; } void cMarkAdStandalone::Process2ndPass() { if (abort) return; if (duplicate) return; if (!decoder) return; if (!length) return; if (!startTime) return; if (time(NULL)<(startTime+(time_t) length)) return; if (!macontext.Video.Info.FramesPerSecond) { isyslog("WARNING: assuming fps of 25"); macontext.Video.Info.FramesPerSecond=25; } if (!marks.Count()) { marks.Load(directory,macontext.Video.Info.FramesPerSecond,isTS); } bool infoheader=false; clMark *p1=NULL,*p2=NULL; if (marks.Count()<4) return; // we cannot do much without marks p1=marks.GetFirst(); if (!p1) return; p1=p1->Next(); if (p1) p2=p1->Next(); while ((p1) && (p2)) { if (!infoheader) { isyslog("2nd pass"); infoheader=true; } off_t offset; int number,frame,iframes; int frange=macontext.Video.Info.FramesPerSecond*120; // 40s + 80s if (marks.ReadIndex(directory,isTS,p1->position-frange,frange,&number,&offset,&frame,&iframes)) { if (!ProcessFile2ndPass(&p1,NULL,number,offset,frame,iframes)) break; frange=macontext.Video.Info.FramesPerSecond*320; // 160s + 160s if (marks.ReadIndex(directory,isTS,p2->position,frange,&number,&offset,&frame,&iframes)) { if (!ProcessFile2ndPass(&p1,&p2,number,offset,frame,iframes)) break; } } else { esyslog("error reading index"); return; } p1=p2->Next(); if (p1) { p2=p1->Next(); } else { p2=NULL; } } } bool cMarkAdStandalone::ProcessFile(int Number) { if (!directory) return false; if (!Number) return false; CheckIndexGrowing(); if (abort) return false; const int datalen=319976; uchar data[datalen]; char *fbuf; if (isTS) { if (asprintf(&fbuf,"%s/%05i.ts",directory,Number)==-1) return false; } else { if (asprintf(&fbuf,"%s/%03i.vdr",directory,Number)==-1) return false; } int f=open(fbuf,O_RDONLY); free(fbuf); if (f==-1) return false; int dataread; dsyslog("processing file %05i",Number); demux->NewFile(); while ((dataread=read(f,data,datalen))>0) { if (abort) break; if ((demux) && (video) && (streaminfo)) { uchar *tspkt = data; int tslen = dataread; while (tslen>0) { int len=demux->Process(tspkt,tslen,&pkt); if (len<0) { esyslog("error demuxing"); abort=true; break; } else { if (pkt.Data) { if ((pkt.Type & PACKET_MASK)==PACKET_VIDEO) { bool dRes=false; if (streaminfo->FindVideoInfos(&macontext,pkt.Data,pkt.Length)) { if ((macontext.Video.Info.Height) && (!noticeHEADER)) { if ((!isTS) && (!noticeVDR_VID)) { isyslog("found %s-video (0x%02X)", macontext.Info.VPid.Type==MARKAD_PIDTYPE_VIDEO_H264 ? "H264": "H262", pkt.Stream); noticeVDR_VID=true; } isyslog("%s %ix%i%c%0.f",(macontext.Video.Info.Height>576) ? "HDTV" : "SDTV", macontext.Video.Info.Width, macontext.Video.Info.Height, macontext.Video.Info.Interlaced ? 'i' : 'p', macontext.Video.Info.FramesPerSecond); noticeHEADER=true; } if (!framecnt) { CalculateCheckPositions(tStart*macontext.Video.Info.FramesPerSecond); } if (macontext.Config->GenIndex) { marks.WriteIndex(directory,isTS,demux->Offset(),macontext.Video.Info.Pict_Type,Number); } framecnt++; if ((macontext.Config->logoExtraction!=-1) && (framecnt>=256)) { isyslog("finished logo extraction"); abort=true; if (f!=-1) close(f); return true; } if (macontext.Video.Info.Pict_Type==MA_I_TYPE) { lastiframe=iframe; if ((iStart<0) && (lastiframe>-iStart)) iStart=lastiframe; if ((iStop<0) && (lastiframe>-iStop)) { iStop=lastiframe; iStopinBroadCast=inBroadCast; } iframe=framecnt-1; dRes=true; } } if ((decoder) && (bDecodeVideo)) dRes=decoder->DecodeVideo(&macontext,pkt.Data,pkt.Length); if (dRes) { if ((framecnt-iframe)<=3) { MarkAdMarks *vmarks=video->Process(lastiframe,iframe); if (vmarks) { for (int i=0; iCount; i++) { AddMark(&vmarks->Number[i]); } } //SaveFrame(lastiframe); // TODO: JUST FOR DEBUGGING! if (iStart>0) { if ((inBroadCast) && (lastiframe>chkSTART)) CheckStart(); } if (iStop>0) { if (lastiframe>chkSTOP) CheckStop(); } } } } if ((pkt.Type & PACKET_MASK)==PACKET_AC3) { if (streaminfo->FindAC3AudioInfos(&macontext,pkt.Data,pkt.Length)) { if ((!isTS) && (!noticeVDR_AC3)) { isyslog("found AC3 (0x%02X)",pkt.Stream); noticeVDR_AC3=true; } if ((framecnt-iframe)<=3) { MarkAdMark *amark=audio->Process(lastiframe,iframe); if (amark) { AddMark(amark); } } } } } tspkt+=len; tslen-=len; } } } if ((gotendmark) && (!macontext.Config->GenIndex)) { if (f!=-1) close(f); return true; } CheckIndexGrowing(); if (abort) { if (f!=-1) close(f); return false; } } close(f); return true; } bool cMarkAdStandalone::Reset(bool FirstPass) { bool ret=true; if (FirstPass) framecnt=0; lastiframe=0; iframe=0; gotendmark=false; memset(&pkt,0,sizeof(pkt)); chkSTART=chkSTOP=INT_MAX; if (FirstPass) { marks.DelAll(); marks.CloseIndex(directory,isTS); } macontext.Video.Info.Pict_Type=0; macontext.Video.Info.AspectRatio.Den=0; macontext.Video.Info.AspectRatio.Num=0; macontext.Audio.Info.Channels=0; if (decoder) { ret=decoder->Clear(); } if (streaminfo) streaminfo->Clear(); if (demux) demux->Clear(); if (video) video->Clear(); if (audio) audio->Clear(); return ret; } void cMarkAdStandalone::ProcessFile() { for (int i=1; i<=MaxFiles; i++) { if (abort) break; if (!ProcessFile(i)) break; if ((gotendmark) && (!macontext.Config->GenIndex)) break; } if (!abort) { CheckLogoMarks(); if (iStop>0) CheckStop(); // no stopmark till now? if ((inBroadCast) && (!gotendmark) && (lastiframe)) { MarkAdMark tempmark; tempmark.Type=MT_RECORDINGSTOP; tempmark.Position=lastiframe; AddMark(&tempmark); } } if (demux) skipped=demux->Skipped(); } void cMarkAdStandalone::Process() { if (abort) return; if (macontext.Config->BackupMarks) marks.Backup(directory,isTS); ProcessFile(); marks.CloseIndex(directory,isTS); if (!abort) { if (marks.Save(directory,macontext.Video.Info.FramesPerSecond,isTS)) { if (length && startTime) { if (time(NULL)>(startTime+(time_t) length)) { int iIndexError=false; int tframecnt=macontext.Config->GenIndex ? framecnt : 0; if (marks.CheckIndex(directory,isTS,&tframecnt,&iIndexError)) { if (iIndexError) { if (macontext.Config->GenIndex) { switch (iIndexError) { case IERR_NOTFOUND: isyslog("no index found"); break; case IERR_TOOSHORT: isyslog("index too short"); break; default: isyslog("index doesn't match marks"); break; } if (RegenerateIndex()) { isyslog("recreated index"); } else { esyslog("failed to recreate index"); } } else { esyslog("index doesn't match marks%s", ((isTS) || ((macontext.Info.VPid.Type== MARKAD_PIDTYPE_VIDEO_H264) && (!isTS))) ? ", sorry you're lost" : ", please run genindex"); } } } if (macontext.Config->SaveInfo) SaveInfo(); } else { // this shouldn't be reached if (macontext.Config->logoExtraction==-1) esyslog("ALERT: stopping before end of broadcast"); } } } } if (macontext.Config->GenIndex) marks.RemoveGeneratedIndex(directory,isTS); } bool cMarkAdStandalone::SetFileUID(char *File) { if (!File) return false; struct stat statbuf; if (!stat(directory,&statbuf)) { if (chown(File,statbuf.st_uid, statbuf.st_gid)==-1) return false; } return true; } bool cMarkAdStandalone::SaveInfo() { isyslog("writing info file"); char *src,*dst; if (asprintf(&src,"%s/info%s",directory,isTS ? "" : ".vdr")==-1) return false; if (asprintf(&dst,"%s/info.bak",directory)==-1) { free(src); return false; } FILE *r,*w; r=fopen(src,"r"); w=fopen(dst,"w+"); if ((!r) || (!w)) { free(src); free(dst); return false; } char *line=NULL; char *lline=NULL; size_t len=0; char lang[4]=""; int component_type_add=0; if (macontext.Video.Info.Height>576) component_type_add=8; int stream_content=0; if (macontext.Info.VPid.Type==MARKAD_PIDTYPE_VIDEO_H262) stream_content=1; if (macontext.Info.VPid.Type==MARKAD_PIDTYPE_VIDEO_H264) stream_content=5; int component_type_43; int component_type_169; if ((macontext.Video.Info.FramesPerSecond==25) || (macontext.Video.Info.FramesPerSecond==50)) { component_type_43=1; component_type_169=3; } else { component_type_43=5; component_type_169=7; } bool err=false; while (getline(&line,&len,r)!=-1) { if (line[0]=='X') { int stream=0,type=0; char descr[256]=""; int result=sscanf(line,"%*c %i %i %3c %250c",&stream,&type,(char *) &lang, (char *) &descr); if ((result!=0) && (result!=EOF)) { switch (stream) { case 1: case 5: if (stream==stream_content) { if ((macontext.Info.AspectRatio.Num==4) && (macontext.Info.AspectRatio.Den==3)) { if (fprintf(w,"X %i %02i %s 4:3\n",stream_content, component_type_43+component_type_add,lang)<=0) err=true; macontext.Info.AspectRatio.Num=0; macontext.Info.AspectRatio.Den=0; } else if ((macontext.Info.AspectRatio.Num==16) && (macontext.Info.AspectRatio.Den==9)) { if (fprintf(w,"X %i %02i %s 16:9\n",stream_content, component_type_169+component_type_add,lang)<=0) err=true; macontext.Info.AspectRatio.Num=0; macontext.Info.AspectRatio.Den=0; } else { if (fprintf(w,"%s",line)<=0) err=true; } } else { if (fprintf(w,"%s",line)<=0) err=true; } break; case 2: if (type==5) { if (macontext.Info.Channels==6) { if (fprintf(w,"X 2 05 %s Dolby Digital 5.1\n",lang)<=0) err=true; macontext.Info.Channels=0; } else if (macontext.Info.Channels==2) { if (fprintf(w,"X 2 05 %s Dolby Digital 2.0\n",lang)<=0) err=true; macontext.Info.Channels=0; } else { if (fprintf(w,"%s",line)<=0) err=true; } } else { if (fprintf(w,"%s",line)<=0) err=true; } break; default: if (fprintf(w,"%s",line)<=0) err=true; break; } } } else { if (line[0]!='@') { if (fprintf(w,"%s",line)<=0) err=true; } else { if (lline) { free(lline); err=true; esyslog("multiple @lines in info file, please report this!"); } lline=strdup(line); } } if (err) break; } if (line) free(line); line=lline; if (lang[0]==0) strcpy(lang,"und"); if (stream_content) { if ((macontext.Info.AspectRatio.Num==4) && (macontext.Info.AspectRatio.Den==3) && (!err)) { if (fprintf(w,"X %i %02i %s 4:3\n",stream_content, component_type_43+component_type_add,lang)<=0) err=true; } if ((macontext.Info.AspectRatio.Num==16) && (macontext.Info.AspectRatio.Den==9) && (!err)) { if (fprintf(w,"X %i %02i %s 16:9\n",stream_content, component_type_169+component_type_add,lang)<=0) err=true; } } if ((macontext.Info.Channels==2) && (!err)) { if (fprintf(w,"X 2 05 %s Dolby Digital 2.0\n",lang)<=0) err=true; } if ((macontext.Info.Channels==6) && (!err)) { if (fprintf(w,"X 2 05 %s Dolby Digital 5.1\n",lang)<=0) err=true; } if (line) { if (fprintf(w,"%s",line)<=0) err=true; free(line); } fclose(w); struct stat statbuf_r; if (fstat(fileno(r),&statbuf_r)==-1) err=true; fclose(r); if (err) { unlink(dst); } else { if (rename(dst,src)==-1) { err=true; } else { // preserve timestamps from old file struct utimbuf oldtimes; oldtimes.actime=statbuf_r.st_atime; oldtimes.modtime=statbuf_r.st_mtime; if (utime(src,&oldtimes)) {}; SetFileUID(src); } } free(src); free(dst); return (err==false); } time_t cMarkAdStandalone::GetBroadcastStart(time_t start, int fd) { // get broadcast start from atime of directory (if the volume is mounted with noatime) struct mntent *ent; struct stat statbuf; FILE *mounts=setmntent(_PATH_MOUNTED,"r"); int mlen=0; int oldmlen=0; bool useatime=false; while ((ent=getmntent(mounts))!=NULL) { if (strstr(directory,ent->mnt_dir)) { mlen=strlen(ent->mnt_dir); if (mlen>oldmlen) { if (strstr(ent->mnt_opts,"noatime")) { useatime=true; } else { useatime=false; } } oldmlen=mlen; } } endmntent(mounts); if ((useatime) && (stat(directory,&statbuf)!=-1)) { isyslog("getting broadcast start from directory atime"); return statbuf.st_atime; } // try to get from mtime // (and hope info.vdr has not changed after the start of the recording) if (fstat(fd,&statbuf)!=-1) { if (fabs(difftime(start,statbuf.st_mtime))<1800) { isyslog("getting broadcast start from info mtime"); return (time_t) statbuf.st_mtime; } } // fallback to the directory -> worst starttime we can use! const char *timestr=strrchr(directory,'/'); if (timestr) { timestr++; if (isdigit(*timestr)) { time_t now = time(NULL); struct tm tm_r; struct tm t = *localtime_r(&now, &tm_r); // init timezone if (sscanf(timestr, "%4d-%02d-%02d.%02d%*c%02d", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, & t.tm_min)==5) { t.tm_year-=1900; t.tm_mon--; t.tm_sec=0; t.tm_isdst=-1; isyslog("getting broadcast start from directory"); return mktime(&t); } } } return (time_t) 0; } bool cMarkAdStandalone::CheckLogo() { if (!macontext.Config) return false; if (!macontext.Config->logoDirectory) return false; if (!macontext.Info.ChannelName) return false; int len=strlen(macontext.Info.ChannelName); if (!len) return false; DIR *dir=opendir(macontext.Config->logoDirectory); if (!dir) return false; struct dirent *dirent; while ((dirent=readdir(dir))) { if (!strncmp(dirent->d_name,macontext.Info.ChannelName,len)) { closedir(dir); return true; } } closedir(dir); return false; } bool cMarkAdStandalone::LoadInfo() { char *buf; if (asprintf(&buf,"%s/info%s",directory,isTS ? "" : ".vdr")==-1) return false; FILE *f; f=fopen(buf,"r"); free(buf); if (!f) return false; char *line=NULL; size_t linelen; while (getline(&line,&linelen,f)!=-1) { if (line[0]=='C') { char channelname[256]=""; int result=sscanf(line,"%*c %*s %250c",(char *) &channelname); if (result==1) { macontext.Info.ChannelName=strdup(channelname); char *lf=strchr(macontext.Info.ChannelName,10); if (lf) *lf=0; char *cr=strchr(macontext.Info.ChannelName,13); if (cr) *cr=0; for (int i=0; i<(int) strlen(macontext.Info.ChannelName); i++) { if (macontext.Info.ChannelName[i]==' ') macontext.Info.ChannelName[i]='_'; if (macontext.Info.ChannelName[i]=='.') macontext.Info.ChannelName[i]='_'; if (macontext.Info.ChannelName[i]=='/') macontext.Info.ChannelName[i]='_'; } } } if (line[0]=='E') { int result=sscanf(line,"%*c %*i %li %i %*i %*x",&startTime,&length); if (result!=2) { startTime=0; length=0; } } if (line[0]=='T') { int result=sscanf(line,"%*c %79c",title); if ((result==0) || (result==EOF)) { title[0]=0; } else { char *lf=strchr(title,10); if (lf) *lf=0; char *cr=strchr(title,13); if (cr) *cr=0; } } if (line[0]=='F') { int fps; int result=sscanf(line,"%*c %i",&fps); if ((result==0) || (result==EOF)) { macontext.Video.Info.FramesPerSecond=0; } else { macontext.Video.Info.FramesPerSecond=fps; } } if (line[0]=='X') { int stream=0,type=0; char descr[256]=""; int result=sscanf(line,"%*c %i %i %250c",&stream,&type,(char *) &descr); if ((result!=0) && (result!=EOF)) { if ((stream==1) || (stream==5)) { if ((type!=1) && (type!=5) && (type!=9) && (type!=13)) { isyslog("broadcast aspectratio 16:9 (from info)"); macontext.Info.AspectRatio.Num=16; macontext.Info.AspectRatio.Den=9; } else { isyslog("broadcast aspectratio 4:3 (from info)"); macontext.Info.AspectRatio.Num=4; macontext.Info.AspectRatio.Den=3; } } if (stream==2) { if (type==5) { // if we have DolbyDigital 2.0 disable AC3 if (strchr(descr,'2')) { isyslog("broadcast with DolbyDigital2.0 (from info)"); macontext.Info.Channels=2; } // if we have DolbyDigital 5.1 disable video decoding if (strchr(descr,'5')) { isyslog("broadcast with DolbyDigital5.1 (from info)"); macontext.Info.Channels=6; } } } } } } if (line) free(line); if ((length) && (!bIgnoreTimerInfo) && (startTime)) { time_t rStart=GetBroadcastStart(startTime,fileno(f)); if (rStart) { tStart=(int) (startTime-rStart); if (tStart<0) { if (length+tStart>0) { isyslog("broadcast start truncated by %im, length will be corrected",-tStart/60); startTime=rStart; length+=tStart; tStart=1; } else { esyslog("cannot determine broadcast start, disabling start/stop detection"); tStart=0; } } } else { tStart=0; } } else { tStart=0; } fclose(f); if (!length) { esyslog("cannot read broadcast length from info, marks can be wrong!"); macontext.Info.AspectRatio.Num=0; macontext.Info.AspectRatio.Den=0; bDecodeVideo=macontext.Config->DecodeVideo; macontext.Video.Options.IgnoreAspectRatio=false; } if (!macontext.Info.ChannelName) { return false; } else { return true; } } bool cMarkAdStandalone::CheckTS() { MaxFiles=0; isTS=false; if (!directory) return false; char *buf; if (asprintf(&buf,"%s/00001.ts",directory)==-1) return false; struct stat statbuf; if (stat(buf,&statbuf)==-1) { if (errno!=ENOENT) { free(buf); return false; } free(buf); if (asprintf(&buf,"%s/001.vdr",directory)==-1) return false; if (stat(buf,&statbuf)==-1) { free(buf); return false; } free(buf); // .VDR detected isTS=false; MaxFiles=999; return true; } free(buf); // .TS detected isTS=true; MaxFiles=65535; return true; } bool cMarkAdStandalone::CheckVDRHD() { char *buf; if (asprintf(&buf,"%s/001.vdr",directory)==-1) return false; int fd=open(buf,O_RDONLY); free(buf); if (fd==-1) return false; uchar pes_buf[32]; if (read(fd,pes_buf,sizeof(pes_buf))!=sizeof(pes_buf)) { close(fd); return false; } close(fd); if ((pes_buf[0]==0) && (pes_buf[1]==0) && (pes_buf[2]==1) && ((pes_buf[3] & 0xF0)==0xE0)) { int payloadstart=9+pes_buf[8]; if (payloadstart>23) return false; uchar *start=&pes_buf[payloadstart]; if ((start[0]==0) && (start[1]==0) && (start[2]==1) && (start[5]==0) && (start[6]==0) && (start[7]==0) && (start[8]==1)) { return true; } } return false; } off_t cMarkAdStandalone::SeekPATPMT() { char *buf; if (asprintf(&buf,"%s/00001.ts",directory)==-1) return (off_t) -1; int fd=open(buf,O_RDONLY); free(buf); if (fd==-1) return (off_t) -1; uchar peek_buf[188]; for (int i=0; i<5000; i++) { int ret=read(fd,peek_buf,sizeof(peek_buf)); if (!ret) { close(fd); return (off_t) -2; } if (ret!=sizeof(peek_buf)) { close(fd); return (off_t) -2; } if (ret<0) { if (errno!=EINTR) { close(fd); return (off_t) -3; } else { sleep(3); } } if ((peek_buf[0]==0x47) && ((peek_buf[1] & 0x5F)==0x40) && (peek_buf[2]==00)) { off_t ret=lseek(fd,0,SEEK_CUR); close(fd); return ret-188; } } close(fd); return (off_t) -1; } bool cMarkAdStandalone::CheckPATPMT(off_t Offset) { if (Offset<(off_t) 0) return false; char *buf; if (asprintf(&buf,"%s/00001.ts",directory)==-1) return false; int fd=open(buf,O_RDONLY); free(buf); if (fd==-1) return false; if (lseek(fd,Offset,SEEK_SET)==(off_t)-1) { close(fd); return false; } uchar patpmt_buf[564]; uchar *patpmt; if (read(fd,patpmt_buf,sizeof(patpmt_buf))!=sizeof(patpmt_buf)) { close(fd); return false; } close(fd); patpmt=patpmt_buf; if ((patpmt[0]==0x47) && ((patpmt[1] & 0x5F)==0x40) && (patpmt[2]==0x11) && ((patpmt[3] & 0x10)==0x10)) patpmt+=188; // skip SDT // some checks if ((patpmt[0]!=0x47) || (patpmt[188]!=0x47)) return false; // no TS-Sync if (((patpmt[1] & 0x5F)!=0x40) && (patpmt[2]!=0)) return false; // no PAT if ((patpmt[3] & 0x10)!=0x10) return false; // PAT not without AFC if ((patpmt[191] & 0x10)!=0x10) return false; // PMT not without AFC struct PAT *pat = (struct PAT *) &patpmt[5]; // more checks if (pat->reserved1!=3) return false; // is always 11 if (pat->reserved3!=7) return false; // is always 111 int pid=pat->pid_L+(pat->pid_H<<8); int pmtpid=((patpmt[189] & 0x1f)<<8)+patpmt[190]; if (pid!=pmtpid) return false; // pid in PAT differs from pid in PMT struct PMT *pmt = (struct PMT *) &patpmt[193]; // still more checks if (pmt->reserved1!=3) return false; // is always 11 if (pmt->reserved2!=3) return false; // is always 11 if (pmt->reserved3!=7) return false; // is always 111 if (pmt->reserved4!=15) return false; // is always 1111 if ((pmt->program_number_H!=pat->program_number_H) || (pmt->program_number_L!=pat->program_number_L)) return false; int desc_len=(pmt->program_info_length_H<<8)+pmt->program_info_length_L; if (desc_len>166) return false; // beyond patpmt buffer int section_end = 196+(pmt->section_length_H<<8)+pmt->section_length_L; section_end-=4; // we don't care about the CRC32 if (section_end>376) return false; //beyond patpmt buffer int i=205+desc_len; while (iES_info_length_H<<8)+si->ES_info_length_L; if (esinfo_len) { es = (struct ES_DESCRIPTOR *) &patpmt[i+sizeof(struct STREAMINFO)]; } // oh no -> more checks! if (si->reserved1!=7) return false; if (si->reserved2!=15) return false; int pid=(si->PID_H<<8)+si->PID_L; switch (si->stream_type) { case 0x1: case 0x2: macontext.Info.VPid.Type=MARKAD_PIDTYPE_VIDEO_H262; // just use the first pid if (!macontext.Info.VPid.Num) macontext.Info.VPid.Num=pid; break; case 0x3: case 0x4: // just use the first pid if (!macontext.Info.APid.Num) macontext.Info.APid.Num=pid; break; case 0x6: if (es) { if (es->Descriptor_Tag==0x6A) macontext.Info.DPid.Num=pid; } break; case 0x1b: macontext.Info.VPid.Type=MARKAD_PIDTYPE_VIDEO_H264; // just use the first pid if (!macontext.Info.VPid.Num) macontext.Info.VPid.Num=pid; break; } i+=(sizeof(struct STREAMINFO)+esinfo_len); } return true; } bool cMarkAdStandalone::RegenerateIndex() { if (!directory) return false; // rename index[.vdr].generated -> index[.vdr] char *oldpath,*newpath; if (asprintf(&oldpath,"%s/index%s.generated",directory, isTS ? "" : ".vdr")==-1) return false; if (asprintf(&newpath,"%s/index%s",directory,isTS ? "" : ".vdr")==-1) { free(oldpath); return false; } if (rename(oldpath,newpath)!=0) { if (errno!=ENOENT) { free(oldpath); free(newpath); return false; } } free(oldpath); free(newpath); return true; } bool cMarkAdStandalone::CreatePidfile() { char *buf=NULL; if (asprintf(&buf,"%s/markad.pid",directory)==-1) return false; // check for other running markad process FILE *oldpid=fopen(buf,"r"); if (oldpid) { // found old pidfile, check if it's still running int pid; if (fscanf(oldpid,"%i\n",&pid)==1) { char procname[256]=""; snprintf(procname,sizeof(procname),"/proc/%i",pid); struct stat statbuf; if (stat(procname,&statbuf)!=-1) { // found another, running markad fprintf(stderr,"another instance is running on %s",directory); abort=duplicate=true; } } fclose(oldpid); } else { // fopen above sets the error to 2, reset it here! errno=0; } if (duplicate) { free(buf); return false; } FILE *pidfile=fopen(buf,"w+"); SetFileUID(buf); free(buf); if (!pidfile) return false; fprintf(pidfile,"%i\n",(int) getpid()); fflush(pidfile); fclose(pidfile); return true; } void cMarkAdStandalone::RemovePidfile() { if (!directory) return; if (duplicate) return; char *buf; if (asprintf(&buf,"%s/markad.pid",directory)!=-1) { unlink(buf); free(buf); } } const char cMarkAdStandalone::frametypes[8]={'?','I','P','B','D','S','s','b'}; cMarkAdStandalone::cMarkAdStandalone(const char *Directory, const MarkAdConfig *config) { setlocale(LC_MESSAGES, ""); directory=Directory; abort=false; gotendmark=false; inBroadCast=false; iStopinBroadCast=false; indexFile=NULL; streaminfo=NULL; demux=NULL; decoder=NULL; video=NULL; audio=NULL; osd=NULL; memset(&pkt,0,sizeof(pkt)); noticeVDR_VID=false; noticeVDR_AC3=false; noticeHEADER=false; noticeFILLER=false; skipped=0; length=0; sleepcnt=0; waittime=iwaittime=0; duplicate=false; title[0]=0; memset(&macontext,0,sizeof(macontext)); macontext.Config=config; bDecodeVideo=config->DecodeVideo; bDecodeAudio=config->DecodeAudio; tStart=iStart=iStop=0; if ((config->ignoreInfo & IGNORE_TIMERINFO)==IGNORE_TIMERINFO) { bIgnoreTimerInfo=true; } else { bIgnoreTimerInfo=false; } macontext.Info.DPid.Type=MARKAD_PIDTYPE_AUDIO_AC3; macontext.Info.APid.Type=MARKAD_PIDTYPE_AUDIO_MP2; if (!config->NoPid) { CreatePidfile(); if (abort) return; } if (LOG2REC) { char *fbuf; if (asprintf(&fbuf,"%s/markad.log",directory)!=-1) { if (freopen(fbuf,"w+",stdout)) {}; SetFileUID(fbuf); free(fbuf); } } long lb; errno=0; lb=sysconf(_SC_LONG_BIT); if (errno==0) { isyslog("starting v%s (%libit)",VERSION,lb); } else { isyslog("starting v%s",VERSION); } isyslog("on %s",Directory); if (!bDecodeAudio) { isyslog("audio decoding disabled by user"); } if (!bDecodeVideo) { isyslog("video decoding disabled by user"); } if (bIgnoreTimerInfo) { isyslog("timer info usage disabled by user"); } if (config->logoExtraction!=-1) { // just to be sure extraction works bDecodeVideo=true; } if (config->Before) sleep(10); if (strstr(directory,"/@")) { isyslog("live-recording, disabling pre-/post timer"); bIgnoreTimerInfo=true; } if (!CheckTS()) { esyslog("no files found"); abort=true; return; } if (isTS) { off_t pos; int sc=0; do { pos=SeekPATPMT(); if (pos==(off_t) -2) { sleep(10); sc++; if (sc>6) break; } } while (pos==(off_t) -2); if (!CheckPATPMT(pos)) { esyslog("no PAT/PMT found (%i) -> cannot process",(int) pos); abort=true; return; } if (asprintf(&indexFile,"%s/index",Directory)==-1) indexFile=NULL; } else { macontext.Info.APid.Num=-1; macontext.Info.DPid.Num=-1; macontext.Info.VPid.Num=-1; if (CheckVDRHD()) { macontext.Info.VPid.Type=MARKAD_PIDTYPE_VIDEO_H264; } else { macontext.Info.VPid.Type=MARKAD_PIDTYPE_VIDEO_H262; } if (asprintf(&indexFile,"%s/index.vdr",Directory)==-1) indexFile=NULL; } macontext.Info.APid.Num=0; // till now we do just nothing with stereo-sound if (!LoadInfo()) { if (bDecodeVideo) { esyslog("failed loading info - logo %s%sdisabled", (config->logoExtraction!=-1) ? "extraction" : "detection", bIgnoreTimerInfo ? " " : " and pre-/post-timer "); tStart=iStart=iStop=0; macontext.Video.Options.IgnoreLogoDetection=true; macontext.Video.Options.WeakMarksOk=true; } } else { if (!CheckLogo() && (config->logoExtraction==-1)) { isyslog("no logo found, logo detection disabled"); macontext.Video.Options.IgnoreLogoDetection=true; macontext.Video.Options.WeakMarksOk=true; } } if (macontext.Video.Options.WeakMarksOk) { isyslog("marks can/will be weak!"); inBroadCast=true; } if (tStart>1) isyslog("pre-timer %im",tStart/60); if (length) isyslog("broadcast length %im",length/60); if (title[0]) { ptitle=title; } else { ptitle=(char *) Directory; } if (config->OSD) { osd= new cOSDMessage(config->svdrphost,config->svdrpport); if (osd) osd->Send("%s %s",tr("starting markad for"),ptitle); } else { osd=NULL; } if (config->markFileName[0]) marks.SetFileName(config->markFileName); if (macontext.Info.VPid.Num) { if (isTS) { isyslog("found %s-video (0x%04x)", macontext.Info.VPid.Type==MARKAD_PIDTYPE_VIDEO_H264 ? "H264": "H262", macontext.Info.VPid.Num); } demux=new cDemux(macontext.Info.VPid.Num,macontext.Info.DPid.Num,macontext.Info.APid.Num, macontext.Info.VPid.Type==MARKAD_PIDTYPE_VIDEO_H264,true); } else { demux=NULL; } if (macontext.Info.APid.Num) { if (macontext.Info.APid.Num!=-1) isyslog("found MP2 (0x%04x)",macontext.Info.APid.Num); } if (macontext.Info.DPid.Num) { if (macontext.Info.DPid.Num!=-1) isyslog("found AC3 (0x%04x)",macontext.Info.DPid.Num); } if (!abort) { decoder = new cMarkAdDecoder(macontext.Info.VPid.Type==MARKAD_PIDTYPE_VIDEO_H264,config->threads); video = new cMarkAdVideo(&macontext); audio = new cMarkAdAudio(&macontext); streaminfo = new cMarkAdStreamInfo; if (macontext.Info.ChannelName) isyslog("channel %s",macontext.Info.ChannelName); } framecnt=0; framecnt2=0; lastiframe=0; iframe=0; chkSTART=chkSTOP=INT_MAX; gettimeofday(&tv1,&tz); } cMarkAdStandalone::~cMarkAdStandalone() { if ((!abort) && (!duplicate)) { if (skipped) { isyslog("skipped %i bytes",skipped); } gettimeofday(&tv2,&tz); time_t sec; suseconds_t usec; sec=tv2.tv_sec-tv1.tv_sec; usec=tv2.tv_usec-tv1.tv_usec; if (usec<0) { usec+=1000000; sec--; } double etime,ftime=0,ptime=0; etime=sec+((double) usec/1000000)-waittime; if (etime>0) ftime=(framecnt+framecnt2)/etime; if (macontext.Video.Info.FramesPerSecond>0) ptime=ftime/macontext.Video.Info.FramesPerSecond; isyslog("processed time %.2fs, %i/%i frames, %.1f fps, %.1f pps", etime,framecnt,framecnt2,ftime,ptime); } if ((osd) && (!duplicate)) { if (abort) { osd->Send("%s %s",tr("markad aborted for"),ptitle); } else { osd->Send("%s %s",tr("markad finished for"),ptitle); } } if (macontext.Info.ChannelName) free(macontext.Info.ChannelName); if (indexFile) free(indexFile); if (demux) delete demux; if (decoder) delete decoder; if (video) delete video; if (audio) delete audio; if (streaminfo) delete streaminfo; if (osd) delete osd; RemovePidfile(); } bool isnumber(const char *s) { while (*s) { if (!isdigit(*s)) return false; s++; } return true; } int usage(int svdrpport) { // nothing done, give the user some help printf("Usage: markad [options] cmd \n" "options:\n" "-b --background\n" " markad runs as a background-process\n" " this will be automatically set if called with \"after\"\n" "-d --disable=