int cDvbSubtitleConverter::Convert(const uchar *Data, int Length) { if (Data && Length > 8) { int PayloadOffset = PesPayloadOffset(Data); if (Length > PayloadOffset) { int64_t pts = PesGetPts(Data); if (pts) dbgconverter("Converter PTS: %"PRId64"\n", pts); const uchar *data = Data + PayloadOffset; int length = Length - PayloadOffset; if (length > 3) { if (data[0] == 0x20 && data[1] == 0x00 && data[2] == 0x0F) { data += 2; length -= 2; } const uchar *b = data; while (length > 0) { if (b[0] == 0x0F) { int n = ExtractSegment(b, length, pts); if (n < 0) break; b += n; length -= n; } else break; } } } return Length; } return 0; }
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; } }
int cParser::ParsePESHeader(uint8_t *buf, size_t len) { // parse PES header unsigned int hdr_len = PesPayloadOffset(buf); // PTS / DTS int64_t pts = PesHasPts(buf) ? PesGetPts(buf) : DVD_NOPTS_VALUE; int64_t dts = PesHasDts(buf) ? PesGetDts(buf) : DVD_NOPTS_VALUE; if (dts == DVD_NOPTS_VALUE) dts = pts; if(pts != 0) m_curDTS = dts; if(dts != 0) m_curPTS = pts; return hdr_len; }
int cDvbSubtitleConverter::ConvertFragments(const uchar *Data, int Length) { if (Data && Length > 8) { int PayloadOffset = PesPayloadOffset(Data); int SubstreamHeaderLength = 4; bool ResetSubtitleAssembler = Data[PayloadOffset + 3] == 0x00; // Compatibility mode for old subtitles plugin: if ((Data[7] & 0x01) && (Data[PayloadOffset - 3] & 0x81) == 0x01 && Data[PayloadOffset - 2] == 0x81) { PayloadOffset--; SubstreamHeaderLength = 1; ResetSubtitleAssembler = Data[8] >= 5; } if (Length > PayloadOffset + SubstreamHeaderLength) { int64_t pts = PesHasPts(Data) ? PesGetPts(Data) : 0; if (pts) dbgconverter("Converter PTS: %"PRId64"\n", pts); const uchar *data = Data + PayloadOffset + SubstreamHeaderLength; // skip substream header int length = Length - PayloadOffset - SubstreamHeaderLength; // skip substream header if (ResetSubtitleAssembler) dvbSubtitleAssembler->Reset(); if (length > 3) { if (data[0] == 0x20 && data[1] == 0x00 && data[2] == 0x0F) dvbSubtitleAssembler->Put(data + 2, length - 2); else dvbSubtitleAssembler->Put(data, length); int Count; while (true) { unsigned char *b = dvbSubtitleAssembler->Get(Count); if (b && b[0] == 0x0F) { if (ExtractSegment(b, Count, pts) == -1) break; } else break; } } } return Length; } return 0; }
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; }