]> git.vomp.tv Git - vompclient-marten.git/commitdiff
New player, ffwd/fbwd, iframe navigation stuff
authorChris Tallon <chris@vomp.tv>
Wed, 20 Sep 2006 22:51:24 +0000 (22:51 +0000)
committerChris Tallon <chris@vomp.tv>
Wed, 20 Sep 2006 22:51:24 +0000 (22:51 +0000)
demuxer.cc
demuxer.h
demuxervdr.h
player.cc
player.h
vdr.cc
vdr.h
video.h
videomvp.cc
videomvp.h
vvideorec.cc

index 90db8146ff6f9602503617d3eef5dba9ad141ece..b519960a19f2436a485718c3d4194b3857e1c1fe 100644 (file)
@@ -314,3 +314,35 @@ UINT Demuxer::PESPacket::findPictureHeader()
   }
   return pos-3;
 }
+
+
+// static function for stripping audio from a buffer containing an I Frame and its audio
+UINT Demuxer::stripAudio(UCHAR* buf, UINT len)
+{
+  UINT read_pos = 0, write_pos = 0;
+  UINT pattern, packet_length;
+  if (len < 4) return 0;
+  pattern = (buf[0] << 16) | (buf[1] << 8) | (buf[2]);
+  while (read_pos + 7 <= len)
+  {
+    pattern = ((pattern << 8) & 0xFFFFFFFF) | buf[read_pos+3];
+    if (pattern < 0x000001E0 || pattern > 0x000001EF)
+      read_pos++;
+    else
+    {
+      packet_length = ((buf[read_pos+4] << 8) | (buf[read_pos+5])) + 6;
+      if (read_pos + packet_length > len)
+        read_pos = len;
+      else
+      {
+        if (read_pos != write_pos)
+          memmove(buf+write_pos, buf+read_pos, packet_length);
+        read_pos += packet_length;
+        write_pos += packet_length;
+        pattern = (buf[read_pos] << 16) | (buf[read_pos+1] << 8)
+                                        | (buf[read_pos+2]);
+      }
+    }
+  }
+  return write_pos;
+}
index c4c93927289c7d6fbc1a4d678c051c4cc8bf440d..ecfc011d3fd7e3d8bf64122c16633ae5de23f6f1 100644 (file)
--- a/demuxer.h
+++ b/demuxer.h
@@ -57,7 +57,7 @@ protected:
       UINT submitted;
       virtual void parseDetails();
       UINT findPictureHeader();
-      
+
       static const ULLONG PTS_INVALID;
   };
   friend class PESPacket;
@@ -95,6 +95,9 @@ protected:
       ASPECT_16_9 = 3
     };
 
+    // Strip audio packets from buffer, leaving video only *static function*
+    static UINT stripAudio(UCHAR* buf, UINT len);
+
   protected:
     // General demuxer objects and status indicators
     static Demuxer* instance;
