vdr  2.4.1
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 4.5.1.1 2019/05/28 09:27:32 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);
39  int FindFrameNumber(uint32_t Pts);
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)
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 
93 int cPtsIndex::FindFrameNumber(uint32_t Pts)
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 
126 private:
129  int wanted;
130  int length;
134 protected:
135  void Action(void);
136 public:
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 {
185  LOCK_THREAD;
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 
238 class cDvbPlayer : public cPlayer, cThread {
239 private:
242  static int Speeds[];
246  const cMarks *marks;
252  bool pauseLive;
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);
269 protected:
270  virtual void Activate(bool On);
271  virtual void Action(void);
272 public:
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
295 int cDvbPlayer::Speeds[] = { 0, -2, -4, -8, 1, 2, 4, 12, 0 };
296 
297 cDvbPlayer::cDvbPlayer(const char *FileName, bool PauseLive)
298 :cThread("dvbplayer")
299 {
300  nonBlockingFileReader = NULL;
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;
310  playMode = pmPlay;
311  playDir = pdForward;
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);
321  replayFile = fileName->Open();
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 
348 void cDvbPlayer::SetMarks(const cMarks *Marks)
349 {
350  marks = Marks;
351 }
352 
353 void 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 {
375  LOCK_THREAD;
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 
390 bool 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 
438 void cDvbPlayer::Activate(bool On)
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  {
497  LOCK_THREAD;
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  }
608  if (ringBuffer->Put(readFrame))
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) {
627  playFrame = ringBuffer->Get();
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);
644  cRemux::SetBrokenLink(p, pc);
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) {
676  if (Setup.PauseAtLastMark) {
677  playMode = pmPause;
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();
712  playMode = pmPlay;
713  playDir = pdForward;
714  SwitchToPlayFrame = 0;
715  }
716  }
717  }
718  }
719 
721  nonBlockingFileReader = NULL;
722  delete nbfr;
723 }
724 
726 {
727  if (playMode == pmPause || playMode == pmStill)
728  Play();
729  else {
730  LOCK_THREAD;
731  if (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward)) {
733  Empty();
734  }
735  DeviceFreeze();
736  playMode = pmPause;
737  }
738 }
739 
741 {
742  if (playMode != pmPlay) {
743  LOCK_THREAD;
744  if (playMode == pmStill || playMode == pmFast || (playMode == pmSlow && playDir == pdBackward)) {
746  Empty();
747  }
748  DevicePlay();
749  playMode = pmPlay;
750  playDir = pdForward;
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: {
775  LOCK_THREAD;
777  Empty();
778  if (DeviceIsPlayingVideo())
779  DeviceMute();
780  playMode = pmFast;
781  playDir = pdForward;
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();
800  playMode = pmSlow;
801  playDir = pdForward;
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: {
825  LOCK_THREAD;
826  Empty();
827  if (DeviceIsPlayingVideo())
828  DeviceMute();
829  playMode = pmFast;
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: {
848  LOCK_THREAD;
849  Empty();
850  DeviceMute();
851  playMode = pmSlow;
855  }
856  break;
857  default: esyslog("ERROR: unknown playMode %d (%s)", playMode, __FUNCTION__);
858  }
859  }
860 }
861 
862 int cDvbPlayer::SkipFrames(int Frames)
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 
877 void cDvbPlayer::SkipSeconds(int Seconds)
878 {
879  if (index && Seconds) {
880  LOCK_THREAD;
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 
894 void cDvbPlayer::Goto(int Index, bool Still)
895 {
896  if (index) {
897  LOCK_THREAD;
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)) {
908  uchar b[MAXFRAMESIZE];
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  }
916  playMode = pmStill;
917  readIndex = Index;
918  }
919  }
920  else {
921  readIndex = Index - 1; // Action() will first increment it!
922  Play();
923  }
924  }
925  }
926 }
927 
929 {
930  if (!cThread::IsMainThread())
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 
943 bool 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 
959 bool 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 
970 bool cDvbPlayer::GetReplayMode(bool &Play, bool &Forward, int &Speed)
971 {
972  Play = (playMode == pmPlay || playMode == pmFast);
973  Forward = (playDir == pdForward);
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 
983 cDvbPlayerControl::cDvbPlayerControl(const char *FileName, bool PauseLive)
984 :cControl(player = new cDvbPlayer(FileName, PauseLive))
985 {
986 }
987 
989 {
990  Stop();
991 }
992 
994 {
995  if (player)
996  player->SetMarks(Marks);
997 }
998 
1000 {
1001  return player && player->Active();
1002 }
1003 
1005 {
1006  cControl::player = NULL;
1007  delete player;
1008  player = NULL;
1009 }
1010 
1012 {
1013  if (player)
1014  player->Pause();
1015 }
1016 
1018 {
1019  if (player)
1020  player->Play();
1021 }
1022 
1024 {
1025  if (player)
1026  player->Forward();
1027 }
1028 
1030 {
1031  if (player)
1032  player->Backward();
1033 }
1034 
1036 {
1037  if (player)
1038  player->SkipSeconds(Seconds);
1039 }
1040 
1042 {
1043  if (player)
1044  return player->SkipFrames(Frames);
1045  return -1;
1046 }
1047 
1048 bool cDvbPlayerControl::GetIndex(int &Current, int &Total, bool SnapToIFrame)
1049 {
1050  if (player) {
1051  player->GetIndex(Current, Total, SnapToIFrame);
1052  return true;
1053  }
1054  return false;
1055 }
1056 
1057 bool cDvbPlayerControl::GetFrameNumber(int &Current, int &Total)
1058 {
1059  if (player) {
1060  player->GetFrameNumber(Current, Total);
1061  return true;
1062  }
1063  return false;
1064 }
1065 
1066 bool cDvbPlayerControl::GetReplayMode(bool &Play, bool &Forward, int &Speed)
1067 {
1068  return player && player->GetReplayMode(Play, Forward, Speed);
1069 }
1070 
1071 void cDvbPlayerControl::Goto(int Position, bool Still)
1072 {
1073  if (player)
1074  player->Goto(Position, Still);
1075 }
unsigned char uchar
Definition: tools.h:31
int FindIndex(uint32_t Pts)
Definition: dvbplayer.c:71
cFrame * dropFrame
Definition: dvbplayer.c:262
cIndexFile * index
Definition: dvbplayer.c:248
void DeviceClear(void)
Definition: player.h:31
cRingBufferFrame * ringBuffer
Definition: dvbplayer.c:244
bool firstPacket
Definition: dvbplayer.c:254
int MultiSpeedMode
Definition: config.h:343
#define SPEED_MULT
Definition: dvbplayer.c:294
virtual void Activate(bool On)
Definition: dvbplayer.c:438
virtual void Clear(void)
Definition: ringbuffer.c:432
#define LOG_ERROR
Definition: tools.h:39
int lastFound
Definition: dvbplayer.c:31
#define MAX_SPEEDS
Definition: dvbplayer.c:293
const cMarks * marks
Definition: dvbplayer.c:246
void Pause(void)
Definition: dvbplayer.c:725
void Play(void)
Definition: dvbplayer.c:1017
void Play(void)
Definition: dvbplayer.c:740
ssize_t Read(void *Data, size_t Size)
Definition: tools.c:1857
cUnbufferedFile * SetOffset(int Number, off_t Offset=0)
Definition: recording.c:2998
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:2143
virtual ~cDvbPlayer()
Definition: dvbplayer.c:337
void Signal(void)
Signals a caller of Wait() that the condition it is waiting for is met.
Definition: thread.c:100
int64_t PesGetPts(const uchar *p)
Definition: remux.h:199
int GetNextIFrame(int Index, bool Forward, uint16_t *FileNumber=NULL, off_t *FileOffset=NULL, int *Length=NULL)
Definition: recording.c:2747
cNonBlockingFileReader * nonBlockingFileReader
Definition: dvbplayer.c:243
void Goto(int Position, bool Still=false)
Definition: dvbplayer.c:894
void SkipSeconds(int Seconds)
Definition: dvbplayer.c:877
void Remove(bool IncState=true)
Removes this key from the lock it was previously used with.
Definition: thread.c:859
#define esyslog(a...)
Definition: tools.h:35
bool Independent(void) const
Definition: ringbuffer.h:130
#define PTSINDEX_ENTRIES
Definition: dvbplayer.c:20
cFileName * fileName
Definition: dvbplayer.c:247
void Drop(cFrame *Frame)
Definition: ringbuffer.c:477
int Index(void) const
Definition: tools.c:2072
#define MAXSTUCKATEOF
Definition: dvbplayer.c:236
static tThreadId IsMainThread(void)
Definition: thread.h:131
cUnbufferedFile * NextFile(void)
Definition: recording.c:3040
bool Save(void)
Definition: dvbplayer.c:414
int SkipEdited
Definition: config.h:349
T max(T a, T b)
Definition: tools.h:60
bool Ok(void)
Definition: recording.h:467
void DevicePlay(void)
Definition: player.h:32
bool PesHasPts(const uchar *p)
Definition: remux.h:189
int ReadFrame(cUnbufferedFile *f, uchar *b, int Length, int Max)
Definition: recording.c:3081
tPtsIndex pi[PTSINDEX_ENTRIES]
Definition: dvbplayer.c:29
eTrackType
Definition: device.h:63
int Last(void)
Returns the index of the last entry in this file, or -1 if the file is empty.
Definition: recording.h:477
virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame=false)
Definition: dvbplayer.c:943
void Empty(void)
Definition: dvbplayer.c:373
int PlayPes(const uchar *Data, int Length, bool VideoOnly=false)
Definition: player.c:26
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
cUnbufferedFile is used for large files that are mainly written or read in a streaming manner...
Definition: tools.h:457
bool DeviceHasIBPTrickSpeed(void)
Definition: player.h:28
double framesPerSecond
Definition: dvbplayer.c:250
void Backward(void)
Definition: dvbplayer.c:810
#define NORMAL_SPEED
Definition: dvbplayer.c:292
cFrame * playFrame
Definition: dvbplayer.c:261
cPtsIndex ptsIndex
Definition: dvbplayer.c:245
void SetMarks(const cMarks *Marks)
Definition: dvbplayer.c:348
void Detach(void)
Definition: player.c:34
cPlayer * player
Definition: player.h:87
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
cString IndexToHMSF(int Index, bool WithFrame, double FramesPerSecond)
Definition: recording.c:3047
#define MALLOC(type, size)
Definition: tools.h:47
static int Speeds[]
Definition: dvbplayer.c:242
void DeviceFreeze(void)
Definition: player.h:33
virtual double FramesPerSecond(void)
Definition: dvbplayer.c:284
void Clear(void)
Definition: dvbplayer.c:48
virtual ~cDvbPlayerControl()
Definition: dvbplayer.c:988
void Unlock(void)
Definition: thread.h:95
Definition: player.h:16
cDvbPlayerControl(const char *FileName, bool PauseLive=false)
Definition: dvbplayer.c:983
virtual bool GetReplayMode(bool &Play, bool &Forward, int &Speed)
Definition: dvbplayer.c:970
const cMark * Get(int Position) const
Definition: recording.c:2224
int Result(uchar **Buffer)
Definition: dvbplayer.c:183
void SetMarks(const cMarks *Marks)
Definition: dvbplayer.c:993
void Broadcast(void)
Definition: thread.c:150
bool NextFile(uint16_t FileNumber=0, off_t FileOffset=-1)
Definition: dvbplayer.c:390
bool isPesRecording
Definition: dvbplayer.c:251
void DeviceStillPicture(const uchar *Data, int Length)
Definition: player.h:36
void Pause(void)
Definition: dvbplayer.c:1011
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:2251
void Backward(void)
Definition: dvbplayer.c:1029
cFrame * Get(void)
Definition: ringbuffer.c:463
int Index(void) const
Definition: ringbuffer.h:128
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
int PauseAtLastMark
Definition: config.h:350
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
cDvbPlayer(const char *FileName, bool PauseLive)
Definition: dvbplayer.c:297
cUnbufferedFile * Open(void)
Definition: recording.c:2964
cSetup Setup
Definition: config.c:372
bool Running(void)
Returns false if a derived cThread object shall leave its Action() function.
Definition: thread.h:101
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
Definition: thread.h:67
bool TimedWait(cMutex &Mutex, int TimeoutMs)
Definition: thread.c:132
virtual bool GetFrameNumber(int &Current, int &Total)
Definition: dvbplayer.c:959
void Request(cUnbufferedFile *File, int Length)
Definition: dvbplayer.c:172
uint32_t Pts(void) const
Definition: ringbuffer.h:129
bool GetIndex(int &Current, int &Total, bool SnapToIFrame=false)
Definition: dvbplayer.c:1048
const T * First(void) const
Returns the first element in this list, or NULL if the list is empty.
Definition: tools.h:606
bool Active(void)
Definition: dvbplayer.c:999
bool IsPesRecording(void) const
Definition: recording.h:171
virtual void SetAudioTrack(eTrackType Type, const tTrackId *TrackId)
Definition: dvbplayer.c:928
bool StoreResume(int Index)
Definition: recording.h:480
bool DevicePoll(cPoller &Poller, int TimeoutMs=0)
Definition: player.h:26
bool GetReplayMode(bool &Play, bool &Forward, int &Speed)
Definition: dvbplayer.c:1066
cDvbPlayer * player
Definition: dvbplayer.h:21
static void SetBrokenLink(uchar *Data, int Length)
Definition: remux.c:102
bool DeviceIsPlayingVideo(void)
Definition: player.h:29
#define PLAYERBUFSIZE
Definition: dvbplayer.c:233
bool Put(cFrame *Frame)
Definition: ringbuffer.c:443
int readIndex
Definition: dvbplayer.c:258
cFrame * readFrame
Definition: dvbplayer.c:260
ePlayModes playMode
Definition: dvbplayer.c:255
int PlayTs(const uchar *Data, int Length, bool VideoOnly=false)
Definition: player.h:47
void Stop(void)
Definition: dvbplayer.c:1004
void DeviceTrickSpeed(int Speed, bool Forward)
Definition: player.h:30
uint64_t DeviceGetSTC(void)
Definition: player.h:37
bool IsEmpty(void)
Definition: dvbplayer.c:54
#define FATALERRNO
Definition: tools.h:52
int GetResume(void)
Definition: recording.h:479
#define MAXFRAMESIZE
Definition: recording.h:436
int Resume(void)
Definition: dvbplayer.c:400
void Forward(void)
Definition: dvbplayer.c:1023
bool Active(void)
Definition: dvbplayer.c:276
int64_t TsGetPts(const uchar *p, int l)
Definition: remux.c:160
#define isyslog(a...)
Definition: tools.h:36
Definition: thread.h:79
int SecondsToFrames(int Seconds, double FramesPerSecond)
Definition: recording.c:3074
double FramesPerSecond(void) const
Definition: recording.h:157
int Count(void) const
Definition: ringbuffer.h:126
cPtsIndex(void)
Definition: dvbplayer.c:42
bool WaitForDataMs(int msToWait)
Definition: dvbplayer.c:223
bool Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *Independent=NULL, int *Length=NULL)
Definition: recording.c:2721
void DeviceMute(void)
Definition: player.h:34
bool pauseLive
Definition: dvbplayer.c:252
bool resyncAfterPause
Definition: dvbplayer.c:263
bool IsStillRecording(void)
Definition: recording.c:2827
int trickSpeed
Definition: dvbplayer.c:257
void Goto(int Index, bool Still=false)
Definition: dvbplayer.c:1071
#define LOCK_THREAD
Definition: thread.h:167
int SkipFrames(int Frames)
Definition: dvbplayer.c:1041
#define MAX_VIDEO_SLOWMOTION
Definition: dvbplayer.c:291
cUnbufferedFile * f
Definition: dvbplayer.c:127
bool GetFrameNumber(int &Current, int &Total)
Definition: dvbplayer.c:1057
int Position(void) const
Definition: recording.h:362
bool readIndependent
Definition: dvbplayer.c:259
void Put(uint32_t Pts, int Index, bool Independent)
Definition: dvbplayer.c:60
int SkipFrames(int Frames)
Definition: dvbplayer.c:862
Definition: tools.h:393
void Cancel(int WaitSeconds=0)
Cancels the thread by first setting &#39;running&#39; to false, so that the Action() loop can finish in an or...
Definition: thread.c:354
void SkipSeconds(int Seconds)
Definition: dvbplayer.c:1035
#define RESUMEBACKUP
Definition: dvbplayer.c:235
void TrickSpeed(int Increment)
Definition: dvbplayer.c:353
bool eof
Definition: dvbplayer.c:253
cUnbufferedFile * replayFile
Definition: dvbplayer.c:249
cMutex mutex
Definition: dvbplayer.c:32
void Lock(void)
Definition: thread.h:94
uchar * Data(void) const
Definition: ringbuffer.h:125
int FindFrameNumber(uint32_t Pts)
Definition: dvbplayer.c:93
ePlayDirs playDir
Definition: dvbplayer.c:256
void Forward(void)
Definition: dvbplayer.c:760