vdr 2.8.3
dvbplayer.c
Go to the documentation of this file.
1/*
2 * dvbplayer.c: The DVB player
3 *
4 * See the main source file 'vdr.c' for copyright information and
5 * how to reach the author.
6 *
7 * $Id: dvbplayer.c 5.17 2026/08/16 09:45:03 kls Exp $
8 */
9
10#include "dvbplayer.h"
11#include <math.h>
12#include <stdlib.h>
13#include "remux.h"
14#include "ringbuffer.h"
15#include "thread.h"
16#include "tools.h"
17
18// --- cPtsIndex -------------------------------------------------------------
19
20#define PTSINDEX_ENTRIES 1024
21
22class cPtsIndex {
23private:
24 struct tPtsIndex {
25 uint32_t pts; // no need for 33 bit - some devices don't even supply the msb
26 int index;
28 };
30 int w, r;
33public:
34 cPtsIndex(void);
35 void Clear(void);
36 bool IsEmpty(void);
37 void Put(uint32_t Pts, int Index, bool Independent);
38 int FindIndex(uint32_t Pts, bool Still);
39 int FindFrameNumber(uint32_t Pts, bool Forward, bool Still);
40 };
41
43{
44 lastFound = 0;
45 Clear();
46}
47
49{
50 cMutexLock MutexLock(&mutex);
51 w = r = 0;
52}
53
55{
56 cMutexLock MutexLock(&mutex);
57 return w == r;
58}
59
60void cPtsIndex::Put(uint32_t Pts, int Index, bool Independent)
61{
62 cMutexLock MutexLock(&mutex);
63 pi[w].pts = Pts;
64 pi[w].independent = Independent;
65 pi[w].index = Index;
66 w = (w + 1) % PTSINDEX_ENTRIES;
67 if (w == r)
68 r = (r + 1) % PTSINDEX_ENTRIES;
69}
70
71int cPtsIndex::FindIndex(uint32_t Pts, bool Still)
72{
73 cMutexLock MutexLock(&mutex);
74 if (w == r || Pts == 0 && !Still) // while 0 is a valid PTS, DeviceGetSTC() might return 0 if, after a jump, the device hasn't displayed a frame, yet
75 return lastFound; // list is empty, let's not jump way off the last known position
76 uint32_t Delta = 0xFFFFFFFF;
77 int Index = -1;
78 for (int i = w; i != r; ) {
79 if (--i < 0)
80 i = PTSINDEX_ENTRIES - 1;
81 uint32_t d = pi[i].pts < Pts ? Pts - pi[i].pts : pi[i].pts - Pts;
82 if (d > 0x7FFFFFFF)
83 d = 0xFFFFFFFF - d; // handle rollover
84 if (d < Delta) {
85 Delta = d;
86 Index = pi[i].index;
87 }
88 }
89 lastFound = Index;
90 return Index;
91}
92
93int cPtsIndex::FindFrameNumber(uint32_t Pts, bool Forward, bool Still)
94{
95 if (!Forward)
96 return FindIndex(Pts, Still); // there are only I frames in backward
97 cMutexLock MutexLock(&mutex);
98 if (w == r || Pts == 0 && !Still) // while 0 is a valid PTS, DeviceGetSTC() might return 0 if, after a jump, the device hasn't displayed a frame, yet
99 return lastFound; // replay always starts at an I frame
100 bool Valid = false;
101 int FrameNumber = 0;
102 int UnplayedIFrame = 2; // GOPs may intersect, so we loop until we processed a complete unplayed GOP
103 for (int i = r; i != w && UnplayedIFrame; ) {
104 int32_t d = int32_t(Pts - pi[i].pts); // typecast handles rollover
105 if (d >= 0) {
106 if (pi[i].independent) {
107 FrameNumber = pi[i].index; // an I frame's index represents its frame number
108 Valid = true;
109 if (d == 0)
110 UnplayedIFrame = 1; // if Pts is at an I frame we only need to check up to the next I frame
111 }
112 else
113 FrameNumber++; // for every played non-I frame, increase frame number
114 }
115 else if (pi[i].independent)
116 --UnplayedIFrame;
117 if (++i >= PTSINDEX_ENTRIES)
118 i = 0;
119 }
120 if (Valid) {
121 lastFound = FrameNumber;
122 return FrameNumber;
123 }
124 return FindIndex(Pts, Still); // fall back during trick speeds
125}
126
127// --- cNonBlockingFileReader ------------------------------------------------
128
130private:
132 // changed under the thread lock, read without it by Reading() and WaitForDataMs():
133 std::atomic<uchar *> buffer;
134 std::atomic<int> wanted;
135 std::atomic<int> length;
139protected:
140 void Action(void);
141public:
144 void Clear(void);
145 void Request(cUnbufferedFile *File, int Length);
146 int Result(uchar **Buffer);
147 bool Reading(void) { return buffer; }
148 bool WaitForDataMs(int msToWait);
149 };
150
152:cThread("non blocking file reader")
153{
154 f = NULL;
155 buffer = NULL;
156 wanted = length = 0;
157 Start();
158}
159
166
168{
169 Lock();
170 f = NULL;
171 free(buffer);
172 buffer = NULL;
173 wanted = length = 0;
174 Unlock();
175}
176
178{
179 Lock();
180 Clear();
181 wanted = Length;
183 f = File;
184 Unlock();
185 newSet.Signal();
186}
187
189{
191 if (buffer && length == wanted) {
192 *Buffer = buffer;
193 buffer = NULL;
194 return wanted;
195 }
196 errno = EAGAIN;
197 return -1;
198}
199
201{
202 while (Running()) {
203 Lock();
204 if (f && buffer && length < wanted) {
205 uchar *b = buffer;
206 int r = f->Read(b + length, wanted - length);
207 if (r > 0)
208 length += r;
209 else if (r == 0) { // r == 0 means EOF
210 if (length > 0)
211 wanted = length.load(); // already read something, so return the rest
212 else
213 length = wanted = 0; // report EOF
214 }
215 else if (FATALERRNO) {
216 LOG_ERROR;
217 length = wanted = r; // this will forward the error status to the caller
218 }
219 if (length == wanted) {
220 cMutexLock NewDataLock(&newDataMutex);
222 }
223 }
224 Unlock();
225 newSet.Wait(1000);
226 }
227}
228
230{
231 cMutexLock NewDataLock(&newDataMutex);
232 if (buffer && length == wanted)
233 return true;
234 return newDataCond.TimedWait(newDataMutex, msToWait);
235}
236
237// --- cDvbPlayer ------------------------------------------------------------
238
239#define PLAYERBUFSIZE (MAXFRAMESIZE * 5)
240
241#define RESUMEBACKUP 10 // number of seconds to back up when resuming an interrupted replay session
242#define MAXSTUCKATEOF 3 // max. number of seconds to wait in case the device doesn't play the last frame
243
244class cDvbPlayer : public cPlayer, cThread {
245private:
248 static int Speeds[];
252 const cMarks *marks;
259 bool eof;
261 // changed under LOCK_THREAD, read without it in Action() and by the Get*() functions:
262 std::atomic<ePlayModes> playMode;
263 std::atomic<ePlayDirs> playDir;
264 std::atomic<int> trickSpeed;
265 std::atomic<int> readIndex;
270 void TrickSpeed(int Increment);
271 void Empty(void);
272 bool NextFile(uint16_t FileNumber = 0, off_t FileOffset = -1);
273 int Resume(void);
274 bool Save(void);
275protected:
276 virtual void Activate(bool On) override;
277 virtual void Action(void) override;
278public:
279 cDvbPlayer(const char *FileName, bool PauseLive);
280 virtual ~cDvbPlayer() override;
281 void SetMarks(const cMarks *Marks);
282 bool Active(void) { return cThread::Running(); }
283 void Pause(void);
284 void Play(void);
285 void Forward(void);
286 void Backward(void);
287 int SkipFrames(int Frames);
288 void SkipSeconds(int Seconds);
289 void Goto(int Position, bool Still = false);
290 virtual double FramesPerSecond(void) override { return framesPerSecond; }
291 virtual void SetAudioTrack(eTrackType Type, const tTrackId *TrackId) override;
292 virtual const cErrors *GetErrors(void) override;
293 virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false) override;
294 virtual bool GetFrameNumber(int &Current, int &Total) override;
295 virtual bool GetReplayMode(bool &Play, bool &Forward, int &Speed) override;
296 };
297
298#define MAX_VIDEO_SLOWMOTION 63 // max. arg to pass to VIDEO_SLOWMOTION // TODO is this value correct?
299#define NORMAL_SPEED 4 // the index of the '1' entry in the following array
300#define MAX_SPEEDS 3 // the offset of the maximum speed from normal speed in either direction
301#define SPEED_MULT 12 // the speed multiplier
302int cDvbPlayer::Speeds[] = { 0, -2, -4, -8, 1, 2, 4, 12, 0 };
303
304cDvbPlayer::cDvbPlayer(const char *FileName, bool PauseLive)
305:cThread("dvbplayer")
306{
308 ringBuffer = NULL;
309 marks = NULL;
310 index = NULL;
311 cRecording Recording(FileName);
312 framesPerSecond = Recording.FramesPerSecond();
313 isPesRecording = Recording.IsPesRecording();
314 pauseLive = PauseLive;
315 eof = false;
316 firstPacket = true;
320 readIndex = -1;
321 readIndependent = false;
322 readFrame = NULL;
323 playFrame = NULL;
324 resyncAfterPause = false;
325 isyslog("replay %s", FileName);
326 fileName = new cFileName(FileName, false, false, isPesRecording);
328 if (!replayFile)
329 return;
331 // Create the index file:
332 index = new cIndexFile(FileName, false, isPesRecording, pauseLive);
333 if (!index)
334 esyslog("ERROR: can't allocate index");
335 else if (!index->Ok()) {
336 delete index;
337 index = NULL;
338 }
339 else if (PauseLive)
340 framesPerSecond = cRecording(FileName).FramesPerSecond(); // the fps rate might have changed from the default
341}
342
344{
345 Save();
346 Detach();
347 delete readFrame; // might not have been stored in the buffer in Action()
348 delete index;
349 delete fileName;
350 delete ringBuffer;
351 // don't delete marks here, we don't own them!
352}
353
354void cDvbPlayer::SetMarks(const cMarks *Marks)
355{
356 marks = Marks;
357}
358
359void cDvbPlayer::TrickSpeed(int Increment)
360{
361 int nts = trickSpeed + Increment;
362 if (Speeds[nts] == 1) {
363 trickSpeed = nts;
364 if (playMode == pmFast)
365 Play();
366 else
367 Pause();
368 }
369 else if (Speeds[nts]) {
370 trickSpeed = nts;
371 int Mult = (playMode == pmSlow && playDir == pdForward) ? 1 : SPEED_MULT;
372 int sp = (Speeds[nts] > 0) ? Mult / Speeds[nts] : -Speeds[nts] * Mult;
373 if (sp > MAX_VIDEO_SLOWMOTION)
376 }
377}
378
380{
384 if (!firstPacket) // don't set the readIndex twice if Empty() is called more than once
385 readIndex = ptsIndex.FindIndex(DeviceGetSTC(), playMode == pmStill) - 1; // Action() will first increment it!
386 delete readFrame; // might not have been stored in the buffer in Action()
387 readFrame = NULL;
388 playFrame = NULL;
389 ringBuffer->Clear();
390 ptsIndex.Clear();
391 DeviceClear();
392 firstPacket = true;
393}
394
395bool cDvbPlayer::NextFile(uint16_t FileNumber, off_t FileOffset)
396{
397 if (FileNumber > 0)
398 replayFile = fileName->SetOffset(FileNumber, FileOffset);
399 else if (replayFile && eof)
401 eof = false;
402 return replayFile != NULL;
403}
404
406{
407 if (index) {
408 int Index = index->GetResume();
409 if (Index < 0)
410 index->StoreResume(0); // resume file doesn't exist, so create it to have the recording marked as "last replayed"
411 else {
412 uint16_t FileNumber;
413 off_t FileOffset;
414 if (index->Get(Index, &FileNumber, &FileOffset) && NextFile(FileNumber, FileOffset)) {
415 index->StoreResume(Index); // to have the recording marked as "last replayed"
416 return Index;
417 }
418 }
419 }
420 return -1;
421}
422
424{
425 if (index) {
427 if (Index >= 0) {
428 if (Setup.SkipEdited && marks) {
429 cStateKey StateKey;
430 marks->Lock(StateKey);
431 if (marks->First() && abs(Index - marks->First()->Position()) <= int(round(RESUMEBACKUP * framesPerSecond)))
432 Index = 0; // when stopping within RESUMEBACKUP seconds of the first mark the recording shall still be considered unviewed
433 StateKey.Remove();
434 }
435 Index -= int(round(RESUMEBACKUP * framesPerSecond));
436 if (Index > 0)
437 Index = index->GetNextIFrame(Index, false);
438 else
439 Index = 0;
440 if (Index >= 0)
441 return index->StoreResume(Index);
442 }
443 }
444 return false;
445}
446
448{
449 if (On) {
450 if (replayFile)
451 Start();
452 }
453 else
454 Cancel(9);
455}
456
458{
459 uchar *p = NULL;
460 int pc = 0;
461
462 readIndex = Resume();
463 if (readIndex > 0)
464 isyslog("resuming replay at index %d (%s)", readIndex.load(), *IndexToHMSF(readIndex, true, framesPerSecond));
465 else if (Setup.SkipEdited && marks) {
466 cStateKey StateKey;
467 marks->Lock(StateKey);
468 if (marks->First() && index) {
469 int Index = marks->First()->Position();
470 uint16_t FileNumber;
471 off_t FileOffset;
472 if (index->Get(Index, &FileNumber, &FileOffset) && NextFile(FileNumber, FileOffset)) {
473 isyslog("starting replay at first mark %d (%s)", Index, *IndexToHMSF(Index, true, framesPerSecond));
474 readIndex = Index;
475 }
476 }
477 StateKey.Remove();
478 }
479 if (readIndex > 0) // will first be incremented in the loop!
480 --readIndex;
481
483 int Length = 0;
484 bool Sleep = false;
485 bool WaitingForData = false;
486 time_t StuckAtEof = 0;
487 uint32_t LastStc = 0;
488 int SwitchToPlayFrame = 0;
489 bool CutIn = false;
490 bool AtLastMark = false;
491 int64_t EndPts = -1;
492
493 if (pauseLive)
494 Goto(0, true);
495 while (Running()) {
496 if (WaitingForData)
497 WaitingForData = !nonBlockingFileReader->WaitForDataMs(3); // this keeps the CPU load low, but reacts immediately on new data
498 else if (Sleep) {
499 cPoller Poller;
500 DevicePoll(Poller, 10);
501 Sleep = false;
502 if (playMode == pmStill || playMode == pmPause || eof)
504 }
505 {
507
508 // Read the next frame from the file:
509
510 if (playMode != pmStill && playMode != pmPause) {
511 if (!readFrame && (replayFile || readIndex >= 0)) {
513 if (!SwitchToPlayFrame && (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward))) {
514 uint16_t FileNumber;
515 off_t FileOffset;
516 bool TimeShiftMode = index->IsStillRecording();
517 int Index = -1;
518 readIndependent = false;
520 if (index->Get(readIndex + 1, &FileNumber, &FileOffset, &readIndependent, &Length))
521 Index = readIndex + 1;
522 }
523 else {
524 int d = int(round(0.4 * framesPerSecond));
525 if (playDir != pdForward)
526 d = -d;
527 int NewIndex = readIndex + d;
528 if (NewIndex <= 0 && readIndex > 0)
529 NewIndex = 1; // make sure the very first frame is delivered
530 NewIndex = index->GetNextIFrame(NewIndex, playDir == pdForward, &FileNumber, &FileOffset, &Length);
531 if (NewIndex < 0 && TimeShiftMode && playDir == pdForward)
532 SwitchToPlayFrame = readIndex;
533 Index = NewIndex;
534 readIndependent = true;
535 }
536 if (Index >= 0) {
537 readIndex = Index;
538 if (!NextFile(FileNumber, FileOffset))
539 continue;
540 }
541 else if (!(TimeShiftMode && playDir == pdForward))
542 eof = true;
543 }
544 else if (index) {
545 uint16_t FileNumber;
546 off_t FileOffset;
547 if (index->Get(readIndex + 1, &FileNumber, &FileOffset, &readIndependent, &Length) && NextFile(FileNumber, FileOffset)) {
548 readIndex++;
550 cStateKey StateKey;
551 marks->Lock(StateKey);
552 const cMark *m = marks->Get(readIndex);
553 if (m && (m->Index() & 0x01) != 0) { // we're at an end mark
554 m = marks->GetNextBegin(m);
555 int Index = -1;
556 if (m)
557 Index = m->Position(); // skip to next begin mark
558 else if (Setup.PauseAtLastMark)
559 AtLastMark = true; // triggers going into Pause mode
560 else if (index->IsStillRecording())
561 Index = index->GetNextIFrame(index->Last() - int(round(MAXSTUCKATEOF * framesPerSecond)), false); // skip, but stay off end of live-recordings
562 else
563 AtLastMark = true; // triggers stopping replay
564 if (Setup.SkipEdited && Index > readIndex) {
565 isyslog("skipping from %d (%s) to %d (%s)", readIndex - 1, *IndexToHMSF(readIndex - 1, true, framesPerSecond), Index, *IndexToHMSF(Index, true, framesPerSecond));
566 readIndex = Index;
567 CutIn = true;
568 }
569 }
570 StateKey.Remove();
571 }
572 }
573 else
574 eof = true;
575 }
576 else // allows replay even if the index file is missing
577 Length = MAXFRAMESIZE;
578 if (Length == -1)
579 Length = MAXFRAMESIZE; // this means we read up to EOF (see cIndex)
580 else if (Length > MAXFRAMESIZE) {
581 esyslog("ERROR: frame larger than buffer (%d > %d)", Length, MAXFRAMESIZE);
582 Length = MAXFRAMESIZE;
583 }
584 if (!eof)
586 }
587 if (!eof) {
588 uchar *b = NULL;
589 int r = nonBlockingFileReader->Result(&b);
590 if (r > 0) {
591 WaitingForData = false;
592 int64_t Pts = isPesRecording ? (PesHasPts(b) ? PesGetPts(b) : -1) : TsGetPts(b, r);
593 readFrame = new cFrame(b, -r, ftUnknown, readIndex, Pts, readIndependent); // hands over b to the ringBuffer
594 if (AtLastMark) {
595 EndPts = Pts;
596 AtLastMark = false;
597 }
598 }
599 else if (r < 0) {
600 if (errno == EAGAIN)
601 WaitingForData = true;
602 else if (FATALERRNO) {
603 LOG_ERROR;
604 break;
605 }
606 }
607 else
608 eof = true;
609 }
610 }
611
612 // Store the frame in the buffer:
613
614 if (readFrame) {
615 if (CutIn) {
616 if (isPesRecording)
618 CutIn = false;
619 }
621 readFrame = NULL;
622 else
623 Sleep = true;
624 }
625 }
626 else {
627 StuckAtEof = 0; // the STC stands still while paused, which is no stall
628 Sleep = true;
629 continue;
630 }
631
632 // Get the next frame from the buffer:
633
634 if (!playFrame) {
636 p = NULL;
637 pc = 0;
638 }
639
640 // Play the frame:
641
642 if (playFrame) {
643 eof = false;
644 StuckAtEof = 0; // a stall at the end of a growing recording must not count at its real end
645 if (!p) {
646 p = playFrame->Data();
647 pc = playFrame->Count();
648 if (p) {
649 if (playFrame->Index() >= 0 && playFrame->Pts() != 0)
651 if (firstPacket) {
652 if (isPesRecording) {
653 PlayPes(NULL, 0);
655 }
656 else
657 PlayTs(NULL, 0);
658 firstPacket = false;
659 }
660 }
661 }
662 if (p) {
663 int w;
664 bool VideoOnly = playMode != pmPlay && !(playMode == pmSlow && playDir == pdForward) && DeviceIsPlayingVideo();
665 if (isPesRecording)
666 w = PlayPes(p, pc, VideoOnly);
667 else
668 w = PlayTs(p, pc, VideoOnly);
669 if (w > 0) {
670 p += w;
671 pc -= w;
672 }
673 else if (w < 0 && FATALERRNO)
674 LOG_ERROR;
675 else
676 Sleep = true;
677 }
678 if (pc <= 0) {
680 playFrame = NULL;
681 p = NULL;
682 }
683 if ((Setup.SkipEdited || Setup.PauseAtLastMark) && EndPts >= 0) {
684 if (PtsDiff(DeviceGetSTC(), EndPts) <= 0) {
686 DeviceFreeze();
688 }
689 else
690 break; // stop replay
691 EndPts = -1;
692 }
693 }
694 }
695 else
696 Sleep = true;
697
698 // Handle hitting begin/end of recording:
699
700 if (eof || SwitchToPlayFrame) {
701 bool SwitchToPlay = false;
702 // In a recording that is still being written, eof in forward play is only a stall:
703 bool FinalEof = eof && (!index || !index->IsStillRecording() || playDir != pdForward);
704 uint32_t Stc = DeviceGetSTC();
705 if (playMode == pmPause)
706 StuckAtEof = 0;
707 else if (FinalEof && DeviceDrain() || (!eof || FinalEof) && StuckAtEof && time(NULL) - StuckAtEof > MAXSTUCKATEOF) {
708 if (playDir == pdForward)
709 break; // automatically stop at end of recording
710 SwitchToPlay = true;
711 }
712 else if (Stc != LastStc)
713 StuckAtEof = 0;
714 else if (!StuckAtEof)
715 StuckAtEof = time(NULL);
716 LastStc = Stc;
717 int Index = ptsIndex.FindIndex(Stc, playMode == pmStill);
718 // The end of a forward replay is decided above: the last frame read isn't the
719 // last one shown if there are B-frames.
720 if ((playDir != pdForward || SwitchToPlayFrame) && (Index <= 0 || SwitchToPlayFrame && Index >= SwitchToPlayFrame))
721 SwitchToPlay = true;
722 if (SwitchToPlay) {
723 if (!SwitchToPlayFrame) {
724 Empty();
725 StuckAtEof = 0;
726 }
727 DevicePlay();
730 SwitchToPlayFrame = 0;
731 }
732 }
733 }
734 }
735
738 delete nbfr;
739}
740
742{
743 if (playMode == pmPause || playMode == pmStill)
744 Play();
745 else {
747 if (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward)) {
749 Empty();
750 }
751 DeviceFreeze();
753 }
754}
755
757{
758 if (playMode != pmPlay) {
760 if (playMode == pmStill || playMode == pmFast || (playMode == pmSlow && playDir == pdBackward)) {
762 Empty();
763 }
764 DevicePlay();
767 if (resyncAfterPause) {
768 int Current, Total;
769 if (GetIndex(Current, Total, true))
770 Goto(Current);
771 resyncAfterPause = false;
772 }
773 }
774}
775
777{
778 if (index) {
779 switch (playMode) {
780 case pmFast:
781 if (Setup.MultiSpeedMode) {
782 TrickSpeed(playDir == pdForward ? 1 : -1);
783 break;
784 }
785 else if (playDir == pdForward) {
786 Play();
787 break;
788 }
789 // run into pmPlay
790 case pmPlay: {
793 Empty();
795 DeviceMute();
800 }
801 break;
802 case pmSlow:
803 if (Setup.MultiSpeedMode) {
804 TrickSpeed(playDir == pdForward ? -1 : 1);
805 break;
806 }
807 else if (playDir == pdForward) {
808 Pause();
809 break;
810 }
811 Empty();
812 // run into pmPause
813 case pmStill:
814 case pmPause: {
816 DeviceMute();
821 }
822 break;
823 default: esyslog("ERROR: unknown playMode %d (%s)", playMode.load(), __FUNCTION__);
824 }
825 }
826}
827
829{
830 if (index) {
831 switch (playMode) {
832 case pmFast:
833 if (Setup.MultiSpeedMode) {
834 TrickSpeed(playDir == pdBackward ? 1 : -1);
835 break;
836 }
837 else if (playDir == pdBackward) {
838 Play();
839 break;
840 }
841 // run into pmPlay
842 case pmPlay: {
845 Empty();
847 DeviceMute();
852 }
853 break;
854 case pmSlow:
855 if (Setup.MultiSpeedMode) {
856 TrickSpeed(playDir == pdBackward ? -1 : 1);
857 break;
858 }
859 else if (playDir == pdBackward) {
860 Pause();
861 break;
862 }
863 // run into pmPause
864 case pmStill:
865 case pmPause: {
867 Empty();
868 DeviceMute();
873 }
874 break;
875 default: esyslog("ERROR: unknown playMode %d (%s)", playMode.load(), __FUNCTION__);
876 }
877 }
878}
879
881{
882 if (index && Frames) {
883 int Current, Total;
884 GetIndex(Current, Total, true);
885 int OldCurrent = Current;
886 // As GetNextIFrame() increments/decrements at least once, the
887 // destination frame (= Current + Frames) must be adjusted by
888 // -1/+1 respectively.
889 Current = index->GetNextIFrame(Current + Frames + (Frames > 0 ? -1 : 1), Frames > 0);
890 return Current >= 0 ? Current : OldCurrent;
891 }
892 return -1;
893}
894
895void cDvbPlayer::SkipSeconds(int Seconds)
896{
897 if (index && Seconds) {
900 Empty();
901 if (Index >= 0) {
902 Index = max(Index + SecondsToFrames(Seconds, framesPerSecond), 0);
903 if (Index > 0)
904 Index = index->GetNextIFrame(Index, false, NULL, NULL, NULL);
905 if (Index >= 0)
906 readIndex = Index - 1; // Action() will first increment it!
907 }
908 Play();
909 }
910}
911
912void cDvbPlayer::Goto(int Index, bool Still)
913{
914 if (index) {
916 Empty();
917 if (++Index <= 0)
918 Index = 1; // not '0', to allow GetNextIFrame() below to work!
919 uint16_t FileNumber;
920 off_t FileOffset;
921 int Length;
922 Index = index->GetNextIFrame(Index, false, &FileNumber, &FileOffset, &Length);
923 if (Index >= 0) {
924 if (Still) {
925 if (NextFile(FileNumber, FileOffset)) {
927 int r = ReadFrame(replayFile, b, Length, sizeof(b));
928 if (r > 0) {
929 if (playMode == pmPause)
930 DevicePlay();
931 DeviceStillPicture(b, r);
932 ptsIndex.Put(isPesRecording ? PesGetPts(b) : TsGetPts(b, r), Index, true);
933 }
935 readIndex = Index - 1; // makes sure a later play starts with this I-frame
936 }
937 }
938 else {
939 readIndex = Index - 1; // Action() will first increment it!
940 Play();
941 }
942 }
943 }
944}
945
947{
949 return; // only do this upon user interaction
950 if (playMode == pmPlay) {
951 if (!ptsIndex.IsEmpty()) {
952 int Current, Total;
953 if (GetIndex(Current, Total, true))
954 Goto(Current);
955 }
956 }
957 else if (playMode == pmPause)
958 resyncAfterPause = true;
959}
960
962{
963 if (index)
964 return index->GetErrors();
965 return NULL;
966}
967
968bool cDvbPlayer::GetIndex(int &Current, int &Total, bool SnapToIFrame)
969{
970 if (index) {
972 if (SnapToIFrame) {
973 int i1 = index->GetNextIFrame(Current + 1, false);
974 int i2 = index->GetNextIFrame(Current, true);
975 Current = (abs(Current - i1) <= abs(Current - i2)) ? i1 : i2;
976 }
977 Total = index->Last();
978 return true;
979 }
980 Current = Total = -1;
981 return false;
982}
983
984bool cDvbPlayer::GetFrameNumber(int &Current, int &Total)
985{
986 if (index) {
988 Total = index->Last();
989 return true;
990 }
991 Current = Total = -1;
992 return false;
993}
994
995bool cDvbPlayer::GetReplayMode(bool &Play, bool &Forward, int &Speed)
996{
997 Play = (playMode == pmPlay || playMode == pmFast);
999 if (playMode == pmFast || playMode == pmSlow)
1000 Speed = Setup.MultiSpeedMode ? abs(trickSpeed - NORMAL_SPEED) : 0;
1001 else
1002 Speed = -1;
1003 return true;
1004}
1005
1006// --- cDvbPlayerControl -----------------------------------------------------
1007
1008cDvbPlayerControl::cDvbPlayerControl(const char *FileName, bool PauseLive)
1009:cControl(NULL)
1010{
1011 player = new cDvbPlayer(FileName, PauseLive);
1013}
1014
1019
1021{
1022 if (player)
1023 player->SetMarks(Marks);
1024}
1025
1027{
1028 return player && player->Active();
1029}
1030
1032{
1033 cControl::player = NULL;
1034 delete player;
1035 player = NULL;
1036}
1037
1039{
1040 if (player)
1041 player->Pause();
1042}
1043
1045{
1046 if (player)
1047 player->Play();
1048}
1049
1051{
1052 if (player)
1053 player->Forward();
1054}
1055
1057{
1058 if (player)
1059 player->Backward();
1060}
1061
1063{
1064 if (player)
1065 player->SkipSeconds(Seconds);
1066}
1067
1069{
1070 if (player)
1071 return player->SkipFrames(Frames);
1072 return -1;
1073}
1074
1076{
1077 if (player)
1078 return player->GetErrors();
1079 return NULL;
1080}
1081
1082bool cDvbPlayerControl::GetIndex(int &Current, int &Total, bool SnapToIFrame)
1083{
1084 if (player) {
1085 player->GetIndex(Current, Total, SnapToIFrame);
1086 return true;
1087 }
1088 return false;
1089}
1090
1091bool cDvbPlayerControl::GetFrameNumber(int &Current, int &Total)
1092{
1093 if (player) {
1094 player->GetFrameNumber(Current, Total);
1095 return true;
1096 }
1097 return false;
1098}
1099
1100bool cDvbPlayerControl::GetReplayMode(bool &Play, bool &Forward, int &Speed)
1101{
1102 return player && player->GetReplayMode(Play, Forward, Speed);
1103}
1104
1105void cDvbPlayerControl::Goto(int Position, bool Still)
1106{
1107 if (player)
1108 player->Goto(Position, Still);
1109}
bool TimedWait(cMutex &Mutex, int TimeoutMs)
Definition thread.c:135
void Broadcast(void)
Definition thread.c:155
bool Wait(int TimeoutMs=0)
Waits at most TimeoutMs milliseconds for a call to Signal(), or forever if TimeoutMs is 0.
Definition thread.c:79
void Signal(void)
Signals a caller of Wait() that the condition it is waiting for is met.
Definition thread.c:101
static void SleepMs(int TimeoutMs)
Creates a cCondWait object and uses it to sleep for TimeoutMs milliseconds, immediately giving up the...
Definition thread.c:73
void SetPlayer(cPlayer *Player)
Definition player.h:115
cControl(cPlayer *Player, bool Hidden=false)
Definition player.c:45
cPlayer * player
Definition player.h:92
void SetMarks(const cMarks *Marks)
Definition dvbplayer.c:1020
virtual ~cDvbPlayerControl() override
Definition dvbplayer.c:1015
bool GetIndex(int &Current, int &Total, bool SnapToIFrame=false)
Definition dvbplayer.c:1082
const cErrors * GetErrors(void)
Definition dvbplayer.c:1075
void SkipSeconds(int Seconds)
Definition dvbplayer.c:1062
cDvbPlayerControl(const char *FileName, bool PauseLive=false)
Definition dvbplayer.c:1008
bool GetReplayMode(bool &Play, bool &Forward, int &Speed)
Definition dvbplayer.c:1100
void Pause(void)
Definition dvbplayer.c:1038
int SkipFrames(int Frames)
Definition dvbplayer.c:1068
void Goto(int Index, bool Still=false)
Definition dvbplayer.c:1105
void Stop(void)
Definition dvbplayer.c:1031
void Forward(void)
Definition dvbplayer.c:1050
bool Active(void)
Definition dvbplayer.c:1026
bool GetFrameNumber(int &Current, int &Total)
Definition dvbplayer.c:1091
void Play(void)
Definition dvbplayer.c:1044
void Backward(void)
Definition dvbplayer.c:1056
cDvbPlayer * player
Definition dvbplayer.h:21
const cMarks * marks
Definition dvbplayer.c:252
virtual const cErrors * GetErrors(void) override
Definition dvbplayer.c:961
cFrame * readFrame
Definition dvbplayer.c:267
std::atomic< ePlayModes > playMode
Definition dvbplayer.c:262
cRingBufferFrame * ringBuffer
Definition dvbplayer.c:250
void SetMarks(const cMarks *Marks)
Definition dvbplayer.c:354
virtual void Action(void) override
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
Definition dvbplayer.c:457
cFrame * playFrame
Definition dvbplayer.c:268
virtual bool GetReplayMode(bool &Play, bool &Forward, int &Speed) override
Definition dvbplayer.c:995
bool Save(void)
Definition dvbplayer.c:423
virtual void Activate(bool On) override
Definition dvbplayer.c:447
std::atomic< int > trickSpeed
Definition dvbplayer.c:264
bool firstPacket
Definition dvbplayer.c:260
void SkipSeconds(int Seconds)
Definition dvbplayer.c:895
int Resume(void)
Definition dvbplayer.c:405
cDvbPlayer(const char *FileName, bool PauseLive)
Definition dvbplayer.c:304
cNonBlockingFileReader * nonBlockingFileReader
Definition dvbplayer.c:249
cIndexFile * index
Definition dvbplayer.c:254
cUnbufferedFile * replayFile
Definition dvbplayer.c:255
bool Active(void)
Definition dvbplayer.c:282
virtual void SetAudioTrack(eTrackType Type, const tTrackId *TrackId) override
Definition dvbplayer.c:946
void Goto(int Position, bool Still=false)
Definition dvbplayer.c:912
double framesPerSecond
Definition dvbplayer.c:256
bool isPesRecording
Definition dvbplayer.c:257
void Play(void)
Definition dvbplayer.c:756
std::atomic< ePlayDirs > playDir
Definition dvbplayer.c:263
cFileName * fileName
Definition dvbplayer.c:253
void Empty(void)
Definition dvbplayer.c:379
static int Speeds[]
Definition dvbplayer.c:302
bool NextFile(uint16_t FileNumber=0, off_t FileOffset=-1)
Definition dvbplayer.c:395
void Pause(void)
Definition dvbplayer.c:741
cPtsIndex ptsIndex
Definition dvbplayer.c:251
bool resyncAfterPause
Definition dvbplayer.c:269
std::atomic< int > readIndex
Definition dvbplayer.c:265
virtual ~cDvbPlayer() override
Definition dvbplayer.c:343
virtual bool GetFrameNumber(int &Current, int &Total) override
Definition dvbplayer.c:984
void Forward(void)
Definition dvbplayer.c:776
void TrickSpeed(int Increment)
Definition dvbplayer.c:359
virtual double FramesPerSecond(void) override
Definition dvbplayer.c:290
int SkipFrames(int Frames)
Definition dvbplayer.c:880
void Backward(void)
Definition dvbplayer.c:828
bool readIndependent
Definition dvbplayer.c:266
virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame=false) override
Definition dvbplayer.c:968
bool pauseLive
Definition dvbplayer.c:258
cUnbufferedFile * NextFile(void)
Definition recording.c:3459
cUnbufferedFile * Open(void)
Definition recording.c:3383
cFileName(const char *FileName, bool Record, bool Blocking=false, bool IsPesRecording=false)
Definition recording.c:3308
cUnbufferedFile * SetOffset(int Number, off_t Offset=0)
Definition recording.c:3417
uchar * Data(void) const
Definition ringbuffer.h:127
uint32_t Pts(void) const
Definition ringbuffer.h:131
int Index(void) const
Definition ringbuffer.h:130
bool Independent(void) const
Definition ringbuffer.h:132
cFrame(const uchar *Data, int Count, eFrameType=ftUnknown, int Index=-1, uint32_t Pts=0, bool independent=false)
Creates a new cFrame object.
Definition ringbuffer.c:394
int Count(void) const
Definition ringbuffer.h:128
int GetNextIFrame(int Index, bool Forward, uint16_t *FileNumber=NULL, off_t *FileOffset=NULL, int *Length=NULL)
Definition recording.c:3166
bool IsStillRecording(void)
Definition recording.c:3246
cIndexFile(const char *FileName, bool Record, bool IsPesRecording=false, bool PauseLive=false)
Definition recording.c:2916
const cErrors * GetErrors(void)
Returns the frame indexes of errors in the recording (if any).
Definition recording.c:3155
int GetResume(void)
Definition recording.h:554
bool StoreResume(int Index)
Definition recording.h:555
bool Ok(void)
Definition recording.h:540
bool Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *Independent=NULL, int *Length=NULL, bool *Errors=NULL, bool *Missing=NULL)
Definition recording.c:3125
int Last(void)
Returns the index of the last entry in this file, or -1 if the file is empty.
Definition recording.h:552
bool Lock(cStateKey &StateKey, bool Write=false, int TimeoutMs=0) const
Tries to get a lock on this list and returns true if successful.
Definition tools.c:2219
int Index(void) const
Definition tools.c:2148
const T * First(void) const
Returns the first element in this list, or NULL if the list is empty.
Definition tools.h:662
int Position(void) const
Definition recording.h:419
const cMark * GetNextBegin(const cMark *EndMark=NULL) const
Returns the next "begin" mark after EndMark, skipping any marks at the same position as EndMark.
Definition recording.c:2588
const cMark * Get(int Position) const
Definition recording.c:2561
std::atomic< int > wanted
Definition dvbplayer.c:134
std::atomic< uchar * > buffer
Definition dvbplayer.c:133
std::atomic< int > length
Definition dvbplayer.c:135
void Request(cUnbufferedFile *File, int Length)
Definition dvbplayer.c:177
void Action(void)
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
Definition dvbplayer.c:200
bool WaitForDataMs(int msToWait)
Definition dvbplayer.c:229
int Result(uchar **Buffer)
Definition dvbplayer.c:188
cUnbufferedFile * f
Definition dvbplayer.c:131
void Detach(void)
Definition player.c:34
void DeviceStillPicture(const uchar *Data, int Length)
Definition player.h:38
uint64_t DeviceGetSTC(void)
Definition player.h:40
int PlayTs(const uchar *Data, int Length, bool VideoOnly=false)
Definition player.h:50
void DevicePlay(void)
Definition player.h:34
int PlayPes(const uchar *Data, int Length, bool VideoOnly=false)
Definition player.c:26
bool DevicePoll(cPoller &Poller, int TimeoutMs=0)
Definition player.h:26
void DeviceMute(void)
Definition player.h:36
void DeviceFreeze(void)
Definition player.h:35
void DeviceSetTempSubtitles(void)
Definition player.h:39
bool DeviceDrain(void)
Definition player.h:29
bool DeviceHasIBPTrickSpeed(void)
Definition player.h:30
cPlayer(ePlayMode PlayMode=pmAudioVideo)
Definition player.c:15
bool DeviceIsPlayingVideo(void)
Definition player.h:31
void DeviceClear(void)
Definition player.h:33
void DeviceTrickSpeed(int Speed, bool Forward)
Definition player.h:32
int FindIndex(uint32_t Pts, bool Still)
Definition dvbplayer.c:71
int FindFrameNumber(uint32_t Pts, bool Forward, bool Still)
Definition dvbplayer.c:93
cPtsIndex(void)
Definition dvbplayer.c:42
tPtsIndex pi[PTSINDEX_ENTRIES]
Definition dvbplayer.c:29
void Clear(void)
Definition dvbplayer.c:48
bool IsEmpty(void)
Definition dvbplayer.c:54
int lastFound
Definition dvbplayer.c:31
cMutex mutex
Definition dvbplayer.c:32
void Put(uint32_t Pts, int Index, bool Independent)
Definition dvbplayer.c:60
cRecording(const cRecording &)
double FramesPerSecond(void) const
Definition recording.h:191
bool IsPesRecording(void) const
Definition recording.h:215
static void SetBrokenLink(uchar *Data, int Length)
Definition remux.c:102
virtual void Clear(void) override
Definition ringbuffer.c:432
cFrame * Get(void)
Definition ringbuffer.c:463
bool Put(cFrame *Frame)
Definition ringbuffer.c:443
cRingBufferFrame(int Size, bool Statistics=false)
Definition ringbuffer.c:420
void Drop(cFrame *Frame)
Definition ringbuffer.c:477
int PauseAtLastMark
Definition config.h:367
int MultiSpeedMode
Definition config.h:360
int SkipEdited
Definition config.h:366
void Remove(bool IncState=true)
Removes this key from the lock it was previously used with.
Definition thread.c:904
void Unlock(void)
Definition thread.h:104
void bool Start(void)
Sets the description of this thread, which will be used when logging starting or stopping of the thre...
Definition thread.c:330
bool Running(void)
Returns false if a derived cThread object shall leave its Action() function.
Definition thread.h:110
void Lock(void)
Definition thread.h:103
cThread(const char *Description=NULL, bool LowPriority=false)
Creates a new thread.
Definition thread.c:264
void Cancel(int WaitSeconds=0)
Cancels the thread by first setting 'running' to false, so that the Action() loop can finish in an or...
Definition thread.c:380
static tThreadId IsMainThread(void)
Definition thread.h:140
cUnbufferedFile is used for large files that are mainly written or read in a streaming manner,...
Definition tools.h:513
ssize_t Read(void *Data, size_t Size)
Definition tools.c:1929
cSetup Setup
Definition config.c:373
eTrackType
Definition device.h:63
#define MAX_VIDEO_SLOWMOTION
Definition dvbplayer.c:298
#define SPEED_MULT
Definition dvbplayer.c:301
#define PTSINDEX_ENTRIES
Definition dvbplayer.c:20
#define NORMAL_SPEED
Definition dvbplayer.c:299
#define RESUMEBACKUP
Definition dvbplayer.c:241
#define MAX_SPEEDS
Definition dvbplayer.c:300
#define MAXSTUCKATEOF
Definition dvbplayer.c:242
#define PLAYERBUFSIZE
Definition dvbplayer.c:239
cString IndexToHMSF(int Index, bool WithFrame, double FramesPerSecond)
Definition recording.c:3559
int SecondsToFrames(int Seconds, double FramesPerSecond)
Definition recording.c:3586
int ReadFrame(cUnbufferedFile *f, uchar *b, int Length, int Max)
Definition recording.c:3593
#define MAXFRAMESIZE
Definition recording.h:504
int64_t PtsDiff(int64_t Pts1, int64_t Pts2)
Returns the difference between two PTS values.
Definition remux.c:236
int64_t TsGetPts(const uchar *p, int l, int Pid)
Definition remux.c:160
bool PesHasPts(const uchar *p)
Definition remux.h:183
int64_t PesGetPts(const uchar *p)
Definition remux.h:193
@ ftUnknown
Definition ringbuffer.h:109
#define LOCK_THREAD
Definition thread.h:181
#define FATALERRNO
Definition tools.h:52
unsigned char uchar
Definition tools.h:31
#define MALLOC(type, size)
Definition tools.h:47
T max(T a, T b)
Definition tools.h:64
#define esyslog(a...)
Definition tools.h:35
#define LOG_ERROR
Definition tools.h:39
#define isyslog(a...)
Definition tools.h:36