vdr  1.7.27
dvbplayer.c
Go to the documentation of this file.
00001 /*
00002  * dvbplayer.c: The DVB player
00003  *
00004  * See the main source file 'vdr.c' for copyright information and
00005  * how to reach the author.
00006  *
00007  * $Id: dvbplayer.c 2.26 2012/03/12 14:36:55 kls Exp $
00008  */
00009 
00010 #include "dvbplayer.h"
00011 #include <math.h>
00012 #include <stdlib.h>
00013 #include "recording.h"
00014 #include "remux.h"
00015 #include "ringbuffer.h"
00016 #include "thread.h"
00017 #include "tools.h"
00018 
00019 // --- cPtsIndex -------------------------------------------------------------
00020 
00021 #define PTSINDEX_ENTRIES 500
00022 
00023 class cPtsIndex {
00024 private:
00025   struct tPtsIndex {
00026     uint32_t pts; // no need for 33 bit - some devices don't even supply the msb
00027     int index;
00028     };
00029   tPtsIndex pi[PTSINDEX_ENTRIES];
00030   int w, r;
00031   int lastFound;
00032   cMutex mutex;
00033 public:
00034   cPtsIndex(void);
00035   void Clear(void);
00036   void Put(uint32_t Pts, int Index);
00037   int FindIndex(uint32_t Pts);
00038   };
00039 
00040 cPtsIndex::cPtsIndex(void)
00041 {
00042   lastFound = 0;
00043   Clear();
00044 }
00045 
00046 void cPtsIndex::Clear(void)
00047 {
00048   cMutexLock MutexLock(&mutex);
00049   w = r = 0;
00050 }
00051 
00052 void cPtsIndex::Put(uint32_t Pts, int Index)
00053 {
00054   cMutexLock MutexLock(&mutex);
00055   pi[w].pts = Pts;
00056   pi[w].index = Index;
00057   w = (w + 1) % PTSINDEX_ENTRIES;
00058   if (w == r)
00059      r = (r + 1) % PTSINDEX_ENTRIES;
00060 }
00061 
00062 int cPtsIndex::FindIndex(uint32_t Pts)
00063 {
00064   cMutexLock MutexLock(&mutex);
00065   if (w == r)
00066      return lastFound; // list is empty, let's not jump way off the last known position
00067   uint32_t Delta = 0xFFFFFFFF;
00068   int Index = -1;
00069   for (int i = w; i != r; ) {
00070       if (--i < 0)
00071          i = PTSINDEX_ENTRIES - 1;
00072       uint32_t d = pi[i].pts < Pts ? Pts - pi[i].pts : pi[i].pts - Pts;
00073       if (d > 0x7FFFFFFF)
00074          d = 0xFFFFFFFF - d; // handle rollover
00075       if (d < Delta) {
00076          Delta = d;
00077          Index = pi[i].index;
00078          }
00079       }
00080   lastFound = Index;
00081   return Index;
00082 }
00083 
00084 // --- cNonBlockingFileReader ------------------------------------------------
00085 
00086 class cNonBlockingFileReader : public cThread {
00087 private:
00088   cUnbufferedFile *f;
00089   uchar *buffer;
00090   int wanted;
00091   int length;
00092   cCondWait newSet;
00093   cCondVar newDataCond;
00094   cMutex newDataMutex;
00095 protected:
00096   void Action(void);
00097 public:
00098   cNonBlockingFileReader(void);
00099   ~cNonBlockingFileReader();
00100   void Clear(void);
00101   void Request(cUnbufferedFile *File, int Length);
00102   int Result(uchar **Buffer);
00103   bool Reading(void) { return buffer; }
00104   bool WaitForDataMs(int msToWait);
00105   };
00106 
00107 cNonBlockingFileReader::cNonBlockingFileReader(void)
00108 :cThread("non blocking file reader")
00109 {
00110   f = NULL;
00111   buffer = NULL;
00112   wanted = length = 0;
00113   Start();
00114 }
00115 
00116 cNonBlockingFileReader::~cNonBlockingFileReader()
00117 {
00118   newSet.Signal();
00119   Cancel(3);
00120   free(buffer);
00121 }
00122 
00123 void cNonBlockingFileReader::Clear(void)
00124 {
00125   Lock();
00126   f = NULL;
00127   free(buffer);
00128   buffer = NULL;
00129   wanted = length = 0;
00130   Unlock();
00131 }
00132 
00133 void cNonBlockingFileReader::Request(cUnbufferedFile *File, int Length)
00134 {
00135   Lock();
00136   Clear();
00137   wanted = Length;
00138   buffer = MALLOC(uchar, wanted);
00139   f = File;
00140   Unlock();
00141   newSet.Signal();
00142 }
00143 
00144 int cNonBlockingFileReader::Result(uchar **Buffer)
00145 {
00146   LOCK_THREAD;
00147   if (buffer && length == wanted) {
00148      *Buffer = buffer;
00149      buffer = NULL;
00150      return wanted;
00151      }
00152   errno = EAGAIN;
00153   return -1;
00154 }
00155 
00156 void cNonBlockingFileReader::Action(void)
00157 {
00158   while (Running()) {
00159         Lock();
00160         if (f && buffer && length < wanted) {
00161            int r = f->Read(buffer + length, wanted - length);
00162            if (r > 0)
00163               length += r;
00164            else if (r == 0) { // r == 0 means EOF
00165               if (length > 0)
00166                  wanted = length; // already read something, so return the rest
00167               else
00168                  length = wanted = 0; // report EOF
00169               }
00170            else if (FATALERRNO) {
00171               LOG_ERROR;
00172               length = wanted = r; // this will forward the error status to the caller
00173               }
00174            if (length == wanted) {
00175               cMutexLock NewDataLock(&newDataMutex);
00176               newDataCond.Broadcast();
00177               }
00178            }
00179         Unlock();
00180         newSet.Wait(1000);
00181         }
00182 }
00183 
00184 bool cNonBlockingFileReader::WaitForDataMs(int msToWait)
00185 {
00186   cMutexLock NewDataLock(&newDataMutex);
00187   if (buffer && length == wanted)
00188      return true;
00189   return newDataCond.TimedWait(newDataMutex, msToWait);
00190 }
00191 
00192 // --- cDvbPlayer ------------------------------------------------------------
00193 
00194 #define PLAYERBUFSIZE  MEGABYTE(1)
00195 
00196 #define RESUMEBACKUP 10 // number of seconds to back up when resuming an interrupted replay session
00197 #define MAXSTUCKATEOF 3 // max. number of seconds to wait in case the device doesn't play the last frame
00198 
00199 class cDvbPlayer : public cPlayer, cThread {
00200 private:
00201   enum ePlayModes { pmPlay, pmPause, pmSlow, pmFast, pmStill };
00202   enum ePlayDirs { pdForward, pdBackward };
00203   static int Speeds[];
00204   cNonBlockingFileReader *nonBlockingFileReader;
00205   cRingBufferFrame *ringBuffer;
00206   cPtsIndex ptsIndex;
00207   cMarks marks;
00208   cFileName *fileName;
00209   cIndexFile *index;
00210   cUnbufferedFile *replayFile;
00211   double framesPerSecond;
00212   bool isPesRecording;
00213   bool pauseLive;
00214   bool eof;
00215   bool firstPacket;
00216   ePlayModes playMode;
00217   ePlayDirs playDir;
00218   int trickSpeed;
00219   int readIndex;
00220   bool readIndependent;
00221   cFrame *readFrame;
00222   cFrame *playFrame;
00223   cFrame *dropFrame;
00224   void TrickSpeed(int Increment);
00225   void Empty(void);
00226   bool NextFile(uint16_t FileNumber = 0, off_t FileOffset = -1);
00227   int Resume(void);
00228   bool Save(void);
00229 protected:
00230   virtual void Activate(bool On);
00231   virtual void Action(void);
00232 public:
00233   cDvbPlayer(const char *FileName, bool PauseLive);
00234   virtual ~cDvbPlayer();
00235   bool Active(void) { return cThread::Running(); }
00236   void Pause(void);
00237   void Play(void);
00238   void Forward(void);
00239   void Backward(void);
00240   int SkipFrames(int Frames);
00241   void SkipSeconds(int Seconds);
00242   void Goto(int Position, bool Still = false);
00243   virtual double FramesPerSecond(void) { return framesPerSecond; }
00244   virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
00245   virtual bool GetReplayMode(bool &Play, bool &Forward, int &Speed);
00246   };
00247 
00248 #define MAX_VIDEO_SLOWMOTION 63 // max. arg to pass to VIDEO_SLOWMOTION // TODO is this value correct?
00249 #define NORMAL_SPEED  4 // the index of the '1' entry in the following array
00250 #define MAX_SPEEDS    3 // the offset of the maximum speed from normal speed in either direction
00251 #define SPEED_MULT   12 // the speed multiplier
00252 int cDvbPlayer::Speeds[] = { 0, -2, -4, -8, 1, 2, 4, 12, 0 };
00253 
00254 cDvbPlayer::cDvbPlayer(const char *FileName, bool PauseLive)
00255 :cThread("dvbplayer")
00256 {
00257   nonBlockingFileReader = NULL;
00258   ringBuffer = NULL;
00259   index = NULL;
00260   cRecording Recording(FileName);
00261   framesPerSecond = Recording.FramesPerSecond();
00262   isPesRecording = Recording.IsPesRecording();
00263   pauseLive = PauseLive;
00264   eof = false;
00265   firstPacket = true;
00266   playMode = pmPlay;
00267   playDir = pdForward;
00268   trickSpeed = NORMAL_SPEED;
00269   readIndex = -1;
00270   readIndependent = false;
00271   readFrame = NULL;
00272   playFrame = NULL;
00273   dropFrame = NULL;
00274   isyslog("replay %s", FileName);
00275   fileName = new cFileName(FileName, false, false, isPesRecording);
00276   replayFile = fileName->Open();
00277   if (!replayFile)
00278      return;
00279   ringBuffer = new cRingBufferFrame(PLAYERBUFSIZE);
00280   // Create the index file:
00281   index = new cIndexFile(FileName, false, isPesRecording, pauseLive);
00282   if (!index)
00283      esyslog("ERROR: can't allocate index");
00284   else if (!index->Ok()) {
00285      delete index;
00286      index = NULL;
00287      }
00288   else if (PauseLive)
00289      framesPerSecond = cRecording(FileName).FramesPerSecond(); // the fps rate might have changed from the default
00290   marks.Load(FileName, framesPerSecond, isPesRecording);
00291 }
00292 
00293 cDvbPlayer::~cDvbPlayer()
00294 {
00295   Save();
00296   Detach();
00297   delete readFrame; // might not have been stored in the buffer in Action()
00298   delete index;
00299   delete fileName;
00300   delete ringBuffer;
00301 }
00302 
00303 void cDvbPlayer::TrickSpeed(int Increment)
00304 {
00305   int nts = trickSpeed + Increment;
00306   if (Speeds[nts] == 1) {
00307      trickSpeed = nts;
00308      if (playMode == pmFast)
00309         Play();
00310      else
00311         Pause();
00312      }
00313   else if (Speeds[nts]) {
00314      trickSpeed = nts;
00315      int Mult = (playMode == pmSlow && playDir == pdForward) ? 1 : SPEED_MULT;
00316      int sp = (Speeds[nts] > 0) ? Mult / Speeds[nts] : -Speeds[nts] * Mult;
00317      if (sp > MAX_VIDEO_SLOWMOTION)
00318         sp = MAX_VIDEO_SLOWMOTION;
00319      DeviceTrickSpeed(sp);
00320      }
00321 }
00322 
00323 void cDvbPlayer::Empty(void)
00324 {
00325   LOCK_THREAD;
00326   if (nonBlockingFileReader)
00327      nonBlockingFileReader->Clear();
00328   if (!firstPacket) // don't set the readIndex twice if Empty() is called more than once
00329      readIndex = ptsIndex.FindIndex(DeviceGetSTC()) - 1;  // Action() will first increment it!
00330   delete readFrame; // might not have been stored in the buffer in Action()
00331   readFrame = NULL;
00332   playFrame = NULL;
00333   dropFrame = NULL;
00334   ringBuffer->Clear();
00335   ptsIndex.Clear();
00336   DeviceClear();
00337   firstPacket = true;
00338 }
00339 
00340 bool cDvbPlayer::NextFile(uint16_t FileNumber, off_t FileOffset)
00341 {
00342   if (FileNumber > 0)
00343      replayFile = fileName->SetOffset(FileNumber, FileOffset);
00344   else if (replayFile && eof)
00345      replayFile = fileName->NextFile();
00346   eof = false;
00347   return replayFile != NULL;
00348 }
00349 
00350 int cDvbPlayer::Resume(void)
00351 {
00352   if (index) {
00353      int Index = index->GetResume();
00354      if (Index >= 0) {
00355         uint16_t FileNumber;
00356         off_t FileOffset;
00357         if (index->Get(Index, &FileNumber, &FileOffset) && NextFile(FileNumber, FileOffset))
00358            return Index;
00359         }
00360      }
00361   return -1;
00362 }
00363 
00364 bool cDvbPlayer::Save(void)
00365 {
00366   if (index) {
00367      int Index = ptsIndex.FindIndex(DeviceGetSTC());
00368      if (Index >= 0) {
00369         // set resume position to 0 if replay stops at the first mark
00370         if (Setup.PlayJump && marks.First() &&
00371             abs(Index - marks.First()->Position()) <= int(round(RESUMEBACKUP * framesPerSecond)))
00372            Index = 0;
00373         Index -= int(round(RESUMEBACKUP * framesPerSecond));
00374         if (Index > 0)
00375            Index = index->GetNextIFrame(Index, false);
00376         else
00377            Index = 0;
00378         if (Index >= 0)
00379            return index->StoreResume(Index);
00380         }
00381      }
00382   return false;
00383 }
00384 
00385 void cDvbPlayer::Activate(bool On)
00386 {
00387   if (On) {
00388      if (replayFile)
00389         Start();
00390      }
00391   else
00392      Cancel(9);
00393 }
00394 
00395 void cDvbPlayer::Action(void)
00396 {
00397   uchar *p = NULL;
00398   int pc = 0;
00399   bool cutIn = false;
00400   int total = -1;
00401 
00402   readIndex = Resume();
00403   if (readIndex >= 0)
00404      isyslog("resuming replay at index %d (%s)", readIndex, *IndexToHMSF(readIndex, true, framesPerSecond));
00405 
00406   if (Setup.PlayJump && readIndex <= 0 && marks.First() && index) {
00407      int Index = marks.First()->Position();
00408      uint16_t FileNumber;
00409      off_t FileOffset;
00410      if (index->Get(Index, &FileNumber, &FileOffset) &&
00411          NextFile(FileNumber, FileOffset)) {
00412         isyslog("PlayJump: start replay at first mark %d (%s)",
00413                 Index, *IndexToHMSF(Index, true, framesPerSecond));
00414         readIndex = Index;
00415         }
00416      }
00417 
00418   bool LastMarkPause = false;
00419   nonBlockingFileReader = new cNonBlockingFileReader;
00420   int Length = 0;
00421   bool Sleep = false;
00422   bool WaitingForData = false;
00423   time_t StuckAtEof = 0;
00424   uint32_t LastStc = 0;
00425   int LastReadIFrame = -1;
00426   int SwitchToPlayFrame = 0;
00427 
00428   if (pauseLive)
00429      Goto(0, true);
00430   while (Running()) {
00431         if (WaitingForData)
00432            nonBlockingFileReader->WaitForDataMs(3); // this keeps the CPU load low, but reacts immediately on new data
00433         else if (Sleep) {
00434            cPoller Poller;
00435            DevicePoll(Poller, 10);
00436            Sleep = false;
00437            if (playMode == pmStill || playMode == pmPause)
00438               cCondWait::SleepMs(3);
00439            }
00440         {
00441           LOCK_THREAD;
00442 
00443           // Read the next frame from the file:
00444 
00445           if (playMode != pmStill && playMode != pmPause && !LastMarkPause) {
00446              if (!readFrame && (replayFile || readIndex >= 0)) {
00447                 if (!nonBlockingFileReader->Reading()) {
00448                    if (!SwitchToPlayFrame && (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward))) {
00449                       uint16_t FileNumber;
00450                       off_t FileOffset;
00451                       bool TimeShiftMode = index->IsStillRecording();
00452                       int Index = -1;
00453                       readIndependent = false;
00454                       if (DeviceHasIBPTrickSpeed() && playDir == pdForward) {
00455                          if (index->Get(readIndex + 1, &FileNumber, &FileOffset, &readIndependent, &Length))
00456                             Index = readIndex + 1;
00457                          }
00458                       else {
00459                          int d = int(round(0.4 * framesPerSecond));
00460                          if (playDir != pdForward)
00461                             d = -d;
00462                          int NewIndex = readIndex + d;
00463                          if (NewIndex <= 0 && readIndex > 0)
00464                             NewIndex = 1; // make sure the very first frame is delivered
00465                          NewIndex = index->GetNextIFrame(NewIndex, playDir == pdForward, &FileNumber, &FileOffset, &Length);
00466                          if (NewIndex < 0 && TimeShiftMode && playDir == pdForward)
00467                             SwitchToPlayFrame = readIndex;
00468                          Index = NewIndex;
00469                          readIndependent = true;
00470                          }
00471                       if (Index >= 0) {
00472                          readIndex = Index;
00473                          if (!NextFile(FileNumber, FileOffset))
00474                             continue;
00475                          }
00476                       else if (!(TimeShiftMode && playDir == pdForward))
00477                          eof = true;
00478                       }
00479                    else if (index) {
00480                       uint16_t FileNumber;
00481                       off_t FileOffset;
00482                       if (Setup.PlayJump || Setup.PauseLastMark) {
00483                          // check for end mark - jump to next mark or pause
00484                          readIndex++;
00485                          marks.Update();
00486                          cMark *m = marks.Get(readIndex);
00487                          if (m && (m->Index() & 0x01) != 0) {
00488                             m = marks.Next(m);
00489                             int Index;
00490                             if (m)
00491                                Index = m->Position();
00492                             else if (Setup.PauseLastMark) {
00493                                // pause at last mark
00494                                isyslog("PauseLastMark: pause at position %d (%s)",
00495                                        readIndex, *IndexToHMSF(readIndex, true, framesPerSecond));
00496                                LastMarkPause = true;
00497                                Index = -1;
00498                                }
00499                             else if (total == index->Last())
00500                                // at last mark jump to end of recording
00501                                Index = index->Last() - 1;
00502                             else
00503                                // jump but stay off end of live-recordings
00504                                Index = index->GetNextIFrame(index->Last() - int(round(MAXSTUCKATEOF * framesPerSecond)), true);
00505                             // don't jump in edited recordings
00506                             if (Setup.PlayJump && Index > readIndex &&
00507                                 Index > index->GetNextIFrame(readIndex, true)) {
00508                                isyslog("PlayJump: %d frames to %d (%s)",
00509                                        Index - readIndex, Index,
00510                                        *IndexToHMSF(Index, true, framesPerSecond));
00511                                readIndex = Index;
00512                                cutIn = true;
00513                                }
00514                             }
00515                          readIndex--;
00516                       }
00517                       // for detecting growing length of live-recordings
00518                       if (index->Get(readIndex + 1, &FileNumber, &FileOffset, &readIndependent) && readIndependent)
00519                          total = index->Last();
00520                       if (index->Get(readIndex + 1, &FileNumber, &FileOffset, &readIndependent, &Length) && NextFile(FileNumber, FileOffset))
00521                          readIndex++;
00522                       else
00523                          eof = true;
00524                       }
00525                    else // allows replay even if the index file is missing
00526                       Length = MAXFRAMESIZE;
00527                    if (Length == -1)
00528                       Length = MAXFRAMESIZE; // this means we read up to EOF (see cIndex)
00529                    else if (Length > MAXFRAMESIZE) {
00530                       esyslog("ERROR: frame larger than buffer (%d > %d)", Length, MAXFRAMESIZE);
00531                       Length = MAXFRAMESIZE;
00532                       }
00533                    if (!eof)
00534                       nonBlockingFileReader->Request(replayFile, Length);
00535                    }
00536                 if (!eof) {
00537                    uchar *b = NULL;
00538                    int r = nonBlockingFileReader->Result(&b);
00539                    if (r > 0) {
00540                       WaitingForData = false;
00541                       uint32_t Pts = 0;
00542                       if (readIndependent) {
00543                          Pts = isPesRecording ? PesGetPts(b) : TsGetPts(b, r);
00544                          LastReadIFrame = readIndex;
00545                          }
00546                       readFrame = new cFrame(b, -r, ftUnknown, readIndex, Pts); // hands over b to the ringBuffer
00547                       }
00548                    else if (r < 0) {
00549                       if (errno == EAGAIN)
00550                          WaitingForData = true;
00551                       else if (FATALERRNO) {
00552                          LOG_ERROR;
00553                          break;
00554                          }
00555                       }
00556                    else
00557                       eof = true;
00558                    }
00559                 }
00560 
00561              // Store the frame in the buffer:
00562 
00563              if (readFrame) {
00564                 if (cutIn) {
00565                    if (isPesRecording)
00566                       cRemux::SetBrokenLink(readFrame->Data(), readFrame->Count());
00567                    else
00568                       TsSetTeiOnBrokenPackets(readFrame->Data(), readFrame->Count());
00569                    cutIn = false;
00570                    }
00571                 if (ringBuffer->Put(readFrame))
00572                    readFrame = NULL;
00573                 else
00574                    Sleep = true;
00575                 }
00576              }
00577           else
00578              Sleep = true;
00579 
00580           if (dropFrame) {
00581              if (!eof || (playDir != pdForward && dropFrame->Index() > 0) || (playDir == pdForward && dropFrame->Index() < readIndex)) {
00582                 ringBuffer->Drop(dropFrame); // the very first and last frame are continously repeated to flush data through the device
00583                 dropFrame = NULL;
00584                 }
00585              }
00586 
00587           // Get the next frame from the buffer:
00588 
00589           if (!playFrame) {
00590              playFrame = ringBuffer->Get();
00591              p = NULL;
00592              pc = 0;
00593              }
00594 
00595           // Play the frame:
00596 
00597           if (playFrame) {
00598              if (!p) {
00599                 p = playFrame->Data();
00600                 pc = playFrame->Count();
00601                 if (p) {
00602                    if (playFrame->Index() >= 0 && playFrame->Pts() != 0)
00603                       ptsIndex.Put(playFrame->Pts(), playFrame->Index());
00604                    if (firstPacket) {
00605                       if (isPesRecording) {
00606                          PlayPes(NULL, 0);
00607                          cRemux::SetBrokenLink(p, pc);
00608                          }
00609                       else
00610                          PlayTs(NULL, 0);
00611                       firstPacket = false;
00612                       }
00613                    }
00614                 }
00615              if (p) {
00616                 int w;
00617                 if (isPesRecording)
00618                    w = PlayPes(p, pc, playMode != pmPlay && !(playMode == pmSlow && playDir == pdForward) && DeviceIsPlayingVideo());
00619                 else
00620                    w = PlayTs(p, pc, playMode != pmPlay && !(playMode == pmSlow && playDir == pdForward) && DeviceIsPlayingVideo());
00621                 if (w > 0) {
00622                    p += w;
00623                    pc -= w;
00624                    }
00625                 else if (w < 0 && FATALERRNO)
00626                    LOG_ERROR;
00627                 else
00628                    Sleep = true;
00629                 }
00630              if (pc <= 0) {
00631                 dropFrame = playFrame;
00632                 playFrame = NULL;
00633                 p = NULL;
00634                 }
00635              }
00636           else {
00637              if (LastMarkPause) {
00638                 LastMarkPause = false;
00639                 playMode = pmPause;
00640                 }
00641              Sleep = true;
00642              }
00643 
00644           // Handle hitting begin/end of recording:
00645 
00646           if (eof || SwitchToPlayFrame) {
00647              bool SwitchToPlay = false;
00648              uint32_t Stc = DeviceGetSTC();
00649              if (Stc != LastStc)
00650                 StuckAtEof = 0;
00651              else if (!StuckAtEof)
00652                 StuckAtEof = time(NULL);
00653              else if (time(NULL) - StuckAtEof > MAXSTUCKATEOF) {
00654                 if (playDir == pdForward)
00655                    break; // automatically stop at end of recording
00656                 SwitchToPlay = true;
00657                 }
00658              LastStc = Stc;
00659              int Index = ptsIndex.FindIndex(Stc);
00660              if (playDir == pdForward && !SwitchToPlayFrame) {
00661                 if (Index >= LastReadIFrame)
00662                    break; // automatically stop at end of recording
00663                 }
00664              else if (Index <= 0 || SwitchToPlayFrame && Index >= SwitchToPlayFrame)
00665                 SwitchToPlay = true;
00666              if (SwitchToPlay) {
00667                 if (!SwitchToPlayFrame)
00668                    Empty();
00669                 DevicePlay();
00670                 playMode = pmPlay;
00671                 playDir = pdForward;
00672                 SwitchToPlayFrame = 0;
00673                 }
00674              }
00675         }
00676         }
00677 
00678   cNonBlockingFileReader *nbfr = nonBlockingFileReader;
00679   nonBlockingFileReader = NULL;
00680   delete nbfr;
00681 }
00682 
00683 void cDvbPlayer::Pause(void)
00684 {
00685   if (playMode == pmPause || playMode == pmStill)
00686      Play();
00687   else {
00688      LOCK_THREAD;
00689      if (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward)) {
00690         if (!(DeviceHasIBPTrickSpeed() && playDir == pdForward))
00691            Empty();
00692         }
00693      DeviceFreeze();
00694      playMode = pmPause;
00695      }
00696 }
00697 
00698 void cDvbPlayer::Play(void)
00699 {
00700   if (playMode != pmPlay) {
00701      LOCK_THREAD;
00702      if (playMode == pmStill || playMode == pmFast || (playMode == pmSlow && playDir == pdBackward)) {
00703         if (!(DeviceHasIBPTrickSpeed() && playDir == pdForward))
00704            Empty();
00705         }
00706      DevicePlay();
00707      playMode = pmPlay;
00708      playDir = pdForward;
00709     }
00710 }
00711 
00712 void cDvbPlayer::Forward(void)
00713 {
00714   if (index) {
00715      switch (playMode) {
00716        case pmFast:
00717             if (Setup.MultiSpeedMode) {
00718                TrickSpeed(playDir == pdForward ? 1 : -1);
00719                break;
00720                }
00721             else if (playDir == pdForward) {
00722                Play();
00723                break;
00724                }
00725             // run into pmPlay
00726        case pmPlay: {
00727             LOCK_THREAD;
00728             if (!(DeviceHasIBPTrickSpeed() && playDir == pdForward))
00729                Empty();
00730             if (DeviceIsPlayingVideo())
00731                DeviceMute();
00732             playMode = pmFast;
00733             playDir = pdForward;
00734             trickSpeed = NORMAL_SPEED;
00735             TrickSpeed(Setup.MultiSpeedMode ? 1 : MAX_SPEEDS);
00736             }
00737             break;
00738        case pmSlow:
00739             if (Setup.MultiSpeedMode) {
00740                TrickSpeed(playDir == pdForward ? -1 : 1);
00741                break;
00742                }
00743             else if (playDir == pdForward) {
00744                Pause();
00745                break;
00746                }
00747             Empty();
00748             // run into pmPause
00749        case pmStill:
00750        case pmPause:
00751             DeviceMute();
00752             playMode = pmSlow;
00753             playDir = pdForward;
00754             trickSpeed = NORMAL_SPEED;
00755             TrickSpeed(Setup.MultiSpeedMode ? -1 : -MAX_SPEEDS);
00756             break;
00757        default: esyslog("ERROR: unknown playMode %d (%s)", playMode, __FUNCTION__);
00758        }
00759      }
00760 }
00761 
00762 void cDvbPlayer::Backward(void)
00763 {
00764   if (index) {
00765      switch (playMode) {
00766        case pmFast:
00767             if (Setup.MultiSpeedMode) {
00768                TrickSpeed(playDir == pdBackward ? 1 : -1);
00769                break;
00770                }
00771             else if (playDir == pdBackward) {
00772                Play();
00773                break;
00774                }
00775             // run into pmPlay
00776        case pmPlay: {
00777             LOCK_THREAD;
00778             Empty();
00779             if (DeviceIsPlayingVideo())
00780                DeviceMute();
00781             playMode = pmFast;
00782             playDir = pdBackward;
00783             trickSpeed = NORMAL_SPEED;
00784             TrickSpeed(Setup.MultiSpeedMode ? 1 : MAX_SPEEDS);
00785             }
00786             break;
00787        case pmSlow:
00788             if (Setup.MultiSpeedMode) {
00789                TrickSpeed(playDir == pdBackward ? -1 : 1);
00790                break;
00791                }
00792             else if (playDir == pdBackward) {
00793                Pause();
00794                break;
00795                }
00796             Empty();
00797             // run into pmPause
00798        case pmStill:
00799        case pmPause: {
00800             LOCK_THREAD;
00801             Empty();
00802             DeviceMute();
00803             playMode = pmSlow;
00804             playDir = pdBackward;
00805             trickSpeed = NORMAL_SPEED;
00806             TrickSpeed(Setup.MultiSpeedMode ? -1 : -MAX_SPEEDS);
00807             }
00808             break;
00809        default: esyslog("ERROR: unknown playMode %d (%s)", playMode, __FUNCTION__);
00810        }
00811      }
00812 }
00813 
00814 int cDvbPlayer::SkipFrames(int Frames)
00815 {
00816   if (index && Frames) {
00817      int Current, Total;
00818      GetIndex(Current, Total, true);
00819      int OldCurrent = Current;
00820      // As GetNextIFrame() increments/decrements at least once, the
00821      // destination frame (= Current + Frames) must be adjusted by
00822      // -1/+1 respectively.
00823      Current = index->GetNextIFrame(Current + Frames + (Frames > 0 ? -1 : 1), Frames > 0);
00824      return Current >= 0 ? Current : OldCurrent;
00825      }
00826   return -1;
00827 }
00828 
00829 void cDvbPlayer::SkipSeconds(int Seconds)
00830 {
00831   if (index && Seconds) {
00832      LOCK_THREAD;
00833      int Index = ptsIndex.FindIndex(DeviceGetSTC());
00834      Empty();
00835      if (Index >= 0) {
00836         Index = max(Index + SecondsToFrames(Seconds, framesPerSecond), 0);
00837         if (Index > 0)
00838            Index = index->GetNextIFrame(Index, false, NULL, NULL, NULL);
00839         if (Index >= 0)
00840            readIndex = Index - 1; // Action() will first increment it!
00841         }
00842      Play();
00843      }
00844 }
00845 
00846 void cDvbPlayer::Goto(int Index, bool Still)
00847 {
00848   if (index) {
00849      LOCK_THREAD;
00850      Empty();
00851      if (++Index <= 0)
00852         Index = 1; // not '0', to allow GetNextIFrame() below to work!
00853      uint16_t FileNumber;
00854      off_t FileOffset;
00855      int Length;
00856      Index = index->GetNextIFrame(Index, false, &FileNumber, &FileOffset, &Length);
00857      if (Index >= 0 && NextFile(FileNumber, FileOffset) && Still) {
00858         uchar b[MAXFRAMESIZE];
00859         int r = ReadFrame(replayFile, b, Length, sizeof(b));
00860         if (r > 0) {
00861            if (playMode == pmPause)
00862               DevicePlay();
00863            DeviceStillPicture(b, r);
00864            ptsIndex.Put(isPesRecording ? PesGetPts(b) : TsGetPts(b, r), Index);
00865            }
00866         playMode = pmStill;
00867         }
00868      readIndex = Index;
00869      }
00870 }
00871 
00872 bool cDvbPlayer::GetIndex(int &Current, int &Total, bool SnapToIFrame)
00873 {
00874   if (index) {
00875      Current = ptsIndex.FindIndex(DeviceGetSTC());
00876      if (SnapToIFrame) {
00877         int i1 = index->GetNextIFrame(Current + 1, false);
00878         int i2 = index->GetNextIFrame(Current, true);
00879         Current = (abs(Current - i1) <= abs(Current - i2)) ? i1 : i2;
00880         }
00881      Total = index->Last();
00882      return true;
00883      }
00884   Current = Total = -1;
00885   return false;
00886 }
00887 
00888 bool cDvbPlayer::GetReplayMode(bool &Play, bool &Forward, int &Speed)
00889 {
00890   Play = (playMode == pmPlay || playMode == pmFast);
00891   Forward = (playDir == pdForward);
00892   if (playMode == pmFast || playMode == pmSlow)
00893      Speed = Setup.MultiSpeedMode ? abs(trickSpeed - NORMAL_SPEED) : 0;
00894   else
00895      Speed = -1;
00896   return true;
00897 }
00898 
00899 // --- cDvbPlayerControl -----------------------------------------------------
00900 
00901 cDvbPlayerControl::cDvbPlayerControl(const char *FileName, bool PauseLive)
00902 :cControl(player = new cDvbPlayer(FileName, PauseLive))
00903 {
00904 }
00905 
00906 cDvbPlayerControl::~cDvbPlayerControl()
00907 {
00908   Stop();
00909 }
00910 
00911 bool cDvbPlayerControl::Active(void)
00912 {
00913   return player && player->Active();
00914 }
00915 
00916 void cDvbPlayerControl::Stop(void)
00917 {
00918   delete player;
00919   player = NULL;
00920 }
00921 
00922 void cDvbPlayerControl::Pause(void)
00923 {
00924   if (player)
00925      player->Pause();
00926 }
00927 
00928 void cDvbPlayerControl::Play(void)
00929 {
00930   if (player)
00931      player->Play();
00932 }
00933 
00934 void cDvbPlayerControl::Forward(void)
00935 {
00936   if (player)
00937      player->Forward();
00938 }
00939 
00940 void cDvbPlayerControl::Backward(void)
00941 {
00942   if (player)
00943      player->Backward();
00944 }
00945 
00946 void cDvbPlayerControl::SkipSeconds(int Seconds)
00947 {
00948   if (player)
00949      player->SkipSeconds(Seconds);
00950 }
00951 
00952 int cDvbPlayerControl::SkipFrames(int Frames)
00953 {
00954   if (player)
00955      return player->SkipFrames(Frames);
00956   return -1;
00957 }
00958 
00959 bool cDvbPlayerControl::GetIndex(int &Current, int &Total, bool SnapToIFrame)
00960 {
00961   if (player) {
00962      player->GetIndex(Current, Total, SnapToIFrame);
00963      return true;
00964      }
00965   return false;
00966 }
00967 
00968 bool cDvbPlayerControl::GetReplayMode(bool &Play, bool &Forward, int &Speed)
00969 {
00970   return player && player->GetReplayMode(Play, Forward, Speed);
00971 }
00972 
00973 void cDvbPlayerControl::Goto(int Position, bool Still)
00974 {
00975   if (player)
00976      player->Goto(Position, Still);
00977 }