vdr 2.7.4
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.7 2025/02/19 15:39:16 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, bool Forward);
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
93int cPtsIndex::FindFrameNumber(uint32_t Pts, bool Forward)
94{
95 if (!Forward)
96 return FindIndex(Pts); // there are only I frames in backward
97 cMutexLock MutexLock(&mutex);
98 if (w == r)
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 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);
215 newDataCond.Broadcast();
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 const cErrors *GetErrors(void);
287 virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
288 virtual bool GetFrameNumber(int &Current, int &Total);
289 virtual bool GetReplayMode(bool &Play, bool &Forward, int &Speed);
290 };
291
292#define MAX_VIDEO_SLOWMOTION 63 // max. arg to pass to VIDEO_SLOWMOTION // TODO is this value correct?
293#define NORMAL_SPEED 4 // the index of the '1' entry in the following array
294#define MAX_SPEEDS 3 // the offset of the maximum speed from normal speed in either direction
295#define SPEED_MULT 12 // the speed multiplier
296int cDvbPlayer::Speeds[] = { 0, -2, -4, -8, 1, 2, 4, 12, 0 };
297
298cDvbPlayer::cDvbPlayer(const char *FileName, bool PauseLive)
299:cThread("dvbplayer")
300{
302 ringBuffer = NULL;
303 marks = NULL;
304 index = NULL;
305 cRecording Recording(FileName);
306 framesPerSecond = Recording.FramesPerSecond();
307 isPesRecording = Recording.IsPesRecording();
308 pauseLive = PauseLive;
309 eof = false;
310 firstPacket = true;
314 readIndex = -1;
315 readIndependent = false;
316 readFrame = NULL;
317 playFrame = NULL;
318 dropFrame = NULL;
319 resyncAfterPause = false;
320 isyslog("replay %s", FileName);
321 fileName = new cFileName(FileName, false, false, isPesRecording);
322 replayFile = fileName->Open();
323 if (!replayFile)
324 return;
326 // Create the index file:
327 index = new cIndexFile(FileName, false, isPesRecording, pauseLive);
328 if (!index)
329 esyslog("ERROR: can't allocate index");
330 else if (!index->Ok()) {
331 delete index;
332 index = NULL;
333 }
334 else if (PauseLive)
335 framesPerSecond = cRecording(FileName).FramesPerSecond(); // the fps rate might have changed from the default
336}
337
339{
340 Save();
341 Detach();
342 delete readFrame; // might not have been stored in the buffer in Action()
343 delete index;
344 delete fileName;
345 delete ringBuffer;
346 // don't delete marks here, we don't own them!
347}
348
349void cDvbPlayer::SetMarks(const cMarks *Marks)
350{
351 marks = Marks;
352}
353
354void cDvbPlayer::TrickSpeed(int Increment)
355{
356 int nts = trickSpeed + Increment;
357 if (Speeds[nts] == 1) {
358 trickSpeed = nts;
359 if (playMode == pmFast)
360 Play();
361 else
362 Pause();
363 }
364 else if (Speeds[nts]) {
365 trickSpeed = nts;
366 int Mult = (playMode == pmSlow && playDir == pdForward) ? 1 : SPEED_MULT;
367 int sp = (Speeds[nts] > 0) ? Mult / Speeds[nts] : -Speeds[nts] * Mult;
368 if (sp > MAX_VIDEO_SLOWMOTION)
371 }
372}
373
375{
378 nonBlockingFileReader->Clear();
379 if (!firstPacket) // don't set the readIndex twice if Empty() is called more than once
380 readIndex = ptsIndex.FindIndex(DeviceGetSTC()) - 1; // Action() will first increment it!
381 delete readFrame; // might not have been stored in the buffer in Action()
382 readFrame = NULL;
383 playFrame = NULL;
384 dropFrame = NULL;
385 ringBuffer->Clear();
386 ptsIndex.Clear();
387 DeviceClear();
388 firstPacket = true;
389}
390
391bool cDvbPlayer::NextFile(uint16_t FileNumber, off_t FileOffset)
392{
393 if (FileNumber > 0)
394 replayFile = fileName->SetOffset(FileNumber, FileOffset);
395 else if (replayFile && eof)
396 replayFile = fileName->NextFile();
397 eof = false;
398 return replayFile != NULL;
399}
400
402{
403 if (index) {
404 int Index = index->GetResume();
405 if (Index >= 0) {
406 uint16_t FileNumber;
407 off_t FileOffset;
408 if (index->Get(Index, &FileNumber, &FileOffset) && NextFile(FileNumber, FileOffset))
409 return Index;
410 }
411 }
412 return -1;
413}
414
416{
417 if (index) {
418 int Index = ptsIndex.FindIndex(DeviceGetSTC());
419 if (Index >= 0) {
420 if (Setup.SkipEdited && marks) {
421 cStateKey StateKey;
422 marks->Lock(StateKey);
423 if (marks->First() && abs(Index - marks->First()->Position()) <= int(round(RESUMEBACKUP * framesPerSecond)))
424 Index = 0; // when stopping within RESUMEBACKUP seconds of the first mark the recording shall still be considered unviewed
425 StateKey.Remove();
426 }
427 Index -= int(round(RESUMEBACKUP * framesPerSecond));
428 if (Index > 0)
429 Index = index->GetNextIFrame(Index, false);
430 else
431 Index = 0;
432 if (Index >= 0)
433 return index->StoreResume(Index);
434 }
435 }
436 return false;
437}
438
440{
441 if (On) {
442 if (replayFile)
443 Start();
444 }
445 else
446 Cancel(9);
447}
448
450{
451 uchar *p = NULL;
452 int pc = 0;
453
454 readIndex = Resume();
455 if (readIndex > 0)
456 isyslog("resuming replay at index %d (%s)", readIndex, *IndexToHMSF(readIndex, true, framesPerSecond));
457 else if (Setup.SkipEdited && marks) {
458 cStateKey StateKey;
459 marks->Lock(StateKey);
460 if (marks->First() && index) {
461 int Index = marks->First()->Position();
462 uint16_t FileNumber;
463 off_t FileOffset;
464 if (index->Get(Index, &FileNumber, &FileOffset) && NextFile(FileNumber, FileOffset)) {
465 isyslog("starting replay at first mark %d (%s)", Index, *IndexToHMSF(Index, true, framesPerSecond));
466 readIndex = Index;
467 }
468 }
469 StateKey.Remove();
470 }
471 if (readIndex > 0) // will first be incremented in the loop!
472 --readIndex;
473
475 int Length = 0;
476 bool Sleep = false;
477 bool WaitingForData = false;
478 time_t StuckAtEof = 0;
479 uint32_t LastStc = 0;
480 int LastReadFrame = -1;
481 int SwitchToPlayFrame = 0;
482 bool CutIn = false;
483 bool AtLastMark = false;
484
485 if (pauseLive)
486 Goto(0, true);
487 while (Running()) {
488 if (WaitingForData)
489 WaitingForData = !nonBlockingFileReader->WaitForDataMs(3); // this keeps the CPU load low, but reacts immediately on new data
490 else if (Sleep) {
491 cPoller Poller;
492 DevicePoll(Poller, 10);
493 Sleep = false;
494 if (playMode == pmStill || playMode == pmPause)
496 }
497 {
499
500 // Read the next frame from the file:
501
502 if (playMode != pmStill && playMode != pmPause) {
503 if (!readFrame && (replayFile || readIndex >= 0)) {
504 if (!nonBlockingFileReader->Reading() && !AtLastMark) {
505 if (!SwitchToPlayFrame && (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward))) {
506 uint16_t FileNumber;
507 off_t FileOffset;
508 bool TimeShiftMode = index->IsStillRecording();
509 int Index = -1;
510 readIndependent = false;
512 if (index->Get(readIndex + 1, &FileNumber, &FileOffset, &readIndependent, &Length))
513 Index = readIndex + 1;
514 }
515 else {
516 int d = int(round(0.4 * framesPerSecond));
517 if (playDir != pdForward)
518 d = -d;
519 int NewIndex = readIndex + d;
520 if (NewIndex <= 0 && readIndex > 0)
521 NewIndex = 1; // make sure the very first frame is delivered
522 NewIndex = index->GetNextIFrame(NewIndex, playDir == pdForward, &FileNumber, &FileOffset, &Length);
523 if (NewIndex < 0 && TimeShiftMode && playDir == pdForward)
524 SwitchToPlayFrame = readIndex;
525 Index = NewIndex;
526 readIndependent = true;
527 }
528 if (Index >= 0) {
529 readIndex = Index;
530 if (!NextFile(FileNumber, FileOffset))
531 continue;
532 }
533 else if (!(TimeShiftMode && playDir == pdForward))
534 eof = true;
535 }
536 else if (index) {
537 uint16_t FileNumber;
538 off_t FileOffset;
539 if (index->Get(readIndex + 1, &FileNumber, &FileOffset, &readIndependent, &Length) && NextFile(FileNumber, FileOffset)) {
540 readIndex++;
541 if ((Setup.SkipEdited || Setup.PauseAtLastMark) && marks) {
542 cStateKey StateKey;
543 marks->Lock(StateKey);
544 const cMark *m = marks->Get(readIndex);
545 if (m && (m->Index() & 0x01) != 0) { // we're at an end mark
546 m = marks->GetNextBegin(m);
547 int Index = -1;
548 if (m)
549 Index = m->Position(); // skip to next begin mark
550 else if (Setup.PauseAtLastMark)
551 AtLastMark = true; // triggers going into Pause mode
552 else if (index->IsStillRecording())
553 Index = index->GetNextIFrame(index->Last() - int(round(MAXSTUCKATEOF * framesPerSecond)), false); // skip, but stay off end of live-recordings
554 else
555 AtLastMark = true; // triggers stopping replay
556 if (Setup.SkipEdited && Index > readIndex) {
557 isyslog("skipping from %d (%s) to %d (%s)", readIndex - 1, *IndexToHMSF(readIndex - 1, true, framesPerSecond), Index, *IndexToHMSF(Index, true, framesPerSecond));
558 readIndex = Index;
559 CutIn = true;
560 }
561 }
562 StateKey.Remove();
563 }
564 }
565 else
566 eof = true;
567 }
568 else // allows replay even if the index file is missing
569 Length = MAXFRAMESIZE;
570 if (Length == -1)
571 Length = MAXFRAMESIZE; // this means we read up to EOF (see cIndex)
572 else if (Length > MAXFRAMESIZE) {
573 esyslog("ERROR: frame larger than buffer (%d > %d)", Length, MAXFRAMESIZE);
574 Length = MAXFRAMESIZE;
575 }
576 if (!eof)
577 nonBlockingFileReader->Request(replayFile, Length);
578 }
579 if (!eof) {
580 uchar *b = NULL;
581 int r = nonBlockingFileReader->Result(&b);
582 if (r > 0) {
583 WaitingForData = false;
584 LastReadFrame = readIndex;
585 uint32_t Pts = isPesRecording ? (PesHasPts(b) ? PesGetPts(b) : -1) : TsGetPts(b, r);
586 readFrame = new cFrame(b, -r, ftUnknown, readIndex, Pts, readIndependent); // hands over b to the ringBuffer
587 }
588 else if (r < 0) {
589 if (errno == EAGAIN)
590 WaitingForData = true;
591 else if (FATALERRNO) {
592 LOG_ERROR;
593 break;
594 }
595 }
596 else
597 eof = true;
598 }
599 }
600
601 // Store the frame in the buffer:
602
603 if (readFrame) {
604 if (CutIn) {
605 if (isPesRecording)
606 cRemux::SetBrokenLink(readFrame->Data(), readFrame->Count());
607 CutIn = false;
608 }
609 if (ringBuffer->Put(readFrame))
610 readFrame = NULL;
611 else
612 Sleep = true;
613 }
614 }
615 else
616 Sleep = true;
617
618 if (dropFrame) {
619 if (!eof || (playDir != pdForward && dropFrame->Index() > 0) || (playDir == pdForward && dropFrame->Index() < readIndex)) {
620 ringBuffer->Drop(dropFrame); // the very first and last frame are continuously repeated to flush data through the device
621 dropFrame = NULL;
622 }
623 }
624
625 // Get the next frame from the buffer:
626
627 if (!playFrame) {
628 playFrame = ringBuffer->Get();
629 p = NULL;
630 pc = 0;
631 }
632
633 // Play the frame:
634
635 if (playFrame) {
636 if (!p) {
637 p = playFrame->Data();
638 pc = playFrame->Count();
639 if (p) {
640 if (playFrame->Index() >= 0 && playFrame->Pts() != 0)
641 ptsIndex.Put(playFrame->Pts(), playFrame->Index(), playFrame->Independent());
642 if (firstPacket) {
643 if (isPesRecording) {
644 PlayPes(NULL, 0);
646 }
647 else
648 PlayTs(NULL, 0);
649 firstPacket = false;
650 }
651 }
652 }
653 if (p) {
654 int w;
655 bool VideoOnly = (dropFrame || playMode != pmPlay && !(playMode == pmSlow && playDir == pdForward)) && DeviceIsPlayingVideo();
656 if (isPesRecording)
657 w = PlayPes(p, pc, VideoOnly);
658 else
659 w = PlayTs(p, pc, VideoOnly);
660 if (w > 0) {
661 p += w;
662 pc -= w;
663 }
664 else if (w < 0 && FATALERRNO)
665 LOG_ERROR;
666 else
667 Sleep = true;
668 }
669 if (pc <= 0) {
671 playFrame = NULL;
672 p = NULL;
673 }
674 }
675 else {
676 if (AtLastMark) {
677 if (Setup.PauseAtLastMark) {
679 AtLastMark = false;
680 }
681 else
682 eof = true;
683 }
684 Sleep = true;
685 }
686
687 // Handle hitting begin/end of recording:
688
689 if (eof || SwitchToPlayFrame) {
690 bool SwitchToPlay = false;
691 uint32_t Stc = DeviceGetSTC();
692 if (Stc != LastStc || playMode == pmPause)
693 StuckAtEof = 0;
694 else if (!StuckAtEof)
695 StuckAtEof = time(NULL);
696 else if (time(NULL) - StuckAtEof > MAXSTUCKATEOF) {
697 if (playDir == pdForward)
698 break; // automatically stop at end of recording
699 SwitchToPlay = true;
700 }
701 LastStc = Stc;
702 int Index = ptsIndex.FindIndex(Stc);
703 if (playDir == pdForward && !SwitchToPlayFrame) {
704 if (Index >= LastReadFrame)
705 break; // automatically stop at end of recording
706 }
707 else if (Index <= 0 || SwitchToPlayFrame && Index >= SwitchToPlayFrame)
708 SwitchToPlay = true;
709 if (SwitchToPlay) {
710 if (!SwitchToPlayFrame)
711 Empty();
712 DevicePlay();
715 SwitchToPlayFrame = 0;
716 }
717 }
718 }
719 }
720
723 delete nbfr;
724}
725
727{
728 if (playMode == pmPause || playMode == pmStill)
729 Play();
730 else {
732 if (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward)) {
734 Empty();
735 }
736 DeviceFreeze();
738 }
739}
740
742{
743 if (playMode != pmPlay) {
745 if (playMode == pmStill || playMode == pmFast || (playMode == pmSlow && playDir == pdBackward)) {
747 Empty();
748 }
749 DevicePlay();
752 if (resyncAfterPause) {
753 int Current, Total;
754 if (GetIndex(Current, Total, true))
755 Goto(Current);
756 resyncAfterPause = false;
757 }
758 }
759}
760
762{
763 if (index) {
764 switch (playMode) {
765 case pmFast:
766 if (Setup.MultiSpeedMode) {
767 TrickSpeed(playDir == pdForward ? 1 : -1);
768 break;
769 }
770 else if (playDir == pdForward) {
771 Play();
772 break;
773 }
774 // run into pmPlay
775 case pmPlay: {
778 Empty();
780 DeviceMute();
784 TrickSpeed(Setup.MultiSpeedMode ? 1 : MAX_SPEEDS);
785 }
786 break;
787 case pmSlow:
788 if (Setup.MultiSpeedMode) {
789 TrickSpeed(playDir == pdForward ? -1 : 1);
790 break;
791 }
792 else if (playDir == pdForward) {
793 Pause();
794 break;
795 }
796 // run into pmPause
797 case pmStill:
798 case pmPause: {
800 Empty();
801 DeviceMute();
805 TrickSpeed(Setup.MultiSpeedMode ? -1 : -MAX_SPEEDS);
806 }
807 break;
808 default: esyslog("ERROR: unknown playMode %d (%s)", playMode, __FUNCTION__);
809 }
810 }
811}
812
814{
815 if (index) {
816 switch (playMode) {
817 case pmFast:
818 if (Setup.MultiSpeedMode) {
819 TrickSpeed(playDir == pdBackward ? 1 : -1);
820 break;
821 }
822 else if (playDir == pdBackward) {
823 Play();
824 break;
825 }
826 // run into pmPlay
827 case pmPlay: {
829 Empty();
831 DeviceMute();
835 TrickSpeed(Setup.MultiSpeedMode ? 1 : MAX_SPEEDS);
836 }
837 break;
838 case pmSlow:
839 if (Setup.MultiSpeedMode) {
840 TrickSpeed(playDir == pdBackward ? -1 : 1);
841 break;
842 }
843 else if (playDir == pdBackward) {
844 Pause();
845 break;
846 }
847 // run into pmPause
848 case pmStill:
849 case pmPause: {
851 Empty();
852 DeviceMute();
856 TrickSpeed(Setup.MultiSpeedMode ? -1 : -MAX_SPEEDS);
857 }
858 break;
859 default: esyslog("ERROR: unknown playMode %d (%s)", playMode, __FUNCTION__);
860 }
861 }
862}
863
865{
866 if (index && Frames) {
867 int Current, Total;
868 GetIndex(Current, Total, true);
869 int OldCurrent = Current;
870 // As GetNextIFrame() increments/decrements at least once, the
871 // destination frame (= Current + Frames) must be adjusted by
872 // -1/+1 respectively.
873 Current = index->GetNextIFrame(Current + Frames + (Frames > 0 ? -1 : 1), Frames > 0);
874 return Current >= 0 ? Current : OldCurrent;
875 }
876 return -1;
877}
878
879void cDvbPlayer::SkipSeconds(int Seconds)
880{
881 if (index && Seconds) {
883 int Index = ptsIndex.FindIndex(DeviceGetSTC());
884 Empty();
885 if (Index >= 0) {
886 Index = max(Index + SecondsToFrames(Seconds, framesPerSecond), 0);
887 if (Index > 0)
888 Index = index->GetNextIFrame(Index, false, NULL, NULL, NULL);
889 if (Index >= 0)
890 readIndex = Index - 1; // Action() will first increment it!
891 }
892 Play();
893 }
894}
895
896void cDvbPlayer::Goto(int Index, bool Still)
897{
898 if (index) {
900 Empty();
901 if (++Index <= 0)
902 Index = 1; // not '0', to allow GetNextIFrame() below to work!
903 uint16_t FileNumber;
904 off_t FileOffset;
905 int Length;
906 Index = index->GetNextIFrame(Index, false, &FileNumber, &FileOffset, &Length);
907 if (Index >= 0) {
908 if (Still) {
909 if (NextFile(FileNumber, FileOffset)) {
911 int r = ReadFrame(replayFile, b, Length, sizeof(b));
912 if (r > 0) {
913 if (playMode == pmPause)
914 DevicePlay();
915 DeviceStillPicture(b, r);
916 ptsIndex.Put(isPesRecording ? PesGetPts(b) : TsGetPts(b, r), Index, true);
917 }
919 readIndex = Index - 1; // makes sure a later play starts with this I-frame
920 }
921 }
922 else {
923 readIndex = Index - 1; // Action() will first increment it!
924 Play();
925 }
926 }
927 }
928}
929
931{
933 return; // only do this upon user interaction
934 if (playMode == pmPlay) {
935 if (!ptsIndex.IsEmpty()) {
936 int Current, Total;
937 if (GetIndex(Current, Total, true))
938 Goto(Current);
939 }
940 }
941 else if (playMode == pmPause)
942 resyncAfterPause = true;
943}
944
946{
947 if (index)
948 return index->GetErrors();
949 return NULL;
950}
951
952bool cDvbPlayer::GetIndex(int &Current, int &Total, bool SnapToIFrame)
953{
954 if (index) {
955 Current = ptsIndex.FindIndex(DeviceGetSTC());
956 if (SnapToIFrame) {
957 int i1 = index->GetNextIFrame(Current + 1, false);
958 int i2 = index->GetNextIFrame(Current, true);
959 Current = (abs(Current - i1) <= abs(Current - i2)) ? i1 : i2;
960 }
961 Total = index->Last();
962 return true;
963 }
964 Current = Total = -1;
965 return false;
966}
967
968bool cDvbPlayer::GetFrameNumber(int &Current, int &Total)
969{
970 if (index) {
971 Current = ptsIndex.FindFrameNumber(DeviceGetSTC(), playDir == pdForward);
972 Total = index->Last();
973 return true;
974 }
975 Current = Total = -1;
976 return false;
977}
978
979bool cDvbPlayer::GetReplayMode(bool &Play, bool &Forward, int &Speed)
980{
981 Play = (playMode == pmPlay || playMode == pmFast);
983 if (playMode == pmFast || playMode == pmSlow)
984 Speed = Setup.MultiSpeedMode ? abs(trickSpeed - NORMAL_SPEED) : 0;
985 else
986 Speed = -1;
987 return true;
988}
989
990// --- cDvbPlayerControl -----------------------------------------------------
991
992cDvbPlayerControl::cDvbPlayerControl(const char *FileName, bool PauseLive)
993:cControl(NULL)
994{
995 player = new cDvbPlayer(FileName, PauseLive);
997}
998
1003
1005{
1006 if (player)
1007 player->SetMarks(Marks);
1008}
1009
1011{
1012 return player && player->Active();
1013}
1014
1016{
1017 cControl::player = NULL;
1018 delete player;
1019 player = NULL;
1020}
1021
1023{
1024 if (player)
1025 player->Pause();
1026}
1027
1029{
1030 if (player)
1031 player->Play();
1032}
1033
1035{
1036 if (player)
1037 player->Forward();
1038}
1039
1041{
1042 if (player)
1043 player->Backward();
1044}
1045
1047{
1048 if (player)
1049 player->SkipSeconds(Seconds);
1050}
1051
1053{
1054 if (player)
1055 return player->SkipFrames(Frames);
1056 return -1;
1057}
1058
1060{
1061 if (player)
1062 return player->GetErrors();
1063 return NULL;
1064}
1065
1066bool cDvbPlayerControl::GetIndex(int &Current, int &Total, bool SnapToIFrame)
1067{
1068 if (player) {
1069 player->GetIndex(Current, Total, SnapToIFrame);
1070 return true;
1071 }
1072 return false;
1073}
1074
1075bool cDvbPlayerControl::GetFrameNumber(int &Current, int &Total)
1076{
1077 if (player) {
1078 player->GetFrameNumber(Current, Total);
1079 return true;
1080 }
1081 return false;
1082}
1083
1084bool cDvbPlayerControl::GetReplayMode(bool &Play, bool &Forward, int &Speed)
1085{
1086 return player && player->GetReplayMode(Play, Forward, Speed);
1087}
1088
1089void cDvbPlayerControl::Goto(int Position, bool Still)
1090{
1091 if (player)
1092 player->Goto(Position, Still);
1093}
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:112
cControl(cPlayer *Player, bool Hidden=false)
Definition player.c:45
cPlayer * player
Definition player.h:89
void SetMarks(const cMarks *Marks)
Definition dvbplayer.c:1004
bool GetIndex(int &Current, int &Total, bool SnapToIFrame=false)
Definition dvbplayer.c:1066
virtual ~cDvbPlayerControl()
Definition dvbplayer.c:999
const cErrors * GetErrors(void)
Definition dvbplayer.c:1059
void SkipSeconds(int Seconds)
Definition dvbplayer.c:1046
cDvbPlayerControl(const char *FileName, bool PauseLive=false)
Definition dvbplayer.c:992
bool GetReplayMode(bool &Play, bool &Forward, int &Speed)
Definition dvbplayer.c:1084
void Pause(void)
Definition dvbplayer.c:1022
int SkipFrames(int Frames)
Definition dvbplayer.c:1052
void Goto(int Index, bool Still=false)
Definition dvbplayer.c:1089
void Stop(void)
Definition dvbplayer.c:1015
void Forward(void)
Definition dvbplayer.c:1034
bool Active(void)
Definition dvbplayer.c:1010
bool GetFrameNumber(int &Current, int &Total)
Definition dvbplayer.c:1075
void Play(void)
Definition dvbplayer.c:1028
void Backward(void)
Definition dvbplayer.c:1040
cDvbPlayer * player
Definition dvbplayer.h:21
const cMarks * marks
Definition dvbplayer.c:246
virtual ~cDvbPlayer()
Definition dvbplayer.c:338
cFrame * readFrame
Definition dvbplayer.c:260
cRingBufferFrame * ringBuffer
Definition dvbplayer.c:244
virtual void Activate(bool On)
Definition dvbplayer.c:439
void SetMarks(const cMarks *Marks)
Definition dvbplayer.c:349
cFrame * playFrame
Definition dvbplayer.c:261
bool Save(void)
Definition dvbplayer.c:415
virtual bool GetFrameNumber(int &Current, int &Total)
Definition dvbplayer.c:968
bool firstPacket
Definition dvbplayer.c:254
virtual const cErrors * GetErrors(void)
Definition dvbplayer.c:945
void SkipSeconds(int Seconds)
Definition dvbplayer.c:879
virtual double FramesPerSecond(void)
Definition dvbplayer.c:284
int Resume(void)
Definition dvbplayer.c:401
cDvbPlayer(const char *FileName, bool PauseLive)
Definition dvbplayer.c:298
cNonBlockingFileReader * nonBlockingFileReader
Definition dvbplayer.c:243
cIndexFile * index
Definition dvbplayer.c:248
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:449
void Goto(int Position, bool Still=false)
Definition dvbplayer.c:896
double framesPerSecond
Definition dvbplayer.c:250
bool isPesRecording
Definition dvbplayer.c:251
void Play(void)
Definition dvbplayer.c:741
cFileName * fileName
Definition dvbplayer.c:247
virtual bool GetReplayMode(bool &Play, bool &Forward, int &Speed)
Definition dvbplayer.c:979
void Empty(void)
Definition dvbplayer.c:374
ePlayDirs playDir
Definition dvbplayer.c:256
virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame=false)
Definition dvbplayer.c:952
static int Speeds[]
Definition dvbplayer.c:296
int trickSpeed
Definition dvbplayer.c:257
bool NextFile(uint16_t FileNumber=0, off_t FileOffset=-1)
Definition dvbplayer.c:391
void Pause(void)
Definition dvbplayer.c:726
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:930
void Forward(void)
Definition dvbplayer.c:761
void TrickSpeed(int Increment)
Definition dvbplayer.c:354
int SkipFrames(int Frames)
Definition dvbplayer.c:864
void Backward(void)
Definition dvbplayer.c:813
bool readIndependent
Definition dvbplayer.c:259
cFrame * dropFrame
Definition dvbplayer.c:262
bool pauseLive
Definition dvbplayer.c:252
ePlayModes playMode
Definition dvbplayer.c:255
int Index(void) const
Definition tools.c:2095
int Position(void) const
Definition recording.h:388
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
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
cPlayer(ePlayMode PlayMode=pmAudioVideo)
Definition player.c:15
bool DeviceIsPlayingVideo(void)
Definition player.h:29
void DeviceClear(void)
Definition player.h:31
void DeviceTrickSpeed(int Speed, bool Forward)
Definition player.h:30
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
int FindFrameNumber(uint32_t Pts, bool Forward)
Definition dvbplayer.c:93
void Put(uint32_t Pts, int Index, bool Independent)
Definition dvbplayer.c:60
double FramesPerSecond(void) const
Definition recording.h:174
bool IsPesRecording(void) const
Definition recording.h:195
static void SetBrokenLink(uchar *Data, int Length)
Definition remux.c:102
void Remove(bool IncState=true)
Removes this key from the lock it was previously used with.
Definition thread.c:868
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
cThread(const char *Description=NULL, bool LowPriority=false)
Creates a new thread.
Definition thread.c:238
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:494
cSetup Setup
Definition config.c:372
eTrackType
Definition device.h:63
#define MAX_VIDEO_SLOWMOTION
Definition dvbplayer.c:292
#define SPEED_MULT
Definition dvbplayer.c:295
#define PTSINDEX_ENTRIES
Definition dvbplayer.c:20
#define NORMAL_SPEED
Definition dvbplayer.c:293
#define RESUMEBACKUP
Definition dvbplayer.c:235
#define MAX_SPEEDS
Definition dvbplayer.c:294
#define MAXSTUCKATEOF
Definition dvbplayer.c:236
#define PLAYERBUFSIZE
Definition dvbplayer.c:233
cString IndexToHMSF(int Index, bool WithFrame, double FramesPerSecond)
Definition recording.c:3392
int SecondsToFrames(int Seconds, double FramesPerSecond)
Definition recording.c:3419
int ReadFrame(cUnbufferedFile *f, uchar *b, int Length, int Max)
Definition recording.c:3426
#define MAXFRAMESIZE
Definition recording.h:473
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