Home

Dokumentation

Impressum

Dokumentation VDR
 

Main Page   Class Hierarchy   Alphabetical List   Data Structures   File List   Data Fields   Globals  

dvbplayer.c

Go to the documentation of this file.
00001 
00010 #include "dvbplayer.h"
00011 #include <stdlib.h>
00012 #include "recording.h"
00013 #include "ringbuffer.h"
00014 #include "thread.h"
00015 #include "tools.h"
00016 
00024 #define AVG_FRAME_SIZE 15000
00025 
00029 #define DVB_BUF_SIZE   (256 * 1024)
00030 
00034 #define BACKTRACE_ENTRIES (DVB_BUF_SIZE / AVG_FRAME_SIZE + 20)
00035 
00036 class cBackTrace {
00037 private:
00038   int index[BACKTRACE_ENTRIES];
00039   int length[BACKTRACE_ENTRIES];
00040   int pos, num;
00041 public:
00042   cBackTrace(void);
00043   void Clear(void);
00044   void Add(int Index, int Length);
00045   int Get(bool Forward);
00046   };
00047 
00048 cBackTrace::cBackTrace(void)
00049 {
00050   Clear();
00051 }
00052 
00053 void cBackTrace::Clear(void)
00054 {
00055   pos = num = 0;
00056 }
00057 
00058 void cBackTrace::Add(int Index, int Length)
00059 {
00060   index[pos] = Index;
00061   length[pos] = Length;
00062   if (++pos >= BACKTRACE_ENTRIES)
00063      pos = 0;
00064   if (num < BACKTRACE_ENTRIES)
00065      num++;
00066 }
00067 
00068 int cBackTrace::Get(bool Forward)
00069 {
00070   int p = pos;
00071   int n = num;
00072 
00076   int l = DVB_BUF_SIZE + (Forward ? 0 : 256 * 1024);
00077   int i = -1;
00078 
00079   while (n && l > 0) {
00080         if (--p < 0)
00081            p = BACKTRACE_ENTRIES - 1;
00082         i = index[p] - 1;
00083         l -= length[p];
00084         n--;
00085         }
00086   return i;
00087 }
00088 
00098 #define VIDEOBUFSIZE  MEGABYTE(1)
00099 
00103 #define RESUMEBACKUP (10 * FRAMESPERSEC)
00104 
00105 class cDvbPlayer : public cPlayer, cThread {
00106 private:
00107   enum ePlayModes { pmPlay, pmPause, pmSlow, pmFast, pmStill };
00108   enum ePlayDirs { pdForward, pdBackward };
00109   static int Speeds[];
00110   cRingBufferFrame *ringBuffer;
00111   cBackTrace *backTrace;
00112   cFileName *fileName;
00113   cIndexFile *index;
00114   int replayFile;
00115   bool eof;
00116   bool active;
00117   bool running;
00118   ePlayModes playMode;
00119   ePlayDirs playDir;
00120   int trickSpeed;
00121   int readIndex, writeIndex;
00122   bool canToggleAudioTrack;
00123   uchar audioTrack;
00124   cFrame *readFrame;
00125   const cFrame *playFrame;
00126   void TrickSpeed(int Increment);
00127   void Empty(void);
00128   void StripAudioPackets(uchar *b, int Length, uchar Except = 0x00);
00129   bool NextFile(uchar FileNumber = 0, int FileOffset = -1);
00130   int Resume(void);
00131   bool Save(void);
00132 protected:
00133   virtual void Activate(bool On);
00134   virtual void Action(void);
00135 public:
00136   cDvbPlayer(const char *FileName);
00137   virtual ~cDvbPlayer();
00138   bool Active(void) { return active; }
00139   void Pause(void);
00140   void Play(void);
00141   void Forward(void);
00142   void Backward(void);
00143   int SkipFrames(int Frames);
00144   void SkipSeconds(int Seconds);
00145   void Goto(int Position, bool Still = false);
00146   virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
00147   virtual bool GetReplayMode(bool &Play, bool &Forward, int &Speed);
00148   virtual int NumAudioTracks(void) const;
00149   virtual const char **GetAudioTracks(int *CurrentTrack = NULL) const;
00150   virtual void SetAudioTrack(int Index);
00151   };
00152 /
00156 #define MAX_VIDEO_SLOWMOTION 63
00157 
00161 #define NORMAL_SPEED  4
00162 
00166 #define MAX_SPEEDS    3
00167 
00171 #define SPEED_MULT   12
00172 int cDvbPlayer::Speeds[] = { 0, -2, -4, -8, 1, 2, 4, 12, 0 };
00173 
00174 cDvbPlayer::cDvbPlayer(const char *FileName)
00175 {
00176   ringBuffer = NULL;
00177   backTrace = NULL;
00178   index = NULL;
00179   eof = false;
00180   active = true;
00181   running = false;
00182   playMode = pmPlay;
00183   playDir = pdForward;
00184   trickSpeed = NORMAL_SPEED;
00185   canToggleAudioTrack = false;
00186   audioTrack = 0xC0;
00187   readIndex = writeIndex = -1;
00188   readFrame = NULL;
00189   playFrame = NULL;
00190   isyslog("replay %s", FileName);
00191   fileName = new cFileName(FileName, false);
00192   replayFile = fileName->Open();
00193   if (replayFile < 0)
00194      return;
00195   ringBuffer = new cRingBufferFrame(VIDEOBUFSIZE);
00196   // Create the index file:
00197   index = new cIndexFile(FileName, false);
00198   if (!index)
00199      esyslog("ERROR: can't allocate index");
00200   else if (!index->Ok()) {
00201      delete index;
00202      index = NULL;
00203      }
00204   backTrace = new cBackTrace;
00205 }
00206 
00207 cDvbPlayer::~cDvbPlayer()
00208 {
00209   Detach();
00210   Save();
00211   delete index;
00212   delete fileName;
00213   delete backTrace;
00214   delete ringBuffer;
00215 }
00216 
00217 void cDvbPlayer::TrickSpeed(int Increment)
00218 {
00219   int nts = trickSpeed + Increment;
00220   if (Speeds[nts] == 1) {
00221      trickSpeed = nts;
00222      if (playMode == pmFast)
00223         Play();
00224      else
00225         Pause();
00226      }
00227   else if (Speeds[nts]) {
00228      trickSpeed = nts;
00229      int Mult = (playMode == pmSlow && playDir == pdForward) ? 1 : SPEED_MULT;
00230      int sp = (Speeds[nts] > 0) ? Mult / Speeds[nts] : -Speeds[nts] * Mult;
00231      if (sp > MAX_VIDEO_SLOWMOTION)
00232         sp = MAX_VIDEO_SLOWMOTION;
00233      DeviceTrickSpeed(sp);
00234      }
00235 }
00236 
00237 void cDvbPlayer::Empty(void)
00238 {
00239   Lock();
00240   if ((readIndex = backTrace->Get(playDir == pdForward)) < 0)
00241      readIndex = writeIndex;
00242   readFrame = NULL;
00243   playFrame = NULL;
00244   ringBuffer->Clear();
00245   backTrace->Clear();
00246   DeviceClear();
00247   Unlock();
00248 }
00249 
00250 void cDvbPlayer::StripAudioPackets(uchar *b, int Length, uchar Except)
00251 {
00252   if (index) {
00253      for (int i = 0; i < Length - 6; i++) {
00254          if (b[i] == 0x00 && b[i + 1] == 0x00 && b[i + 2] == 0x01) {
00255             uchar c = b[i + 3];
00256             int l = b[i + 4] * 256 + b[i + 5] + 6;
00257             switch (c) {
00261               case 0xBD:
00262                    if (Except)
00263                       PlayAudio(&b[i], l);
00264                    // continue with deleting the data - otherwise it disturbs DVB replay
00268               case 0xC0 ... 0xC1:
00269                    if (c == 0xC1)
00270                       canToggleAudioTrack = true;
00271                    if (!Except || c != Except) {
00272                       int n = l;
00273                       for (int j = i; j < Length && n--; j++)
00274                           b[j] = 0x00;
00275                       }
00276                    break;
00280               case 0xE0 ... 0xEF:
00281                    break;
00282               default:
00283                    //esyslog("ERROR: unexpected packet id %02X", c);
00284                    l = 0;
00285               }
00286             if (l)
00287                i += l - 1; // the loop increments, too!
00288             }
00289          /*XXX
00290          else
00291             esyslog("ERROR: broken packet header");
00292             XXX*/
00293          }
00294      }
00295 }
00296 
00297 bool cDvbPlayer::NextFile(uchar FileNumber, int FileOffset)
00298 {
00299   if (FileNumber > 0)
00300      replayFile = fileName->SetOffset(FileNumber, FileOffset);
00301   else if (replayFile >= 0 && eof)
00302      replayFile = fileName->NextFile();
00303   eof = false;
00304   return replayFile >= 0;
00305 }
00306 
00307 int cDvbPlayer::Resume(void)
00308 {
00309   if (index) {
00310      int Index = index->GetResume();
00311      if (Index >= 0) {
00312         uchar FileNumber;
00313         int FileOffset;
00314         if (index->Get(Index, &FileNumber, &FileOffset) && NextFile(FileNumber, FileOffset))
00315            return Index;
00316         }
00317      }
00318   return -1;
00319 }
00320 
00321 bool cDvbPlayer::Save(void)
00322 {
00323   if (index) {
00324      int Index = writeIndex;
00325      if (Index >= 0) {
00326         Index -= RESUMEBACKUP;
00327         if (Index > 0)
00328            Index = index->GetNextIFrame(Index, false);
00329         else
00330            Index = 0;
00331         if (Index >= 0)
00332            return index->StoreResume(Index);
00333         }
00334      }
00335   return false;
00336 }
00337 
00338 void cDvbPlayer::Activate(bool On)
00339 {
00340   if (On) {
00341      if (replayFile >= 0)
00342         Start();
00343      }
00344   else if (active) {
00345      running = false;
00346      Cancel(3);
00347      active = false;
00348      }
00349 }
00350 
00351 void cDvbPlayer::Action(void)
00352 {
00353   dsyslog("dvbplayer thread started (pid=%d)", getpid());
00354 
00355   uchar b[MAXFRAMESIZE];
00356   const uchar *p = NULL;
00357   int pc = 0;
00358 
00359   readIndex = Resume();
00360   if (readIndex >= 0)
00361      isyslog("resuming replay at index %d (%s)", readIndex, IndexToHMSF(readIndex, true));
00362 
00363   running = true;
00364   while (running && (NextFile() || readIndex >= 0 || ringBuffer->Available())) {
00365         cPoller Poller;
00366         if (!readFrame)
00367            Poller.Add(replayFile, false);
00368         if (DevicePoll(Poller, 100)) {
00369 
00370            LOCK_THREAD;
00371 
00376            if (!readFrame && (replayFile >= 0 || readIndex >= 0)) {
00377               if (playMode != pmStill) {
00378                  int r = 0;
00379                  if (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward)) {
00380                     uchar FileNumber;
00381                     int FileOffset, Length;
00382                     int Index = index->GetNextIFrame(readIndex, playDir == pdForward, &FileNumber, &FileOffset, &Length, true);
00383                     if (Index >= 0) {
00384                        if (!NextFile(FileNumber, FileOffset))
00385                           continue;
00386                        }
00387                     else {
00393                        DevicePlay();
00394                        playMode = pmPlay;
00395                        playDir = pdForward;
00396                        continue;
00397                        }
00398                     readIndex = Index;
00399                     r = ReadFrame(replayFile, b, Length, sizeof(b));
00405                     StripAudioPackets(b, r);
00406                     }
00407                  else if (index) {
00408                     uchar FileNumber;
00409                     int FileOffset, Length;
00410                     readIndex++;
00411                     if (!(index->Get(readIndex, &FileNumber, &FileOffset, NULL, &Length) && NextFile(FileNumber, FileOffset))) {
00412                        readIndex = -1;
00413                        eof = true;
00414                        continue;
00415                        }
00416                     r = ReadFrame(replayFile, b, Length, sizeof(b));
00417                     StripAudioPackets(b, r, audioTrack);
00418                     }
00419                  else
00423                     r = read(replayFile, b, sizeof(b));
00424                  if (r > 0)
00425                     readFrame = new cFrame(b, r, ftUnknown, readIndex);
00426                  else if (r == 0)
00427                     eof = true;
00428                  else if (r < 0 && FATALERRNO) {
00429                     LOG_ERROR;
00430                     break;
00431                     }
00432                  }
00433               else//XXX
00437                  usleep(1);
00438               }
00439 
00443            if (readFrame) {
00444               if (ringBuffer->Put(readFrame))
00445                  readFrame = NULL;
00446               }
00447 
00451            if (!playFrame) {
00452               playFrame = ringBuffer->Get();
00453               p = NULL;
00454               pc = 0;
00455               }
00456 
00460            if (playFrame) {
00461               if (!p) {
00462                  p = playFrame->Data();
00463                  pc = playFrame->Count();
00464                  }
00465               if (p) {
00466                  int w = PlayVideo(p, pc);
00467                  if (w > 0) {
00468                     p += w;
00469                     pc -= w;
00470                     }
00471                  else if (w < 0 && FATALERRNO) {
00472                     LOG_ERROR;
00473                     break;
00474                     }
00475                  }
00476               if (pc == 0) {
00477                  writeIndex = playFrame->Index();
00478                  backTrace->Add(playFrame->Index(), playFrame->Count());
00479                  ringBuffer->Drop(playFrame);
00480                  playFrame = NULL;
00481                  p = 0;
00482                  }
00483               }
00484            }
00485         }
00486   active = running = false;
00487 
00488   dsyslog("dvbplayer thread ended (pid=%d)", getpid());
00489 }
00490 
00491 void cDvbPlayer::Pause(void)
00492 {
00493   if (playMode == pmPause || playMode == pmStill)
00494      Play();
00495   else {
00496      LOCK_THREAD;
00497      if (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward))
00498         Empty();
00499      DeviceFreeze();
00500      playMode = pmPause;
00501      }
00502 }
00503 
00504 void cDvbPlayer::Play(void)
00505 {
00506   if (playMode != pmPlay) {
00507      LOCK_THREAD;
00508      if (playMode == pmStill || playMode == pmFast || (playMode == pmSlow && playDir == pdBackward))
00509         Empty();
00510      DevicePlay();
00511      playMode = pmPlay;
00512      playDir = pdForward;
00513     }
00514 }
00515 
00516 void cDvbPlayer::Forward(void)
00517 {
00518   if (index) {
00519      switch (playMode) {
00520        case pmFast:
00521             if (Setup.MultiSpeedMode) {
00522                TrickSpeed(playDir == pdForward ? 1 : -1);
00523                break;
00524                }
00525 
00529             else if (playDir == pdForward) {
00530                Play();
00531                break;
00532                }
00533        case pmPlay: {
00534             LOCK_THREAD;
00535             Empty();
00536             DeviceMute();
00537             playMode = pmFast;
00538             playDir = pdForward;
00539             trickSpeed = NORMAL_SPEED;
00540             TrickSpeed(Setup.MultiSpeedMode ? 1 : MAX_SPEEDS);
00541             }
00542             break;
00543        case pmSlow:
00544             if (Setup.MultiSpeedMode) {
00545                TrickSpeed(playDir == pdForward ? -1 : 1);
00546                break;
00547                }
00548 
00552             else if (playDir == pdForward) {
00553                Pause();
00554                break;
00555                }
00556 
00557        case pmStill:
00558        case pmPause:
00559             DeviceMute();
00560             playMode = pmSlow;
00561             playDir = pdForward;
00562             trickSpeed = NORMAL_SPEED;
00563             TrickSpeed(Setup.MultiSpeedMode ? -1 : -MAX_SPEEDS);
00564             break;
00565        }
00566      }
00567 }
00568 
00569 void cDvbPlayer::Backward(void)
00570 {
00571   if (index) {
00572      switch (playMode) {
00573        case pmFast:
00574             if (Setup.MultiSpeedMode) {
00575                TrickSpeed(playDir == pdBackward ? 1 : -1);
00576                break;
00577                }
00578 
00582             else if (playDir == pdBackward) {
00583                Play();
00584                break;
00585                }
00586 
00587        case pmPlay: {
00588             LOCK_THREAD;
00589             Empty();
00590             DeviceMute();
00591             playMode = pmFast;
00592             playDir = pdBackward;
00593             trickSpeed = NORMAL_SPEED;
00594             TrickSpeed(Setup.MultiSpeedMode ? 1 : MAX_SPEEDS);
00595             }
00596             break;
00597        case pmSlow:
00598             if (Setup.MultiSpeedMode) {
00599                TrickSpeed(playDir == pdBackward ? -1 : 1);
00600                break;
00601                }
00602 
00606             else if (playDir == pdBackward) {
00607                Pause();
00608                break;
00609                }
00610 
00611        case pmStill:
00612        case pmPause: {
00613             LOCK_THREAD;
00614             Empty();
00615             DeviceMute();
00616             playMode = pmSlow;
00617             playDir = pdBackward;
00618             trickSpeed = NORMAL_SPEED;
00619             TrickSpeed(Setup.MultiSpeedMode ? -1 : -MAX_SPEEDS);
00620             }
00621             break;
00622        }
00623      }
00624 }
00625 
00626 int cDvbPlayer::SkipFrames(int Frames)
00627 {
00628   if (index && Frames) {
00629      int Current, Total;
00630      GetIndex(Current, Total, true);
00631      int OldCurrent = Current;
00632      Current = index->GetNextIFrame(Current + Frames, Frames > 0);
00633      return Current >= 0 ? Current : OldCurrent;
00634      }
00635   return -1;
00636 }
00637 
00638 void cDvbPlayer::SkipSeconds(int Seconds)
00639 {
00640   if (index && Seconds) {
00641      LOCK_THREAD;
00642      Empty();
00643      int Index = writeIndex;
00644      if (Index >= 0) {
00645         Index = max(Index + Seconds * FRAMESPERSEC, 0);
00646         if (Index > 0)
00647            Index = index->GetNextIFrame(Index, false, NULL, NULL, NULL, true);
00648         if (Index >= 0)
00649            readIndex = writeIndex = Index - 1; // Action() will first increment it!
00650         }
00651      Play();
00652      }
00653 }
00654 
00655 void cDvbPlayer::Goto(int Index, bool Still)
00656 {
00657   if (index) {
00658      LOCK_THREAD;
00659      Empty();
00660      if (++Index <= 0)
00661         Index = 1; // not '0', to allow GetNextIFrame() below to work!
00662      uchar FileNumber;
00663      int FileOffset, Length;
00664      Index = index->GetNextIFrame(Index, false, &FileNumber, &FileOffset, &Length);
00665      if (Index >= 0 && NextFile(FileNumber, FileOffset) && Still) {
00666         uchar b[MAXFRAMESIZE];
00667         int r = ReadFrame(replayFile, b, Length, sizeof(b));
00668         if (r > 0) {
00669            if (playMode == pmPause)
00670               DevicePlay();
00671            StripAudioPackets(b, r);
00672            DeviceStillPicture(b, r);
00673            }
00674         playMode = pmStill;
00675         }
00676      readIndex = writeIndex = Index;
00677      }
00678 }
00679 
00680 bool cDvbPlayer::GetIndex(int &Current, int &Total, bool SnapToIFrame)
00681 {
00682   if (index) {
00683      if (playMode == pmStill)
00684         Current = max(readIndex, 0);
00685      else {
00686         Current = max(writeIndex, 0);
00687         if (SnapToIFrame) {
00688            int i1 = index->GetNextIFrame(Current + 1, false);
00689            int i2 = index->GetNextIFrame(Current, true);
00690            Current = (abs(Current - i1) <= abs(Current - i2)) ? i1 : i2;
00691            }
00692         }
00693      Total = index->Last();
00694      return true;
00695      }
00696   Current = Total = -1;
00697   return false;
00698 }
00699 
00700 bool cDvbPlayer::GetReplayMode(bool &Play, bool &Forward, int &Speed)
00701 {
00702   Play = (playMode == pmPlay || playMode == pmFast);
00703   Forward = (playDir == pdForward);
00704   if (playMode == pmFast || playMode == pmSlow)
00705      Speed = Setup.MultiSpeedMode ? abs(trickSpeed - NORMAL_SPEED) : 0;
00706   else
00707      Speed = -1;
00708   return true;
00709 }
00710 
00711 int cDvbPlayer::NumAudioTracks(void) const
00712 {
00713   return canToggleAudioTrack ? 2 : 1;
00714 }
00715 
00716 const char **cDvbPlayer::GetAudioTracks(int *CurrentTrack) const
00717 {
00718   if (NumAudioTracks()) {
00719      if (CurrentTrack)
00720         *CurrentTrack = (audioTrack == 0xC0) ? 0 : 1;
00721      static const char *audioTracks1[] = { "Audio 1", NULL };
00722      static const char *audioTracks2[] = { "Audio 1", "Audio 2", NULL };
00723      return NumAudioTracks() > 1 ? audioTracks2 : audioTracks1;
00724      }
00725   return NULL;
00726 }
00727 
00728 void cDvbPlayer::SetAudioTrack(int Index)
00729 {
00730   if ((audioTrack == 0xC0) != (Index == 0)) {
00731      audioTrack = (Index == 1) ? 0xC1 : 0xC0;
00732      Empty();
00733      }
00734 }
00735 
00740 cDvbPlayerControl::cDvbPlayerControl(const char *FileName)
00741 :cControl(player = new cDvbPlayer(FileName))
00742 {
00743 }
00744 
00745 cDvbPlayerControl::~cDvbPlayerControl()
00746 {
00747   Stop();
00748 }
00749 
00750 bool cDvbPlayerControl::Active(void)
00751 {
00752   return player && player->Active();
00753 }
00754 
00755 void cDvbPlayerControl::Stop(void)
00756 {
00757   delete player;
00758   player = NULL;
00759 }
00760 
00761 void cDvbPlayerControl::Pause(void)
00762 {
00763   if (player)
00764      player->Pause();
00765 }
00766 
00767 void cDvbPlayerControl::Play(void)
00768 {
00769   if (player)
00770      player->Play();
00771 }
00772 
00773 void cDvbPlayerControl::Forward(void)
00774 {
00775   if (player)
00776      player->Forward();
00777 }
00778 
00779 void cDvbPlayerControl::Backward(void)
00780 {
00781   if (player)
00782      player->Backward();
00783 }
00784 
00785 void cDvbPlayerControl::SkipSeconds(int Seconds)
00786 {
00787   if (player)
00788      player->SkipSeconds(Seconds);
00789 }
00790 
00791 int cDvbPlayerControl::SkipFrames(int Frames)
00792 {
00793   if (player)
00794      return player->SkipFrames(Frames);
00795   return -1;
00796 }
00797 
00798 bool cDvbPlayerControl::GetIndex(int &Current, int &Total, bool SnapToIFrame)
00799 {
00800   if (player) {
00801      player->GetIndex(Current, Total, SnapToIFrame);
00802      return true;
00803      }
00804   return false;
00805 }
00806 
00807 bool cDvbPlayerControl::GetReplayMode(bool &Play, bool &Forward, int &Speed)
00808 {
00809   return player && player->GetReplayMode(Play, Forward, Speed);
00810 }
00811 
00812 void cDvbPlayerControl::Goto(int Position, bool Still)
00813 {
00814   if (player)
00815      player->Goto(Position, Still);
00816 }

Generated on Wed Feb 5 23:30:08 2003 for VDR by doxygen1.3-rc2