vdr 2.6.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.2 2022/12/05 14:45:51 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);
39 int FindFrameNumber(uint32_t Pts);
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)
72{
73 cMutexLock MutexLock(&mutex);
74 if (w == r)
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
94{
95 cMutexLock MutexLock(&mutex);
96 if (w == r)
97 return lastFound; // replay always starts at an I frame
98 bool Valid = false;
99 int d;
100 int FrameNumber = 0;
101 int UnplayedIFrame = 2; // GOPs may intersect, so we're looping until we found two unplayed I frames
102 for (int i = r; i != w && UnplayedIFrame; ) {
103 d = Pts - pi[i].pts;
104 if (d > 0x7FFFFFFF)
105 d = 0xFFFFFFFF - d; // handle rollover
106 if (d > 0) {
107 if (pi[i].independent) {
108 FrameNumber = pi[i].index; // an I frame's index represents its frame number
109 Valid = true;
110 }
111 else
112 FrameNumber++; // for every played non-I frame, increase frame number
113 }
114 else
115 if (pi[i].independent)
116 --UnplayedIFrame;
117 if (++i >= PTSINDEX_ENTRIES)
118 i = 0;
119 }
120 return Valid ? FrameNumber : FindIndex(Pts); // fall back during trick speeds
121}
122
123// --- cNonBlockingFileReader ------------------------------------------------
124
126private:
134protected:
135 void Action(void);
136public:
139 void Clear(void);
140 void Request(cUnbufferedFile *File, int Length);
141 int Result(uchar **Buffer);
142 bool Reading(void) { return buffer; }
143 bool WaitForDataMs(int msToWait);
144 };
145
147:cThread("non blocking file reader")
148{
149 f = NULL;
150 buffer = NULL;
151 wanted = length = 0;
152 Start();
153}
154
156{
157 newSet.Signal();
158 Cancel(3);
159 free(buffer);
160}
161
163{
164 Lock();
165 f = NULL;
166 free(buffer);
167 buffer = NULL;
168 wanted = length = 0;
169 Unlock();
170}
171
173{
174 Lock();
175 Clear();
176 wanted = Length;
178 f = File;
179 Unlock();
180 newSet.Signal();
181}
182
184{
186 if (buffer && length == wanted) {
187 *Buffer = buffer;
188 buffer = NULL;
189 return wanted;
190 }
191 errno = EAGAIN;
192 return -1;
193}
194
196{
197 while (Running()) {
198 Lock();
199 if (f && buffer && length < wanted) {
200 int r = f->Read(buffer + length, wanted - length);
201 if (r > 0)
202 length += r;
203 else if (r == 0) { // r == 0 means EOF
204 if (length > 0)
205 wanted = length; // already read something, so return the rest
206 else
207 length = wanted = 0; // report EOF
208 }
209 else if (FATALERRNO) {
210 LOG_ERROR;
211 length = wanted = r; // this will forward the error status to the caller
212 }
213 if (length == wanted) {
214 cMutexLock NewDataLock(&newDataMutex);
216 }
217 }
218 Unlock();
219 newSet.Wait(1000);
220 }
221}
222
224{
225 cMutexLock NewDataLock(&newDataMutex);
226 if (buffer && length == wanted)
227 return true;
228 return newDataCond.TimedWait(newDataMutex, msToWait);
229}
230
231// --- cDvbPlayer ------------------------------------------------------------
232
233#define PLAYERBUFSIZE (MAXFRAMESIZE * 5)
234
235#define RESUMEBACKUP 10 // number of seconds to back up when resuming an interrupted replay session
236#define MAXSTUCKATEOF 3 // max. number of seconds to wait in case the device doesn't play the last frame
237
238class cDvbPlayer : public cPlayer, cThread {
239private:
242 static int Speeds[];
246 const cMarks *marks;
253 bool eof;
264 void TrickSpeed(int Increment);
265 void Empty(void);
266 bool NextFile(uint16_t FileNumber = 0, off_t FileOffset = -1);
267 int Resume(void);
268 bool Save(void);
269protected:
270 virtual void Activate(bool On);
271 virtual void Action(void);
272public:
273 cDvbPlayer(const char *FileName, bool PauseLive);
274 virtual ~cDvbPlayer();
275 void SetMarks(const cMarks *Marks);
276 bool Active(void) { return cThread::Running(); }
277 void Pause(void);
278 void Play(void);
279 void Forward(void);
280 void Backward(void);
281 int SkipFrames(int Frames);
282 void SkipSeconds(int Seconds);
283 void Goto(int Position, bool Still = false);
284 virtual double FramesPerSecond(void) { return framesPerSecond; }
285 virtual void SetAudioTrack(eTrackType Type, const tTrackId *TrackId);
286 virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
287 virtual bool GetFrameNumber(int &Current, int &Total);
288 virtual bool GetReplayMode(bool &Play, bool &Forward, int &Speed);
289 };
290
291#define MAX_VIDEO_SLOWMOTION 63 // max. arg to pass to VIDEO_SLOWMOTION // TODO is this value correct?
292#define NORMAL_SPEED 4 // the index of the '1' entry in the following array
293#define MAX_SPEEDS 3 // the offset of the maximum speed from normal speed in either direction
294#define SPEED_MULT 12 // the speed multiplier
295int cDvbPlayer::Speeds[] = { 0, -2, -4, -8, 1, 2, 4, 12, 0 };
296
297cDvbPlayer::cDvbPlayer(const char *FileName, bool PauseLive)
298:cThread("dvbplayer")
299{
301 ringBuffer = NULL;
302 marks = NULL;
303 index = NULL;
304 cRecording Recording(FileName);
305 framesPerSecond = Recording.FramesPerSecond();
306 isPesRecording = Recording.IsPesRecording();
307 pauseLive = PauseLive;
308 eof = false;
309 firstPacket = true;
313 readIndex = -1;
314 readIndependent = false;
315 readFrame = NULL;
316 playFrame = NULL;
317 dropFrame = NULL;
318 resyncAfterPause = false;
319 isyslog("replay %s", FileName);
320 fileName = new cFileName(FileName, false, false, isPesRecording);
322 if (!replayFile)
323 return;
325 // Create the index file:
326 index = new cIndexFile(FileName, false, isPesRecording, pauseLive);
327 if (!index)
328 esyslog("ERROR: can't allocate index");
329 else if (!index->Ok()) {
330 delete index;
331 index = NULL;
332 }
333 else if (PauseLive)
334 framesPerSecond = cRecording(FileName).FramesPerSecond(); // the fps rate might have changed from the default
335}
336
338{
339 Save();
340 Detach();
341 delete readFrame; // might not have been stored in the buffer in Action()
342 delete index;
343 delete fileName;
344 delete ringBuffer;
345 // don't delete marks here, we don't own them!
346}
347
348void cDvbPlayer::SetMarks(const cMarks *Marks)
349{
350 marks = Marks;
351}
352
353void cDvbPlayer::TrickSpeed(int Increment)
354{
355 int nts = trickSpeed + Increment;
356 if (Speeds[nts] == 1) {
357 trickSpeed = nts;
358 if (playMode == pmFast)
359 Play();
360 else
361 Pause();
362 }
363 else if (Speeds[nts]) {
364 trickSpeed = nts;
365 int Mult = (playMode == pmSlow && playDir == pdForward) ? 1 : SPEED_MULT;
366 int sp = (Speeds[nts] > 0) ? Mult / Speeds[nts] : -Speeds[nts] * Mult;
367 if (sp > MAX_VIDEO_SLOWMOTION)
370 }
371}
372
374{
378 if (!firstPacket) // don't set the readIndex twice if Empty() is called more than once
379 readIndex = ptsIndex.FindIndex(DeviceGetSTC()) - 1; // Action() will first increment it!
380 delete readFrame; // might not have been stored in the buffer in Action()
381 readFrame = NULL;
382 playFrame = NULL;
383 dropFrame = NULL;
384 ringBuffer->Clear();
385 ptsIndex.Clear();
386 DeviceClear();
387 firstPacket = true;
388}
389
390bool cDvbPlayer::NextFile(uint16_t FileNumber, off_t FileOffset)
391{
392 if (FileNumber > 0)
393 replayFile = fileName->SetOffset(FileNumber, FileOffset);
394 else if (replayFile && eof)
396 eof = false;
397 return replayFile != NULL;
398}
399
401{
402 if (index) {
403 int Index = index->GetResume();
404 if (Index >= 0) {
405 uint16_t FileNumber;
406 off_t FileOffset;
407 if (index->Get(Index, &FileNumber, &FileOffset) && NextFile(FileNumber, FileOffset))
408 return Index;
409 }
410 }
411 return -1;
412}
413
415{
416 if (index) {
417 int Index = ptsIndex.FindIndex(DeviceGetSTC());
418 if (Index >= 0) {
419 if (Setup.SkipEdited && marks) {
420 cStateKey StateKey;
421 marks->Lock(StateKey);
422 if (marks->First() && abs(Index - marks->First()->Position()) <= int(round(RESUMEBACKUP * framesPerSecond)))
423 Index = 0; // when stopping within RESUMEBACKUP seconds of the first mark the recording shall still be considered unviewed
424 StateKey.Remove();
425 }
426 Index -= int(round(RESUMEBACKUP * framesPerSecond));
427 if (Index > 0)
428 Index = index->GetNextIFrame(Index, false);
429 else
430 Index = 0;
431 if (Index >= 0)
432 return index->StoreResume(Index);
433 }
434 }
435 return false;
436}
437
439{
440 if (On) {
441 if (replayFile)
442 Start();
443 }
444 else
445 Cancel(9);
446}
447
449{
450 uchar *p = NULL;
451 int pc = 0;
452
453 readIndex = Resume();
454 if (readIndex > 0)
455 isyslog("resuming replay at index %d (%s)", readIndex, *IndexToHMSF(readIndex, true, framesPerSecond));
456 else if (Setup.SkipEdited && marks) {
457 cStateKey StateKey;
458 marks->Lock(StateKey);
459 if (marks->First() && index) {
460 int Index = marks->First()->Position();
461 uint16_t FileNumber;
462 off_t FileOffset;
463 if (index->Get(Index, &FileNumber, &FileOffset) && NextFile(FileNumber, FileOffset)) {
464 isyslog("starting replay at first mark %d (%s)", Index, *IndexToHMSF(Index, true, framesPerSecond));
465 readIndex = Index;
466 }
467 }
468 StateKey.Remove();
469 }
470 if (readIndex > 0) // will first be incremented in the loop!
471 --readIndex;
472
474 int Length = 0;
475 bool Sleep = false;
476 bool WaitingForData = false;
477 time_t StuckAtEof = 0;
478 uint32_t LastStc = 0;
479 int LastReadFrame = -1;
480 int SwitchToPlayFrame = 0;
481 bool CutIn = false;
482 bool AtLastMark = false;
483
484 if (pauseLive)
485 Goto(0, true);
486 while (Running()) {
487 if (WaitingForData)
488 WaitingForData = !nonBlockingFileReader->WaitForDataMs(3); // this keeps the CPU load low, but reacts immediately on new data
489 else if (Sleep) {
490 cPoller Poller;
491 DevicePoll(Poller, 10);
492 Sleep = false;
493 if (playMode == pmStill || playMode == pmPause)
495 }
496 {
498
499 // Read the next frame from the file:
500
501 if (playMode != pmStill && playMode != pmPause) {
502 if (!readFrame && (replayFile || readIndex >= 0)) {
503 if (!nonBlockingFileReader->Reading() && !AtLastMark) {
504 if (!SwitchToPlayFrame && (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward))) {
505 uint16_t FileNumber;
506 off_t FileOffset;
507 bool TimeShiftMode = index->IsStillRecording();
508 int Index = -1;
509 readIndependent = false;
511 if (index->Get(readIndex + 1, &FileNumber, &FileOffset, &readIndependent, &Length))
512 Index = readIndex + 1;
513 }
514 else {
515 int d = int(round(0.4 * framesPerSecond));
516 if (playDir != pdForward)
517 d = -d;
518 int NewIndex = readIndex + d;
519 if (NewIndex <= 0 && readIndex > 0)
520 NewIndex = 1; // make sure the very first frame is delivered
521 NewIndex = index->GetNextIFrame(NewIndex, playDir == pdForward, &FileNumber, &FileOffset, &Length);
522 if (NewIndex < 0 && TimeShiftMode && playDir == pdForward)
523 SwitchToPlayFrame = readIndex;
524 Index = NewIndex;
525 readIndependent = true;
526 }
527 if (Index >= 0) {
528 readIndex = Index;
529 if (!NextFile(FileNumber, FileOffset))
530 continue;
531 }
532 else if (!(TimeShiftMode && playDir == pdForward))
533 eof = true;
534 }
535 else if (index) {
536 uint16_t FileNumber;
537 off_t FileOffset;
538 if (index->Get(readIndex + 1, &FileNumber, &FileOffset, &readIndependent, &Length) && NextFile(FileNumber, FileOffset)) {
539 readIndex++;
541 cStateKey StateKey;
542 marks->Lock(StateKey);
543 const cMark *m = marks->Get(readIndex);
544 if (m && (m->Index() & 0x01) != 0) { // we're at an end mark
545 m = marks->GetNextBegin(m);
546 int Index = -1;
547 if (m)
548 Index = m->Position(); // skip to next begin mark
549 else if (Setup.PauseAtLastMark)
550 AtLastMark = true; // triggers going into Pause mode
551 else if (index->IsStillRecording())
552 Index = index->GetNextIFrame(index->Last() - int(round(MAXSTUCKATEOF * framesPerSecond)), false); // skip, but stay off end of live-recordings
553 else
554 AtLastMark = true; // triggers stopping replay
555 if (Setup.SkipEdited && Index > readIndex) {
556 isyslog("skipping from %d (%s) to %d (%s)", readIndex - 1, *IndexToHMSF(readIndex - 1, true, framesPerSecond), Index, *IndexToHMSF(Index, true, framesPerSecond));
557 readIndex = Index;
558 CutIn = true;
559 }
560 }
561 StateKey.Remove();
562 }
563 }
564 else
565 eof = true;
566 }
567 else // allows replay even if the index file is missing
568 Length = MAXFRAMESIZE;
569 if (Length == -1)
570 Length = MAXFRAMESIZE; // this means we read up to EOF (see cIndex)
571 else if (Length > MAXFRAMESIZE) {
572 esyslog("ERROR: frame larger than buffer (%d > %d)", Length, MAXFRAMESIZE);
573 Length = MAXFRAMESIZE;
574 }
575 if (!eof)
577 }
578 if (!eof) {
579 uchar *b = NULL;
580 int r = nonBlockingFileReader->Result(&b);
581 if (r > 0) {
582 WaitingForData = false;
583 LastReadFrame = readIndex;
584 uint32_t Pts = isPesRecording ? (PesHasPts(b) ? PesGetPts(b) : -1) : TsGetPts(b, r);
585 readFrame = new cFrame(b, -r, ftUnknown, readIndex, Pts, readIndependent); // hands over b to the ringBuffer
586 }
587 else if (r < 0) {
588 if (errno == EAGAIN)
589 WaitingForData = true;
590 else if (FATALERRNO) {
591 LOG_ERROR;
592 break;
593 }
594 }
595 else
596 eof = true;
597 }
598 }
599
600 // Store the frame in the buffer:
601
602 if (readFrame) {
603 if (CutIn) {
604 if (isPesRecording)
606 CutIn = false;
607 }
609 readFrame = NULL;
610 else
611 Sleep = true;
612 }
613 }
614 else
615 Sleep = true;
616
617 if (dropFrame) {
618 if (!eof || (playDir != pdForward && dropFrame->Index() > 0) || (playDir == pdForward && dropFrame->Index() < readIndex)) {
619 ringBuffer->Drop(dropFrame); // the very first and last frame are continuously repeated to flush data through the device
620 dropFrame = NULL;
621 }
622 }
623
624 // Get the next frame from the buffer:
625
626 if (!playFrame) {
628 p = NULL;
629 pc = 0;
630 }
631
632 // Play the frame:
633
634 if (playFrame) {
635 if (!p) {
636 p = playFrame->Data();
637 pc = playFrame->Count();
638 if (p) {
639 if (playFrame->Index() >= 0 && playFrame->Pts() != 0)
641 if (firstPacket) {
642 if (isPesRecording) {
643 PlayPes(NULL, 0);
645 }
646 else
647 PlayTs(NULL, 0);
648 firstPacket = false;
649 }
650 }
651 }
652 if (p) {
653 int w;
654 bool VideoOnly = (dropFrame || playMode != pmPlay && !(playMode == pmSlow && playDir == pdForward)) && DeviceIsPlayingVideo();
655 if (isPesRecording)
656 w = PlayPes(p, pc, VideoOnly);
657 else
658 w = PlayTs(p, pc, VideoOnly);
659 if (w > 0) {
660 p += w;
661 pc -= w;
662 }
663 else if (w < 0 && FATALERRNO)
664 LOG_ERROR;
665 else
666 Sleep = true;
667 }
668 if (pc <= 0) {
670 playFrame = NULL;
671 p = NULL;
672 }
673 }
674 else {
675 if (AtLastMark) {
678 AtLastMark = false;
679 }
680 else
681 eof = true;
682 }
683 Sleep = true;
684 }
685
686 // Handle hitting begin/end of recording:
687
688 if (eof || SwitchToPlayFrame) {
689 bool SwitchToPlay = false;
690 uint32_t Stc = DeviceGetSTC();
691 if (Stc != LastStc || playMode == pmPause)
692 StuckAtEof = 0;
693 else if (!StuckAtEof)
694 StuckAtEof = time(NULL);
695 else if (time(NULL) - StuckAtEof > MAXSTUCKATEOF) {
696 if (playDir == pdForward)
697 break; // automatically stop at end of recording
698 SwitchToPlay = true;
699 }
700 LastStc = Stc;
701 int Index = ptsIndex.FindIndex(Stc);
702 if (playDir == pdForward && !SwitchToPlayFrame) {
703 if (Index >= LastReadFrame)
704 break; // automatically stop at end of recording
705 }
706 else if (Index <= 0 || SwitchToPlayFrame && Index >= SwitchToPlayFrame)
707 SwitchToPlay = true;
708 if (SwitchToPlay) {
709 if (!SwitchToPlayFrame)
710 Empty();
711 DevicePlay();
714 SwitchToPlayFrame = 0;
715 }
716 }
717 }
718 }
719
722 delete nbfr;
723}
724
726{
727 if (playMode == pmPause || playMode == pmStill)
728 Play();
729 else {
731 if (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward)) {
733 Empty();
734 }
735 DeviceFreeze();
737 }
738}
739
741{
742 if (playMode != pmPlay) {
744 if (playMode == pmStill || playMode == pmFast || (playMode == pmSlow && playDir == pdBackward)) {
746 Empty();
747 }
748 DevicePlay();
751 if (resyncAfterPause) {
752 int Current, Total;
753 if (GetIndex(Current, Total, true))
754 Goto(Current);
755 resyncAfterPause = false;
756 }
757 }
758}
759
761{
762 if (index) {
763 switch (playMode) {
764 case pmFast:
765 if (Setup.MultiSpeedMode) {
766 TrickSpeed(playDir == pdForward ? 1 : -1);
767 break;
768 }
769 else if (playDir == pdForward) {
770 Play();
771 break;
772 }
773 // run into pmPlay
774 case pmPlay: {
777 Empty();
779 DeviceMute();
784 }
785 break;
786 case pmSlow:
787 if (Setup.MultiSpeedMode) {
788 TrickSpeed(playDir == pdForward ? -1 : 1);
789 break;
790 }
791 else if (playDir == pdForward) {
792 Pause();
793 break;
794 }
795 Empty();
796 // run into pmPause
797 case pmStill:
798 case pmPause:
799 DeviceMute();
804 break;
805 default: esyslog("ERROR: unknown playMode %d (%s)", playMode, __FUNCTION__);
806 }
807 }
808}
809
811{
812 if (index) {
813 switch (playMode) {
814 case pmFast:
815 if (Setup.MultiSpeedMode) {
816 TrickSpeed(playDir == pdBackward ? 1 : -1);
817 break;
818 }
819 else if (playDir == pdBackward) {
820 Play();
821 break;
822 }
823 // run into pmPlay
824 case pmPlay: {
826 Empty();
828 DeviceMute();
833 }
834 break;
835 case pmSlow:
836 if (Setup.MultiSpeedMode) {
837 TrickSpeed(playDir == pdBackward ? -1 : 1);
838 break;
839 }
840 else if (playDir == pdBackward) {
841 Pause();
842 break;
843 }
844 Empty();
845 // run into pmPause
846 case pmStill:
847 case pmPause: {
849 Empty();
850 DeviceMute();
855 }
856 break;
857 default: esyslog("ERROR: unknown playMode %d (%s)", playMode, __FUNCTION__);
858 }
859 }
860}
861
863{
864 if (index && Frames) {
865 int Current, Total;
866 GetIndex(Current, Total, true);
867 int OldCurrent = Current;
868 // As GetNextIFrame() increments/decrements at least once, the
869 // destination frame (= Current + Frames) must be adjusted by
870 // -1/+1 respectively.
871 Current = index->GetNextIFrame(Current + Frames + (Frames > 0 ? -1 : 1), Frames > 0);
872 return Current >= 0 ? Current : OldCurrent;
873 }
874 return -1;
875}
876
877void cDvbPlayer::SkipSeconds(int Seconds)
878{
879 if (index && Seconds) {
881 int Index = ptsIndex.FindIndex(DeviceGetSTC());
882 Empty();
883 if (Index >= 0) {
884 Index = max(Index + SecondsToFrames(Seconds, framesPerSecond), 0);
885 if (Index > 0)
886 Index = index->GetNextIFrame(Index, false, NULL, NULL, NULL);
887 if (Index >= 0)
888 readIndex = Index - 1; // Action() will first increment it!
889 }
890 Play();
891 }
892}
893
894void cDvbPlayer::Goto(int Index, bool Still)
895{
896 if (index) {
898 Empty();
899 if (++Index <= 0)
900 Index = 1; // not '0', to allow GetNextIFrame() below to work!
901 uint16_t FileNumber;
902 off_t FileOffset;
903 int Length;
904 Index = index->GetNextIFrame(Index, false, &FileNumber, &FileOffset, &Length);
905 if (Index >= 0) {
906 if (Still) {
907 if (NextFile(FileNumber, FileOffset)) {
909 int r = ReadFrame(replayFile, b, Length, sizeof(b));
910 if (r > 0) {
911 if (playMode == pmPause)
912 DevicePlay();
913 DeviceStillPicture(b, r);
914 ptsIndex.Put(isPesRecording ? PesGetPts(b) : TsGetPts(b, r), Index, true);
915 }
917 readIndex = Index - 1; // makes sure a later play starts with this I-frame
918 }
919 }
920 else {
921 readIndex = Index - 1; // Action() will first increment it!
922 Play();
923 }
924 }
925 }
926}
927
929{
931 return; // only do this upon user interaction
932 if (playMode == pmPlay) {
933 if (!ptsIndex.IsEmpty()) {
934 int Current, Total;
935 if (GetIndex(Current, Total, true))
936 Goto(Current);
937 }
938 }
939 else if (playMode == pmPause)
940 resyncAfterPause = true;
941}
942
943bool cDvbPlayer::GetIndex(int &Current, int &Total, bool SnapToIFrame)
944{
945 if (index) {
946 Current = ptsIndex.FindIndex(DeviceGetSTC());
947 if (SnapToIFrame) {
948 int i1 = index->GetNextIFrame(Current + 1, false);
949 int i2 = index->GetNextIFrame(Current, true);
950 Current = (abs(Current - i1) <= abs(Current - i2)) ? i1 : i2;
951 }
952 Total = index->Last();
953 return true;
954 }
955 Current = Total = -1;
956 return false;
957}
958
959bool cDvbPlayer::GetFrameNumber(int &Current, int &Total)
960{
961 if (index) {
963 Total = index->Last();
964 return true;
965 }
966 Current = Total = -1;
967 return false;
968}
969
970bool cDvbPlayer::GetReplayMode(bool &Play, bool &Forward, int &Speed)
971{
972 Play = (playMode == pmPlay || playMode == pmFast);
974 if (playMode == pmFast || playMode == pmSlow)
975 Speed = Setup.MultiSpeedMode ? abs(trickSpeed - NORMAL_SPEED) : 0;
976 else
977 Speed = -1;
978 return true;
979}
980
981// --- cDvbPlayerControl -----------------------------------------------------
982
983cDvbPlayerControl::cDvbPlayerControl(const char *FileName, bool PauseLive)
984:cControl(NULL, PauseLive)
985{
986 player = new cDvbPlayer(FileName, PauseLive);
988}
989
991{
992 Stop();
993}
994
996{
997 if (player)
998 player->SetMarks(Marks);
999}
1000
1002{
1003 return player && player->Active();
1004}
1005
1007{
1008 cControl::player = NULL;
1009 delete player;
1010 player = NULL;
1011}
1012
1014{
1015 if (player)
1016 player->Pause();
1017}
1018
1020{
1021 if (player)
1022 player->Play();
1023}
1024
1026{
1027 if (player)
1028 player->Forward();
1029}
1030
1032{
1033 if (player)
1034 player->Backward();
1035}
1036
1038{
1039 if (player)
1040 player->SkipSeconds(Seconds);
1041}
1042
1044{
1045 if (player)
1046 return player->SkipFrames(Frames);
1047 return -1;
1048}
1049
1050bool cDvbPlayerControl::GetIndex(int &Current, int &Total, bool SnapToIFrame)
1051{
1052 if (player) {
1053 player->GetIndex(Current, Total, SnapToIFrame);
1054 return true;
1055 }
1056 return false;
1057}
1058
1059bool cDvbPlayerControl::GetFrameNumber(int &Current, int &Total)
1060{
1061 if (player) {
1062 player->GetFrameNumber(Current, Total);
1063 return true;
1064 }
1065 return false;
1066}
1067
1068bool cDvbPlayerControl::GetReplayMode(bool &Play, bool &Forward, int &Speed)
1069{
1070 return player && player->GetReplayMode(Play, Forward, Speed);
1071}
1072
1073void cDvbPlayerControl::Goto(int Position, bool Still)
1074{
1075 if (player)
1076 player->Goto(Position, Still);
1077}
bool TimedWait(cMutex &Mutex, int TimeoutMs)
Definition: thread.c:132
void Broadcast(void)
Definition: thread.c:150
bool Wait(int TimeoutMs=0)
Waits at most TimeoutMs milliseconds for a call to Signal(), or forever if TimeoutMs is 0.
Definition: thread.c:78
void Signal(void)
Signals a caller of Wait() that the condition it is waiting for is met.
Definition: thread.c:100
static void SleepMs(int TimeoutMs)
Creates a cCondWait object and uses it to sleep for TimeoutMs milliseconds, immediately giving up the...
Definition: thread.c:72
void SetPlayer(cPlayer *Player)
Definition: player.h:110
cPlayer * player
Definition: player.h:87
void SetMarks(const cMarks *Marks)
Definition: dvbplayer.c:995
bool GetIndex(int &Current, int &Total, bool SnapToIFrame=false)
Definition: dvbplayer.c:1050
virtual ~cDvbPlayerControl()
Definition: dvbplayer.c:990
void SkipSeconds(int Seconds)
Definition: dvbplayer.c:1037
cDvbPlayerControl(const char *FileName, bool PauseLive=false)
Definition: dvbplayer.c:983
bool GetReplayMode(bool &Play, bool &Forward, int &Speed)
Definition: dvbplayer.c:1068
void Pause(void)
Definition: dvbplayer.c:1013
int SkipFrames(int Frames)
Definition: dvbplayer.c:1043
void Goto(int Index, bool Still=false)
Definition: dvbplayer.c:1073
void Stop(void)
Definition: dvbplayer.c:1006
void Forward(void)
Definition: dvbplayer.c:1025
bool Active(void)
Definition: dvbplayer.c:1001
bool GetFrameNumber(int &Current, int &Total)
Definition: dvbplayer.c:1059
void Play(void)
Definition: dvbplayer.c:1019
void Backward(void)
Definition: dvbplayer.c:1031
cDvbPlayer * player
Definition: dvbplayer.h:21
const cMarks * marks
Definition: dvbplayer.c:246
virtual ~cDvbPlayer()
Definition: dvbplayer.c:337
cFrame * readFrame
Definition: dvbplayer.c:260
cRingBufferFrame * ringBuffer
Definition: dvbplayer.c:244
virtual void Activate(bool On)
Definition: dvbplayer.c:438
void SetMarks(const cMarks *Marks)
Definition: dvbplayer.c:348
cFrame * playFrame
Definition: dvbplayer.c:261
bool Save(void)
Definition: dvbplayer.c:414
virtual bool GetFrameNumber(int &Current, int &Total)
Definition: dvbplayer.c:959
bool firstPacket
Definition: dvbplayer.c:254
void SkipSeconds(int Seconds)
Definition: dvbplayer.c:877
virtual double FramesPerSecond(void)
Definition: dvbplayer.c:284
int Resume(void)
Definition: dvbplayer.c:400
cDvbPlayer(const char *FileName, bool PauseLive)
Definition: dvbplayer.c:297
cNonBlockingFileReader * nonBlockingFileReader
Definition: dvbplayer.c:243
cIndexFile * index
Definition: dvbplayer.c:248
bool eof
Definition: dvbplayer.c:253
cUnbufferedFile * replayFile
Definition: dvbplayer.c:249
bool Active(void)
Definition: dvbplayer.c:276
virtual 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:448
void Goto(int Position, bool Still=false)
Definition: dvbplayer.c:894
double framesPerSecond
Definition: dvbplayer.c:250
bool isPesRecording
Definition: dvbplayer.c:251
void Play(void)
Definition: dvbplayer.c:740
cFileName * fileName
Definition: dvbplayer.c:247
virtual bool GetReplayMode(bool &Play, bool &Forward, int &Speed)
Definition: dvbplayer.c:970
void Empty(void)
Definition: dvbplayer.c:373
ePlayDirs playDir
Definition: dvbplayer.c:256
virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame=false)
Definition: dvbplayer.c:943
static int Speeds[]
Definition: dvbplayer.c:242
int trickSpeed
Definition: dvbplayer.c:257
bool NextFile(uint16_t FileNumber=0, off_t FileOffset=-1)
Definition: dvbplayer.c:390
void Pause(void)
Definition: dvbplayer.c:725
cPtsIndex ptsIndex
Definition: dvbplayer.c:245
bool resyncAfterPause
Definition: dvbplayer.c:263
int readIndex
Definition: dvbplayer.c:258
virtual void SetAudioTrack(eTrackType Type, const tTrackId *TrackId)
Definition: dvbplayer.c:928
void Forward(void)
Definition: dvbplayer.c:760
void TrickSpeed(int Increment)
Definition: dvbplayer.c:353
int SkipFrames(int Frames)
Definition: dvbplayer.c:862
void Backward(void)
Definition: dvbplayer.c:810
bool readIndependent
Definition: dvbplayer.c:259
cFrame * dropFrame
Definition: dvbplayer.c:262
bool pauseLive
Definition: dvbplayer.c:252
ePlayModes playMode
Definition: dvbplayer.c:255
cUnbufferedFile * NextFile(void)
Definition: recording.c:3083
cUnbufferedFile * Open(void)
Definition: recording.c:3007
cUnbufferedFile * SetOffset(int Number, off_t Offset=0)
Definition: recording.c:3041
uchar * Data(void) const
Definition: ringbuffer.h:125
uint32_t Pts(void) const
Definition: ringbuffer.h:129
int Index(void) const
Definition: ringbuffer.h:128
bool Independent(void) const
Definition: ringbuffer.h:130
int Count(void) const
Definition: ringbuffer.h:126
int GetNextIFrame(int Index, bool Forward, uint16_t *FileNumber=NULL, off_t *FileOffset=NULL, int *Length=NULL)
Definition: recording.c:2790
bool IsStillRecording(void)
Definition: recording.c:2870
int GetResume(void)
Definition: recording.h:484
bool StoreResume(int Index)
Definition: recording.h:485
bool Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *Independent=NULL, int *Length=NULL)
Definition: recording.c:2764
bool Ok(void)
Definition: recording.h:472
int Last(void)
Returns the index of the last entry in this file, or -1 if the file is empty.
Definition: recording.h:482
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:2179
int Index(void) const
Definition: tools.c:2108
const T * First(void) const
Returns the first element in this list, or NULL if the list is empty.
Definition: tools.h:653
int Position(void) const
Definition: recording.h:363
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:2287
const cMark * Get(int Position) const
Definition: recording.c:2260
Definition: thread.h:67
void Request(cUnbufferedFile *File, int Length)
Definition: dvbplayer.c:172
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:195
bool WaitForDataMs(int msToWait)
Definition: dvbplayer.c:223
int Result(uchar **Buffer)
Definition: dvbplayer.c:183
cUnbufferedFile * f
Definition: dvbplayer.c:127
Definition: player.h:16
void Detach(void)
Definition: player.c:34
void DeviceStillPicture(const uchar *Data, int Length)
Definition: player.h:36
uint64_t DeviceGetSTC(void)
Definition: player.h:37
int PlayTs(const uchar *Data, int Length, bool VideoOnly=false)
Definition: player.h:47
void DevicePlay(void)
Definition: player.h:32
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:34
void DeviceFreeze(void)
Definition: player.h:33
bool DeviceHasIBPTrickSpeed(void)
Definition: player.h:28
bool DeviceIsPlayingVideo(void)
Definition: player.h:29
void DeviceClear(void)
Definition: player.h:31
void DeviceTrickSpeed(int Speed, bool Forward)
Definition: player.h:30
Definition: tools.h:431
int FindIndex(uint32_t Pts)
Definition: dvbplayer.c:71
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
int FindFrameNumber(uint32_t Pts)
Definition: dvbplayer.c:93
double FramesPerSecond(void) const
Definition: recording.h:160
bool IsPesRecording(void) const
Definition: recording.h:174
static void SetBrokenLink(uchar *Data, int Length)
Definition: remux.c:102
cFrame * Get(void)
Definition: ringbuffer.c:463
bool Put(cFrame *Frame)
Definition: ringbuffer.c:443
virtual void Clear(void)
Definition: ringbuffer.c:432
void Drop(cFrame *Frame)
Definition: ringbuffer.c:477
int PauseAtLastMark
Definition: config.h:356
int MultiSpeedMode
Definition: config.h:349
int SkipEdited
Definition: config.h:355
void Remove(bool IncState=true)
Removes this key from the lock it was previously used with.
Definition: thread.c:867
Definition: thread.h:79
void Unlock(void)
Definition: thread.h:95
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:304
bool Running(void)
Returns false if a derived cThread object shall leave its Action() function.
Definition: thread.h:101
void Lock(void)
Definition: thread.h:94
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:354
static tThreadId IsMainThread(void)
Definition: thread.h:131
cUnbufferedFile is used for large files that are mainly written or read in a streaming manner,...
Definition: tools.h:504
ssize_t Read(void *Data, size_t Size)
Definition: tools.c:1889
cSetup Setup
Definition: config.c:372
eTrackType
Definition: device.h:63
#define MAX_VIDEO_SLOWMOTION
Definition: dvbplayer.c:291
#define SPEED_MULT
Definition: dvbplayer.c:294
#define PTSINDEX_ENTRIES
Definition: dvbplayer.c:20
#define NORMAL_SPEED
Definition: dvbplayer.c:292
#define RESUMEBACKUP
Definition: dvbplayer.c:235
#define MAX_SPEEDS
Definition: dvbplayer.c:293
#define MAXSTUCKATEOF
Definition: dvbplayer.c:236
#define PLAYERBUFSIZE
Definition: dvbplayer.c:233
cString IndexToHMSF(int Index, bool WithFrame, double FramesPerSecond)
Definition: recording.c:3183
int SecondsToFrames(int Seconds, double FramesPerSecond)
Definition: recording.c:3210
int ReadFrame(cUnbufferedFile *f, uchar *b, int Length, int Max)
Definition: recording.c:3217
#define MAXFRAMESIZE
Definition: recording.h:441
int64_t TsGetPts(const uchar *p, int l)
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:107
#define LOCK_THREAD
Definition: thread.h:167
#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