vdr  1.7.31
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.28 2012/06/09 14:37:24 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  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 
52 void cPtsIndex::Put(uint32_t Pts, int Index)
53 {
54  cMutexLock MutexLock(&mutex);
55  pi[w].pts = Pts;
56  pi[w].index = Index;
57  w = (w + 1) % PTSINDEX_ENTRIES;
58  if (w == r)
59  r = (r + 1) % PTSINDEX_ENTRIES;
60 }
61 
62 int cPtsIndex::FindIndex(uint32_t Pts)
63 {
64  cMutexLock MutexLock(&mutex);
65  if (w == r)
66  return lastFound; // list is empty, let's not jump way off the last known position
67  uint32_t Delta = 0xFFFFFFFF;
68  int Index = -1;
69  for (int i = w; i != r; ) {
70  if (--i < 0)
71  i = PTSINDEX_ENTRIES - 1;
72  uint32_t d = pi[i].pts < Pts ? Pts - pi[i].pts : pi[i].pts - Pts;
73  if (d > 0x7FFFFFFF)
74  d = 0xFFFFFFFF - d; // handle rollover
75  if (d < Delta) {
76  Delta = d;
77  Index = pi[i].index;
78  }
79  }
80  lastFound = Index;
81  return Index;
82 }
83 
84 // --- cNonBlockingFileReader ------------------------------------------------
85 
87 private:
90  int wanted;
91  int length;
95 protected:
96  void Action(void);
97 public:
100  void Clear(void);
101  void Request(cUnbufferedFile *File, int Length);
102  int Result(uchar **Buffer);
103  bool Reading(void) { return buffer; }
104  bool WaitForDataMs(int msToWait);
105  };
106 
108 :cThread("non blocking file reader")
109 {
110  f = NULL;
111  buffer = NULL;
112  wanted = length = 0;
113  Start();
114 }
115 
117 {
118  newSet.Signal();
119  Cancel(3);
120  free(buffer);
121 }
122 
124 {
125  Lock();
126  f = NULL;
127  free(buffer);
128  buffer = NULL;
129  wanted = length = 0;
130  Unlock();
131 }
132 
134 {
135  Lock();
136  Clear();
137  wanted = Length;
139  f = File;
140  Unlock();
141  newSet.Signal();
142 }
143 
145 {
146  LOCK_THREAD;
147  if (buffer && length == wanted) {
148  *Buffer = buffer;
149  buffer = NULL;
150  return wanted;
151  }
152  errno = EAGAIN;
153  return -1;
154 }
155 
157 {
158  while (Running()) {
159  Lock();
160  if (f && buffer && length < wanted) {
161  int r = f->Read(buffer + length, wanted - length);
162  if (r > 0)
163  length += r;
164  else if (r == 0) { // r == 0 means EOF
165  if (length > 0)
166  wanted = length; // already read something, so return the rest
167  else
168  length = wanted = 0; // report EOF
169  }
170  else if (FATALERRNO) {
171  LOG_ERROR;
172  length = wanted = r; // this will forward the error status to the caller
173  }
174  if (length == wanted) {
175  cMutexLock NewDataLock(&newDataMutex);
177  }
178  }
179  Unlock();
180  newSet.Wait(1000);
181  }
182 }
183 
185 {
186  cMutexLock NewDataLock(&newDataMutex);
187  if (buffer && length == wanted)
188  return true;
189  return newDataCond.TimedWait(newDataMutex, msToWait);
190 }
191 
192 // --- cDvbPlayer ------------------------------------------------------------
193 
194 #define PLAYERBUFSIZE MEGABYTE(1)
195 
196 #define RESUMEBACKUP 10 // number of seconds to back up when resuming an interrupted replay session
197 #define MAXSTUCKATEOF 3 // max. number of seconds to wait in case the device doesn't play the last frame
198 
199 class cDvbPlayer : public cPlayer, cThread {
200 private:
203  static int Speeds[];
213  bool pauseLive;
214  bool eof;
224  void TrickSpeed(int Increment);
225  void Empty(void);
226  bool NextFile(uint16_t FileNumber = 0, off_t FileOffset = -1);
227  int Resume(void);
228  bool Save(void);
229 protected:
230  virtual void Activate(bool On);
231  virtual void Action(void);
232 public:
233  cDvbPlayer(const char *FileName, bool PauseLive);
234  virtual ~cDvbPlayer();
235  bool Active(void) { return cThread::Running(); }
236  void Pause(void);
237  void Play(void);
238  void Forward(void);
239  void Backward(void);
240  int SkipFrames(int Frames);
241  void SkipSeconds(int Seconds);
242  void Goto(int Position, bool Still = false);
243  virtual double FramesPerSecond(void) { return framesPerSecond; }
244  virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
245  virtual bool GetReplayMode(bool &Play, bool &Forward, int &Speed);
246  };
247 
248 #define MAX_VIDEO_SLOWMOTION 63 // max. arg to pass to VIDEO_SLOWMOTION // TODO is this value correct?
249 #define NORMAL_SPEED 4 // the index of the '1' entry in the following array
250 #define MAX_SPEEDS 3 // the offset of the maximum speed from normal speed in either direction
251 #define SPEED_MULT 12 // the speed multiplier
252 int cDvbPlayer::Speeds[] = { 0, -2, -4, -8, 1, 2, 4, 12, 0 };
253 
254 cDvbPlayer::cDvbPlayer(const char *FileName, bool PauseLive)
255 :cThread("dvbplayer")
256 {
257  nonBlockingFileReader = NULL;
258  ringBuffer = NULL;
259  index = NULL;
260  cRecording Recording(FileName);
261  framesPerSecond = Recording.FramesPerSecond();
262  isPesRecording = Recording.IsPesRecording();
263  pauseLive = PauseLive;
264  eof = false;
265  firstPacket = true;
266  playMode = pmPlay;
267  playDir = pdForward;
269  readIndex = -1;
270  readIndependent = false;
271  readFrame = NULL;
272  playFrame = NULL;
273  dropFrame = NULL;
274  isyslog("replay %s", FileName);
275  fileName = new cFileName(FileName, false, false, isPesRecording);
276  replayFile = fileName->Open();
277  if (!replayFile)
278  return;
280  // Create the index file:
281  index = new cIndexFile(FileName, false, isPesRecording, pauseLive);
282  if (!index)
283  esyslog("ERROR: can't allocate index");
284  else if (!index->Ok()) {
285  delete index;
286  index = NULL;
287  }
288  else if (PauseLive)
289  framesPerSecond = cRecording(FileName).FramesPerSecond(); // the fps rate might have changed from the default
291 }
292 
294 {
295  Save();
296  Detach();
297  delete readFrame; // might not have been stored in the buffer in Action()
298  delete index;
299  delete fileName;
300  delete ringBuffer;
301 }
302 
303 void cDvbPlayer::TrickSpeed(int Increment)
304 {
305  int nts = trickSpeed + Increment;
306  if (Speeds[nts] == 1) {
307  trickSpeed = nts;
308  if (playMode == pmFast)
309  Play();
310  else
311  Pause();
312  }
313  else if (Speeds[nts]) {
314  trickSpeed = nts;
315  int Mult = (playMode == pmSlow && playDir == pdForward) ? 1 : SPEED_MULT;
316  int sp = (Speeds[nts] > 0) ? Mult / Speeds[nts] : -Speeds[nts] * Mult;
317  if (sp > MAX_VIDEO_SLOWMOTION)
319  DeviceTrickSpeed(sp);
320  }
321 }
322 
324 {
325  LOCK_THREAD;
328  if (!firstPacket) // don't set the readIndex twice if Empty() is called more than once
329  readIndex = ptsIndex.FindIndex(DeviceGetSTC()) - 1; // Action() will first increment it!
330  delete readFrame; // might not have been stored in the buffer in Action()
331  readFrame = NULL;
332  playFrame = NULL;
333  dropFrame = NULL;
334  ringBuffer->Clear();
335  ptsIndex.Clear();
336  DeviceClear();
337  firstPacket = true;
338 }
339 
340 bool cDvbPlayer::NextFile(uint16_t FileNumber, off_t FileOffset)
341 {
342  if (FileNumber > 0)
343  replayFile = fileName->SetOffset(FileNumber, FileOffset);
344  else if (replayFile && eof)
346  eof = false;
347  return replayFile != NULL;
348 }
349 
351 {
352  if (index) {
353  int Index = index->GetResume();
354  if (Index >= 0) {
355  uint16_t FileNumber;
356  off_t FileOffset;
357  if (index->Get(Index, &FileNumber, &FileOffset) && NextFile(FileNumber, FileOffset))
358  return Index;
359  }
360  }
361  return -1;
362 }
363 
365 {
366  if (index) {
367  int Index = ptsIndex.FindIndex(DeviceGetSTC());
368  if (Index >= 0) {
369  int backup = int(round(RESUMEBACKUP * framesPerSecond));
370  // set resume position to 0 if replay stops at the first mark
371  if (Setup.PlayJump && marks.First() &&
372  abs(Index - marks.First()->Position()) <= backup)
373  Index = 0;
374  if (Index >= index->Last() - backup)
375  Index = 0;
376  else {
377  Index -= backup;
378  if (Index > 0)
379  Index = index->GetNextIFrame(Index, false);
380  else
381  Index = 0;
382  }
383  if (Index >= 0)
384  return index->StoreResume(Index);
385  }
386  }
387  return false;
388 }
389 
390 void cDvbPlayer::Activate(bool On)
391 {
392  if (On) {
393  if (replayFile)
394  Start();
395  }
396  else
397  Cancel(9);
398 }
399 
401 {
402  uchar *p = NULL;
403  int pc = 0;
404  bool cutIn = false;
405  int total = -1;
406 
407  readIndex = Resume();
408  if (readIndex >= 0)
409  isyslog("resuming replay at index %d (%s)", readIndex, *IndexToHMSF(readIndex, true, framesPerSecond));
410 
411  if (Setup.PlayJump && readIndex <= 0 && marks.First() && index) {
412  int Index = marks.First()->Position();
413  uint16_t FileNumber;
414  off_t FileOffset;
415  if (index->Get(Index, &FileNumber, &FileOffset) &&
416  NextFile(FileNumber, FileOffset)) {
417  isyslog("PlayJump: start replay at first mark %d (%s)",
418  Index, *IndexToHMSF(Index, true, framesPerSecond));
419  readIndex = Index;
420  }
421  }
422 
423  bool LastMarkPause = false;
425  int Length = 0;
426  bool Sleep = false;
427  bool WaitingForData = false;
428  time_t StuckAtEof = 0;
429  uint32_t LastStc = 0;
430  int LastReadIFrame = -1;
431  int SwitchToPlayFrame = 0;
432 
433  if (pauseLive)
434  Goto(0, true);
435  while (Running()) {
436  if (WaitingForData)
437  WaitingForData = !nonBlockingFileReader->WaitForDataMs(3); // this keeps the CPU load low, but reacts immediately on new data
438  else if (Sleep) {
439  cPoller Poller;
440  DevicePoll(Poller, 10);
441  Sleep = false;
442  if (playMode == pmStill || playMode == pmPause)
444  }
445  {
446  LOCK_THREAD;
447 
448  // Read the next frame from the file:
449 
450  if (playMode != pmStill && playMode != pmPause && !LastMarkPause) {
451  if (!readFrame && (replayFile || readIndex >= 0)) {
452  if (!nonBlockingFileReader->Reading()) {
453  if (!SwitchToPlayFrame && (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward))) {
454  uint16_t FileNumber;
455  off_t FileOffset;
456  bool TimeShiftMode = index->IsStillRecording();
457  int Index = -1;
458  readIndependent = false;
460  if (index->Get(readIndex + 1, &FileNumber, &FileOffset, &readIndependent, &Length))
461  Index = readIndex + 1;
462  }
463  else {
464  int d = int(round(0.4 * framesPerSecond));
465  if (playDir != pdForward)
466  d = -d;
467  int NewIndex = readIndex + d;
468  if (NewIndex <= 0 && readIndex > 0)
469  NewIndex = 1; // make sure the very first frame is delivered
470  NewIndex = index->GetNextIFrame(NewIndex, playDir == pdForward, &FileNumber, &FileOffset, &Length);
471  if (NewIndex < 0 && TimeShiftMode && playDir == pdForward)
472  SwitchToPlayFrame = readIndex;
473  Index = NewIndex;
474  readIndependent = true;
475  }
476  if (Index >= 0) {
477  readIndex = Index;
478  if (!NextFile(FileNumber, FileOffset))
479  continue;
480  }
481  else if (!(TimeShiftMode && playDir == pdForward))
482  eof = true;
483  }
484  else if (index) {
485  uint16_t FileNumber;
486  off_t FileOffset;
488  // check for end mark - jump to next mark or pause
489  readIndex++;
490  marks.Update();
491  cMark *m = marks.Get(readIndex);
492  if (m && (m->Index() & 0x01) != 0) {
493  m = marks.Next(m);
494  int Index;
495  if (m)
496  Index = m->Position();
497  else if (Setup.PauseLastMark) {
498  // pause at last mark
499  isyslog("PauseLastMark: pause at position %d (%s)",
501  LastMarkPause = true;
502  Index = -1;
503  }
504  else if (total == index->Last())
505  // at last mark jump to end of recording
506  Index = index->Last() - 1;
507  else
508  // jump but stay off end of live-recordings
509  Index = index->GetNextIFrame(index->Last() - int(round(MAXSTUCKATEOF * framesPerSecond)), true);
510  // don't jump in edited recordings
511  if (Setup.PlayJump && Index > readIndex &&
512  Index > index->GetNextIFrame(readIndex, true)) {
513  isyslog("PlayJump: %d frames to %d (%s)",
514  Index - readIndex, Index,
515  *IndexToHMSF(Index, true, framesPerSecond));
516  readIndex = Index;
517  cutIn = true;
518  }
519  }
520  readIndex--;
521  }
522  // for detecting growing length of live-recordings
523  if (index->Get(readIndex + 1, &FileNumber, &FileOffset, &readIndependent) && readIndependent)
524  total = index->Last();
525  if (index->Get(readIndex + 1, &FileNumber, &FileOffset, &readIndependent, &Length) && NextFile(FileNumber, FileOffset))
526  readIndex++;
527  else
528  eof = true;
529  }
530  else // allows replay even if the index file is missing
531  Length = MAXFRAMESIZE;
532  if (Length == -1)
533  Length = MAXFRAMESIZE; // this means we read up to EOF (see cIndex)
534  else if (Length > MAXFRAMESIZE) {
535  esyslog("ERROR: frame larger than buffer (%d > %d)", Length, MAXFRAMESIZE);
536  Length = MAXFRAMESIZE;
537  }
538  if (!eof)
540  }
541  if (!eof) {
542  uchar *b = NULL;
543  int r = nonBlockingFileReader->Result(&b);
544  if (r > 0) {
545  WaitingForData = false;
546  uint32_t Pts = 0;
547  if (readIndependent) {
548  Pts = isPesRecording ? PesGetPts(b) : TsGetPts(b, r);
549  LastReadIFrame = readIndex;
550  }
551  readFrame = new cFrame(b, -r, ftUnknown, readIndex, Pts); // hands over b to the ringBuffer
552  }
553  else if (r < 0) {
554  if (errno == EAGAIN)
555  WaitingForData = true;
556  else if (FATALERRNO) {
557  LOG_ERROR;
558  break;
559  }
560  }
561  else
562  eof = true;
563  }
564  }
565 
566  // Store the frame in the buffer:
567 
568  if (readFrame) {
569  if (cutIn) {
570  if (isPesRecording)
572  else
574  cutIn = false;
575  }
576  if (ringBuffer->Put(readFrame))
577  readFrame = NULL;
578  else
579  Sleep = true;
580  }
581  }
582  else
583  Sleep = true;
584 
585  if (dropFrame) {
586  if (!eof || (playDir != pdForward && dropFrame->Index() > 0) || (playDir == pdForward && dropFrame->Index() < readIndex)) {
587  ringBuffer->Drop(dropFrame); // the very first and last frame are continuously repeated to flush data through the device
588  dropFrame = NULL;
589  }
590  }
591 
592  // Get the next frame from the buffer:
593 
594  if (!playFrame) {
595  playFrame = ringBuffer->Get();
596  p = NULL;
597  pc = 0;
598  }
599 
600  // Play the frame:
601 
602  if (playFrame) {
603  if (!p) {
604  p = playFrame->Data();
605  pc = playFrame->Count();
606  if (p) {
607  if (playFrame->Index() >= 0 && playFrame->Pts() != 0)
609  if (firstPacket) {
610  if (isPesRecording) {
611  PlayPes(NULL, 0);
612  cRemux::SetBrokenLink(p, pc);
613  }
614  else
615  PlayTs(NULL, 0);
616  firstPacket = false;
617  }
618  }
619  }
620  if (p) {
621  int w;
622  if (isPesRecording)
623  w = PlayPes(p, pc, playMode != pmPlay && !(playMode == pmSlow && playDir == pdForward) && DeviceIsPlayingVideo());
624  else
625  w = PlayTs(p, pc, playMode != pmPlay && !(playMode == pmSlow && playDir == pdForward) && DeviceIsPlayingVideo());
626  if (w > 0) {
627  p += w;
628  pc -= w;
629  }
630  else if (w < 0 && FATALERRNO)
631  LOG_ERROR;
632  else
633  Sleep = true;
634  }
635  if (pc <= 0) {
637  playFrame = NULL;
638  p = NULL;
639  }
640  }
641  else {
642  if (LastMarkPause) {
643  LastMarkPause = false;
644  playMode = pmPause;
645  }
646  Sleep = true;
647  }
648 
649  // Handle hitting begin/end of recording:
650 
651  if (eof || SwitchToPlayFrame) {
652  bool SwitchToPlay = false;
653  uint32_t Stc = DeviceGetSTC();
654  if (Stc != LastStc)
655  StuckAtEof = 0;
656  else if (!StuckAtEof)
657  StuckAtEof = time(NULL);
658  else if (time(NULL) - StuckAtEof > MAXSTUCKATEOF) {
659  if (playDir == pdForward)
660  break; // automatically stop at end of recording
661  SwitchToPlay = true;
662  }
663  LastStc = Stc;
664  int Index = ptsIndex.FindIndex(Stc);
665  if (playDir == pdForward && !SwitchToPlayFrame) {
666  if (Index >= LastReadIFrame)
667  break; // automatically stop at end of recording
668  }
669  else if (Index <= 0 || SwitchToPlayFrame && Index >= SwitchToPlayFrame)
670  SwitchToPlay = true;
671  if (SwitchToPlay) {
672  if (!SwitchToPlayFrame)
673  Empty();
674  DevicePlay();
675  playMode = pmPlay;
676  playDir = pdForward;
677  SwitchToPlayFrame = 0;
678  }
679  }
680  }
681  }
682 
684  nonBlockingFileReader = NULL;
685  delete nbfr;
686 }
687 
689 {
690  if (playMode == pmPause || playMode == pmStill)
691  Play();
692  else {
693  LOCK_THREAD;
694  if (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward)) {
696  Empty();
697  }
698  DeviceFreeze();
699  playMode = pmPause;
700  }
701 }
702 
704 {
705  if (playMode != pmPlay) {
706  LOCK_THREAD;
707  if (playMode == pmStill || playMode == pmFast || (playMode == pmSlow && playDir == pdBackward)) {
709  Empty();
710  }
711  DevicePlay();
712  playMode = pmPlay;
713  playDir = pdForward;
714  }
715 }
716 
718 {
719  if (index) {
720  switch (playMode) {
721  case pmFast:
722  if (Setup.MultiSpeedMode) {
723  TrickSpeed(playDir == pdForward ? 1 : -1);
724  break;
725  }
726  else if (playDir == pdForward) {
727  Play();
728  break;
729  }
730  // run into pmPlay
731  case pmPlay: {
732  LOCK_THREAD;
734  Empty();
735  if (DeviceIsPlayingVideo())
736  DeviceMute();
737  playMode = pmFast;
738  playDir = pdForward;
741  }
742  break;
743  case pmSlow:
744  if (Setup.MultiSpeedMode) {
745  TrickSpeed(playDir == pdForward ? -1 : 1);
746  break;
747  }
748  else if (playDir == pdForward) {
749  Pause();
750  break;
751  }
752  Empty();
753  // run into pmPause
754  case pmStill:
755  case pmPause:
756  DeviceMute();
757  playMode = pmSlow;
758  playDir = pdForward;
761  break;
762  default: esyslog("ERROR: unknown playMode %d (%s)", playMode, __FUNCTION__);
763  }
764  }
765 }
766 
768 {
769  if (index) {
770  switch (playMode) {
771  case pmFast:
772  if (Setup.MultiSpeedMode) {
773  TrickSpeed(playDir == pdBackward ? 1 : -1);
774  break;
775  }
776  else if (playDir == pdBackward) {
777  Play();
778  break;
779  }
780  // run into pmPlay
781  case pmPlay: {
782  LOCK_THREAD;
783  Empty();
784  if (DeviceIsPlayingVideo())
785  DeviceMute();
786  playMode = pmFast;
790  }
791  break;
792  case pmSlow:
793  if (Setup.MultiSpeedMode) {
794  TrickSpeed(playDir == pdBackward ? -1 : 1);
795  break;
796  }
797  else if (playDir == pdBackward) {
798  Pause();
799  break;
800  }
801  Empty();
802  // run into pmPause
803  case pmStill:
804  case pmPause: {
805  LOCK_THREAD;
806  Empty();
807  DeviceMute();
808  playMode = pmSlow;
812  }
813  break;
814  default: esyslog("ERROR: unknown playMode %d (%s)", playMode, __FUNCTION__);
815  }
816  }
817 }
818 
819 int cDvbPlayer::SkipFrames(int Frames)
820 {
821  if (index && Frames) {
822  int Current, Total;
823  GetIndex(Current, Total, true);
824  int OldCurrent = Current;
825  // As GetNextIFrame() increments/decrements at least once, the
826  // destination frame (= Current + Frames) must be adjusted by
827  // -1/+1 respectively.
828  Current = index->GetNextIFrame(Current + Frames + (Frames > 0 ? -1 : 1), Frames > 0);
829  return Current >= 0 ? Current : OldCurrent;
830  }
831  return -1;
832 }
833 
834 void cDvbPlayer::SkipSeconds(int Seconds)
835 {
836  if (index && Seconds) {
837  LOCK_THREAD;
838  int Index = ptsIndex.FindIndex(DeviceGetSTC());
839  Empty();
840  if (Index >= 0) {
841  Index = max(Index + SecondsToFrames(Seconds, framesPerSecond), 0);
842  if (Index > 0)
843  Index = index->GetNextIFrame(Index, false, NULL, NULL, NULL);
844  if (Index >= 0)
845  readIndex = Index - 1; // Action() will first increment it!
846  }
847  Play();
848  }
849 }
850 
851 void cDvbPlayer::Goto(int Index, bool Still)
852 {
853  if (index) {
854  LOCK_THREAD;
855  Empty();
856  if (++Index <= 0)
857  Index = 1; // not '0', to allow GetNextIFrame() below to work!
858  uint16_t FileNumber;
859  off_t FileOffset;
860  int Length;
861  Index = index->GetNextIFrame(Index, false, &FileNumber, &FileOffset, &Length);
862  if (Index >= 0 && NextFile(FileNumber, FileOffset) && Still) {
863  uchar b[MAXFRAMESIZE];
864  int r = ReadFrame(replayFile, b, Length, sizeof(b));
865  if (r > 0) {
866  if (playMode == pmPause)
867  DevicePlay();
868  DeviceStillPicture(b, r);
869  ptsIndex.Put(isPesRecording ? PesGetPts(b) : TsGetPts(b, r), Index);
870  }
871  playMode = pmStill;
872  }
873  readIndex = Index;
874  }
875 }
876 
877 bool cDvbPlayer::GetIndex(int &Current, int &Total, bool SnapToIFrame)
878 {
879  if (index) {
880  Current = ptsIndex.FindIndex(DeviceGetSTC());
881  if (SnapToIFrame) {
882  int i1 = index->GetNextIFrame(Current + 1, false);
883  int i2 = index->GetNextIFrame(Current, true);
884  Current = (abs(Current - i1) <= abs(Current - i2)) ? i1 : i2;
885  }
886  Total = index->Last();
887  return true;
888  }
889  Current = Total = -1;
890  return false;
891 }
892 
893 bool cDvbPlayer::GetReplayMode(bool &Play, bool &Forward, int &Speed)
894 {
895  Play = (playMode == pmPlay || playMode == pmFast);
896  Forward = (playDir == pdForward);
897  if (playMode == pmFast || playMode == pmSlow)
898  Speed = Setup.MultiSpeedMode ? abs(trickSpeed - NORMAL_SPEED) : 0;
899  else
900  Speed = -1;
901  return true;
902 }
903 
904 // --- cDvbPlayerControl -----------------------------------------------------
905 
906 cDvbPlayerControl::cDvbPlayerControl(const char *FileName, bool PauseLive)
907 :cControl(player = new cDvbPlayer(FileName, PauseLive))
908 {
909 }
910 
912 {
913  Stop();
914 }
915 
917 {
918  return player && player->Active();
919 }
920 
922 {
923  delete player;
924  player = NULL;
925 }
926 
928 {
929  if (player)
930  player->Pause();
931 }
932 
934 {
935  if (player)
936  player->Play();
937 }
938 
940 {
941  if (player)
942  player->Forward();
943 }
944 
946 {
947  if (player)
948  player->Backward();
949 }
950 
952 {
953  if (player)
954  player->SkipSeconds(Seconds);
955 }
956 
958 {
959  if (player)
960  return player->SkipFrames(Frames);
961  return -1;
962 }
963 
964 bool cDvbPlayerControl::GetIndex(int &Current, int &Total, bool SnapToIFrame)
965 {
966  if (player) {
967  player->GetIndex(Current, Total, SnapToIFrame);
968  return true;
969  }
970  return false;
971 }
972 
973 bool cDvbPlayerControl::GetReplayMode(bool &Play, bool &Forward, int &Speed)
974 {
975  return player && player->GetReplayMode(Play, Forward, Speed);
976 }
977 
978 void cDvbPlayerControl::Goto(int Position, bool Still)
979 {
980  if (player)
981  player->Goto(Position, Still);
982 }