Rename Player class to PlayerVideoRec. Switch to std::mutex
authorChris Tallon <chris@vomp.tv>
Mon, 24 Feb 2020 18:27:43 +0000 (18:27 +0000)
committerChris Tallon <chris@vomp.tv>
Mon, 24 Feb 2020 18:27:43 +0000 (18:27 +0000)
19 files changed:
objects.mk
osd.cc
osd.h
osddirectfb.cc
osdopengl.cc
osdopenvg.cc
osdwinpixel.cc
osdwinpixel.h
osdwinvector.cc
osdwinvector.h
player.cc [deleted file]
player.h [deleted file]
playerradio.h
playervideorec.cc [new file with mode: 0644]
playervideorec.h [new file with mode: 0644]
vdr.cc
vradiorec.cc
vvideorec.cc
vvideorec.h

index d87d0efff270f24fb15956bc3d43bfabc9ee948c..62d908908b4718295dc79771a26808c950e06fd0 100644 (file)
@@ -1,7 +1,7 @@
 OBJ_COMMON = command.o tcp.o dsock.o thread.o timers.o i18n.o vdp6.o               \
              message.o messagequeue.o wol.o audio.o video.o log.o                  \
              vdr.o recman.o recording.o recinfo.o channel.o rectimer.o event.o     \
-             directory.o mark.o option.o player.o playerradio.o vfeed.o afeed.o    \
+             directory.o mark.o option.o playervideorec.o playerradio.o vfeed.o afeed.o    \
              demuxer.o demuxervdr.o demuxerts.o stream.o osd.o surface.o           \
              region.o colour.o boxstack.o boxx.o tbboxx.o vrecording.o             \
              vinfo.o vquestion.o vrecordinglist.o vrecordinglistclassic.o          \
@@ -22,7 +22,7 @@ OBJ_COMMON = command.o tcp.o dsock.o thread.o timers.o i18n.o vdp6.o
 
 OBJ_RASPBERRY = main.o threadp.o osdopenvg.o                                       \
                 ledraspberry.o videoomx.o audioomx.o imageomx.o                    \
-                wjpegsimple.o inputlinux.o inputcec.o signal.o
+                wjpegsimple.o inputlinux.o inputcec.o
 
 OBJ_WINDOWS = winmain.o threadwin.o inputwin.o ledwin.o videowin.o                 \
               audiowin.o windowsosd.o dsallocator.o dssourcefilter.o dssourcepin.o \
diff --git a/osd.cc b/osd.cc
index ab8bf23f776aa0867f9937047a85a3be03cb9d3f..e497911a28c34405cd14a5e9a4b0a8f5c29b6359 100644 (file)
--- a/osd.cc
+++ b/osd.cc
@@ -28,7 +28,6 @@ Osd::Osd()
 {
   if (instance) return;
   instance = this;
-  initted = 0;
 
   fdOsd = 0;
   screen = NULL;
diff --git a/osd.h b/osd.h
index aef86fa33ac44eea3a47c61c7bf304d22d842ed5..89db75e882d14845db5551c33c739b9950b20cd8 100644 (file)
--- a/osd.h
+++ b/osd.h
@@ -50,7 +50,7 @@ class Osd
 
   protected:
     static Osd* instance;
-    int initted;
+    bool initted{};
     Surface* screen;
     int fdOsd;
 };
index 844288c749ff3af9d47fc1a49297609ba9d2993e..d7980c8df473c3de518080f563e1d54c833798a5 100644 (file)
@@ -78,7 +78,7 @@ int OsdDirectFB::init()
   osd_layer->GetConfiguration(osd_layer,&layer_config);
   
   
-  initted = 1; // must set this here or create surface won't work
+  initted = true; // must set this here or create surface won't work
     /* TODO clean up sizes */
   Video* video = Video::getInstance();
  
