vdr
1.7.27
|
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 }