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