@@ -92,7 +92,7 @@ int OsdDirectFB::init()
 int OsdDirectFB::shutdown()
 {
   if (!initted) return 0;
-  initted = 0;
+  initted = false;
   delete screen;
   if (osd_layer) osd_layer->Release(osd_layer);
   if (dfb) dfb->Release(dfb);
index e1986cbed1325973da833b606d27b6e3650bb6a0..2b18a3570562f02e33c1acb33daa46b9f4d8edff 100644 (file)
@@ -192,7 +192,7 @@ int OsdOpenGL::init()
 
   screen->create(video->getScreenWidth(), video->getScreenHeight());
   screen->display();
-  initted = 1; // must set this here or create surface won't work
+  initted = true; // must set this here or create surface won't work
 
   //glGenBuffers(1, &vB);
   //glGenBuffers(1, &iB);
@@ -270,7 +270,7 @@ int OsdOpenGL::shutdown()
 {
   if (!initted) return 0;
   glmutex.lock();
-  initted = 0;
+  initted = false;
   threadStop();
   delete screen;
   screen=NULL;
index 4a2f15f69c40b292484e48231403708f8e1a4856..8101a3d989b76e5c8b59089a3d4be5403fc291fd 100644 (file)
@@ -303,7 +303,7 @@ int OsdOpenVG::init()
   Log::getInstance()->log("OSD", Log::DEBUG, "Making egl current out 1%d",syscall(SYS_gettid));
   eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );
   //Now we will create the Screen
-  initted = 1; // must set this here or create surface won't work
+  initted = true; // must set this here or create surface won't work
 
 
   /*if (((VideoOMX*)Video::getInstance())->initUsingOSDObjects()!=1) { //call Video for init  stuff
@@ -488,7 +488,7 @@ int OsdOpenVG::shutdown()
 
   if (!initted) return 0;
 
-  initted = 0;
+  initted = false;
   Log::getInstance()->log("OSD", Log::DEBUG, "shutdown mark1");
   threadStop();
   Log::getInstance()->log("OSD", Log::DEBUG, "shutdown mark1a");
index a64d34d6cc1ce6e2c085bb0209f460acd275a7b3..16983323956eb32969d77b768e8e8cdcc6308663 100644 (file)
@@ -70,7 +70,7 @@ int OsdWinPixel::init()
   screen = new SurfaceWin(Surface::SCREEN);
   screen->create(video->getScreenWidth(), video->getScreenHeight());
   screen->display();
-  initted = 1; // must set this here or create surface won't work
+  initted = true; // must set this here or create surface won't work
 
   startRenderLoop();
 
@@ -81,7 +81,7 @@ int OsdWinPixel::init()
 int OsdWinPixel::shutdown()
 {
   if (!initted) return 0;
-  initted = 0;
+  initted = false;
   stopRenderLoop();
   shutdownDirect3D9Objects();
  
index a9e41433e15cdbdbcc22b54bc06b4a6d710a7015..8ad2cc659bf35da7ffd17eac18d66993fa809edc 100644 (file)
@@ -39,7 +39,7 @@ class OsdWinPixel : public Osd, public WindowsOsd
     int init();
     int shutdown();
 
-       int isInitialized() { return initted; }
+       bool isInitialized() { return initted; }
 
     int getFD();
 
index f9ef386788696286d13464b20c86180e2e330b14..d6c7b344a5f135faf7a684bda8b6f5d1f1cc61f3 100644 (file)
@@ -398,7 +398,7 @@ int OsdWinVector::init()
 
   loadFont(false);
 
-  initted = 1; // must set this here or create surface won't work
+  initted = true; // must set this here or create surface won't work
 
   startRenderLoop();
 
index 058e2051c1a0e5d78b86fc01361bc82ef54ad1ed..923882a8eccc856a45bfcf3ec80af2c532e8f2b1 100644 (file)
@@ -45,7 +45,7 @@ class OsdWinVector : public OsdVector, public WindowsOsd
     int init();
     int shutdown();
 
-       int isInitialized() { return initted; }
+       bool isInitialized() { return initted; }
 
        void getScreenSize(int &width, int &height);
        void getRealScreenSize(int &width, int &height);
diff --git a/player.cc b/player.cc
deleted file mode 100644 (file)
index 6dba51f..0000000
--- a/player.cc
+++ /dev/null
@@ -1,1434 +0,0 @@
-/*
-    Copyright 2004-2008 Chris Tallon
-
-    This file is part of VOMP.
-
-    VOMP is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    VOMP is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with VOMP.  If not, see <https://www.gnu.org/licenses/>.
-*/
-
-#include "player.h"
-
-#include "log.h"
-#include "audio.h"
-#include "video.h"
-#include "demuxervdr.h"
-#include "demuxerts.h"
-#include "vdr.h"
-#include "messagequeue.h"
-#include "input.h"
-#include "message.h"
-#include "dvbsubtitles.h"
-#include "osdreceiver.h"
-
-#define USER_RESPONSE_TIME 500 // Milliseconds
-
-// ----------------------------------- Called from outside, one offs or info funcs
-
-Player::Player(MessageQueue* tmessageQueue, void* tmessageReceiver, OSDReceiver* tosdReceiver)
-: vfeed(this), afeed(this), tfeed(this)
-{
-  messageQueue = tmessageQueue;
-  messageReceiver = tmessageReceiver;
-  osdReceiver = tosdReceiver;
-  audio = Audio::getInstance();
-  video = Video::getInstance();
-  logger = Log::getInstance();
-  vdr = VDR::getInstance();
-
-  video->turnVideoOn();
-}
-
-Player::~Player()
-{
-  if (initted) shutdown();
-}
-
-int Player::init(bool p_isPesRecording, double framespersecond)
-{
-  if (initted) return 0;
-#ifndef WIN32
-  pthread_mutex_init(&mutex, NULL);
-#else
-  mutex=CreateMutex(NULL,FALSE,NULL);
-#endif
-  is_pesrecording = p_isPesRecording;
-  fps = framespersecond;
-  if (is_pesrecording)
-    demuxer = new DemuxerVDR();
-  else
-    demuxer = new DemuxerTS();
-  if (!demuxer) return 0;
-  subtitles = new DVBSubtitles(osdReceiver);
-  if (!subtitles) return 0;
-
-  teletext = new TeletextDecoderVBIEBU();
-  if (!teletext) return 0;
-  teletext->setRecordigMode(true);
-  unsigned int demux_video_size = 2097152;
-  unsigned int demux_audio_size = 524288;
-  if (video->supportsh264())
-  {
-    demux_video_size *= 5 * 2;
-  }
-  if (audio->maysupportAc3())
-  {
-    //demux_audio_size*=2;
-  }
-  if (!demuxer->init(this, audio, video,teletext, demux_video_size,demux_audio_size,65536, framespersecond, subtitles))
-  {
-    logger->log("Player", Log::ERR, "Demuxer failed to init");
-    shutdown();
-    return 0;
-  }
-
-  vfeed.init();
-  afeed.init();
-  tfeed.init();
-
-  video->stop();
-  video->blank();
-  audio->stop();
-
-  if (Command::getInstance()->getSubDefault())
-    turnSubtitlesOn(true);
-  else
-    turnSubtitlesOn(false);
-
-  initted = true;
-  return 1;
-}
-
-int Player::shutdown()
-{
-  if (!initted) return 0;
-  switchState(S_STOP);
-  initted = false;
-
-  delete demuxer;
-  demuxer = NULL;
-  delete subtitles;
-  subtitles = NULL;
-  delete teletext;
-  teletext = NULL;
-
-#ifdef WIN32
-  CloseHandle(mutex);
-#endif
-
-  return 1;
-}
-
-void Player::setStartFrame(ULONG startFrame)
-{
-  ULONG nextiframeNumber;
-  ULONG iframeLength;
-  ULONG iframeNumber;
-  ULLONG filePos;
-
-  // newFrame could be anywhere, go forwards to next I-Frame
-  if (!vdr->getNextIFrame(startFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
-
-  // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
-  vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
-
-  logger->log("Player", Log::DEBUG, "setStartFrame %lu %lu %lu", startFrame, nextiframeNumber,iframeNumber);
-  currentFrameNumber = iframeNumber;
-}
-
-void Player::setLengthBytes(ULLONG length)
-{
-  lengthBytes = length;
-  logger->log("Player", Log::DEBUG, "Player has received length bytes of %llu", lengthBytes);
-}
-
-void Player::setLengthFrames(ULONG length)
-{
-  lengthFrames = length;
-  logger->log("Player", Log::DEBUG, "Player has received length frames of %lu", lengthFrames);
-}
-
-ULONG Player::getLengthFrames()
-{
-  return lengthFrames;
-}
-
-ULONG Player::getCurrentFrameNum()
-{
-  if (startup) return 0;
-  switch(state)
-  {
-    case S_PLAY:
-    case S_PAUSE_P:
-      return demuxer->getFrameNumFromPTS(video->getCurrentTimestamp());
-    case S_PAUSE_I:
-    case S_FFWD:
-    case S_FBWD:
-      return currentFrameNumber;
-    default:
-      return 0; // shouldn't happen
-  }
-}
-
-bool* Player::getDemuxerMpegAudioChannels()
-{
-  return demuxer->getmpAudioChannels();
-}
-
-bool* Player::getDemuxerAc3AudioChannels()
-{
-  return demuxer->getac3AudioChannels();
-}
-
-bool* Player::getDemuxerSubtitleChannels()
-{
-  return demuxer->getSubtitleChannels();
-}
-
-int Player::getCurrentAudioChannel()
-{
-  if (is_pesrecording)
-    return demuxer->getselAudioChannel();
-  else
-    return dynamic_cast<DemuxerTS*>(demuxer)->getAID();
-}
-
-int Player::getCurrentSubtitleChannel()
-{
-  if (is_pesrecording)
-    return demuxer->getselSubtitleChannel();
-  else
-    return dynamic_cast<DemuxerTS*>(demuxer)->getSubID();
-}
-
-void Player::setSubtitleChannel(int newChannel)
-{
-  if (is_pesrecording)
-    demuxer->setDVBSubtitleStream(newChannel);
-  else
-    dynamic_cast<DemuxerTS*>(demuxer)->setSubID(newChannel);
-}
-
-int *Player::getTeletxtSubtitlePages()
-{
-  return teletext->getSubtitlePages();
-}
-
-void Player::setAudioChannel(int newChannel, int type, int streamtype)
-{
-  if (is_pesrecording)
-  {
-    demuxer->setAudioStream(newChannel);
-    return;
-  }
-  else
-  {
-    dynamic_cast<DemuxerTS*>(demuxer)->setAID(newChannel,type,streamtype,false);
-    return;
-  }
-}
-
-bool Player::toggleSubtitles()
-{
-  if (!subtitlesShowing)
-  {
-    subtitlesShowing = true;
-    subtitles->show();
-  }
-  else
-  {
-    subtitlesShowing = false;
-    subtitles->hide();
-  }
-  return subtitlesShowing;
-}
-
-void  Player::turnSubtitlesOn(bool ison)
-{
- if (ison)
-  {
-    subtitlesShowing = true;
-    subtitles->show();
-  }
-  else
-  {
-    subtitlesShowing = false;
-    subtitles->hide();
-  }
-}
-
-void Player::tellSubtitlesOSDVisible(bool visible)
-{
-  subtitles->setOSDMenuVisibility(visible);
-}
-
-Channel * Player::getDemuxerChannel()
-{
-  if (!is_pesrecording)
-  {
-    return dynamic_cast<DemuxerTS*>(demuxer)->getChannelInfo();
-  }
-  return NULL; //Should not happen!
-}
-
-
-// ----------------------------------- Externally called events
-
-void Player::play()
-{
-  if (!initted) return;
-  if (state == S_PLAY) return;
-  lock();
-
-  bool doUnlock = false;
-  if (state == S_PAUSE_P) doUnlock = true;
-  switchState(S_PLAY);
-  if (doUnlock) unLock();
-}
-
-void Player::playpause()
-{
-  if (!initted) return;
-  lock();
-
-  bool doUnlock = false;
-  if (state==S_PLAY)
-  {
-    doUnlock=true;
-    switchState(S_PAUSE_P);
-  }
-  else
-  {
-    if (state == S_PAUSE_P) doUnlock = true;
-    switchState(S_PLAY);
-  }
-  if (doUnlock) unLock();
-}
-
-void Player::stop()
-{
-  if (!initted) return;
-  if (state == S_STOP) return;
-  lock();
-  logger->log("Player", Log::DEBUG, "Stop called lock");
-  switchState(S_STOP);
-  unLock();
-}
-
-void Player::pause()
-{
-  if (!initted) return;
-  lock();
-
-  if ((state == S_FFWD) || (state == S_FBWD))
-  {
-    switchState(S_PAUSE_I);
-  }
-  else if ((state == S_PAUSE_I) || (state == S_PAUSE_P))
-  {
-    switchState(S_PLAY);
-  }
-  else
-  {
-    switchState(S_PAUSE_P);
-  }
-
-  unLock();
-}
-
-void Player::fastForward()
-{
-  if (!initted) return;
-  lock();
-
-  if (state == S_FFWD)
-  {
-    // change the rate
-    switch(ifactor)
-    {
-      case 4: ifactor = 8; break;
-      case 8: ifactor = 16; break;
-      case 16: ifactor = 32; break;
-      case 32: ifactor = 4; break;
-    }
-  }
-  else
-  {
-    ifactor = 4;
-    switchState(S_FFWD);
-  }
-  unLock();
-}
-
-void Player::fastBackward()
-{
-  if (!initted) return;
-  lock();
-
-  if (state == S_FBWD)
-  {
-    // change the rate
-    switch(ifactor)
-    {
-      case 4: ifactor = 8; break;
-      case 8: ifactor = 16; break;
-      case 16: ifactor = 32; break;
-      case 32: ifactor = 4; break;
-    }
-  }
-  else
-  {
-    ifactor = 4;
-    switchState(S_FBWD);
-  }
-  unLock();
-}
-
-void Player::jumpToPercent(double percent)
-{
-  lock();
-  logger->log("Player", Log::DEBUG, "JUMP TO %f%%", percent);
-  ULONG newFrame = static_cast<ULONG>(percent * lengthFrames / 100);
-  switchState(S_JUMP, newFrame);
-//  unLock(); - let thread unlock this
-}
-
-void Player::jumpToMark(int mark)
-{
-  lock();
-  logger->log("Player", Log::DEBUG, "JUMP TO MARK %i%%", mark);
-  switchState(S_JUMP, mark);
-//  unLock(); - let thread unlock this
-}
-
-void Player::jumpToFrameP(int newFrame)
-{
-  lock();
-  logger->log("Player", Log::DEBUG, "JUMP TO FRAME AND PAUSE %i", newFrame);
-  switchState(S_JUMP_PI, newFrame);
-  unLock();
-}
-
-void Player::skipForward(int seconds)
-{
-  lock();
-  logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
-  ULONG newFrame = getCurrentFrameNum();
-  if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
-  newFrame += static_cast<ULONG>(static_cast<double>(seconds) * fps);
-  if (newFrame > lengthFrames) { switchState(S_PLAY); unLock(); }
-  else switchState(S_JUMP, newFrame);
-//  unLock(); - let thread unlock this
-}
-
-void Player::skipBackward(int seconds)
-{
-  lock();
-  logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
-  long newFrame = getCurrentFrameNum();
-  if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
-  newFrame -= static_cast<ULONG>(static_cast<double>(seconds) * fps);
-  if (newFrame < 0) newFrame = 0;
-  switchState(S_JUMP, newFrame);
-//  unLock(); - let thread unlock this
-}
-
-// ----------------------------------- Implementations called events
-
-void Player::switchState(UCHAR toState, ULONG jumpFrame)
-{
-  if (!initted) return;
-
-  logger->log("Player", Log::DEBUG, "Switch state from %u to %u", state, toState);
-
-  switch(state) // current state selector
-  {
-    case S_PLAY: // from S_PLAY -----------------------------------
-    {
-      switch(toState)
-      {
-        case S_PLAY: // to S_PLAY
-        {
-          return;
-        }
-        case S_PAUSE_P: // to S_PAUSE_P
-        {
-          #ifdef VOMP_PLATFORM_RASPBERRY
-          vfeed.stop(); // can't vfeed during pause
-          #endif
-
-          video->pause();
-          audio->pause();
-          state = S_PAUSE_P;
-          return;
-        }
-        case S_PAUSE_I: // to S_PAUSE_I
-        {
-          // can't occur
-          return;
-        }
-        case S_FFWD: // to S_FFWD
-        {
-          currentFrameNumber = getCurrentFrameNum();
-          audio->systemMuteOn();
-          threadStop();
-          vfeed.stop();
-          afeed.stop();
-          tfeed.stop();
-          subtitles->stop();
-          demuxer->flush();
-          state = S_FFWD;
-          threadStart();
-          return;
-        }
-        case S_FBWD: // to S_FBWD
-        {
-          currentFrameNumber = getCurrentFrameNum();
-          audio->systemMuteOn();
-          threadStop();
-          vfeed.stop();
-          afeed.stop();
-          tfeed.stop();
-          subtitles->stop();
-          demuxer->flush();
-          state = S_FBWD;
-          threadStart();
-          return;
-        }
-        case S_STOP: // to S_STOP
-        {
-          vfeed.stop();
-          afeed.stop();
-          tfeed.stop();
-          subtitles->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_JUMP_PI: // to S_JUMP_PI
-        {
-          audio->systemMuteOn();
-          threadStop();
-          vfeed.stop();
-          afeed.stop();
-          tfeed.stop();
-          subtitles->stop();
-          demuxer->flush();
-          state = S_PAUSE_I;
-          video->reset();
-          video->play();
-          restartAtFramePI(jumpFrame);
-          return;
-        }
-      }
-    }
-    FALLTHROUGH // keep compiler happy (all posibilities return)
-    case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
-    {
-      switch(toState)
-      {
-        case S_PLAY: // to S_PLAY
-        {
-          video->unPause();
-          audio->unPause();
-
-          #ifdef VOMP_PLATFORM_RASPBERRY
-          vfeed.start(false);
-          #endif
-
-          state = S_PLAY;
-          return;
-        }
-        case S_PAUSE_P: // to S_PAUSE_P
-        {
-          return;
-        }
-        case S_PAUSE_I: // to S_PAUSE_I
-        {
-          return;
-        }
-        case S_FFWD: // to S_FFWD
-        {
-          currentFrameNumber = getCurrentFrameNum();
-          audio->systemMuteOn();
-          vfeed.stop();
-          afeed.stop();
-          tfeed.stop();
-          subtitles->stop();
-          threadStop();
-          video->unPause();
-          audio->unPause();
-          state = S_FFWD;
-          threadStart();
-          return;
-        }
-        case S_FBWD: // to S_FBWD
-        {
-          currentFrameNumber = getCurrentFrameNum();
-          audio->systemMuteOn();
-          vfeed.stop();
-          afeed.stop();
-          tfeed.stop();
-          subtitles->stop();
-          threadStop();
-          video->unPause();
-          audio->unPause();
-          state = S_FBWD;
-          threadStart();
-          return;
-        }
-        case S_STOP: // to S_STOP
-        {
-          vfeed.stop();
-          afeed.stop();
-          tfeed.stop();
-          subtitles->stop();
-          threadStop();
-          video->stop();
-          video->blank();
-          audio->stop();
-          video->reset();
-          audio->unPause();
-          demuxer->reset();
-          audio->systemMuteOff();
-          state = S_STOP;
-          return;
-        }
-        case S_JUMP: // to S_JUMP
-        {
-          state = S_PLAY;
-          audio->systemMuteOn();
-          audio->unPause();
-          restartAtFrame(jumpFrame);
-          return;
-        }
-        case S_JUMP_PI: // to S_JUMP_PI
-        {
-          audio->systemMuteOn();
-          audio->unPause();
-          threadStop();
-          vfeed.stop();
-          afeed.stop();
-          tfeed.stop();
-          subtitles->stop();
-          demuxer->flush();
-          state = S_PAUSE_I;
-          video->reset();
-          video->play();
-          restartAtFramePI(jumpFrame);
-          return;
-        }
-      }
-    }
-    FALLTHROUGH // keep compiler happy (all posibilities return)
-    case S_PAUSE_I: // from S_PAUSE_I -----------------------------------
-    {
-      switch(toState)
-      {
-        case S_PLAY: // to S_PLAY
-        {
-          state = S_PLAY;
-          restartAtFrame(currentFrameNumber);
-          return;
-        }
-        case S_PAUSE_P: // to S_PAUSE_P
-        {
-          return;
-        }
-        case S_PAUSE_I: // to S_PAUSE_I
-        {
-          return;
-        }
-        case S_FFWD: // to S_FFWD
-        {
-          state = S_FFWD;
-          threadStart();
-          return;
-        }
-        case S_FBWD: // to S_FBWD
-        {
-          state = S_FBWD;
-          threadStart();
-          return;
-        }
-        case S_STOP: // to S_STOP
-        {
-          video->stop();
-          video->blank();
-          audio->stop();
-          video->reset();
-          demuxer->reset();
-          audio->systemMuteOff();
-          state = S_STOP;
-          return;
-        }
-        case S_JUMP: // to S_JUMP
-        {
-          state = S_PLAY;
-          restartAtFrame(jumpFrame);
-          return;
-        }
-        case S_JUMP_PI: // to S_JUMP_PI
-        {
-          restartAtFramePI(jumpFrame);
-          return;
-        }
-      }
-    }
-    FALLTHROUGH // keep compiler happy (all posibilities return)
-    case S_FFWD: // from S_FFWD -----------------------------------
-    {
-      switch(toState)
-      {
-        case S_PLAY: // to S_PLAY
-        {
-          state = S_PLAY;
-          ULONG stepback = static_cast<ULONG>(USER_RESPONSE_TIME * ifactor * fps / 1000);
-          if (stepback < currentFrameNumber)
-            currentFrameNumber -= stepback;
-          else
-            currentFrameNumber = 0;
-          restartAtFrame(currentFrameNumber);
-          return;
-        }
-        case S_PAUSE_P: // to S_PAUSE_P
-        {
-          // can't occur
-          return;
-        }
-        case S_PAUSE_I: // to S_PAUSE_I
-        {
-          threadStop();
-          state = S_PAUSE_I;
-          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_JUMP_PI: // to S_JUMP_PI
-        {
-          threadStop();
-          state = S_PAUSE_I;
-          restartAtFramePI(jumpFrame);
-          return;
-        }
-      }
-    }
-    FALLTHROUGH // keep compiler happy (all posibilities return)
-    case S_FBWD: // from S_FBWD -----------------------------------
-    {
-      switch(toState)
-      {
-        case S_PLAY: // to S_PLAY
-        {
-          state = S_PLAY;
-          restartAtFrame(currentFrameNumber);
-          return;
-        }
-        case S_PAUSE_P: // to S_PAUSE_P
-        {
-          // can't occur
-          return;
-        }
-        case S_PAUSE_I: // to S_PAUSE_I
-        {
-          threadStop();
-          state = S_PAUSE_I;
-          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_JUMP_PI: // to S_JUMP_PI
-        {
-          threadStop();
-          state = S_PAUSE_I;
-          restartAtFramePI(jumpFrame);
-          return;
-        }
-      }
-    }
-    FALLTHROUGH // keep compiler happy (all posibilities return)
-    case S_STOP: // from S_STOP -----------------------------------
-    {
-      switch(toState)
-      {
-        case S_PLAY: // to S_PLAY
-        {
-          startup = true;
-
-          audio->reset();
-          audio->setStreamType(Audio::MPEG2_PES);
-          audio->systemMuteOff();
-          video->reset();
-          demuxer->reset();
-          // FIXME use restartAtFrame here?
-          if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
-          demuxer->setFrameNum(currentFrameNumber);
-          demuxer->seek();
-          videoStartup = true;
-          state = S_PLAY;
-          threadStart();
-          logger->log("Player", Log::DEBUG, "Immediate play");
-          afeed.start();
-          vfeed.start();
-          tfeed.start();
-          subtitles->start();
-          video->sync();
-          audio->sync();
-          audio->play();
-          video->pause();
-          return;
-        }
-        case S_PAUSE_P: // to S_PAUSE_P
-        {
-          return;
-        }
-        case S_PAUSE_I: // to S_PAUSE_I
-        {
-          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_PI: // to S_JUMP_PI
-        {
-          return;
-        }
-      }
-    }
-    // case S_JUMP cannot be a start state because it auto flips to play
-    // case S_JUMP_PI cannot be a start state because it auto flips to S_PAUSE_I
-  }
-}
-
-// ----------------------------------- Internal functions
-
-void Player::lock()
-{
-#ifndef WIN32
-  pthread_mutex_lock(&mutex);
-  logger->log("Player", Log::DEBUG, "LOCKED");
-
-#else
-   WaitForSingleObject(mutex, INFINITE);
-#endif
-}
-
-void Player::unLock()
-{
-#ifndef WIN32
-  logger->log("Player", Log::DEBUG, "UNLOCKING");
-  pthread_mutex_unlock(&mutex);
-#else
-   ReleaseMutex(mutex);
-#endif
-}
-
-void Player::restartAtFrame(ULONG newFrame)
-{
-  vfeed.stop();
-  afeed.stop();
-  tfeed.stop();
-  subtitles->stop();
-  threadStop();
-  video->stop();
-  video->reset();
-  audio->reset();
-  audio->setStreamType(Audio::MPEG2_PES);
-  demuxer->flush();
-  demuxer->seek();
-  currentFrameNumber = newFrame;
-  demuxer->setFrameNum(newFrame);
-  videoStartup = true;
-  afeed.start();
-  tfeed.start();
-  vfeed.start();
-  subtitles->start();
-  threadStart();
-  audio->play();
-  video->sync();
-  audio->sync();
-  audio->systemMuteOff();
-  audio->doMuting();
-}
-
-
-void Player::restartAtFramePI(ULONG newFrame)
-{
-  ULLONG filePos;
-  ULONG nextiframeNumber;
-  ULONG iframeLength;
-  ULONG iframeNumber;
-
-  UCHAR* buffer;
-  UINT amountReceived;
-  UINT videoLength;
-
-  // newFrame could be anywhere, go forwards to next I-Frame
-  if (!vdr->getNextIFrame(newFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
-
-  // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
-  vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
-
-  buffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
-  if (!vdr->isConnected())
-  {
-    if (buffer) free(buffer);
-    doConnectionLost();
-  }
-  else
-  {
-    videoLength = demuxer->stripAudio(buffer, amountReceived);
-    video->displayIFrame(buffer, videoLength);
-    video->displayIFrame(buffer, videoLength); // If you do it twice, it works :)
-    free(buffer);
-    currentFrameNumber = iframeNumber;
-  }
-}
-
-void Player::doConnectionLost()
-{
-  logger->log("Player", Log::DEBUG, "Connection lost, sending message");
-  Message* m = new Message();
-  m->to = messageReceiver;
-  m->from = this;
-  m->message = Message::PLAYER_EVENT;
-  m->parameter = Player::CONNECTION_LOST;
-  messageQueue->postMessage(m);
-}
-
-// ----------------------------------- Callback
-
-void Player::call(void* caller)
-{
-  if (caller == demuxer)
-  {
-    logger->log("Player", Log::DEBUG, "Callback from demuxer");
-
-    if (video->getTVsize() == Video::ASPECT4X3)
-    {
-      logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
-      return;
-    }
-
-    int parx,pary;
-    int dxCurrentAspect = demuxer->getAspectRatio(&parx,&pary);
-    if (dxCurrentAspect == Demuxer::ASPECT_4_3)
-    {
-      logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
-      video->setAspectRatio(Video::ASPECT4X3,parx,pary);
-
-      Message* m = new Message();
-      m->from = this;
-      m->to = messageReceiver;
-      m->message = Message::PLAYER_EVENT;
-      m->parameter = Player::ASPECT43;
-      messageQueue->postMessage(m);
-    }
-    else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
-    {
-      logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
-      video->setAspectRatio(Video::ASPECT16X9,parx,pary);
-
-      Message* m = new Message();
-      m->from = this;
-      m->to = messageReceiver;
-      m->message = Message::PLAYER_EVENT;
-      m->parameter = Player::ASPECT169;
-      messageQueue->postMessage(m);
-    }
-    else
-    {
-      logger->log("Player", Log::DEBUG, "Demuxer said video is something else... setting it anyway");
-         video->setAspectRatio(static_cast<UCHAR>(dxCurrentAspect), parx, pary);
-    }
-
-  }
-  else
-  {
-    if (videoStartup)
-    {
-      videoStartup = false;
-      video->reset();
-      video->play();
-      video->sync();
-      vfeed.release();
-      unLock();
-    }
-
-    threadSignalNoLock();
-  }
-}
-
-// ----------------------------------- 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 ((state == S_FFWD) || (state == S_FBWD))
-  {
-    if (video->PTSIFramePlayback()) threadPTSFeedScan();
-    else 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->to = messageReceiver;
-      m->from = this;
-      m->message = Message::PLAYER_EVENT;
-      m->parameter = STOP_PLAYBACK;
-      logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
-      messageQueue->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();
-    audio->setStreamType(Audio::MPEG2_PES);
-    demuxer->flush();
-    demuxer->seek();
-    demuxer->setFrameNum(currentFrameNumber);
-    videoStartup = true;
-    afeed.start();
-    tfeed.start();
-    vfeed.start();
-    subtitles->start();
-    audio->play();
-    audio->sync();
-    audio->systemMuteOff();
-    audio->doMuting();
-  }
-
-  if (state == S_PLAY) threadFeedPlay();
-}
-
-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 = static_cast<UINT>(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 = static_cast<UINT>(lengthBytes - feedPosition);
-      else // normal
-        askFor = blockSize;
-    }
-    //logger->log("Player", Log::DEBUG, "Get Block in");
-
-    threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
-    //logger->log("Player", Log::DEBUG, "Get Block out");
-
-    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();
-
-    while(writeLength < thisRead)
-    {
-      //logger->log("Player", Log::DEBUG, "Put in");
-      thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
-      //logger->log("Player", Log::DEBUG, "Put out");
-      writeLength += thisWrite;
-
-      if (!thisWrite)
-      {
-        // demuxer is full and can't take anymore
-        threadLock();
-        if (!threadActive) { threadUnlock(); threadCheckExit(); }
-        threadWaitForSignal();
-        threadUnlock();
-      }
-
-      threadCheckExit();
-    }
-
-    free(threadBuffer);
-    threadBuffer = NULL;
-
-  }
-
-  // end of recording
-  logger->log("Player", Log::DEBUG, "Recording playback ends");
-
-  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->to = messageReceiver;
-  m->from = this;
-  m->message = Message::PLAYER_EVENT;
-  m->parameter = Player::STOP_PLAYBACK;
-  logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
-  messageQueue->postMessage(m);
-}
-
-
-void Player::threadPTSFeedScan()
-{
-  // This method manipulates the PTS instead of waiting, this is for the android devices, maybe later for windows?
-
-  ULONG direction = 0;
-  int dir_fac=-1;
-  ULONG baseFrameNumber = 0;
-  ULONG iframeNumber = 0;
-  ULONG iframeLength = 0;
-  ULONG currentfeedFrameNumber=currentFrameNumber;
-  ULONG firstFrameNumber=currentFrameNumber;
-  ULLONG filePos;
-  UINT amountReceived;
-  UINT videoLength;
-
-  UINT playtime=0;
-
-  int frameTimeOffset = 0; // Time in msec between frames
-
-  if (state == S_FFWD)
-  {
-    direction = 1; // and 0 for backward
-    dir_fac = 1;
-  }
-  video->EnterIframePlayback();
-
-  while(1)
-  {
-    baseFrameNumber = currentfeedFrameNumber;
-
-    threadCheckExit();
-    if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
-       return;
-
-    if (iframeNumber >= lengthFrames) return;
-        // scan has got to the end of what we knew to be there before we started scanning
-
-    baseFrameNumber = iframeNumber;
-
-    frameTimeOffset =(int) ((double)(abs((int)iframeNumber - (int)currentfeedFrameNumber) * 1000) / (fps * (double)ifactor));
-
-    logger->log("Player", Log::DEBUG, "XXX Got frame");
-
-    threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
-
-    if (!vdr->isConnected())
-    {
-      if (threadBuffer) free(threadBuffer);
-      doConnectionLost();
-      break;
-    }
-
-
-    threadCheckExit();
-
-
-    videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
-    demuxer->changeTimes(threadBuffer,videoLength,playtime);
-    int count=0;
-    while (!video->displayIFrame(threadBuffer, videoLength)) // the device might block
-    {
-       MILLISLEEP(20);
-       threadCheckExit();
-       count++;
-       if (count%300==0) {
-               ULLONG cur_time=video->getCurrentTimestamp();
-       //       logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
-               //              firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
-               if (cur_time!=0) {
-                   currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.));
-               }
-       }
-
-    }
-    playtime +=frameTimeOffset;
-    currentfeedFrameNumber = iframeNumber;
-    {
-               ULLONG cur_time=video->getCurrentTimestamp();
-       //       logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
-               //              firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
-               if (cur_time!=0) {
-                   currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.));
-               }
-       }
-
-    free(threadBuffer);
-    threadBuffer = NULL;
-  }
-}
-
-
-
-void Player::threadFeedScan()
-{
-  // This method is actually really simple - get frame from vdr,
-  // spit it at the video chip, wait for a time. Most of the code here
-  // is to get the wait right so that the scan occurs at the correct rate.
-
-  ULONG direction = 0;
-  ULONG baseFrameNumber = 0;
-  ULONG iframeNumber = 0;
-  ULONG iframeLength = 0;
-  ULLONG filePos;
-  UINT amountReceived;
-  UINT videoLength;
-
-#ifndef WIN32
-  struct timeval clock0 = {0,0};  // Time stamp after fetching I-frame info
-  struct timeval clock1 = {0,0};  // Time stamp after fetching I-frame data
-  struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame
-#else
-  DWORD clock0 = 0, clock1 = 0, clock2 = 0;
-#endif
-
-  int frameTimeOffset = 0; // Time in msec between frames
-  int disp_msec = 0;  // Time taken to display data
-  int total_msec = 0; // Time taken to fetch data and display it
-  int sleepTime = 0;
-
-  if (state == S_FFWD) direction = 1; // and 0 for backward
-
-  while(1)
-  {
-    // Fetch I-frames until we get one that can be displayed in good time
-    // Repeat while clock0 + total_msec > clock2 + frameTimeOffset
-
-    baseFrameNumber = currentFrameNumber;
-    do
-    {
-      threadCheckExit();
-      if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
-        return;
-
-      if (iframeNumber >= lengthFrames) return;
-        // scan has got to the end of what we knew to be there before we started scanning
-
-      baseFrameNumber = iframeNumber;
-      frameTimeOffset =(int) ((double)(abs((int)iframeNumber - (int)currentFrameNumber) * 1000) / (fps * (double)ifactor));
-#ifndef WIN32
-      gettimeofday(&clock0, NULL);
-#else
-      clock0 = timeGetTime();
-#endif
-    }
-#ifndef WIN32
-    while (clock2.tv_sec != 0 &&
-          (clock0.tv_sec - clock2.tv_sec) * 1000 +
-          (clock0.tv_usec - clock2.tv_usec) / 1000 > frameTimeOffset - total_msec);
-#else
-    while (clock2 != 0 && clock0 + total_msec > clock2 + frameTimeOffset);
-#endif
-    logger->log("Player", Log::DEBUG, "XXX Got frame");
-
-    threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
-
-    if (!vdr->isConnected() || !amountReceived)
-    {
-      if (threadBuffer) free(threadBuffer);
-      doConnectionLost();
-      break;
-    }
-    
-#ifndef WIN32
-    gettimeofday(&clock1, NULL);
-    if (clock2.tv_sec != 0)
-      sleepTime = (clock2.tv_sec - clock1.tv_sec) * 1000
-                + (clock2.tv_usec - clock1.tv_usec) / 1000
-                + frameTimeOffset - disp_msec;
-#else
-    clock1 = timeGetTime();
-    if (clock2 != 0)
-      sleepTime = clock2 + frameTimeOffset - disp_msec - clock1;
-#endif
-    if (sleepTime < 0) sleepTime = 0;
-    threadCheckExit();
-    MILLISLEEP(sleepTime);
-   logger->log("Player", Log::DEBUG, "XXX Slept for %d", sleepTime);
-
-    videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
-    video->displayIFrame(threadBuffer, videoLength);
-    currentFrameNumber = iframeNumber;
-    free(threadBuffer);
-    threadBuffer = NULL;
-
-#ifndef WIN32
-    gettimeofday(&clock2, NULL);
-    total_msec = (clock2.tv_sec - clock0.tv_sec) * 1000
-               + (clock2.tv_usec - clock0.tv_usec) / 1000
-               - sleepTime;
-    disp_msec = (clock2.tv_sec - clock1.tv_sec) * 1000
-              + (clock2.tv_usec - clock1.tv_usec) / 1000
-              - sleepTime;
-#else
-    clock2 = timeGetTime();
-    total_msec = clock2 - clock0 - sleepTime;
-    disp_msec = clock2 - clock1 - sleepTime;
-#endif
-    logger->log("Player", Log::DEBUG, "XXX disp_msec = %4d total_msec = %4d", disp_msec, total_msec);
-  }
-}
-
-void Player::threadPostStopCleanup()
-{
-  if (threadBuffer)
-  {
-    free(threadBuffer);
-    threadBuffer = NULL;
-  }
-}
-
-// ----------------------------------- Dev
-
-#ifdef DEV
-void Player::test1()
-{
-  logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
-}
-
-void Player::test2()
-{
-  logger->log("Player", Log::DEBUG, "PLAYER TEST 2");
-}
-#endif
diff --git a/player.h b/player.h
deleted file mode 100644 (file)
index 45d221c..0000000
--- a/player.h
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
-    Copyright 2004-2008 Chris Tallon
-
-    This file is part of VOMP.
-
-    VOMP is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    VOMP is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with VOMP; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-*/
-
-#ifndef PLAYER_H
-#define PLAYER_H
-
-#include <stdio.h>
-#include <stdlib.h>
-#ifndef WIN32
-#include <sys/time.h>
-#endif
-#include <time.h>
-
-#include "threadsystem.h"
-
-#include "callback.h"
-#include "defines.h"
-#include "vfeed.h"
-#include "afeed.h"
-#include "tfeed.h"
-
-#include "teletextdecodervbiebu.h"
-
-class MessageQueue;
-class Audio;
-class Video;
-class VDR;
-class Log;
-class Demuxer;
-class OSDReceiver;
-class DVBSubtitles;
-class Channel;
-
-class Player : public Thread_TYPE, public Callback
-{
-  public:
-    Player(MessageQueue* messageQueue, void* messageReceiver, OSDReceiver* osdReceiver);
-    virtual ~Player();
-
-    int init(bool p_isPesRecording,double framespersec);
-    int shutdown();
-    void setStartFrame(ULONG frameNum);
-    void setLengthBytes(ULLONG length);
-    void setLengthFrames(ULONG length);
-    void setAudioChannel(int newChannel, int type, int streamtype);
-    void setSubtitleChannel(int newChannel);
-    bool toggleSubtitles();
-    void turnSubtitlesOn(bool ison); 
-    bool isSubtitlesOn() { return subtitlesShowing; }
-    void tellSubtitlesOSDVisible(bool visible);
-
-    void play();
-    void stop();
-    void pause();
-    void playpause();
-    void fastForward();
-    void fastBackward();
-    void jumpToPercent(double percent);
-    void skipForward(int seconds);
-    void skipBackward(int seconds);
-    void jumpToMark(int mark);
-    void jumpToFrameP(int newFrame);
-
-    UCHAR getState() { return state; }
-    ULONG getCurrentFrameNum();
-    ULONG getLengthFrames();
-    UCHAR getIScanRate() { return ifactor; }
-    bool* getDemuxerMpegAudioChannels();
-    bool* getDemuxerAc3AudioChannels();
-    bool* getDemuxerSubtitleChannels();
-    int *getTeletxtSubtitlePages();
-    int getCurrentAudioChannel();
-    int getCurrentSubtitleChannel();
-    bool isPesRecording() { return is_pesrecording; }
-    Channel *getDemuxerChannel();
-
-   TeletextDecoderVBIEBU * getTeletextDecoder() { return teletext; }
-
-    void call(void*); // for callback interface
-
-    const static UCHAR S_PLAY = 1;
-    const static UCHAR S_PAUSE_P = 2;
-    const static UCHAR S_PAUSE_I = 3;
-    const static UCHAR S_FFWD = 4;
-    const static UCHAR S_FBWD = 5;
-    const static UCHAR S_STOP = 6;
-    const static UCHAR S_JUMP = 7;
-    const static UCHAR S_JUMP_PI = 8; // Jump to Pause_I mode
-
-    // Player events
-
-    // FIXME so far this just duplicates the old system + the wss
-
-    const static UCHAR CONNECTION_LOST = 1;
-    const static UCHAR STOP_PLAYBACK = 2;
-    const static UCHAR STREAM_END = 3;
-    const static UCHAR ASPECT43 = 4;
-    const static UCHAR ASPECT169 = 5;
-
-#ifdef DEV
-    void test1();
-    void test2();
-#endif
-
-  protected:
-    void threadMethod();
-    void threadPostStopCleanup();
-
-  private:
-    void switchState(UCHAR newState, ULONG jumpFrame=0);
-
-    void threadFeedPlay();
-    void threadFeedScan();
-    void threadPTSFeedScan();
-
-    void doConnectionLost();
-    void restartAtFrame(ULONG newFrame);
-    void restartAtFramePI(ULONG newFrame);
-
-    bool subtitlesShowing{};
-    MessageQueue* messageQueue;
-    void* messageReceiver;
-    OSDReceiver* osdReceiver;
-    Log* logger;
-    Audio* audio;
-    Video* video;
-    Demuxer* demuxer;
-    DVBSubtitles* subtitles;
-    VDR* vdr;
-    VFeed vfeed;
-    AFeed afeed;
-    TFeed tfeed;
-    TeletextDecoderVBIEBU *teletext;
-  
-    
-
-    bool initted{};
-    bool startup;
-    bool videoStartup{};
-
-    bool is_pesrecording{true};
-       double fps;
-
-#ifndef WIN32
-    pthread_mutex_t mutex;
-#else
-    HANDLE mutex;
-#endif
-    void lock();
-    void unLock();
-
-    ULLONG lengthBytes{};
-    ULONG lengthFrames{};
-    ULONG currentFrameNumber{};
-    UINT blockSize{100000};
-    UINT startupBlockSize{250000};
-    UCHAR* threadBuffer{};
-    UCHAR state{S_STOP};
-    UCHAR ifactor{4}; // 4, 8, 16, 32
-};
-
-#endif
-
-
-/*
-
-Possible states:
-
-Play, Pause, FFwd, FBwd, (Stop), [Jump]
-
-                    Possible  Working
-
-Play   -> PauseP       *         *
-       -> PauseI
-       -> FFwd         *         *
-       -> FBwd         *         *
-       -> Stop         *         *
-       -> Jump         *         *
-       -> Jump_PI      *         *
-
-PauseP -> Play         *         *
-       -> PauseI
-       -> FFwd         *         *
-       -> FBwd         *         *
-       -> Stop         *         *
-       -> Jump         *         *
-       -> Jump_PI      *         *
-
-PauseI -> Play         *         *
-       -> PauseP
-       -> FFwd         *         *
-       -> FBwd         *         *
-       -> Stop         *         *
-       -> Jump         *         *
-       -> Jump_PI      *         *
-
-FFwd   -> Play         *         *
-       -> PauseP
-       -> PauseI       *         *
-       -> FBwd         *         *
-       -> Stop         *         *
-       -> Jump         *         *
-       -> Jump_PI      *         *
-
-FBwd   -> Play         *         *
-       -> PauseP
-       -> PauseI       *         *
-       -> FFwd         *         *
-       -> Stop         *         *
-       -> Jump         *         *
-       -> Jump_PI      *         *
-
-Stop   -> Play         *         *
-       -> PauseP
-       -> PauseI
-       -> FFwd
-       -> FBwd
-       -> Jump
-       -> Jump_PI
-
-*/
index 9cf5f70eafa38ef930de45c03a00a1338eae4694..07f9f410ed3899e3309f6384cdb1223391470c81 100644 (file)
@@ -74,6 +74,7 @@ class PlayerRadio : public Thread_TYPE, public Callback
 
     const static UCHAR S_PLAY = 1;
     const static UCHAR S_PAUSE_P = 2;
+    const static UCHAR S_PAUSE_I = 3;
     const static UCHAR S_STOP = 6;
     const static UCHAR S_JUMP = 7;
 
diff --git a/playervideorec.cc b/playervideorec.cc
new file mode 100644 (file)
index 0000000..0d138bf
--- /dev/null
@@ -0,0 +1,1402 @@
+/*
+    Copyright 2004-2008 Chris Tallon
+
+    This file is part of VOMP.
+
+    VOMP is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    VOMP is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with VOMP.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#include "log.h"
+#include "audio.h"
+#include "video.h"
+#include "demuxervdr.h"
+#include "demuxerts.h"
+#include "vdr.h"
+#include "messagequeue.h"
+#include "input.h"
+#include "message.h"
+#include "dvbsubtitles.h"
+#include "osdreceiver.h"
+
+#include "playervideorec.h"
+
+#define USER_RESPONSE_TIME 500 // Milliseconds
+
+// ----------------------------------- Called from outside, one offs or info funcs
+
+PlayerVideoRec::PlayerVideoRec(MessageQueue* tmessageQueue, void* tmessageReceiver, OSDReceiver* tosdReceiver)
+: vfeed(this), afeed(this), tfeed(this), messageQueue(tmessageQueue),
+  messageReceiver(tmessageReceiver), osdReceiver(tosdReceiver)
+{
+  audio = Audio::getInstance();
+  video = Video::getInstance();
+  logger = Log::getInstance();
+  vdr = VDR::getInstance();
+
+  video->turnVideoOn();
+}
+
+PlayerVideoRec::~PlayerVideoRec()
+{
+  if (initted) shutdown();
+}
+
+int PlayerVideoRec::init(bool p_isPesRecording, double framespersecond)
+{
+  if (initted) return 0;
+  is_pesrecording = p_isPesRecording;
+  fps = framespersecond;
+  if (is_pesrecording)
+    demuxer = new DemuxerVDR();
+  else
+    demuxer = new DemuxerTS();
+  if (!demuxer) return 0;
+  subtitles = new DVBSubtitles(osdReceiver);
+  if (!subtitles) return 0;
+
+  teletext = new TeletextDecoderVBIEBU();
+  if (!teletext) return 0;
+  teletext->setRecordigMode(true);
+  unsigned int demux_video_size = 2097152;
+  unsigned int demux_audio_size = 524288;
+  if (video->supportsh264())
+  {
+    demux_video_size *= 5 * 2;
+  }
+  if (audio->maysupportAc3())
+  {
+    //demux_audio_size*=2;
+  }
+  if (!demuxer->init(this, audio, video,teletext, demux_video_size,demux_audio_size,65536, framespersecond, subtitles))
+  {
+    logger->log("Player", Log::ERR, "Demuxer failed to init");
+    shutdown();
+    return 0;
+  }
+
+  vfeed.init();
+  afeed.init();
+  tfeed.init();
+
+  video->stop();
+  video->blank();
+  audio->stop();
+
+  if (Command::getInstance()->getSubDefault())
+    turnSubtitlesOn(true);
+  else
+    turnSubtitlesOn(false);
+
+  initted = true;
+  return 1;
+}
+
+int PlayerVideoRec::shutdown()
+{
+  if (!initted) return 0;
+  switchState(S_STOP);
+  initted = false;
+
+  delete demuxer;
+  demuxer = NULL;
+  delete subtitles;
+  subtitles = NULL;
+  delete teletext;
+  teletext = NULL;
+
+  return 1;
+}
+
+void PlayerVideoRec::setStartFrame(ULONG startFrame)
+{
+  ULONG nextiframeNumber;
+  ULONG iframeLength;
+  ULONG iframeNumber;
+  ULLONG filePos;
+
+  // newFrame could be anywhere, go forwards to next I-Frame
+  if (!vdr->getNextIFrame(startFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
+
+  // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
+  vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
+
+  logger->log("Player", Log::DEBUG, "setStartFrame %lu %lu %lu", startFrame, nextiframeNumber,iframeNumber);
+  currentFrameNumber = iframeNumber;
+}
+
+void PlayerVideoRec::setLengthBytes(ULLONG length)
+{
+  lengthBytes = length;
+  logger->log("Player", Log::DEBUG, "Player has received length bytes of %llu", lengthBytes);
+}
+
+void PlayerVideoRec::setLengthFrames(ULONG length)
+{
+  lengthFrames = length;
+  logger->log("Player", Log::DEBUG, "Player has received length frames of %lu", lengthFrames);
+}
+
+ULONG PlayerVideoRec::getLengthFrames()
+{
+  return lengthFrames;
+}
+
+ULONG PlayerVideoRec::getCurrentFrameNum()
+{
+  if (startup) return 0;
+  switch(state)
+  {
+    case S_PLAY:
+    case S_PAUSE_P:
+      return demuxer->getFrameNumFromPTS(video->getCurrentTimestamp());
+    case S_PAUSE_I:
+    case S_FFWD:
+    case S_FBWD:
+      return currentFrameNumber;
+    default:
+      return 0; // shouldn't happen
+  }
+}
+
+bool* PlayerVideoRec::getDemuxerMpegAudioChannels()
+{
+  return demuxer->getmpAudioChannels();
+}
+
+bool* PlayerVideoRec::getDemuxerAc3AudioChannels()
+{
+  return demuxer->getac3AudioChannels();
+}
+
+bool* PlayerVideoRec::getDemuxerSubtitleChannels()
+{
+  return demuxer->getSubtitleChannels();
+}
+
+int PlayerVideoRec::getCurrentAudioChannel()
+{
+  if (is_pesrecording)
+    return demuxer->getselAudioChannel();
+  else
+    return dynamic_cast<DemuxerTS*>(demuxer)->getAID();
+}
+
+int PlayerVideoRec::getCurrentSubtitleChannel()
+{
+  if (is_pesrecording)
+    return demuxer->getselSubtitleChannel();
+  else
+    return dynamic_cast<DemuxerTS*>(demuxer)->getSubID();
+}
+
+void PlayerVideoRec::setSubtitleChannel(int newChannel)
+{
+  if (is_pesrecording)
+    demuxer->setDVBSubtitleStream(newChannel);
+  else
+    dynamic_cast<DemuxerTS*>(demuxer)->setSubID(newChannel);
+}
+
+int *PlayerVideoRec::getTeletxtSubtitlePages()
+{
+  return teletext->getSubtitlePages();
+}
+
+void PlayerVideoRec::setAudioChannel(int newChannel, int type, int streamtype)
+{
+  if (is_pesrecording)
+  {
+    demuxer->setAudioStream(newChannel);
+    return;
+  }
+  else
+  {
+    dynamic_cast<DemuxerTS*>(demuxer)->setAID(newChannel,type,streamtype,false);
+    return;
+  }
+}
+
+bool PlayerVideoRec::toggleSubtitles()
+{
+  if (!subtitlesShowing)
+  {
+    subtitlesShowing = true;
+    subtitles->show();
+  }
+  else
+  {
+    subtitlesShowing = false;
+    subtitles->hide();
+  }
+  return subtitlesShowing;
+}
+
+void  PlayerVideoRec::turnSubtitlesOn(bool ison)
+{
+ if (ison)
+  {
+    subtitlesShowing = true;
+    subtitles->show();
+  }
+  else
+  {
+    subtitlesShowing = false;
+    subtitles->hide();
+  }
+}
+
+void PlayerVideoRec::tellSubtitlesOSDVisible(bool visible)
+{
+  subtitles->setOSDMenuVisibility(visible);
+}
+
+Channel * PlayerVideoRec::getDemuxerChannel()
+{
+  if (!is_pesrecording)
+  {
+    return dynamic_cast<DemuxerTS*>(demuxer)->getChannelInfo();
+  }
+  return NULL; //Should not happen!
+}
+
+
+// ----------------------------------- Externally called events
+
+void PlayerVideoRec::play()
+{
+  if (!initted) return;
+  if (state == S_PLAY) return;
+  stateMutex.lock();
+
+  bool doUnlock = false;
+  if (state == S_PAUSE_P) doUnlock = true;
+  switchState(S_PLAY);
+  if (doUnlock) stateMutex.unlock();
+}
+
+void PlayerVideoRec::playpause()
+{
+  if (!initted) return;
+  stateMutex.lock();
+
+  bool doUnlock = false;
+  if (state==S_PLAY)
+  {
+    doUnlock=true;
+    switchState(S_PAUSE_P);
+  }
+  else
+  {
+    if (state == S_PAUSE_P) doUnlock = true;
+    switchState(S_PLAY);
+  }
+  if (doUnlock) stateMutex.unlock();
+}
+
+void PlayerVideoRec::stop()
+{
+  if (!initted) return;
+  if (state == S_STOP) return;
+  stateMutex.lock();
+  logger->log("Player", Log::DEBUG, "Stop called lock");
+  switchState(S_STOP);
+  stateMutex.unlock();
+}
+
+void PlayerVideoRec::pause()
+{
+  if (!initted) return;
+  stateMutex.lock();
+
+  if ((state == S_FFWD) || (state == S_FBWD))
+  {
+    switchState(S_PAUSE_I);
+  }
+  else if ((state == S_PAUSE_I) || (state == S_PAUSE_P))
+  {
+    switchState(S_PLAY);
+  }
+  else
+  {
+    switchState(S_PAUSE_P);
+  }
+
+  stateMutex.unlock();
+}
+
+void PlayerVideoRec::fastForward()
+{
+  if (!initted) return;
+  stateMutex.lock();
+
+  if (state == S_FFWD)
+  {
+    // change the rate
+    switch(ifactor)
+    {
+      case 4: ifactor = 8; break;
+      case 8: ifactor = 16; break;
+      case 16: ifactor = 32; break;
+      case 32: ifactor = 4; break;
+    }
+  }
+  else
+  {
+    ifactor = 4;
+    switchState(S_FFWD);
+  }
+  stateMutex.unlock();
+}
+
+void PlayerVideoRec::fastBackward()
+{
+  if (!initted) return;
+  stateMutex.lock();
+
+  if (state == S_FBWD)
+  {
+    // change the rate
+    switch(ifactor)
+    {
+      case 4: ifactor = 8; break;
+      case 8: ifactor = 16; break;
+      case 16: ifactor = 32; break;
+      case 32: ifactor = 4; break;
+    }
+  }
+  else
+  {
+    ifactor = 4;
+    switchState(S_FBWD);
+  }
+  stateMutex.unlock();
+}
+
+void PlayerVideoRec::jumpToPercent(double percent)
+{
+  stateMutex.lock();
+  logger->log("Player", Log::DEBUG, "JUMP TO %f%%", percent);
+  ULONG newFrame = static_cast<ULONG>(percent * lengthFrames / 100);
+  switchState(S_JUMP, newFrame);
+//  stateMutex.unlock(); - let thread unlock this
+}
+
+void PlayerVideoRec::jumpToMark(int mark)
+{
+  stateMutex.lock();
+  logger->log("Player", Log::DEBUG, "JUMP TO MARK %i%%", mark);
+  switchState(S_JUMP, mark);
+//  stateMutex.unlock(); - let thread unlock this
+}
+
+void PlayerVideoRec::jumpToFrameP(int newFrame)
+{
+  stateMutex.lock();
+  logger->log("Player", Log::DEBUG, "JUMP TO FRAME AND PAUSE %i", newFrame);
+  switchState(S_JUMP_PI, newFrame);
+  stateMutex.unlock();
+}
+
+void PlayerVideoRec::skipForward(int seconds)
+{
+  stateMutex.lock();
+  logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
+  ULONG newFrame = getCurrentFrameNum();
+  if (newFrame == 0) { stateMutex.unlock(); return; } // Current pos from demuxer is not valid
+  newFrame += static_cast<ULONG>(static_cast<double>(seconds) * fps);
+  if (newFrame > lengthFrames) { switchState(S_PLAY); stateMutex.unlock(); }
+  else switchState(S_JUMP, newFrame);
+//  stateMutex.unlock(); - let thread unlock this
+}
+
+void PlayerVideoRec::skipBackward(int seconds)
+{
+  stateMutex.lock();
+  logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
+  long newFrame = getCurrentFrameNum();
+  if (newFrame == 0) { stateMutex.unlock(); return; } // Current pos from demuxer is not valid
+  newFrame -= static_cast<ULONG>(static_cast<double>(seconds) * fps);
+  if (newFrame < 0) newFrame = 0;
+  switchState(S_JUMP, newFrame);
+//  stateMutex.unlock(); - let thread unlock this
+}
+
+// ----------------------------------- Implementations called events
+
+void PlayerVideoRec::switchState(UCHAR toState, ULONG jumpFrame)
+{
+  if (!initted) return;
+
+  logger->log("Player", Log::DEBUG, "Switch state from %u to %u", state, toState);
+
+  switch(state) // current state selector
+  {
+    case S_PLAY: // from S_PLAY -----------------------------------
+    {
+      switch(toState)
+      {
+        case S_PLAY: // to S_PLAY
+        {
+          return;
+        }
+        case S_PAUSE_P: // to S_PAUSE_P
+        {
+          #ifdef VOMP_PLATFORM_RASPBERRY
+          vfeed.stop(); // can't vfeed during pause
+          #endif
+
+          video->pause();
+          audio->pause();
+          state = S_PAUSE_P;
+          return;
+        }
+        case S_PAUSE_I: // to S_PAUSE_I
+        {
+          // can't occur
+          return;
+        }
+        case S_FFWD: // to S_FFWD
+        {
+          currentFrameNumber = getCurrentFrameNum();
+          audio->systemMuteOn();
+          threadStop();
+          vfeed.stop();
+          afeed.stop();
+          tfeed.stop();
+          subtitles->stop();
+          demuxer->flush();
+          state = S_FFWD;
+          threadStart();
+          return;
+        }
+        case S_FBWD: // to S_FBWD
+        {
+          currentFrameNumber = getCurrentFrameNum();
+          audio->systemMuteOn();
+          threadStop();
+          vfeed.stop();
+          afeed.stop();
+          tfeed.stop();
+          subtitles->stop();
+          demuxer->flush();
+          state = S_FBWD;
+          threadStart();
+          return;
+        }
+        case S_STOP: // to S_STOP
+        {
+          vfeed.stop();
+          afeed.stop();
+          tfeed.stop();
+          subtitles->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_JUMP_PI: // to S_JUMP_PI
+        {
+          audio->systemMuteOn();
+          threadStop();
+          vfeed.stop();
+          afeed.stop();
+          tfeed.stop();
+          subtitles->stop();
+          demuxer->flush();
+          state = S_PAUSE_I;
+          video->reset();
+          video->play();
+          restartAtFramePI(jumpFrame);
+          return;
+        }
+      }
+    }
+    FALLTHROUGH // keep compiler happy (all posibilities return)
+    case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
+    {
+      switch(toState)
+      {
+        case S_PLAY: // to S_PLAY
+        {
+          video->unPause();
+          audio->unPause();
+
+          #ifdef VOMP_PLATFORM_RASPBERRY
+          vfeed.start(false);
+          #endif
+
+          state = S_PLAY;
+          return;
+        }
+        case S_PAUSE_P: // to S_PAUSE_P
+        {
+          return;
+        }
+        case S_PAUSE_I: // to S_PAUSE_I
+        {
+          return;
+        }
+        case S_FFWD: // to S_FFWD
+        {
+          currentFrameNumber = getCurrentFrameNum();
+          audio->systemMuteOn();
+          vfeed.stop();
+          afeed.stop();
+          tfeed.stop();
+          subtitles->stop();
+          threadStop();
+          video->unPause();
+          audio->unPause();
+          state = S_FFWD;
+          threadStart();
+          return;
+        }
+        case S_FBWD: // to S_FBWD
+        {
+          currentFrameNumber = getCurrentFrameNum();
+          audio->systemMuteOn();
+          vfeed.stop();
+          afeed.stop();
+          tfeed.stop();
+          subtitles->stop();
+          threadStop();
+          video->unPause();
+          audio->unPause();
+          state = S_FBWD;
+          threadStart();
+          return;
+        }
+        case S_STOP: // to S_STOP
+        {
+          vfeed.stop();
+          afeed.stop();
+          tfeed.stop();
+          subtitles->stop();
+          threadStop();
+          video->stop();
+          video->blank();
+          audio->stop();
+          video->reset();
+          audio->unPause();
+          demuxer->reset();
+          audio->systemMuteOff();
+          state = S_STOP;
+          return;
+        }
+        case S_JUMP: // to S_JUMP
+        {
+          state = S_PLAY;
+          audio->systemMuteOn();
+          audio->unPause();
+          restartAtFrame(jumpFrame);
+          return;
+        }
+        case S_JUMP_PI: // to S_JUMP_PI
+        {
+          audio->systemMuteOn();
+          audio->unPause();
+          threadStop();
+          vfeed.stop();
+          afeed.stop();
+          tfeed.stop();
+          subtitles->stop();
+          demuxer->flush();
+          state = S_PAUSE_I;
+          video->reset();
+          video->play();
+          restartAtFramePI(jumpFrame);
+          return;
+        }
+      }
+    }
+    FALLTHROUGH // keep compiler happy (all posibilities return)
+    case S_PAUSE_I: // from S_PAUSE_I -----------------------------------
+    {
+      switch(toState)
+      {
+        case S_PLAY: // to S_PLAY
+        {
+          state = S_PLAY;
+          restartAtFrame(currentFrameNumber);
+          return;
+        }
+        case S_PAUSE_P: // to S_PAUSE_P
+        {
+          return;
+        }
+        case S_PAUSE_I: // to S_PAUSE_I
+        {
+          return;
+        }
+        case S_FFWD: // to S_FFWD
+        {
+          state = S_FFWD;
+          threadStart();
+          return;
+        }
+        case S_FBWD: // to S_FBWD
+        {
+          state = S_FBWD;
+          threadStart();
+          return;
+        }
+        case S_STOP: // to S_STOP
+        {
+          video->stop();
+          video->blank();
+          audio->stop();
+          video->reset();
+          demuxer->reset();
+          audio->systemMuteOff();
+          state = S_STOP;
+          return;
+        }
+        case S_JUMP: // to S_JUMP
+        {
+          state = S_PLAY;
+          restartAtFrame(jumpFrame);
+          return;
+        }
+        case S_JUMP_PI: // to S_JUMP_PI
+        {
+          restartAtFramePI(jumpFrame);
+          return;
+        }
+      }
+    }
+    FALLTHROUGH // keep compiler happy (all posibilities return)
+    case S_FFWD: // from S_FFWD -----------------------------------
+    {
+      switch(toState)
+      {
+        case S_PLAY: // to S_PLAY
+        {
+          state = S_PLAY;
+          ULONG stepback = static_cast<ULONG>(USER_RESPONSE_TIME * ifactor * fps / 1000);
+          if (stepback < currentFrameNumber)
+            currentFrameNumber -= stepback;
+          else
+            currentFrameNumber = 0;
+          restartAtFrame(currentFrameNumber);
+          return;
+        }
+        case S_PAUSE_P: // to S_PAUSE_P
+        {
+          // can't occur
+          return;
+        }
+        case S_PAUSE_I: // to S_PAUSE_I
+        {
+          threadStop();
+          state = S_PAUSE_I;
+          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_JUMP_PI: // to S_JUMP_PI
+        {
+          threadStop();
+          state = S_PAUSE_I;
+          restartAtFramePI(jumpFrame);
+          return;
+        }
+      }
+    }
+    FALLTHROUGH // keep compiler happy (all posibilities return)
+    case S_FBWD: // from S_FBWD -----------------------------------
+    {
+      switch(toState)
+      {
+        case S_PLAY: // to S_PLAY
+        {
+          state = S_PLAY;
+          restartAtFrame(currentFrameNumber);
+          return;
+        }
+        case S_PAUSE_P: // to S_PAUSE_P
+        {
+          // can't occur
+          return;
+        }
+        case S_PAUSE_I: // to S_PAUSE_I
+        {
+          threadStop();
+          state = S_PAUSE_I;
+          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_JUMP_PI: // to S_JUMP_PI
+        {
+          threadStop();
+          state = S_PAUSE_I;
+          restartAtFramePI(jumpFrame);
+          return;
+        }
+      }
+    }
+    FALLTHROUGH // keep compiler happy (all posibilities return)
+    case S_STOP: // from S_STOP -----------------------------------
+    {
+      switch(toState)
+      {
+        case S_PLAY: // to S_PLAY
+        {
+          startup = true;
+
+          audio->reset();
+          audio->setStreamType(Audio::MPEG2_PES);
+          audio->systemMuteOff();
+          video->reset();
+          demuxer->reset();
+          // FIXME use restartAtFrame here?
+          if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
+          demuxer->setFrameNum(currentFrameNumber);
+          demuxer->seek();
+          videoStartup = true;
+          state = S_PLAY;
+          threadStart();
+          logger->log("Player", Log::DEBUG, "Immediate play");
+          afeed.start();
+          vfeed.start();
+          tfeed.start();
+          subtitles->start();
+          video->sync();
+          audio->sync();
+          audio->play();
+          video->pause();
+          return;
+        }
+        case S_PAUSE_P: // to S_PAUSE_P
+        {
+          return;
+        }
+        case S_PAUSE_I: // to S_PAUSE_I
+        {
+          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_PI: // to S_JUMP_PI
+        {
+          return;
+        }
+      }
+    }
+    // case S_JUMP cannot be a start state because it auto flips to play
+    // case S_JUMP_PI cannot be a start state because it auto flips to S_PAUSE_I
+  }
+}
+
+// ----------------------------------- Internal functions
+
+void PlayerVideoRec::restartAtFrame(ULONG newFrame)
+{
+  vfeed.stop();
+  afeed.stop();
+  tfeed.stop();
+  subtitles->stop();
+  threadStop();
+  video->stop();
+  video->reset();
+  audio->reset();
+  audio->setStreamType(Audio::MPEG2_PES);
+  demuxer->flush();
+  demuxer->seek();
+  currentFrameNumber = newFrame;
+  demuxer->setFrameNum(newFrame);
+  videoStartup = true;
+  afeed.start();
+  tfeed.start();
+  vfeed.start();
+  subtitles->start();
+  threadStart();
+  audio->play();
+  video->sync();
+  audio->sync();
+  audio->systemMuteOff();
+  audio->doMuting();
+}
+
+
+void PlayerVideoRec::restartAtFramePI(ULONG newFrame)
+{
+  ULLONG filePos;
+  ULONG nextiframeNumber;
+  ULONG iframeLength;
+  ULONG iframeNumber;
+
+  UCHAR* buffer;
+  UINT amountReceived;
+  UINT videoLength;
+
+  // newFrame could be anywhere, go forwards to next I-Frame
+  if (!vdr->getNextIFrame(newFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
+
+  // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
+  vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
+
+  buffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
+  if (!vdr->isConnected())
+  {
+    if (buffer) free(buffer);
+    doConnectionLost();
+  }
+  else
+  {
+    videoLength = demuxer->stripAudio(buffer, amountReceived);
+    video->displayIFrame(buffer, videoLength);
+    video->displayIFrame(buffer, videoLength); // If you do it twice, it works :)
+    free(buffer);
+    currentFrameNumber = iframeNumber;
+  }
+}
+
+void PlayerVideoRec::doConnectionLost()
+{
+  logger->log("Player", Log::DEBUG, "Connection lost, sending message");
+  Message* m = new Message();
+  m->to = messageReceiver;
+  m->from = this;
+  m->message = Message::PLAYER_EVENT;
+  m->parameter = PlayerVideoRec::CONNECTION_LOST;
+  messageQueue->postMessage(m);
+}
+
+// ----------------------------------- Callback
+
+void PlayerVideoRec::call(void* caller)
+{
+  if (caller == demuxer)
+  {
+    logger->log("Player", Log::DEBUG, "Callback from demuxer");
+
+    if (video->getTVsize() == Video::ASPECT4X3)
+    {
+      logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
+      return;
+    }
+
+    int parx,pary;
+    int dxCurrentAspect = demuxer->getAspectRatio(&parx,&pary);
+    if (dxCurrentAspect == Demuxer::ASPECT_4_3)
+    {
+      logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
+      video->setAspectRatio(Video::ASPECT4X3,parx,pary);
+
+      Message* m = new Message();
+      m->from = this;
+      m->to = messageReceiver;
+      m->message = Message::PLAYER_EVENT;
+      m->parameter = PlayerVideoRec::ASPECT43;
+      messageQueue->postMessage(m);
+    }
+    else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
+    {
+      logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
+      video->setAspectRatio(Video::ASPECT16X9,parx,pary);
+
+      Message* m = new Message();
+      m->from = this;
+      m->to = messageReceiver;
+      m->message = Message::PLAYER_EVENT;
+      m->parameter = PlayerVideoRec::ASPECT169;
+      messageQueue->postMessage(m);
+    }
+    else
+    {
+      logger->log("Player", Log::DEBUG, "Demuxer said video is something else... setting it anyway");
+         video->setAspectRatio(static_cast<UCHAR>(dxCurrentAspect), parx, pary);
+    }
+
+  }
+  else
+  {
+    if (videoStartup)
+    {
+      videoStartup = false;
+      video->reset();
+      video->play();
+      video->sync();
+      vfeed.release();
+      stateMutex.unlock();
+    }
+
+    threadSignalNoLock();
+  }
+}
+
+// ----------------------------------- Feed thread
+
+void PlayerVideoRec::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 ((state == S_FFWD) || (state == S_FBWD))
+  {
+    if (video->PTSIFramePlayback()) threadPTSFeedScan();
+    else 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->to = messageReceiver;
+      m->from = this;
+      m->message = Message::PLAYER_EVENT;
+      m->parameter = STOP_PLAYBACK;
+      logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
+      messageQueue->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();
+    audio->setStreamType(Audio::MPEG2_PES);
+    demuxer->flush();
+    demuxer->seek();
+    demuxer->setFrameNum(currentFrameNumber);
+    videoStartup = true;
+    afeed.start();
+    tfeed.start();
+    vfeed.start();
+    subtitles->start();
+    audio->play();
+    audio->sync();
+    audio->systemMuteOff();
+    audio->doMuting();
+  }
+
+  if (state == S_PLAY) threadFeedPlay();
+}
+
+void PlayerVideoRec::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 = static_cast<UINT>(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 = static_cast<UINT>(lengthBytes - feedPosition);
+      else // normal
+        askFor = blockSize;
+    }
+    //logger->log("Player", Log::DEBUG, "Get Block in");
+
+    threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
+    //logger->log("Player", Log::DEBUG, "Get Block out");
+
+    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();
+
+    while(writeLength < thisRead)
+    {
+      //logger->log("Player", Log::DEBUG, "Put in");
+      thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
+      //logger->log("Player", Log::DEBUG, "Put out");
+      writeLength += thisWrite;
+
+      if (!thisWrite)
+      {
+        // demuxer is full and can't take anymore
+        threadLock();
+        if (!threadActive) { threadUnlock(); threadCheckExit(); }
+        threadWaitForSignal();
+        threadUnlock();
+      }
+
+      threadCheckExit();
+    }
+
+    free(threadBuffer);
+    threadBuffer = NULL;
+
+  }
+
+  // end of recording
+  logger->log("Player", Log::DEBUG, "Recording playback ends");
+
+  if (videoStartup) // oh woe. there never was a stream, I was conned!
+  {
+    videoStartup = false;
+    stateMutex.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->to = messageReceiver;
+  m->from = this;
+  m->message = Message::PLAYER_EVENT;
+  m->parameter = PlayerVideoRec::STOP_PLAYBACK;
+  logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
+  messageQueue->postMessage(m);
+}
+
+
+void PlayerVideoRec::threadPTSFeedScan()
+{
+  // This method manipulates the PTS instead of waiting, this is for the android devices, maybe later for windows?
+
+  ULONG direction = 0;
+  int dir_fac=-1;
+  ULONG baseFrameNumber = 0;
+  ULONG iframeNumber = 0;
+  ULONG iframeLength = 0;
+  ULONG currentfeedFrameNumber=currentFrameNumber;
+  ULONG firstFrameNumber=currentFrameNumber;
+  ULLONG filePos;
+  UINT amountReceived;
+  UINT videoLength;
+
+  UINT playtime=0;
+
+  int frameTimeOffset = 0; // Time in msec between frames
+
+  if (state == S_FFWD)
+  {
+    direction = 1; // and 0 for backward
+    dir_fac = 1;
+  }
+  video->EnterIframePlayback();
+
+  while(1)
+  {
+    baseFrameNumber = currentfeedFrameNumber;
+
+    threadCheckExit();
+    if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
+       return;
+
+    if (iframeNumber >= lengthFrames) return;
+        // scan has got to the end of what we knew to be there before we started scanning
+
+    baseFrameNumber = iframeNumber;
+
+    frameTimeOffset =(int) ((double)(abs((int)iframeNumber - (int)currentfeedFrameNumber) * 1000) / (fps * (double)ifactor));
+
+    logger->log("Player", Log::DEBUG, "XXX Got frame");
+
+    threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
+
+    if (!vdr->isConnected())
+    {
+      if (threadBuffer) free(threadBuffer);
+      doConnectionLost();
+      break;
+    }
+
+
+    threadCheckExit();
+
+
+    videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
+    demuxer->changeTimes(threadBuffer,videoLength,playtime);
+    int count=0;
+    while (!video->displayIFrame(threadBuffer, videoLength)) // the device might block
+    {
+       MILLISLEEP(20);
+       threadCheckExit();
+       count++;
+       if (count%300==0) {
+               ULLONG cur_time=video->getCurrentTimestamp();
+       //       logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
+               //              firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
+               if (cur_time!=0) {
+                   currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.));
+               }
+       }
+
+    }
+    playtime +=frameTimeOffset;
+    currentfeedFrameNumber = iframeNumber;
+    {
+               ULLONG cur_time=video->getCurrentTimestamp();
+       //       logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
+               //              firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
+               if (cur_time!=0) {
+                   currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.));
+               }
+       }
+
+    free(threadBuffer);
+    threadBuffer = NULL;
+  }
+}
+
+
+
+void PlayerVideoRec::threadFeedScan()
+{
+  // This method is actually really simple - get frame from vdr,
+  // spit it at the video chip, wait for a time. Most of the code here
+  // is to get the wait right so that the scan occurs at the correct rate.
+
+  ULONG direction = 0;
+  ULONG baseFrameNumber = 0;
+  ULONG iframeNumber = 0;
+  ULONG iframeLength = 0;
+  ULLONG filePos;
+  UINT amountReceived;
+  UINT videoLength;
+
+#ifndef WIN32
+  struct timeval clock0 = {0,0};  // Time stamp after fetching I-frame info
+  struct timeval clock1 = {0,0};  // Time stamp after fetching I-frame data
+  struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame
+#else
+  DWORD clock0 = 0, clock1 = 0, clock2 = 0;
+#endif
+
+  int frameTimeOffset = 0; // Time in msec between frames
+  int disp_msec = 0;  // Time taken to display data
+  int total_msec = 0; // Time taken to fetch data and display it
+  int sleepTime = 0;
+
+  if (state == S_FFWD) direction = 1; // and 0 for backward
+
+  while(1)
+  {
+    // Fetch I-frames until we get one that can be displayed in good time
+    // Repeat while clock0 + total_msec > clock2 + frameTimeOffset
+
+    baseFrameNumber = currentFrameNumber;
+    do
+    {
+      threadCheckExit();
+      if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
+        return;
+
+      if (iframeNumber >= lengthFrames) return;
+        // scan has got to the end of what we knew to be there before we started scanning
+
+      baseFrameNumber = iframeNumber;
+      frameTimeOffset =(int) ((double)(abs((int)iframeNumber - (int)currentFrameNumber) * 1000) / (fps * (double)ifactor));
+#ifndef WIN32
+      gettimeofday(&clock0, NULL);
+#else
+      clock0 = timeGetTime();
+#endif
+    }
+#ifndef WIN32
+    while (clock2.tv_sec != 0 &&
+          (clock0.tv_sec - clock2.tv_sec) * 1000 +
+          (clock0.tv_usec - clock2.tv_usec) / 1000 > frameTimeOffset - total_msec);
+#else
+    while (clock2 != 0 && clock0 + total_msec > clock2 + frameTimeOffset);
+#endif
+    logger->log("Player", Log::DEBUG, "XXX Got frame");
+
+    threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
+
+    if (!vdr->isConnected() || !amountReceived)
+    {
+      if (threadBuffer) free(threadBuffer);
+      doConnectionLost();
+      break;
+    }
+    
+#ifndef WIN32
+    gettimeofday(&clock1, NULL);
+    if (clock2.tv_sec != 0)
+      sleepTime = (clock2.tv_sec - clock1.tv_sec) * 1000
+                + (clock2.tv_usec - clock1.tv_usec) / 1000
+                + frameTimeOffset - disp_msec;
+#else
+    clock1 = timeGetTime();
+    if (clock2 != 0)
+      sleepTime = clock2 + frameTimeOffset - disp_msec - clock1;
+#endif
+    if (sleepTime < 0) sleepTime = 0;
+    threadCheckExit();
+    MILLISLEEP(sleepTime);
+   logger->log("Player", Log::DEBUG, "XXX Slept for %d", sleepTime);
+
+    videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
+    video->displayIFrame(threadBuffer, videoLength);
+    currentFrameNumber = iframeNumber;
+    free(threadBuffer);
+    threadBuffer = NULL;
+
+#ifndef WIN32
+    gettimeofday(&clock2, NULL);
+    total_msec = (clock2.tv_sec - clock0.tv_sec) * 1000
+               + (clock2.tv_usec - clock0.tv_usec) / 1000
+               - sleepTime;
+    disp_msec = (clock2.tv_sec - clock1.tv_sec) * 1000
+              + (clock2.tv_usec - clock1.tv_usec) / 1000
+              - sleepTime;
+#else
+    clock2 = timeGetTime();
+    total_msec = clock2 - clock0 - sleepTime;
+    disp_msec = clock2 - clock1 - sleepTime;
+#endif
+    logger->log("Player", Log::DEBUG, "XXX disp_msec = %4d total_msec = %4d", disp_msec, total_msec);
+  }
+}
+
+void PlayerVideoRec::threadPostStopCleanup()
+{
+  if (threadBuffer)
+  {
+    free(threadBuffer);
+    threadBuffer = NULL;
+  }
+}
+
+// ----------------------------------- Dev
+
+#ifdef DEV
+void PlayerVideoRec::test1()
+{
+  logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
+}
+
+void PlayerVideoRec::test2()
+{
+  logger->log("Player", Log::DEBUG, "PLAYER TEST 2");
+}
+#endif
diff --git a/playervideorec.h b/playervideorec.h
new file mode 100644 (file)
index 0000000..05e47b8
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+    Copyright 2004-2008 Chris Tallon
+
+    This file is part of VOMP.
+
+    VOMP is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    VOMP is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with VOMP; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+
+#ifndef PLAYERVIDEOREC_H
+#define PLAYERVIDEOREC_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#ifndef WIN32
+#include <sys/time.h>
+#endif
+#include <time.h>
+#include <mutex>
+
+#include "threadsystem.h"
+
+#include "callback.h"
+#include "defines.h"
+#include "vfeed.h"
+#include "afeed.h"
+#include "tfeed.h"
+
+#include "teletextdecodervbiebu.h"
+
+class MessageQueue;
+class Audio;
+class Video;
+class VDR;
+class Log;
+class Demuxer;
+class OSDReceiver;
+class DVBSubtitles;
+class Channel;
+
+class PlayerVideoRec : public Thread_TYPE, public Callback
+{
+  public:
+    PlayerVideoRec(MessageQueue* messageQueue, void* messageReceiver, OSDReceiver* osdReceiver);
+    virtual ~PlayerVideoRec();
+
+    int init(bool p_isPesRecording,double framespersec);
+    int shutdown();
+    void setStartFrame(ULONG frameNum);
+    void setLengthBytes(ULLONG length);
+    void setLengthFrames(ULONG length);
+    void setAudioChannel(int newChannel, int type, int streamtype);
+    void setSubtitleChannel(int newChannel);
+    bool toggleSubtitles();
+    void turnSubtitlesOn(bool ison); 
+    bool isSubtitlesOn() { return subtitlesShowing; }
+    void tellSubtitlesOSDVisible(bool visible);
+
+    void play();
+    void stop();
+    void pause();
+    void playpause();
+    void fastForward();
+    void fastBackward();
+    void jumpToPercent(double percent);
+    void skipForward(int seconds);
+    void skipBackward(int seconds);
+    void jumpToMark(int mark);
+    void jumpToFrameP(int newFrame);
+
+    UCHAR getState() { return state; }
+    ULONG getCurrentFrameNum();
+    ULONG getLengthFrames();
+    UCHAR getIScanRate() { return ifactor; }
+    bool* getDemuxerMpegAudioChannels();
+    bool* getDemuxerAc3AudioChannels();
+    bool* getDemuxerSubtitleChannels();
+    int *getTeletxtSubtitlePages();
+    int getCurrentAudioChannel();
+    int getCurrentSubtitleChannel();
+    bool isPesRecording() { return is_pesrecording; }
+    Channel *getDemuxerChannel();
+
+   TeletextDecoderVBIEBU * getTeletextDecoder() { return teletext; }
+
+    void call(void*); // for callback interface
+
+    const static UCHAR S_PLAY = 1;
+    const static UCHAR S_PAUSE_P = 2;
+    const static UCHAR S_PAUSE_I = 3;
+    const static UCHAR S_FFWD = 4;
+    const static UCHAR S_FBWD = 5;
+    const static UCHAR S_STOP = 6;
+    const static UCHAR S_JUMP = 7;
+    const static UCHAR S_JUMP_PI = 8; // Jump to Pause_I mode
+
+    // Player events
+
+    // FIXME so far this just duplicates the old system + the wss
+
+    const static UCHAR CONNECTION_LOST = 1;
+    const static UCHAR STOP_PLAYBACK = 2;
+    const static UCHAR STREAM_END = 3;
+    const static UCHAR ASPECT43 = 4;
+    const static UCHAR ASPECT169 = 5;
+
+#ifdef DEV
+    void test1();
+    void test2();
+#endif
+
+  protected:
+    void threadMethod();
+    void threadPostStopCleanup();
+
+  private:
+    void switchState(UCHAR newState, ULONG jumpFrame=0);
+
+    void threadFeedPlay();
+    void threadFeedScan();
+    void threadPTSFeedScan();
+
+    void doConnectionLost();
+    void restartAtFrame(ULONG newFrame);
+    void restartAtFramePI(ULONG newFrame);
+
+    bool subtitlesShowing{};
+    MessageQueue* messageQueue;
+    void* messageReceiver;
+    OSDReceiver* osdReceiver;
+    Log* logger;
+    Audio* audio;
+    Video* video;
+    Demuxer* demuxer;
+    DVBSubtitles* subtitles;
+    VDR* vdr;
+    VFeed vfeed;
+    AFeed afeed;
+    TFeed tfeed;
+    TeletextDecoderVBIEBU *teletext;
+  
+    bool initted{};
+    bool startup;
+    bool videoStartup{};
+
+    bool is_pesrecording{true};
+    double fps;
+
+    std::mutex stateMutex;
+
+    ULLONG lengthBytes{};
+    ULONG lengthFrames{};
+    ULONG currentFrameNumber{};
+    UINT blockSize{100000};
+    UINT startupBlockSize{250000};
+    UCHAR* threadBuffer{};
+    UCHAR state{S_STOP};
+    UCHAR ifactor{4}; // 4, 8, 16, 32
+};
+
+#endif
+
+
+/*
+
+Possible states:
+
+Play, Pause, FFwd, FBwd, (Stop), [Jump]
+
+                    Possible  Working
+
+Play   -> PauseP       *         *
+       -> PauseI
+       -> FFwd         *         *
+       -> FBwd         *         *
+       -> Stop         *         *
+       -> Jump         *         *
+       -> Jump_PI      *         *
+
+PauseP -> Play         *         *
+       -> PauseI
+       -> FFwd         *         *
+       -> FBwd         *         *
+       -> Stop         *         *
+       -> Jump         *         *
+       -> Jump_PI      *         *
+
+PauseI -> Play         *         *
+       -> PauseP
+       -> FFwd         *         *
+       -> FBwd         *         *
+       -> Stop         *         *
+       -> Jump         *         *
+       -> Jump_PI      *         *
+
+FFwd   -> Play         *         *
+       -> PauseP
+       -> PauseI       *         *
+       -> FBwd         *         *
+       -> Stop         *         *
+       -> Jump         *         *
+       -> Jump_PI      *         *
+
+FBwd   -> Play         *         *
+       -> PauseP
+       -> PauseI       *         *
+       -> FFwd         *         *
+       -> Stop         *         *
+       -> Jump         *         *
+       -> Jump_PI      *         *
+
+Stop   -> Play         *         *
+       -> PauseP
+       -> PauseI
+       -> FFwd
+       -> FBwd
+       -> Jump
+       -> Jump_PI
+
+*/
diff --git a/vdr.cc b/vdr.cc
index c54e8f4318d4aa35b4f5a0ae9a882c709b3954a3..dc2820dd112a0bd4431656556738b315ec145f3a 100644 (file)
--- a/vdr.cc
+++ b/vdr.cc
@@ -409,7 +409,7 @@ void VDR::threadMethod()
 
       vresp = new VDR_ResponsePacket();  
       vresp->setResponse(requestID, reinterpret_cast<UCHAR*>(userData), userDataLength);
-      logger->log("VDR", Log::DEBUG, "Rxd a response packet, requestID=%lu, len=%lu", requestID, userDataLength);
+//      logger->log("VDR", Log::DEBUG, "Rxd a response packet, requestID=%lu, len=%lu", requestID, userDataLength);
 
       if (!edFindAndCall(vresp)) // makes ED lock, find receiver for vresp (using ed_cb_find() ) and then call (using ed_cb_call() )
       {
index d53b7fa84f22c7e9d2567daac0fa8e1c8874958c..f574ef3b7afe2ab2988c53e11e79c4ef0813abf2 100644 (file)
@@ -19,7 +19,6 @@
 
 #include "command.h"
 #include "osd.h"
-#include "player.h"
 #include "wsymbol.h"
 #include "recording.h"
 #include "recinfo.h"
@@ -312,7 +311,7 @@ void VRadioRec::processMessage(Message* m)
 
     switch(m->parameter)
     {
-      case Player::CONNECTION_LOST: // connection lost detected
+      case PlayerRadio::CONNECTION_LOST: // connection lost detected
       {
         // I can't handle this, send it to command
         Message* m2 = new Message();
@@ -321,7 +320,7 @@ void VRadioRec::processMessage(Message* m)
         MessageQueue::getInstance()->postMessage(m2);
         break;
       }
-      case Player::STOP_PLAYBACK:
+      case PlayerRadio::STOP_PLAYBACK:
       {
         // FIXME Obselete ish - improve this
         Message* m2 = new Message(); // Must be done after this thread finishes, and must break into master mutex
@@ -386,9 +385,9 @@ void VRadioRec::doBar(int action)
   else
   {
     playerState = player->getState();
-    if (playerState == Player::S_PAUSE_P)      w.nextSymbol = WSymbol::PAUSE;
-    else if (playerState == Player::S_PAUSE_I) w.nextSymbol = WSymbol::PAUSE;
-    else                                       w.nextSymbol = WSymbol::PLAY;
+    if (playerState == PlayerRadio::S_PAUSE_P)      w.nextSymbol = WSymbol::PAUSE;
+    else if (playerState == PlayerRadio::S_PAUSE_I) w.nextSymbol = WSymbol::PAUSE;
+    else                                            w.nextSymbol = WSymbol::PLAY;
   }
 
   w.draw();
index f212b94629f70c6276dc2acf18c5e0c1c813480b..e71fa0d37c2decb54a7604fe28e10a0d048df489 100644 (file)
@@ -25,7 +25,7 @@
 #include "audio.h"
 #include "vdr.h"
 #include "video.h"
-#include "player.h"
+#include "playervideorec.h"
 #include "recording.h"
 #include "vaudioselector.h"
 #include "message.h"
@@ -56,7 +56,7 @@ VVideoRec::VVideoRec(Recording* rec, bool ish264)
 
   video->seth264mode(ish264);
 
-  player = new Player(Command::getInstance(), this, this);
+  player = new PlayerVideoRec(Command::getInstance(), this, this);
   player->init(myRec->IsPesRecording,myRec->recInfo->fps);
 
   char* cstartMargin = vdr->configLoad("Timers", "Start margin");
@@ -518,7 +518,7 @@ void VVideoRec::processMessage(Message* m)
     if (m->message != Message::PLAYER_EVENT) return;
     switch(m->parameter)
     {
-      case Player::CONNECTION_LOST: // connection lost detected
+      case PlayerVideoRec::CONNECTION_LOST: // connection lost detected
       {
         // I can't handle this, send it to command
         Message* m2 = new Message();
@@ -527,7 +527,7 @@ void VVideoRec::processMessage(Message* m)
         MessageQueue::getInstance()->postMessage(m2);
         break;
       }
-      case Player::STOP_PLAYBACK:
+      case PlayerVideoRec::STOP_PLAYBACK:
       {
         // FIXME Obselete ish - improve this
         Message* m2 = new Message(); // Must be done after this thread finishes, and must break into master mutex
@@ -536,11 +536,11 @@ void VVideoRec::processMessage(Message* m)
         MessageQueue::getInstance()->postMessage(m2);
         break;
       }
-      case Player::ASPECT43:
+      case PlayerVideoRec::ASPECT43:
       {
         break;
       }
-      case Player::ASPECT169:
+      case PlayerVideoRec::ASPECT169:
       {
         break;
       }
@@ -559,7 +559,7 @@ void VVideoRec::processMessage(Message* m)
       case 0x10: { //dvbsubtitle
           player->setSubtitleChannel((m->parameter & 0xFFFF));
           player->turnSubtitlesOn(true);
-          VTeletextView *vtxt=((Player*)player)->getTeletextDecoder()->getTeletxtView();
+          VTeletextView *vtxt = player->getTeletextDecoder()->getTeletxtView();
           if (vtxt && vtxt->isInSubtitleMode()) {
               BoxStack::getInstance()->remove(vtxt);
           }
@@ -567,7 +567,7 @@ void VVideoRec::processMessage(Message* m)
       case 0xFF: { //nosubtitles
           
            player->turnSubtitlesOn(false);
-           VTeletextView *vtxt=((Player*)player)->getTeletextDecoder()->getTeletxtView();
+           VTeletextView *vtxt = player->getTeletextDecoder()->getTeletxtView();
            if (vtxt && vtxt->isInSubtitleMode()) {
               BoxStack::getInstance()->remove(vtxt);
            }  
@@ -576,7 +576,7 @@ void VVideoRec::processMessage(Message* m)
       case 0x11: { //videotext
           player->turnSubtitlesOn(false);
           doTeletext();
-          ((Player*)player)->getTeletextDecoder()->setPage((m->parameter & 0xFFFF));
+          player->getTeletextDecoder()->setPage((m->parameter & 0xFFFF));
                  } break;
       };
       if (vas) {
@@ -815,16 +815,16 @@ void VVideoRec::doBar(int action_in)
   else
   {
     playerState = player->getState();
-    if (playerState == Player::S_PAUSE_P)      w.nextSymbol = WSymbol::PAUSE;
-    else if (playerState == Player::S_PAUSE_I) w.nextSymbol = WSymbol::PAUSE;
-    else if (playerState == Player::S_FFWD)    w.nextSymbol = WSymbol::FFWD;
-    else if (playerState == Player::S_FBWD)    w.nextSymbol = WSymbol::FBWD;
+    if (playerState == PlayerVideoRec::S_PAUSE_P)      w.nextSymbol = WSymbol::PAUSE;
+    else if (playerState == PlayerVideoRec::S_PAUSE_I) w.nextSymbol = WSymbol::PAUSE;
+    else if (playerState == PlayerVideoRec::S_FFWD)    w.nextSymbol = WSymbol::FFWD;
+    else if (playerState == PlayerVideoRec::S_FBWD)    w.nextSymbol = WSymbol::FBWD;
     else                                       w.nextSymbol = WSymbol::PLAY;
   }
 
   w.draw();
 
-  if ((playerState == Player::S_FFWD) || (playerState == Player::S_FBWD))
+  if ((playerState == PlayerVideoRec::S_FFWD) || (playerState == PlayerVideoRec::S_FBWD))
   {
     // draw blips to show how fast the scan is
     UCHAR scanrate = player->getIScanRate();
@@ -844,7 +844,7 @@ void VVideoRec::doBar(int action_in)
          timers->cancelTimer(this, 1);
 
 
-         if ((playerState == Player::S_FFWD) || (playerState == Player::S_FBWD)) barScanHold = true;
+         if ((playerState == PlayerVideoRec::S_FFWD) || (playerState == PlayerVideoRec::S_FBWD)) barScanHold = true;
          else barScanHold = false;
 
          if (!barGenHold && !barScanHold && !barVasHold) timers->setTimerD(this, 1, 4);
@@ -889,7 +889,7 @@ void VVideoRec::drawBarClocks()
     // will repaint all the bar (it will call this function again, but
     // this section won't run because stickyBarF will then == false)
 
-    if ((playerState != Player::S_FFWD) && (playerState != Player::S_FBWD))
+    if ((playerState != PlayerVideoRec::S_FFWD) && (playerState != PlayerVideoRec::S_FBWD))
     {
       barScanHold = false;
       doBar(0);
index 9dfa4fdd78f1de009cbc78d24c6c9beb33b933df..2a68ee6dedb0131b6d30abf1fcb7cca83d388f24 100644 (file)
@@ -31,7 +31,7 @@
 #include "video.h"
 
 class VDR;
-class Player;
+class PlayerVideoRec;
 class Recording;
 class VAudioSelector;
 class Message;
@@ -65,7 +65,7 @@ class VVideoRec : public Boxx, public TimerReceiver, public OSDReceiver
     VDR* vdr;
     Video* video;
     Timers* timers;
-    Player* player;
+    PlayerVideoRec* player;
     Recording* myRec;
 
     VAudioSelector* vas;