index 65b0e8e06e55dcf8a700d575c1fc9b4255da02e1..ff2d0302487043700b72f0e30233bcd5ecd50932 100644 (file)
@@ -30,10 +30,10 @@ class DemuxerVDR : public Demuxer
 {
   class PESPacketVDR : public PESPacket
   {
-    void parseDetails();    
+    void parseDetails();
   };
   friend class PESPacketVDR;
-    
+
   public:
     DemuxerVDR();
     void reset();
index 7477586590d3c62c7a6bd94f79ac82bc3a5fe7fc..6dfb7da1e1e8ac895969d6e2933485cb2ededdc2 100644 (file)
--- a/player.cc
+++ b/player.cc
@@ -29,18 +29,12 @@ Player::Player(MessageQueue* messageQueue, bool tIsRecording, bool tIsRadio)
   audio = Audio::getInstance();
   video = Video::getInstance();
   logger = Log::getInstance();
+  vdr = VDR::getInstance();
   initted = false;
-  paused = false;
-  playing = false;
-  ffwd = false;
-  fbwd = false;
   lengthBytes = 0;
   lengthFrames = 0;
-  feedPosition = 0;
-  feedMode = MODE_NORMAL;
-  lastRescan = 0;
-//  startTS = 0;
-//  endTS = 0;
+  currentFrameNumber = 0;
+  state = S_STOP;
 
   videoStartup = false;
   preBuffering = false;
@@ -101,26 +95,12 @@ int Player::init()
 int Player::shutdown()
 {
   if (!initted) return 0;
+  switchState(S_STOP);
   initted = false;
 
- // copy of stop
-  if (playing)
-  {
-    playing = false;
-    threadStop();
-    video->stop();
-    video->blank();
-    audio->stop();
-    vfeed.stop();
-
-    afeed.stop();
-    video->reset();
-    demuxer->reset();
-    feedPosition = 0;
-  }
-
   delete demuxer;
   demuxer = NULL;
+
 #ifdef WIN32
   CloseHandle(mutex);
 #endif
@@ -130,20 +110,11 @@ int Player::shutdown()
 
 void Player::setStartFrame(ULONG startFrame)
 {
-  startFrameNum = startFrame;
-  if (startFrameNum)
-  {
-    feedPosition = VDR::getInstance()->positionFromFrameNumber(startFrameNum);
-  }
-  else
-  {
-    feedPosition = 0;
-  }
+  currentFrameNumber = startFrame;
 }
 
 void Player::setLengthBytes(ULLONG length)
 {
-  lastRescan = time(NULL);
   lengthBytes = length;
   logger->log("Player", Log::DEBUG, "Player has received length bytes of %llu", lengthBytes);
 }
@@ -167,297 +138,386 @@ ULONG Player::getCurrentFrameNum()
 
 // ----------------------------------- Externally called events
 
-int Player::play()
+void Player::play()
 {
+  if (!initted) return;
+  if (state == S_PLAY) return;
   lock();
   bool doUnlock = false;
-  int result = playInt(&doUnlock);
+  if (state == S_PAUSE) doUnlock = true;
+  switchState(S_PLAY);
   if (doUnlock) unLock();
-  return result;
 }
 
 void Player::stop()
 {
+  if (!initted) return;
+  if (state == S_STOP) return;
   lock();
   logger->log("Player", Log::DEBUG, "Stop called lock");
-  stopInt();
+  switchState(S_STOP);
   unLock();
 }
 
 void Player::togglePause()
 {
+  if (!initted) return;
   lock();
-  togglePauseInt();
+  if (state == S_PAUSE) switchState(S_PLAY);
+  else switchState(S_PAUSE);
   unLock();
 }
 
 void Player::toggleFastForward()
 {
+  if (!initted) return;
   lock();
-  toggleFastForwardInt();
+  if (state == S_FFWD) switchState(S_PLAY);
+  else switchState(S_FFWD);
   unLock();
 }
 
 void Player::toggleFastBackward()
 {
+  if (!initted) return;
   lock();
-  toggleFastBackwardInt();
+  if (state == S_FBWD) switchState(S_PLAY);
+  else switchState(S_FBWD);
   unLock();
 }
 
 void Player::jumpToPercent(int percent)
 {
   lock();
-  jumpToPercentInt(percent);
+  logger->log("Player", Log::DEBUG, "JUMP TO %i%%", percent);
+  ULONG newFrame = percent * lengthFrames / 100;
+  switchState(S_JUMP, newFrame);
 //  unLock(); - let thread unlock this
 }
 
 void Player::skipForward(int seconds)
 {
   lock();
-  skipForwardInt(seconds);
+  logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
+  ULONG newFrame = getCurrentFrameNum() + (seconds * video->getFPS());
+  if (newFrame > lengthFrames) { switchState(S_PLAY); unLock(); }
+  else switchState(S_JUMP, newFrame);
 //  unLock(); - let thread unlock this
 }
 
 void Player::skipBackward(int seconds)
 {
   lock();
-  skipBackwardInt(seconds);
+  logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
+  long newFrameNum = getCurrentFrameNum() - (seconds * video->getFPS());
+  if (newFrameNum < 0) newFrameNum = 0;
+  switchState(S_JUMP, newFrameNum);
 //  unLock(); - let thread unlock this
 }
 
 // ----------------------------------- Implementations called events
 
-
-int Player::playInt(bool* doUnlock)
-{
-  if (!initted) return 0;
-
-  // If we are just paused, unpause!
-  if (paused)
-  {
-    togglePauseInt();
-    *doUnlock = true;
-    return 1;
-  }
-
-  // If we are fast forwarding, set to normal
-  if (ffwd)
-  {
-    toggleFastForwardInt();
-    return 1;
-  }
-
-  // If we are fast backwarding, set to normal
-  if (fbwd)
-  {
-    toggleFastBackwardInt();
-    return 1;
-  }
-
-  // If we are already playing, ignore. no resyncing to do now!
-  if (playing)
-  {
-    *doUnlock = true;
-    return 1;
-  }
-
-  // Standard play start
-  logger->log("Player", Log::DEBUG, "Standard play start");
-
-  startup = true;
-
-  audio->reset();
-  video->reset();
-  demuxer->reset();
-  if (isRecording)
-  {
-    if (startFrameNum > lengthFrames) startFrameNum = 0;
-    demuxer->setFrameNum(startFrameNum);
-  }
-  if (!isRadio) demuxer->seek();
-
-  videoStartup = true;
-  threadStart();
-
-  if (isRecording)
-  {
-    logger->log("Player", Log::DEBUG, "Immediate play");
-    afeed.start();
-    vfeed.start();
-    video->sync();
-    audio->sync();
-    audio->play();
-    video->pause();
-  }
-  else // do prebuffering
-  {
-    logger->log("Player", Log::DEBUG, "Prebuffering...");
-//    afeed.start();
-//    vfeed.start();
-    preBuffering = true;
-  }
-
-  playing = true;
-  return 1;
-}
-
-void Player::stopInt()
-{
-  if (!initted) return;
-  if (!playing) return;
-
-  if (ffwd || fbwd)
-  {
-    ffwd = false;
-    fbwd = false;
-    afeed.enable();
-    video->unFastForward();
-    audio->systemMuteOff();
-    feedMode = MODE_NORMAL;
-  }
-
-  playing = false;
-  paused = false;
-
-  vfeed.stop();
-  afeed.stop();
-  if (threadIsActive()) threadStop();
-  video->stop();
-  video->blank();
-  audio->stop();
-  audio->unPause();
-  video->reset();
-  demuxer->reset();
-
-  feedPosition = 0;
-}
-
-void Player::togglePauseInt()
-{
-  if (!initted) return;
-  if (!playing) return;
-
-  if (ffwd) toggleFastForwardInt();
-  if (fbwd) toggleFastBackwardInt();
-
-  if (paused)
-  {
-    video->unPause();
-    audio->unPause();
-    paused = false;
-  }
-  else
-  {
-    video->pause();
-    audio->pause();
-    paused = true;
-  }
-}
-
-void Player::toggleFastForwardInt()
+void Player::switchState(UCHAR toState, ULONG jumpFrame)
 {
   if (!initted) return;
-  if (!playing) return;
 
-  if (paused) togglePauseInt();
-  if (fbwd) toggleFastBackwardInt();
+  logger->log("Player", Log::DEBUG, "Switch state from %u to %u", state, toState);
 
-  if (ffwd)
+  switch(state) // current state selector
   {
-    ffwd = false;
-    threadStop();
-    vfeed.stop();
-    afeed.stop();
-    video->stop();
-    audio->stop();
-    video->reset();
-    audio->reset();
-    demuxer->flush();
-    if (!isRadio) demuxer->seek();
-
-    videoStartup = true;
-    afeed.enable();
-    afeed.start();
-    vfeed.start();
-    threadStart();
-    audio->play();
-    video->sync();
-    audio->sync();
-    audio->systemMuteOff();
-    audio->doMuting();
-    fbwd = false;
-  }
-  else
-  {
-    ffwd = true;
-#ifndef WIN32
-    afeed.disable();
-#endif
-    audio->systemMuteOn();
-    video->fastForward();
-  }
-}
-
-void Player::toggleFastBackwardInt()
-{
-  if (!initted) return;
-  if (!playing) return;
-
-  if (paused) togglePauseInt();
-  if (ffwd) toggleFastForwardInt();
-
-  if (fbwd)
-  {
-    fbwd = false;
-    afeed.enable();
-    audio->systemMuteOff();
-
-//    threadStop();
-    feedMode = MODE_NORMAL;
-//    threadStart();
-  }
-  else
-  {
-    fbwd = false;
-#ifndef WIN32
-    afeed.disable();
-#endif
-    audio->systemMuteOn();
-
-    threadStop();
-    feedMode = MODE_BACKWARDS;
-    video->reset();
-    video->play();
-    demuxer->flush();
-    if (!isRadio) demuxer->seek();
-
-    threadStart();
+    case S_PLAY: // from S_PLAY -----------------------------------
+    {
+      switch(toState)
+      {
+        case S_PLAY: // to S_PLAY
+        {
+          return;
+        }
+        case S_PAUSE: // to S_PAUSE
+        {
+          video->pause();
+          audio->pause();
+          state = S_PAUSE;
+          return;
+        }
+        case S_FFWD: // to S_FFWD
+        {
+          currentFrameNumber = getCurrentFrameNum();
+          audio->systemMuteOn();
+          threadStop();
+          vfeed.stop();
+          afeed.stop();
+          demuxer->flush();
+          state = S_FFWD;
+          threadStart();
+          return;
+        }
+        case S_FBWD: // to S_FBWD
+        {
+          currentFrameNumber = getCurrentFrameNum();
+          audio->systemMuteOn();
+          threadStop();
+          vfeed.stop();
+          afeed.stop();
+          demuxer->flush();
+          state = S_FBWD;
+          threadStart();
+          return;
+        }
+        case S_STOP: // to S_STOP
+        {
+          vfeed.stop();
+          afeed.stop();
+          threadStop();
+          video->stop();
+          video->blank();
+          audio->stop();
+          audio->unPause();
+          video->reset();
+          demuxer->reset();
+          state = S_STOP;
+          return;
+        }
+        case S_JUMP: // to S_JUMP
+        {
+          restartAtFrame(jumpFrame);
+          return;
+        }
+      }
+    }
+    case S_PAUSE: // from S_PAUSE -----------------------------------
+    {
+      switch(toState)
+      {
+        case S_PLAY: // to S_PLAY
+        {
+          if (threadIsActive())
+          {
+            video->unPause();
+            audio->unPause();
+            state = S_PLAY;
+          }
+          else
+          {
+            state = S_PLAY;
+            restartAtFrame(currentFrameNumber);
+          }
+
+          return;
+        }
+        case S_PAUSE: // to S_PAUSE
+        {
+          return;
+        }
+        case S_FFWD: // to S_FFWD
+        {
+          currentFrameNumber = getCurrentFrameNum();
+          audio->systemMuteOn();
+          vfeed.stop();
+          afeed.stop();
+          if (threadIsActive()) threadStop();
+          video->unPause();
+          audio->unPause();
+          state = S_FFWD;
+          threadStart();
+          return;
+        }
+        case S_FBWD: // to S_FBWD
+        {
+          currentFrameNumber = getCurrentFrameNum();
+          audio->systemMuteOn();
+          vfeed.stop();
+          afeed.stop();
+          if (threadIsActive()) threadStop();
+          video->unPause();
+          audio->unPause();
+          state = S_FBWD;
+          threadStart();
+          return;
+        }
+        case S_STOP: // to S_STOP
+        {
+          vfeed.stop();
+          afeed.stop();
+          if (threadIsActive()) threadStop();
+          video->stop();
+          video->blank();
+          audio->stop();
+          video->reset();
+          audio->unPause();
+          demuxer->reset();
+          audio->systemMuteOff();
+          state = S_FFWD;
+          return;
+        }
+        case S_JUMP: // to S_JUMP
+        {
+          state = S_PLAY;
+          audio->unPause();
+          restartAtFrame(jumpFrame);
+          return;
+        }
+      }
+    }
+    case S_FFWD: // from S_FFWD -----------------------------------
+    {
+      switch(toState)
+      {
+        case S_PLAY: // to S_PLAY
+        {
+          state = S_PLAY;
+          restartAtFrame(currentFrameNumber);
+          return;
+        }
+        case S_PAUSE: // to S_PAUSE
+        {
+          threadStop();
+          state = S_PAUSE;
+          return;
+        }
+        case S_FFWD: // to S_FFWD
+        {
+          return;
+        }
+        case S_FBWD: // to S_FBWD
+        {
+          threadStop();
+          state = S_FBWD;
+          threadStart();
+          return;
+        }
+        case S_STOP: // to S_STOP
+        {
+          threadStop();
+          video->stop();
+          video->blank();
+          audio->stop();
+          video->reset();
+          demuxer->reset();
+          state = S_STOP;
+          return;
+        }
+        case S_JUMP: // to S_JUMP
+        {
+          state = S_PLAY;
+          restartAtFrame(jumpFrame);
+          return;
+        }
+      }
+    }
+    case S_FBWD: // from S_FBWD -----------------------------------
+    {
+      switch(toState)
+      {
+        case S_PLAY: // to S_PLAY
+        {
+          state = S_PLAY;
+          restartAtFrame(currentFrameNumber);
+          return;
+        }
+        case S_PAUSE: // to S_PAUSE
+        {
+          threadStop();
+          state = S_PAUSE;
+          return;
+        }
+        case S_FFWD: // to S_FFWD
+        {
+          threadStop();
+          state = S_FFWD;
+          threadStart();
+          return;
+        }
+        case S_FBWD: // to S_FBWD
+        {
+          return;
+        }
+        case S_STOP: // to S_STOP
+        {
+          threadStop();
+          video->stop();
+          video->blank();
+          audio->stop();
+          video->reset();
+          demuxer->reset();
+          state = S_STOP;
+          return;
+        }
+        case S_JUMP: // to S_JUMP
+        {
+          state = S_PLAY;
+          restartAtFrame(jumpFrame);
+          return;
+        }
+      }
+    }
+    case S_STOP: // from S_STOP -----------------------------------
+    {
+      switch(toState)
+      {
+        case S_PLAY: // to S_PLAY
+        {
+          startup = true;
+
+          audio->reset();
+          audio->systemMuteOff();
+          video->reset();
+          demuxer->reset();
+          if (isRecording)
+          {
+            // FIXME use restartAtFrame here?
+            if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
+            demuxer->setFrameNum(currentFrameNumber);
+          }
+          if (!isRadio) demuxer->seek();
+
+          videoStartup = true;
+          state = S_PLAY;
+          threadStart();
+
+          if (isRecording)
+          {
+            logger->log("Player", Log::DEBUG, "Immediate play");
+            afeed.start();
+            vfeed.start();
+            video->sync();
+            audio->sync();
+            audio->play();
+            video->pause();
+          }
+          else // do prebuffering
+          {
+            logger->log("Player", Log::DEBUG, "Prebuffering...");
+            preBuffering = true;
+          }
+          return;
+        }
+        case S_PAUSE: // to S_PAUSE
+        {
+          return;
+        }
+        case S_FFWD: // to S_FFWD
+        {
+          return;
+        }
+        case S_FBWD: // to S_FBWD
+        {
+          return;
+        }
+        case S_STOP: // to S_STOP
+        {
+          return;
+        }
+        case S_JUMP: // to S_JUMP
+        {
+          return;
+        }
+      }
+    }
+    // case S_JUMP cannot be selected as a start state because it auto flips to play
   }
 }
 
-void Player::skipForwardInt(int seconds)
-{
-  logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
-  restartAtFrame(getCurrentFrameNum() + (seconds * video->getFPS()));
-}
-
-void Player::skipBackwardInt(int seconds)
-{
-  logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
-  long newFrameNum = getCurrentFrameNum() - (seconds * video->getFPS());
-  if (newFrameNum < 0) newFrameNum = 0;
-  restartAtFrame(newFrameNum);
-}
-
-void Player::jumpToPercentInt(int percent)
-{
-  logger->log("Player", Log::DEBUG, "JUMP TO %i%%", percent);
-  ULONG newFrame = percent * lengthFrames / 100;
-  restartAtFrame(newFrame);
-}
-
-
 // ----------------------------------- Internal functions
 
 void Player::lock()
@@ -483,13 +543,6 @@ void Player::unLock()
 
 void Player::restartAtFrame(ULONG newFrame)
 {
-  if (paused) togglePauseInt();
-  if (ffwd) toggleFastForwardInt();
-
-  ULLONG newPosition = VDR::getInstance()->positionFromFrameNumber(newFrame);
-  if (!VDR::getInstance()->isConnected()) { doConnectionLost(); return; }
-  logger->log("Player", Log::DEBUG, "wantedframe %i feedpos %llu goto %llu", newFrame, feedPosition, newPosition);
-
   vfeed.stop();
   afeed.stop();
   threadStop();
@@ -498,7 +551,7 @@ void Player::restartAtFrame(ULONG newFrame)
   audio->reset();
   demuxer->flush();
   if (!isRadio) demuxer->seek();
-  feedPosition = newPosition;
+  currentFrameNumber = newFrame;
   demuxer->setFrameNum(newFrame);
   videoStartup = true;
   afeed.start();
@@ -509,7 +562,6 @@ void Player::restartAtFrame(ULONG newFrame)
   audio->sync();
   audio->systemMuteOff();
   audio->doMuting();
-  fbwd = false;
 }
 
 void Player::doConnectionLost()
@@ -561,6 +613,7 @@ void Player::call(void* caller)
       video->sync();
       vfeed.release();
       unLock();
+      logger->log("Player", Log::DEBUG, "BANG BANG BANG BANG BANG BANG BANG BANG BANG BANG BANG");
     }
 
     threadSignalNoLock();
@@ -570,14 +623,57 @@ void Player::call(void* caller)
 // ----------------------------------- Feed thread
 
 void Player::threadMethod()
+{
+  // this method used to be simple, the only thing it does
+  // is farm out to threadFeed Live/Play/Scan
+  // All the guff is to support scan hitting one end
+
+  if (isRecording)
+  {
+    if ((state == S_FFWD) || (state == S_FBWD))
+    {
+      threadFeedScan();
+      // if this returns then scan hit one end
+      if (state == S_FFWD) // scan hit the end. stop
+      {
+        threadCheckExit();
+        Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
+        m->message = Message::STOP_PLAYBACK;
+        logger->log("Player", Log::DEBUG, "Posting message to %p...", commandMessageQueue);
+        commandMessageQueue->postMessage(m);
+        logger->log("Player", Log::DEBUG, "Message posted...");
+        return;
+      }
+      // if execution gets to here, threadFeedScan hit the start, go to play mode
+      state = S_PLAY;
+      audio->reset();
+      demuxer->flush();
+      if (!isRadio) demuxer->seek();
+      demuxer->setFrameNum(currentFrameNumber);
+      videoStartup = true;
+      afeed.start();
+      vfeed.start();
+      audio->play();
+      audio->sync();
+      audio->systemMuteOff();
+      audio->doMuting();
+    }
+
+    if (state == S_PLAY) threadFeedPlay();
+  }
+  else
+  {
+    threadFeedLive();
+  }
+}
+
+void Player::threadFeedLive()
 {
   UINT thisRead;
   UINT writeLength;
   UINT thisWrite;
   UINT preBufferTotal = 0;
 
-  VDR* vdr = VDR::getInstance();
-
   UINT askFor;
   while(1)
   {
@@ -587,44 +683,12 @@ void Player::threadMethod()
 
     threadCheckExit();
 
-    // If we havn't rescanned for a while..
-    if (isRecording && ((lastRescan + 60) < time(NULL)))
-    {
-      lengthBytes = vdr->rescanRecording(&lengthFrames);
-      if (!vdr->isConnected()) { doConnectionLost(); return; }
-      logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
-      lastRescan = time(NULL);
-//      setEndTS();
-    }
-
-    if (lengthBytes) // is playing a recording
-    {
-      if (feedPosition >= lengthBytes) break;  // finished playback
-
-      if (startup)
-      {
-        if (startupBlockSize > lengthBytes)
-          askFor = lengthBytes; // is a very small recording!
-        else
-          askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
-      }
-      else
-      {
-        if ((feedPosition + blockSize) > lengthBytes) // last block of recording
-          askFor = lengthBytes - feedPosition;
-        else // normal
-          askFor = blockSize;
-      }
-    }
-    else // is playing live
-    {
-      if (startup)
-        askFor = startupBlockSize; // find audio streams sized block
-      else
-        askFor = blockSize; // normal
-    }
+    if (startup)
+      askFor = startupBlockSize; // find audio streams sized block
+    else
+      askFor = blockSize; // normal
 
-    threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
+    threadBuffer = vdr->getBlock(0, askFor, &thisRead);
 
     if (!vdr->isConnected())
     {
@@ -639,11 +703,6 @@ void Player::threadMethod()
       int a_stream = demuxer->scan(threadBuffer, thisRead);
       demuxer->setAudioStream(a_stream);
       logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
-
-//      setStartTS(thisRead);
-
-//      if (isRecording) setEndTS();
-
       startup = false;
     }
 
@@ -668,22 +727,109 @@ void Player::threadMethod()
       }
     }
 
-    if (feedMode == MODE_NORMAL)
-    {
-      feedPosition += thisRead;
-    }
-    else if (feedMode == MODE_BACKWARDS)
+    threadCheckExit();
+
+    while(writeLength < thisRead)
     {
-      if (feedPosition >= blockSize)
+      thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
+      writeLength += thisWrite;
+
+      if (!thisWrite)
       {
-        feedPosition -= blockSize;
-        if (!isRadio) demuxer->seek();
+        // demuxer is full and can't take anymore
+        threadLock();
+        threadWaitForSignal();
+        threadUnlock();
       }
+
+      threadCheckExit();
+    }
+
+    free(threadBuffer);
+    threadBuffer = NULL;
+
+  }
+
+  logger->log("Player", Log::DEBUG, "Live play failed to start or interrupted");
+
+  if (videoStartup) // oh woe. there never was a stream, I was conned!
+  {
+    videoStartup = false;
+    unLock();
+    MILLISLEEP(500); // I think this will solve a race
+  }
+
+  threadCheckExit();
+
+  Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
+  m->message = Message::STREAM_END;
+  logger->log("Player", Log::DEBUG, "Posting message to %p...", commandMessageQueue);
+  commandMessageQueue->postMessage(m);
+  logger->log("Player", Log::DEBUG, "Message posted...");
+}
+
+void Player::threadFeedPlay()
+{
+  ULLONG feedPosition;
+  UINT thisRead, writeLength, thisWrite, askFor;
+  time_t lastRescan = time(NULL);
+
+  feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
+  if (!vdr->isConnected()) { doConnectionLost(); return; }
+  logger->log("Player", Log::DEBUG, "startFeedPlay: wantedframe %i goto %llu", currentFrameNumber, feedPosition);
+
+
+  while(1)
+  {
+    thisRead = 0;
+    writeLength = 0;
+    thisWrite = 0;
+
+    threadCheckExit();
+
+    // If we havn't rescanned for a while..
+    if ((lastRescan + 60) < time(NULL))
+    {
+      lengthBytes = vdr->rescanRecording(&lengthFrames);
+      if (!vdr->isConnected()) { doConnectionLost(); return; }
+      logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
+      lastRescan = time(NULL);
+    }
+
+    if (feedPosition >= lengthBytes) break;  // finished playback
+
+    if (startup)
+    {
+      if (startupBlockSize > lengthBytes)
+        askFor = lengthBytes; // is a very small recording!
       else
-      {
-        // got to the start of the recording.. revert to play mode? how?
-        feedPosition += thisRead;
-      }
+        askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
+    }
+    else
+    {
+      if ((feedPosition + blockSize) > lengthBytes) // last block of recording
+        askFor = lengthBytes - feedPosition;
+      else // normal
+        askFor = blockSize;
+    }
+
+    threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
+    feedPosition += thisRead;
+
+    if (!vdr->isConnected())
+    {
+      doConnectionLost();
+      return;
+    }
+
+    if (!threadBuffer) break;
+
+    if (startup)
+    {
+      int a_stream = demuxer->scan(threadBuffer, thisRead);
+      demuxer->setAudioStream(a_stream);
+      logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
+      startup = false;
     }
 
     threadCheckExit();
@@ -692,16 +838,13 @@ void Player::threadMethod()
     {
       thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
       writeLength += thisWrite;
-//      logger->log("Player", Log::DEBUG, "Put %i to demuxer", thisWrite);
 
       if (!thisWrite)
       {
-//        logger->log("Player", Log::DEBUG, "DEMUXER FULL!!!");
         // demuxer is full and can't take anymore
         threadLock();
         threadWaitForSignal();
         threadUnlock();
-//        logger->log("Player", Log::DEBUG, "BACK FROM WAIT");
       }
 
       threadCheckExit();
@@ -725,13 +868,38 @@ void Player::threadMethod()
   threadCheckExit();
 
   Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
-  if (lengthBytes) m->message = Message::STOP_PLAYBACK;  // recording
-  else m->message = Message::STREAM_END;                  // live
+  m->message = Message::STOP_PLAYBACK;  // recording
   logger->log("Player", Log::DEBUG, "Posting message to %p...", commandMessageQueue);
   commandMessageQueue->postMessage(m);
   logger->log("Player", Log::DEBUG, "Message posted...");
 }
 
+void Player::threadFeedScan()
+{
+  ULONG direction = 0;
+  ULONG iframeNumber = 0;
+  ULONG iframeLength = 0;
+  ULLONG filePos;
+  UINT amountReceived;
+  UINT videoLength;
+
+  if (state == S_FFWD) direction = 1; // and 0 for backward
+
+  while(1)
+  {
+    if (!vdr->getNextIFrame(currentFrameNumber, direction, &filePos, &iframeNumber, &iframeLength)) break;
+    threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
+    videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
+    video->displayIFrame(threadBuffer, videoLength);
+    currentFrameNumber = iframeNumber;
+    free(threadBuffer);
+    threadBuffer = NULL;
+    threadCheckExit();
+    MILLISLEEP(100);
+  }
+  // scan has hit one end
+}
+
 void Player::threadPostStopCleanup()
 {
   if (threadBuffer)
index 59826bb468742def16a08de15639598209aad96e..c8a1ed0d8b720c50e9a9de489f0317419b492490 100644 (file)
--- a/player.h
+++ b/player.h
@@ -52,7 +52,7 @@ class Player : public Thread_TYPE, public Callback
     void setLengthBytes(ULLONG length);
     void setLengthFrames(ULONG length);
 
-    int play();
+    void play();
     void stop();
     void togglePause();
     void toggleFastForward();
@@ -61,14 +61,19 @@ class Player : public Thread_TYPE, public Callback
     void skipForward(int seconds);
     void skipBackward(int seconds);
 
-    bool isPaused() { return paused; }
-    bool isFfwd() { return ffwd; }
-    bool isFbwd() { return fbwd; }
+    UCHAR getState() { return state; }
     ULONG getCurrentFrameNum();
     ULONG getLengthFrames();
 
     void call(void*); // for callback interface
 
+    const static UCHAR S_PLAY = 1;
+    const static UCHAR S_PAUSE = 2;
+    const static UCHAR S_FFWD = 3;
+    const static UCHAR S_FBWD = 4;
+    const static UCHAR S_STOP = 5;
+    const static UCHAR S_JUMP = 6;
+
 #ifdef DEV
     void test1();
     void test2();
@@ -79,14 +84,11 @@ class Player : public Thread_TYPE, public Callback
     void threadPostStopCleanup();
 
   private:
-    int playInt(bool* doUnlock);
-    void stopInt();
-    void togglePauseInt();
-    void toggleFastForwardInt();
-    void toggleFastBackwardInt();
-    void jumpToPercentInt(int percent);
-    void skipForwardInt(int seconds);
-    void skipBackwardInt(int seconds);
+    void switchState(UCHAR newState, ULONG jumpFrame=0);
+
+    void threadFeedLive();
+    void threadFeedPlay();
+    void threadFeedScan();
 
     void setEndTS();
     void doConnectionLost();
@@ -97,6 +99,7 @@ class Player : public Thread_TYPE, public Callback
     Audio* audio;
     Video* video;
     Demuxer* demuxer;
+    VDR* vdr;
     VFeed vfeed;
     AFeed afeed;
 
@@ -115,22 +118,62 @@ class Player : public Thread_TYPE, public Callback
     void lock();
     void unLock();
 
-    ULONG startFrameNum;
     ULLONG lengthBytes;
     ULONG lengthFrames;
-    ULLONG feedPosition;
-    UCHAR feedMode;
-    time_t lastRescan;
+    ULONG currentFrameNumber;
     UINT blockSize;
     UINT startupBlockSize;
-    const static UCHAR MODE_NORMAL = 1;
-    const static UCHAR MODE_BACKWARDS = 2;
     UCHAR* threadBuffer;
-
-    bool playing;    // As in not stopped, (playing && paused) can == TRUE
-    bool paused;     // Must be in playing state as well
-    bool ffwd;       // Must be in playing state as well
-    bool fbwd;       // Must be in playing state as well
+    UCHAR state;
 };
 
 #endif
+
+
+/*
+
+Possible states:
+
+Play, Pause, FFwd, FBwd, (Stop), [Jump]
+
+                    Possible    Working
+
+Play -> Pause          *           *
+     -> FFwd           *           *
+     -> FBwd           *           *
+     -> Stop           *           *
+     -> Jump           *           *
+
+                                   From prev.play / prev.fast
+
+Pause -> Play          *                *             *
+      -> FFwd          *                *             *
+      -> FBwd          *                *             *
+      -> Stop          *                *             *
+      -> Jump          *                *             *
+
+FFwd -> Play           *           *
+     -> Pause          *           *
+     -> FBwd           *           *
+     -> Stop           *           *
+     -> Jump           *           *
+
+FBwd -> Play           *           *
+     -> Pause          *           *
+     -> FFwd           *           *
+     -> Stop           *           *
+     -> Jump           *           *
+
+Stop -> Play           *           *
+     -> Pause
+     -> FFwd
+     -> FBwd
+     -> Jump
+
+Jump -> Play
+     -> Pause
+     -> FFwd
+     -> FBwd
+     -> Stop
+
+*/
diff --git a/vdr.cc b/vdr.cc
index 5a8db295aaf49ceaed729cf64b06b181c57eb157..26860d43210ee6fefa23e29fcf4b950006e63654 100644 (file)
--- a/vdr.cc
+++ b/vdr.cc
@@ -803,6 +803,55 @@ ULONG VDR::frameNumberFromPosition(ULLONG position)
   return framenumber;
 }
 
+bool VDR::getNextIFrame(ULONG frameNumber, ULONG direction, ULLONG* rfilePosition, ULONG* rframeNumber, ULONG* rframeLength)
+{
+  unsigned long totalLength = 16;
+  UCHAR* buffer = new UCHAR[totalLength];
+
+  *(unsigned long*)&buffer[0] = htonl(totalLength - 4);
+  *(unsigned long*)&buffer[4] = htonl(VDR_GETNEXTIFRAME);
+  *(unsigned long*)&buffer[8] = htonl(frameNumber);
+  *(unsigned long*)&buffer[12] = htonl(direction);
+
+  MUTEX_LOCK(&mutex);
+  if (!connected) { MUTEX_UNLOCK(&mutex); return false; }
+
+  unsigned int a = tcp->sendPacket(buffer, totalLength);
+  delete []buffer;
+
+  if (a != totalLength)
+  {
+    disconnect();
+    MUTEX_UNLOCK(&mutex);
+    return false;
+  }
+
+  if (!getPacket())
+  {
+    MUTEX_UNLOCK(&mutex);
+    return false;
+  }
+
+  if (serverError())
+  {
+    Log::getInstance()->log("VDR", Log::DEBUG, "Detected getNextIFrame error");
+    freePacket();
+    MUTEX_UNLOCK(&mutex);
+    return false;
+  }
+
+  *rfilePosition = extractULLONG();
+  *rframeNumber = extractULONG();
+  *rframeLength = extractULONG();
+
+  freePacket();
+  MUTEX_UNLOCK(&mutex);
+
+  Log::getInstance()->log("VDR", Log::DEBUG, "VDR GNIF said %llu %lu %lu", *rfilePosition, *rframeNumber, *rframeLength);
+
+  return true;
+}
+
 EventList* VDR::getChannelSchedule(ULONG number)
 {
   time_t now;
diff --git a/vdr.h b/vdr.h
index de5a97cc826d5be81d2801779cb32541c18b614f..c3d9f8a000bf35a9982dc25477e13706e9c0eec5 100644 (file)
--- a/vdr.h
+++ b/vdr.h
@@ -121,6 +121,8 @@ class VDR
     ULLONG     rescanRecording(ULONG* lengthFrames);
     ULLONG     positionFromFrameNumber(ULONG frameNumber);
     ULONG      frameNumberFromPosition(ULLONG position);
+    bool       getNextIFrame(ULONG frameNumber, ULONG direction, ULLONG* rfilePosition, ULONG* rframeNumber, ULONG* rframeLength);
+               // Direction: 0=backwards, 1=forwards
 
     ChannelList* getChannelsList(ULONG type);
     int          streamChannel(ULONG number);
@@ -177,6 +179,7 @@ class VDR
     const static ULONG VDR_POSFROMFRAME        = 16;
     const static ULONG VDR_FRAMEFROMPOS        = 17;
     const static ULONG VDR_MOVERECORDING       = 18;
+    const static ULONG VDR_GETNEXTIFRAME       = 19;
 
     int  getPacket();
     void freePacket();
diff --git a/video.h b/video.h
index 026aee2645a104f664247b0ddbc655a7a19dee1b..3842175bba29efce48e6b94223131b2a98614ac5 100644 (file)
--- a/video.h
+++ b/video.h
@@ -64,6 +64,7 @@ class Video: public DrainTarget
     virtual int attachFrameBuffer()=0; // What does this do?
     virtual ULONG timecodeToFrameNumber(ULLONG timecode)=0;
     virtual ULLONG getCurrentTimestamp()=0;
+    virtual void displayIFrame(const UCHAR* buffer, UINT length)=0;
 
     virtual void turnVideoOn(){};
     virtual void turnVideoOff(){};
index 279a1344d00e0ee3fbede4baefcd6956d206fad1..4e2397d70defed428b2f39a3911e4942bd3ef742 100644 (file)
@@ -398,3 +398,8 @@ UINT VideoMVP::DeliverMediaSample(const UCHAR* buffer, UINT* samplepos)
 void VideoMVP::ResetTimeOffsets()
 {
 }
+
+void VideoMVP::displayIFrame(const UCHAR* buffer, UINT length)
+{
+  write(fdVideo, buffer, length);
+}
index b8db9c6ea55de6ddf1f70fe322efcdf22c23a71e..bbcdb49c8c988317a5cc758fba891cc7f9da60c4 100644 (file)
@@ -70,6 +70,7 @@ class VideoMVP : public Video
     int attachFrameBuffer(); // What does this do?
     ULONG timecodeToFrameNumber(ULLONG timecode);
     ULLONG getCurrentTimestamp();
+    void displayIFrame(const UCHAR* buffer, UINT length);
 
     // Writing Data to Videodevice
     virtual void PrepareMediaSample(const MediaPacketList&, UINT samplepos);
index 4b7b27184fc4dbe82d6838b0ee0717f5fbb2f5c7..2b86724acab5332c43ab37460a7ee253c5765075 100644 (file)
@@ -187,6 +187,12 @@ int VVideoRec::handleCommand(int command)
       doBar(0);
       return 2;
     }
+    case Remote::REVERSE:
+    {
+      player->toggleFastBackward();
+      doBar(0);
+      return 2;
+    }
     case Remote::YELLOW:
     {
       doBar(2);
@@ -335,10 +341,11 @@ void VVideoRec::doBar(int action)
   }
   else
   {
-    if (player->isPaused())     w.nextSymbol = WSymbol::PAUSE;
-    else if (player->isFfwd())  w.nextSymbol = WSymbol::FFWD;
-    else if (player->isFbwd())  w.nextSymbol = WSymbol::FBWD;
-    else                        w.nextSymbol = WSymbol::PLAY;
+    UCHAR playerState = player->getState();
+    if (playerState == Player::S_PAUSE)      w.nextSymbol = WSymbol::PAUSE;
+    else if (playerState == Player::S_FFWD)  w.nextSymbol = WSymbol::FFWD;
+    else if (playerState == Player::S_FBWD)  w.nextSymbol = WSymbol::FBWD;
+    else                                     w.nextSymbol = WSymbol::PLAY;
   }
 
   w.draw();