bool cDvbPlayer::GetIndex(int &Current, int &Total, bool SnapToIFrame) { if (index) { Current = ptsIndex.FindIndex(DeviceGetSTC()); if (SnapToIFrame) { int i1 = index->GetNextIFrame(Current + 1, false); int i2 = index->GetNextIFrame(Current, true); Current = (abs(Current - i1) <= abs(Current - i2)) ? i1 : i2; } Total = index->Last(); return true; } Current = Total = -1; return false; }
void cDvbPlayer::Goto(int Index, bool Still) { if (index) { LOCK_THREAD; Empty(); if (++Index <= 0) Index = 1; // not '0', to allow GetNextIFrame() below to work! uint16_t FileNumber; off_t FileOffset; int Length; Index = index->GetNextIFrame(Index, false, &FileNumber, &FileOffset, &Length); if (Index >= 0 && NextFile(FileNumber, FileOffset) && Still) { uchar b[MAXFRAMESIZE]; int r = ReadFrame(replayFile, b, Length, sizeof(b)); if (r > 0) { if (playMode == pmPause) DevicePlay(); DeviceStillPicture(b, r); ptsIndex.Put(isPesRecording ? PesGetPts(b) : TsGetPts(b, r), Index); } playMode = pmStill; } readIndex = Index; } }
bool cDvbPlayer::Save(void) { if (index) { int Index = ptsIndex.FindIndex(DeviceGetSTC()); if (Index >= 0) { Index -= int(round(RESUMEBACKUP * framesPerSecond)); if (Index > 0) Index = index->GetNextIFrame(Index, false); else Index = 0; if (Index >= 0) return index->StoreResume(Index); } } return false; }
void cFileWriter::Action(void) { time_t t = time(NULL); unsigned int skipped = 0; while (Running()) { int Count; uchar *p = remux->Get(Count, &pictureType, 1); while(skipped < 10 && (remux->SFmode() == SF_UNKNOWN || remux->TSmode() == rAuto)){ // TB: give remuxer a chance to detect the stream type skipped++; Count = 0; continue; } if (p && Count) { // esyslog("COUNT %i\n", Count); if (!Running() && pictureType == I_FRAME) // finish the recording before the next 'I' frame break; if (NextFile()) { #if 1 // Add PAT+PMT at every filestart and every MB if ((!fileSize || diffSize > PATPMT_DISTANCE) && remux->TSmode()==SF_H264) { uchar patpmt[2*188]; int plen; plen=remux->GetPATPMT(patpmt, 2*188); if (plen) { if (recordFile->Write(patpmt, plen) < 0) { LOG_ERROR_STR(fileName->Name()); break; } fileSize+=plen; } diffSize=0; } #endif if (index && pictureType != NO_PICTURE) index->Write(pictureType, fileName->Number(), fileSize); if (recordFile->Write(p, Count) < 0) { LOG_ERROR_STR(fileName->Name()); break; } fileSize += Count; diffSize += Count; remux->Del(Count); } else break; t = time(NULL); } else if (time(NULL) - t > MAXBROKENTIMEOUT) { esyslog("ERROR: video data stream broken"); //cThread::EmergencyExit(true); Skins.Message(mtError, tr("can't record - check your configuration")); t = time(NULL); } } }
int cDvbPlayer::SkipFrames(int Frames) { if (index && Frames) { int Current, Total; GetIndex(Current, Total, true); int OldCurrent = Current; // As GetNextIFrame() increments/decrements at least once, the // destination frame (= Current + Frames) must be adjusted by // -1/+1 respectively. Current = index->GetNextIFrame(Current + Frames + (Frames > 0 ? -1 : 1), Frames > 0); return Current >= 0 ? Current : OldCurrent; } return -1; }
void cDvbPlayer::SkipSeconds(int Seconds) { if (index && Seconds) { LOCK_THREAD; int Index = ptsIndex.FindIndex(DeviceGetSTC()); Empty(); if (Index >= 0) { Index = max(Index + SecondsToFrames(Seconds, framesPerSecond), 0); if (Index > 0) Index = index->GetNextIFrame(Index, false, NULL, NULL, NULL); if (Index >= 0) readIndex = Index - 1; // Action() will first increment it! } Play(); } }
void cCuttingThread::Action(void) { { sched_param tmp; tmp.sched_priority = CUTTER_PRIORITY; if(!pthread_setschedparam(pthread_self(), SCHED_OTHER, &tmp)) printf("cCuttingThread::Action: cant set priority\n"); } int bytes = 0; int __attribute__((unused)) burst_size = CUTTER_MAX_BANDWIDTH * CUTTER_TIMESLICE / 1000; // max bytes/timeslice cTimeMs __attribute__((unused)) t; cMark *Mark = fromMarks.First(); if (Mark) { fromFile = fromFileName->Open(); toFile = toFileName->Open(); if (!fromFile || !toFile) return; CheckTS(fromFile); fromFile->SetReadAhead(MEGABYTE(20)); int Index = Mark->position; Mark = fromMarks.Next(Mark); int FileSize = 0; int CurrentFileNumber = 0; int LastIFrame = 0; toMarks.Add(0); toMarks.Save(); uchar buffer[MAXFRAMESIZE]; bool LastMark = false; bool cutIn = true; while (Running()) { uchar FileNumber; int FileOffset, Length; uchar PictureType; // Make sure there is enough disk space: AssertFreeDiskSpace(-1); // Read one frame: if (fromIndex->Get(Index++, &FileNumber, &FileOffset, &PictureType, &Length)) { if (FileNumber != CurrentFileNumber) { fromFile = fromFileName->SetOffset(FileNumber, FileOffset); fromFile->SetReadAhead(MEGABYTE(20)); CurrentFileNumber = FileNumber; } if (fromFile) { int len = ReadFrame(fromFile, buffer, Length, sizeof(buffer)); if (len < 0) { error = "ReadFrame"; break; } if (len != Length) { CurrentFileNumber = 0; // this re-syncs in case the frame was larger than the buffer Length = len; } } else { error = "fromFile"; break; } } else break; // Write one frame: if (PictureType == I_FRAME || PATPMT != NULL) { // every file shall start with an I_FRAME if (LastMark) // edited version shall end before next I-frame break; if (FileSize == 0) { if (PATPMT != NULL) { if (toFile->Write(PATPMT, 2*TS_SIZE) < 0) // Add PATPMT to start of every file break; else FileSize+=TS_SIZE*2; } } else if (FileSize > MEGABYTE(Setup.MaxVideoFileSize)) { toFile = toFileName->NextFile(); if (!toFile) { error = "toFile 1"; break; } FileSize = 0; if (PATPMT != NULL) { if (toFile->Write(PATPMT, 2*TS_SIZE) < 0) // Add PATPMT to start of every file break; else FileSize+=TS_SIZE*2; } } LastIFrame = 0; if (cutIn) { cRemux::SetBrokenLink(buffer, Length); cutIn = false; } } if (toFile->Write(buffer, Length) < 0) { error = "safe_write"; break; } if (!toIndex->Write(PictureType, toFileName->Number(), FileSize)) { error = "toIndex"; break; } FileSize += Length; if (!LastIFrame) LastIFrame = toIndex->Last(); // Check editing marks: if (Mark && Index >= Mark->position) { Mark = fromMarks.Next(Mark); toMarks.Add(LastIFrame); if (Mark) toMarks.Add(toIndex->Last() + 1); toMarks.Save(); if (Mark) { Index = Mark->position; Mark = fromMarks.Next(Mark); CurrentFileNumber = 0; // triggers SetOffset before reading next frame cutIn = true; if (Setup.SplitEditedFiles) { toFile = toFileName->NextFile(); if (!toFile) { error = "toFile 2"; break; } FileSize = 0; } } else LastMark = true; } bytes += Length; if(bytes >= burst_size) { int elapsed = t.Elapsed(); int sleep = 0; #if CUTTER_REL_BANDWIDTH > 0 && CUTTER_REL_BANDWIDTH < 100 // stay under max. relative bandwidth sleep = (elapsed * 100 / CUTTER_REL_BANDWIDTH) - elapsed; //if(sleep<=0 && elapsed<=2) sleep = 1; //if(sleep) esyslog("cutter: relative bandwidth limit, sleep %d ms (chunk %dk / %dms)", sleep, burst_size/1024, CUTTER_TIMESLICE); #endif // stay under max. absolute bandwidth if(elapsed < CUTTER_TIMESLICE) { sleep = max(CUTTER_TIMESLICE - elapsed, sleep); //if(sleep) esyslog("cutter: absolute bandwidth limit, sleep %d ms (chunk %dk / %dms)", sleep, burst_size/1024, CUTTER_TIMESLICE); } if(sleep>0) cCondWait::SleepMs(sleep); t.Set(); bytes = 0; } } Recordings.TouchUpdate(); } else esyslog("no editing marks found!"); }
void cDvbPlayer::Action(void) { uchar *p = NULL; int pc = 0; readIndex = Resume(); if (readIndex >= 0) isyslog("resuming replay at index %d (%s)", readIndex, *IndexToHMSF(readIndex, true, framesPerSecond)); nonBlockingFileReader = new cNonBlockingFileReader; int Length = 0; bool Sleep = false; bool WaitingForData = false; time_t StuckAtEof = 0; uint32_t LastStc = 0; int LastReadIFrame = -1; int SwitchToPlayFrame = 0; if (pauseLive) Goto(0, true); while (Running()) { if (WaitingForData) nonBlockingFileReader->WaitForDataMs(3); // this keeps the CPU load low, but reacts immediately on new data else if (Sleep) { cPoller Poller; DevicePoll(Poller, 10); Sleep = false; if (playMode == pmStill || playMode == pmPause) cCondWait::SleepMs(3); } { LOCK_THREAD; // Read the next frame from the file: if (playMode != pmStill && playMode != pmPause) { if (!readFrame && (replayFile || readIndex >= 0)) { if (!nonBlockingFileReader->Reading()) { if (!SwitchToPlayFrame && (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward))) { uint16_t FileNumber; off_t FileOffset; bool TimeShiftMode = index->IsStillRecording(); int Index = -1; readIndependent = false; if (DeviceHasIBPTrickSpeed() && playDir == pdForward) { if (index->Get(readIndex + 1, &FileNumber, &FileOffset, &readIndependent, &Length)) Index = readIndex + 1; } else { int d = int(round(0.4 * framesPerSecond)); if (playDir != pdForward) d = -d; int NewIndex = readIndex + d; if (NewIndex <= 0 && readIndex > 0) NewIndex = 1; // make sure the very first frame is delivered NewIndex = index->GetNextIFrame(NewIndex, playDir == pdForward, &FileNumber, &FileOffset, &Length); if (NewIndex < 0 && TimeShiftMode && playDir == pdForward) SwitchToPlayFrame = readIndex; Index = NewIndex; readIndependent = true; } if (Index >= 0) { readIndex = Index; if (!NextFile(FileNumber, FileOffset)) continue; } else if (!(TimeShiftMode && playDir == pdForward)) eof = true; } else if (index) { uint16_t FileNumber; off_t FileOffset; if (index->Get(readIndex + 1, &FileNumber, &FileOffset, &readIndependent, &Length) && NextFile(FileNumber, FileOffset)) readIndex++; else eof = true; } else // allows replay even if the index file is missing Length = MAXFRAMESIZE; if (Length == -1) Length = MAXFRAMESIZE; // this means we read up to EOF (see cIndex) else if (Length > MAXFRAMESIZE) { esyslog("ERROR: frame larger than buffer (%d > %d)", Length, MAXFRAMESIZE); Length = MAXFRAMESIZE; } if (!eof) nonBlockingFileReader->Request(replayFile, Length); } if (!eof) { uchar *b = NULL; int r = nonBlockingFileReader->Result(&b); if (r > 0) { WaitingForData = false; uint32_t Pts = 0; if (readIndependent) { Pts = isPesRecording ? PesGetPts(b) : TsGetPts(b, r); LastReadIFrame = readIndex; } readFrame = new cFrame(b, -r, ftUnknown, readIndex, Pts); // hands over b to the ringBuffer } else if (r < 0) { if (errno == EAGAIN) WaitingForData = true; else if (FATALERRNO) { LOG_ERROR; break; } } else eof = true; } } // Store the frame in the buffer: if (readFrame) { if (ringBuffer->Put(readFrame)) readFrame = NULL; else Sleep = true; } } else Sleep = true; if (dropFrame) { if (!eof || (playDir != pdForward && dropFrame->Index() > 0) || (playDir == pdForward && dropFrame->Index() < readIndex)) { ringBuffer->Drop(dropFrame); // the very first and last frame are continously repeated to flush data through the device dropFrame = NULL; } } // Get the next frame from the buffer: if (!playFrame) { playFrame = ringBuffer->Get(); p = NULL; pc = 0; } // Play the frame: if (playFrame) { if (!p) { p = playFrame->Data(); pc = playFrame->Count(); if (p) { if (playFrame->Index() >= 0 && playFrame->Pts() != 0) ptsIndex.Put(playFrame->Pts(), playFrame->Index()); if (firstPacket) { if (isPesRecording) { PlayPes(NULL, 0); cRemux::SetBrokenLink(p, pc); } else PlayTs(NULL, 0); firstPacket = false; } } } if (p) { int w; if (isPesRecording) w = PlayPes(p, pc, playMode != pmPlay && !(playMode == pmSlow && playDir == pdForward) && DeviceIsPlayingVideo()); else w = PlayTs(p, pc, playMode != pmPlay && !(playMode == pmSlow && playDir == pdForward) && DeviceIsPlayingVideo()); if (w > 0) { p += w; pc -= w; } else if (w < 0 && FATALERRNO) LOG_ERROR; else Sleep = true; } if (pc <= 0) { dropFrame = playFrame; playFrame = NULL; p = NULL; } } else Sleep = true; // Handle hitting begin/end of recording: if (eof || SwitchToPlayFrame) { bool SwitchToPlay = false; uint32_t Stc = DeviceGetSTC(); if (Stc != LastStc) StuckAtEof = 0; else if (!StuckAtEof) StuckAtEof = time(NULL); else if (time(NULL) - StuckAtEof > MAXSTUCKATEOF) { if (playDir == pdForward) break; // automatically stop at end of recording SwitchToPlay = true; } LastStc = Stc; int Index = ptsIndex.FindIndex(Stc); if (playDir == pdForward && !SwitchToPlayFrame) { if (Index >= LastReadIFrame) break; // automatically stop at end of recording } else if (Index <= 0 || SwitchToPlayFrame && Index >= SwitchToPlayFrame) SwitchToPlay = true; if (SwitchToPlay) { if (!SwitchToPlayFrame) Empty(); DevicePlay(); playMode = pmPlay; playDir = pdForward; SwitchToPlayFrame = 0; } } } } cNonBlockingFileReader *nbfr = nonBlockingFileReader; nonBlockingFileReader = NULL; delete nbfr; }