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