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