]> git.vomp.tv Git - vompclient-marten.git/commitdiff
Some radio playback support
authorChris Tallon <chris@vomp.tv>
Sun, 19 Nov 2006 18:27:43 +0000 (18:27 +0000)
committerChris Tallon <chris@vomp.tv>
Sun, 19 Nov 2006 18:27:43 +0000 (18:27 +0000)
command.cc
demuxer.cc
objects.mk
player.h
playerradio.cc [new file with mode: 0644]
playerradio.h [new file with mode: 0644]
vradiorec.cc [new file with mode: 0644]
vradiorec.h [new file with mode: 0644]
vrecordinglist.cc
vrecordinglist.h
vvideorec.h

index 2eced78551619ee9ddc4b3a49a7a9b3dad7fea72..e7715f87a95450b78c028daa957ee83f21aca606 100644 (file)
@@ -770,11 +770,10 @@ void Command::doJustConnected(VConnect* vconnect)
 
     // Enter pre-keys here
 //    handleCommand(Remote::THREE);
-//    handleCommand(Remote::SKIPFORWARD);
+//    handleCommand(Remote::UP);
 //    handleCommand(Remote::OK);
-//    handleCommand(Remote::PLAY);
 //    handleCommand(Remote::OK);
-//    handleCommand(Remote::DOWN);
+//    handleCommand(Remote::PLAY);
 //    handleCommand(Remote::DOWN);
 //    handleCommand(Remote::DOWN);
 //    handleCommand(Remote::OK);
index f03b279ac0a8c82a0de3c567aecc76d17e138cf2..aff0f7f0b0830a5e0e49d965289cdd59f7a68ffe 100644 (file)
@@ -138,7 +138,7 @@ void Demuxer::setAspectRatio(enum AspectRatio ar)
     {
       arcnt = 0;
       aspect_ratio = ar;
-      callback->call(this);
+      if (callback) callback->call(this);
     }
   }
   else
index d5805b2582665cf1af3cfda73cfd2f1ca5869fd3..50b10d46c486f13a1a65a11b42dda0407a05cb72 100644 (file)
@@ -2,7 +2,7 @@ OBJECTS1 = command.o log.o tcp.o dsock.o thread.o timers.o i18n.o mutex.o     \
            message.o messagequeue.o udp.o                                     \\r
            vdr.o recman.o recording.o recinfo.o channel.o rectimer.o event.o  \\r
            directory.o                                                        \\r
-           player.o vfeed.o afeed.o                                           \\r
+           player.o playerradio.o vfeed.o afeed.o                             \\r
            demuxer.o demuxervdr.o stream.o draintarget.o                      \\r
            viewman.o box.o region.o colour.o view.o                           \\r
            vinfo.o vquestion.o vwallpaper.o vrecordinglist.o vlivebanner.o    \\r
@@ -10,6 +10,7 @@ OBJECTS1 = command.o log.o tcp.o dsock.o thread.o timers.o i18n.o mutex.o     \
            vtimerlist.o vtimeredit.o voptionsmenu.o vrecordingmenu.o          \\r
            vchannellist.o vwelcome.o vvideolive.o vvideorec.o vepgsettimer.o  \\r
            vchannelselect.o vserverselect.o vconnect.o vepg.o vrecmove.o      \\r
+           vradiorec.o                                                        \\r
            widget.o wselectlist.o wjpeg.o wsymbol.o wbutton.o                 \\r
                                         woptionbox.o wtextbox.o wwss.o                                     \\r
            fonts/helvB24.o fonts/helvB18.o                                    \\r
index 39a4d871502364a9c47715542dce2c3639060d23..2e920fd8f0ddc77f719b20c4c8d2eb0b5056dcad 100644 (file)
--- a/player.h
+++ b/player.h
@@ -107,7 +107,6 @@ class Player : public Thread_TYPE, public Callback
     void threadFeedPlay();
     void threadFeedScan();
 
-    void setEndTS();
     void doConnectionLost();
     void restartAtFrame(ULONG newFrame);
 
diff --git a/playerradio.cc b/playerradio.cc
new file mode 100644 (file)
index 0000000..417c7d2
--- /dev/null
@@ -0,0 +1,640 @@
+/*
+    Copyright 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "playerradio.h"
+
+// ----------------------------------- Called from outside, one offs or info funcs
+
+PlayerRadio::PlayerRadio(MessageQueue* tmessageQueue, void* tmessageReceiver, bool tIsRecording)
+: afeed(this)
+{
+  messageQueue = tmessageQueue;
+  messageReceiver = tmessageReceiver;
+  audio = Audio::getInstance();
+  logger = Log::getInstance();
+  vdr = VDR::getInstance();
+  initted = false;
+  lengthBytes = 0;
+  lengthFrames = 0;
+  streamPos = 0;
+  state = S_STOP;
+
+  startPTS = 0;
+  lengthSeconds = 0;
+
+  isRecording = tIsRecording;
+
+  threadBuffer = NULL;
+
+  blockSize = 10000;
+  startupBlockSize = 20000;
+  preBufferSize = 20000;
+}
+
+PlayerRadio::~PlayerRadio()
+{
+  if (initted) shutdown();
+}
+
+int PlayerRadio::init(ULLONG tlengthBytes, ULONG tlengthFrames)
+{
+  if (initted) return 0;
+#ifndef WIN32
+  pthread_mutex_init(&mutex, NULL);
+#else
+  mutex=CreateMutex(NULL,FALSE,NULL);
+#endif
+
+  demuxer = new DemuxerVDR();
+  if (!demuxer) return 0;
+
+  if (!demuxer->init(this, audio, NULL))
+  {
+    logger->log("PlayerRadio", Log::ERR, "Demuxer failed to init");
+    shutdown();
+    return 0;
+  }
+
+  afeed.init();
+  audio->stop();
+
+  lengthBytes = tlengthBytes;
+  lengthFrames = tlengthFrames;
+
+  logger->log("PlayerRadio", Log::DEBUG, "PlayerRadio has received length bytes of %llu", lengthBytes);
+
+  UINT thisRead = 0;
+  ULLONG endPTS = 0;
+  int success;
+
+  threadBuffer = vdr->getBlock(0, 10000, &thisRead);
+  if (!threadBuffer)
+  {
+    logger->log("PlayerRadio", Log::ERR, "Failed to get start block");
+    shutdown();
+    return 0;
+  }
+
+  success = demuxer->findPTS(threadBuffer, thisRead, &startPTS);
+  if (!success)
+  {
+    logger->log("PlayerRadio", Log::ERR, "Failed to get start PTS");
+    shutdown();
+    return 0;
+  }
+
+  threadBuffer = vdr->getBlock(tlengthBytes - 10000, 10000, &thisRead);
+  if (!threadBuffer)
+  {
+    logger->log("PlayerRadio", Log::ERR, "Failed to get end block");
+    shutdown();
+    return 0;
+  }
+
+  success = demuxer->findPTS(threadBuffer, thisRead, &endPTS);
+  if (!success)
+  {
+    logger->log("PlayerRadio", Log::ERR, "Failed to get end PTS");
+    shutdown();
+    return 0;
+  }
+
+  logger->log("PlayerRadio", Log::DEBUG, "Start: %llu, End: %llu", startPTS, endPTS);
+
+  if (startPTS < endPTS)
+  {
+    lengthSeconds = (endPTS - startPTS) / 90000;
+  }
+  else
+  {
+    lengthSeconds = (startPTS - endPTS) / 90000;
+  }
+
+  initted = true;
+  return 1;
+}
+
+int PlayerRadio::shutdown()
+{
+  if (!initted) return 0;
+  switchState(S_STOP);
+  initted = false;
+
+  delete demuxer;
+  demuxer = NULL;
+
+#ifdef WIN32
+  CloseHandle(mutex);
+#endif
+
+  return 1;
+}
+
+
+void PlayerRadio::setStartBytes(ULLONG startBytes)
+{
+  streamPos = startBytes;
+}
+
+ULONG PlayerRadio::getLengthSeconds()
+{
+  return lengthSeconds;
+}
+
+ULONG PlayerRadio::getCurrentSeconds()
+{
+  if (startup) return 0;
+  return 0;
+}
+
+// ----------------------------------- Externally called events
+
+void PlayerRadio::play()
+{
+  if (!initted) return;
+  if (state == S_PLAY) return;
+  lock();
+  switchState(S_PLAY);
+  unLock();
+}
+
+void PlayerRadio::stop()
+{
+  if (!initted) return;
+  if (state == S_STOP) return;
+  lock();
+  logger->log("PlayerRadio", Log::DEBUG, "Stop called lock");
+  switchState(S_STOP);
+  unLock();
+}
+
+void PlayerRadio::pause()
+{
+  if (!initted) return;
+  lock();
+
+  if (state == S_PAUSE_P)
+  {
+    switchState(S_PLAY);
+  }
+  else
+  {
+    switchState(S_PAUSE_P);
+  }
+
+  unLock();
+}
+
+void PlayerRadio::jumpToPercent(int percent)
+{
+  lock();
+  logger->log("PlayerRadio", Log::DEBUG, "JUMP TO %i%%", percent);
+  ULONG newFrame = percent * lengthFrames / 100;
+  switchState(S_JUMP, newFrame);
+  unLock();
+}
+
+void PlayerRadio::skipForward(int seconds)
+{
+  lock();
+  logger->log("PlayerRadio", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
+  ULONG newFrame = getCurrentSeconds();
+  if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
+//  newFrame += seconds * video->getFPS();
+  if (newFrame > lengthFrames) { switchState(S_PLAY); unLock(); }
+  else switchState(S_JUMP, newFrame);
+  unLock();
+}
+
+void PlayerRadio::skipBackward(int seconds)
+{
+  lock();
+  logger->log("PlayerRadio", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
+  long newFrame = getCurrentSeconds();
+  if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
+//  newFrame -= seconds * video->getFPS();
+  if (newFrame < 0) newFrame = 0;
+  switchState(S_JUMP, newFrame);
+  unLock();
+}
+
+// ----------------------------------- Implementations called events
+
+void PlayerRadio::switchState(UCHAR toState, ULONG jumpFrame)
+{
+  if (!initted) return;
+
+  logger->log("PlayerRadio", 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
+        {
+          audio->pause();
+          state = S_PAUSE_P;
+          return;
+        }
+        case S_STOP: // to S_STOP
+        {
+          afeed.stop();
+          threadStop();
+          audio->stop();
+          audio->unPause();
+          demuxer->reset();
+          state = S_STOP;
+          return;
+        }
+        case S_JUMP: // to S_JUMP
+        {
+          restartAtFrame(jumpFrame);
+          return;
+        }
+      }
+    }
+    case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
+    {
+      switch(toState)
+      {
+        case S_PLAY: // to S_PLAY
+        {
+          audio->unPause();
+          state = S_PLAY;
+          return;
+        }
+        case S_PAUSE_P: // to S_PAUSE_P
+        {
+          return;
+        }
+        case S_STOP: // to S_STOP
+        {
+          afeed.stop();
+          threadStop();
+          audio->stop();
+          audio->unPause();
+          demuxer->reset();
+          audio->systemMuteOff();
+          state = S_STOP;
+          return;
+        }
+        case S_JUMP: // to S_JUMP
+        {
+          state = S_PLAY;
+          audio->unPause();
+          restartAtFrame(jumpFrame);
+          return;
+        }
+      }
+    }
+    case S_STOP: // from S_STOP -----------------------------------
+    {
+      switch(toState)
+      {
+        case S_PLAY: // to S_PLAY
+        {
+          startup = true;
+
+          audio->reset();
+          audio->systemMuteOff();
+          demuxer->reset();
+          if (isRecording)
+          {
+            // FIXME use restartAtFrame here?
+            if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
+            demuxer->setFrameNum(currentFrameNumber);
+          }
+
+          state = S_PLAY;
+          threadStart();
+
+          if (isRecording)
+          {
+            logger->log("PlayerRadio", Log::DEBUG, "Immediate play");
+            afeed.start();
+            audio->sync();
+            audio->play();
+          }
+          else // do prebuffering
+          {
+            logger->log("PlayerRadio", Log::DEBUG, "Prebuffering...");
+            preBuffering = true;
+          }
+          return;
+        }
+        case S_PAUSE_P: // to S_PAUSE_P
+        {
+          return;
+        }
+        case S_STOP: // to S_STOP
+        {
+          return;
+        }
+        case S_JUMP: // to S_JUMP
+        {
+          return;
+        }
+      }
+    }
+    // case S_JUMP cannot be selected as a start state because it auto flips to play
+  }
+}
+
+// ----------------------------------- Internal functions
+
+void PlayerRadio::lock()
+{
+#ifndef WIN32
+  pthread_mutex_lock(&mutex);
+  logger->log("PlayerRadio", Log::DEBUG, "LOCKED");
+
+#else
+   WaitForSingleObject(mutex, INFINITE);
+#endif
+}
+
+void PlayerRadio::unLock()
+{
+#ifndef WIN32
+  logger->log("PlayerRadio", Log::DEBUG, "UNLOCKING");
+  pthread_mutex_unlock(&mutex);
+#else
+   ReleaseMutex(mutex);
+#endif
+}
+
+void PlayerRadio::restartAtFrame(ULONG newFrame)
+{
+  afeed.stop();
+  threadStop();
+  audio->reset();
+  demuxer->flush();
+  currentFrameNumber = newFrame;
+  demuxer->setFrameNum(newFrame);
+  afeed.start();
+  threadStart();
+  audio->play();
+  audio->sync();
+  audio->systemMuteOff();
+  audio->doMuting();
+}
+
+void PlayerRadio::doConnectionLost()
+{
+  logger->log("PlayerRadio", Log::DEBUG, "Connection lost, sending message");
+  Message* m = new Message();
+  m->to = messageReceiver;
+  m->from = this;
+  m->message = Message::PLAYER_EVENT;
+  m->parameter = PlayerRadio::CONNECTION_LOST;
+  messageQueue->postMessage(m);
+}
+
+// ----------------------------------- Callback
+
+void PlayerRadio::call(void* caller)
+{
+  threadSignalNoLock();
+}
+
+// ----------------------------------- Feed thread
+
+void PlayerRadio::threadMethod()
+{
+  if (isRecording)
+  {
+    if (state == S_PLAY) threadFeedPlay();
+  }
+  else
+  {
+    threadFeedLive();
+  }
+}
+
+void PlayerRadio::threadFeedLive()
+{
+  UINT thisRead;
+  UINT writeLength;
+  UINT thisWrite;
+  UINT preBufferTotal = 0;
+
+  UINT askFor;
+  while(1)
+  {
+    thisRead = 0;
+    writeLength = 0;
+    thisWrite = 0;
+
+    threadCheckExit();
+
+    if (startup)
+      askFor = startupBlockSize; // find audio streams sized block
+    else
+      askFor = blockSize; // normal
+
+    threadBuffer = vdr->getBlock(0, askFor, &thisRead);
+
+    if (!vdr->isConnected())
+    {
+      doConnectionLost();
+      return;
+    }
+
+    if (!threadBuffer) break;
+
+    if (startup)
+    {
+      int a_stream = demuxer->scan(threadBuffer, thisRead);
+      demuxer->setAudioStream(a_stream);
+      logger->log("PlayerRadio", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
+      startup = false;
+    }
+
+    if (preBuffering)
+    {
+      preBufferTotal += thisRead;
+      if (preBufferTotal >= preBufferSize)
+      {
+        logger->log("PlayerRadio", Log::DEBUG, "Got >500K, prebuffering complete");
+
+        preBuffering = false;
+        preBufferTotal = 0;
+
+        audio->sync();
+        audio->play();
+        afeed.start();
+//        unLock(); // thread will be locked by play until here
+        // FIXME - see if this can segfault because it is starting threads out of the master mutex
+      }
+    }
+
+    threadCheckExit();
+
+    while(writeLength < thisRead)
+    {
+      thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
+      writeLength += thisWrite;
+
+      if (!thisWrite)
+      {
+        // demuxer is full and can't take anymore
+        threadLock();
+        threadWaitForSignal();
+        threadUnlock();
+      }
+
+      threadCheckExit();
+    }
+
+    free(threadBuffer);
+    threadBuffer = NULL;
+
+  }
+
+  logger->log("PlayerRadio", Log::DEBUG, "Live play failed to start or interrupted");
+
+  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 = PlayerRadio::STREAM_END;
+  logger->log("PlayerRadio", Log::DEBUG, "Posting message to %p...", messageQueue);
+  messageQueue->postMessage(m);
+  logger->log("PlayerRadio", Log::DEBUG, "Message posted...");
+}
+
+void PlayerRadio::threadFeedPlay()
+{
+  ULLONG feedPosition;
+  UINT thisRead, writeLength, thisWrite, askFor;
+  time_t lastRescan = time(NULL);
+
+  feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
+  if (!vdr->isConnected()) { doConnectionLost(); return; }
+  logger->log("PlayerRadio", 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("PlayerRadio", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
+      lastRescan = time(NULL);
+    }
+
+    if (feedPosition >= lengthBytes) break;  // finished playback
+
+    if (startup)
+    {
+      if (startupBlockSize > lengthBytes)
+        askFor = lengthBytes; // is a very small recording!
+      else
+        askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
+    }
+    else
+    {
+      if ((feedPosition + blockSize) > lengthBytes) // last block of recording
+        askFor = lengthBytes - feedPosition;
+      else // normal
+        askFor = blockSize;
+    }
+
+    threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
+    feedPosition += thisRead;
+
+    if (!vdr->isConnected())
+    {
+      doConnectionLost();
+      return;
+    }
+
+    if (!threadBuffer) break;
+
+    if (startup)
+    {
+      int a_stream = demuxer->scan(threadBuffer, thisRead);
+      demuxer->setAudioStream(a_stream);
+      logger->log("PlayerRadio", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
+      startup = false;
+    }
+
+    threadCheckExit();
+
+    while(writeLength < thisRead)
+    {
+      thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
+      writeLength += thisWrite;
+
+      if (!thisWrite)
+      {
+        // demuxer is full and can't take anymore
+        threadLock();
+        threadWaitForSignal();
+        threadUnlock();
+      }
+
+      threadCheckExit();
+    }
+
+    free(threadBuffer);
+    threadBuffer = NULL;
+
+  }
+
+  // end of recording
+  logger->log("PlayerRadio", Log::DEBUG, "Recording playback ends");
+
+  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 = PlayerRadio::STOP_PLAYBACK;
+  logger->log("PlayerRadio", Log::DEBUG, "Posting message to %p...", messageQueue);
+  messageQueue->postMessage(m);
+}
+
+void PlayerRadio::threadPostStopCleanup()
+{
+  if (threadBuffer)
+  {
+    free(threadBuffer);
+    threadBuffer = NULL;
+  }
+}
diff --git a/playerradio.h b/playerradio.h
new file mode 100644 (file)
index 0000000..26f204d
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+    Copyright 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#ifndef PLAYERRADIO_H
+#define PLAYERRADIO_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#ifndef WIN32
+#include <sys/time.h>
+#endif
+#include <time.h>
+
+#include "audio.h"
+#include "demuxervdr.h"
+#include "afeed.h"
+#include "remote.h"
+#include "vdr.h"
+#include "callback.h"
+#include "message.h"
+#include "messagequeue.h"
+
+#ifdef WIN32
+#include "threadwin.h"
+#else
+#include "threadp.h"
+#endif
+
+class PlayerRadio : public Thread_TYPE, public Callback
+{
+  public:
+    PlayerRadio(MessageQueue* messageQueue, void* messageReceiver, bool isRecording);
+    virtual ~PlayerRadio();
+
+    int init(ULLONG lengthBytes, ULONG lengthFrames);
+    int shutdown();
+    void setStartBytes(ULLONG startBytes);
+
+    void play();
+    void stop();
+    void pause();
+    void jumpToPercent(int percent);
+    void skipForward(int seconds);
+    void skipBackward(int seconds);
+
+    UCHAR getState() { return state; }
+    ULONG getCurrentSeconds();
+    ULONG getLengthSeconds();
+
+    void call(void*); // for callback interface
+
+    const static UCHAR S_PLAY = 1;
+    const static UCHAR S_PAUSE_P = 2;
+    const static UCHAR S_STOP = 6;
+    const static UCHAR S_JUMP = 7;
+
+    // Player events
+
+    const static UCHAR CONNECTION_LOST = 1;
+    const static UCHAR STOP_PLAYBACK = 2;
+    const static UCHAR STREAM_END = 3;
+
+  protected:
+    void threadMethod();
+    void threadPostStopCleanup();
+
+  private:
+    void switchState(UCHAR newState, ULONG jumpFrame=0);
+
+    void threadFeedLive();
+    void threadFeedPlay();
+    void threadFeedScan();
+
+    void doConnectionLost();
+    void restartAtFrame(ULONG newFrame);
+
+    MessageQueue* messageQueue;
+    void* messageReceiver;
+    Log* logger;
+    Audio* audio;
+    Demuxer* demuxer;
+    VDR* vdr;
+    AFeed afeed;
+
+    bool initted;
+    bool startup;
+    bool isRecording;
+    bool preBuffering;
+
+    ULLONG startPTS;
+    ULONG lengthSeconds;
+
+#ifndef WIN32
+    pthread_mutex_t mutex;
+#else
+    HANDLE mutex;
+#endif
+    void lock();
+    void unLock();
+
+    ULLONG lengthBytes;
+    ULLONG streamPos;
+    ULONG lengthFrames;
+    ULONG currentFrameNumber;
+    UINT blockSize;
+    UINT startupBlockSize;
+    UINT preBufferSize;
+    UCHAR* threadBuffer;
+    UCHAR state;
+};
+
+#endif
+
+
+/*
+
+Possible states:
+
+Play, Pause, FFwd, FBwd, (Stop), [Jump]
+
+                    Possible  Working
+
+Play   -> PauseP       *         *
+       -> Stop         *         *
+       -> Jump         *         *
+
+PauseP -> Play         *         *
+       -> Stop         *         *
+       -> Jump         *         *
+
+PauseI -> Play         *         *
+       -> PauseP
+       -> Stop         *         *
+       -> Jump         *         *
+
+Stop   -> Play         *         *
+       -> PauseP
+       -> Jump
+
+Jump   -> Play
+       -> PauseP
+       -> Stop
+
+*/
diff --git a/vradiorec.cc b/vradiorec.cc
new file mode 100644 (file)
index 0000000..c31cd38
--- /dev/null
@@ -0,0 +1,505 @@
+/*
+    Copyright 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "vradiorec.h"
+
+VRadioRec::VRadioRec(Recording* rec)
+{
+  vdr = VDR::getInstance();
+  video = Video::getInstance();
+  timers = Timers::getInstance();
+  myRec = rec;
+  playing = false;
+  startMargin = 0;
+  endMargin = 0;
+
+  player = new PlayerRadio(Command::getInstance(), this, true);
+
+  char* cstartMargin = vdr->configLoad("Timers", "Start margin");
+  char* cendMargin = vdr->configLoad("Timers", "End margin");
+  if (!cstartMargin)
+  {
+    startMargin = 300; // 5 mins default
+  }
+  else
+  {
+    startMargin = atoi(cstartMargin) * 60;
+    delete[] cstartMargin;
+  }
+
+  if (!cendMargin)
+  {
+    endMargin = 300; // 5 mins default
+  }
+  else
+  {
+    endMargin = atoi(cendMargin) * 60;
+    delete[] cendMargin;
+  }
+
+  Log::getInstance()->log("VRadioRec", Log::DEBUG, "SM: %u EM: %u", startMargin, endMargin);
+
+  create(video->getScreenWidth(), video->getScreenHeight());
+  setBackgroundColour(Colour::BLACK);
+
+  barRegion.x = 0;
+  barRegion.y = video->getScreenHeight() - 58;   // FIXME, need to be - 1? and below?
+  barRegion.w = video->getScreenWidth();
+  barRegion.h = 58;
+
+  clocksRegion.x = barRegion.x + 140;
+  clocksRegion.y = barRegion.y + 12;
+  clocksRegion.w = 170;
+  clocksRegion.h = surface->getFontHeight();
+
+
+  barBlue.set(0, 0, 150, 150);
+
+  barShowing = false;
+}
+
+VRadioRec::~VRadioRec()
+{
+  if (playing) stopPlay();
+
+  timers->cancelTimer(this, 1);
+  timers->cancelTimer(this, 2);
+
+  // kill recInfo in case resumePoint has changed (likely)
+  myRec->dropRecInfo();
+  // FIXME - do this properly - save the resume point back to the server manually and update
+  // rec->recInfo->resumePoint - this will fix the ~10s offset problem as well
+}
+
+void VRadioRec::draw()
+{
+  View::draw();
+}
+
+void VRadioRec::go()
+{
+  Log::getInstance()->log("VRadioRec", Log::DEBUG, "Starting stream: %s", myRec->getFileName());
+  ULONG lengthFrames = 0;
+  ULLONG lengthBytes = vdr->streamRecording(myRec->getFileName(), &lengthFrames);
+
+  bool cantStart = false;
+
+  if (!lengthBytes) cantStart = true;
+  else if (!player->init(lengthBytes, lengthFrames)) cantStart = true;
+  else
+  {
+    doBar(0);
+  //  player->setStartBytes(startBytes);
+    player->play();
+    playing = true;
+  }
+
+  if (cantStart)
+  {
+    stopPlay(); // clean up
+
+    if (!vdr->isConnected())
+    {
+      Command::getInstance()->connectionLost();
+      return;
+    }
+
+    ViewMan* viewman = ViewMan::getInstance();
+
+    Message* m = new Message();
+    m->message = Message::CLOSE_ME;
+    m->from = this;
+    m->to = viewman;
+    Command::getInstance()->postMessageNoLock(m);
+
+    VInfo* vi = new VInfo();
+    vi->create(400, 150);
+    if (video->getFormat() == Video::PAL)
+      vi->setScreenPos(170, 200);
+    else
+      vi->setScreenPos(160, 150);
+    vi->setExitable();
+    vi->setBorderOn(1);
+    vi->setTitleBarOn(0);
+    vi->setOneLiner(tr("Error playing recording"));
+    vi->draw();
+
+    m = new Message();
+    m->message = Message::ADD_VIEW;
+    m->to = viewman;
+    m->parameter = (ULONG)vi;
+    Command::getInstance()->postMessageNoLock(m);
+  }
+}
+
+int VRadioRec::handleCommand(int command)
+{
+  switch(command)
+  {
+    case Remote::PLAY:
+    {
+      player->play();
+      doBar(0);
+      return 2;
+    }
+
+    case Remote::STOP:
+    case Remote::BACK:
+    case Remote::MENU:
+    {
+      if (playing) stopPlay();
+      return 4;
+    }
+    case Remote::PAUSE:
+    {
+      player->pause();
+      doBar(0);
+      return 2;
+    }
+    case Remote::SKIPFORWARD:
+    {
+      doBar(3);
+      player->skipForward(60);
+      return 2;
+    }
+    case Remote::SKIPBACK:
+    {
+      doBar(4);
+      player->skipBackward(60);
+      return 2;
+    }
+    case Remote::YELLOW:
+    {
+      doBar(2);
+      player->skipBackward(10);
+      return 2;
+    }
+    case Remote::BLUE:
+    {
+      doBar(1);
+      player->skipForward(10);
+      return 2;
+    }
+    case Remote::OK:
+    {
+      if (barShowing) removeBar();
+      else doBar(0);
+      return 2;
+    }
+
+    case Remote::ZERO:  player->jumpToPercent(0);  doBar(0);  return 2;
+    case Remote::ONE:   player->jumpToPercent(10); doBar(0);  return 2;
+    case Remote::TWO:   player->jumpToPercent(20); doBar(0);  return 2;
+    case Remote::THREE: player->jumpToPercent(30); doBar(0);  return 2;
+    case Remote::FOUR:  player->jumpToPercent(40); doBar(0);  return 2;
+    case Remote::FIVE:  player->jumpToPercent(50); doBar(0);  return 2;
+    case Remote::SIX:   player->jumpToPercent(60); doBar(0);  return 2;
+    case Remote::SEVEN: player->jumpToPercent(70); doBar(0);  return 2;
+    case Remote::EIGHT: player->jumpToPercent(80); doBar(0);  return 2;
+    case Remote::NINE:  player->jumpToPercent(90); doBar(0);  return 2;
+
+#ifdef DEV
+    case Remote::RED:
+    {
+      //player->test1();
+
+
+      /*
+      // for testing EPG in NTSC with a NTSC test video
+      Video::getInstance()->setMode(Video::QUARTER);
+      Video::getInstance()->setPosition(170, 5);
+      VEpg* vepg = new VEpg(NULL, 0);
+      vepg->draw();
+      ViewMan::getInstance()->add(vepg);
+      ViewMan::getInstance()->updateView(vepg);
+      */
+
+      return 2;
+    }
+    case Remote::GREEN:
+    {
+      //player->test2();
+      return 2;
+    }
+#endif
+
+  }
+
+  return 1;
+}
+
+void VRadioRec::processMessage(Message* m)
+{
+  if (m->from != player) return;
+  if (m->message != Message::PLAYER_EVENT) return;
+
+  Log::getInstance()->log("VRadioRec", Log::DEBUG, "Message received");
+
+  switch(m->parameter)
+  {
+    case Player::CONNECTION_LOST: // connection lost detected
+    {
+      // I can't handle this, send it to command
+      Message* m = new Message();
+      m->to = Command::getInstance();
+      m->message = Message::CONNECTION_LOST;
+      Command::getInstance()->postMessageNoLock(m);
+      break;
+    }
+    case Player::STOP_PLAYBACK:
+    {
+      // FIXME Obselete ish - improve this
+      Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
+      m->to = Command::getInstance();
+      m->message = Message::STOP_PLAYBACK;
+      Command::getInstance()->postMessageNoLock(m);
+      break;
+    }
+  }
+}
+
+void VRadioRec::stopPlay()
+{
+  Log::getInstance()->log("VRadioRec", Log::DEBUG, "Pre stopPlay");
+
+  // FIXME work out a better soln for this
+  // Fix a problem to do with thread sync here
+  // because the bar gets a timer every 0.2s and it seems to take up to 0.1s,
+  // (or maybe just the wrong thread being selected?) for the main loop to lock and process
+  // the video stop message it is possible for a bar message to stack up after a stop message
+  // when the bar message is finally processed the prog crashes because this is deleted by then
+  removeBar();
+  //
+
+  player->stop();
+  vdr->stopStreaming();
+  delete player;
+
+  playing = false;
+
+  if (!vdr->isConnected()) { Command::getInstance()->connectionLost(); return; }
+  Log::getInstance()->log("VRadioRec", Log::DEBUG, "Post stopPlay");
+}
+
+void VRadioRec::doBar(int action)
+{
+  barShowing = true;
+
+  rectangle(barRegion, barBlue);
+
+  /* Work out what to display - choices:
+
+  Playing  >
+  Paused   ||
+
+  Specials, informed by parameter
+
+  Skip forward 10s    >|
+  Skip backward 10s   |<
+  Skip forward 1m     >>|
+  Skip backward 1m    |<<
+
+  */
+
+  WSymbol w;
+  w.setSurface(surface);
+  w.nextSymbol = 0;
+  w.setSurfaceOffset(barRegion.x + 66, barRegion.y + 16);
+
+  UCHAR playerState = 0;
+
+  if (action)
+  {
+    if (action == 1)       w.nextSymbol = WSymbol::SKIPFORWARD;
+    else if (action == 2)  w.nextSymbol = WSymbol::SKIPBACK;
+    else if (action == 3)  w.nextSymbol = WSymbol::SKIPFORWARD2;
+    else if (action == 4)  w.nextSymbol = WSymbol::SKIPBACK2;
+  }
+  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;
+  }
+
+  w.draw();
+
+  drawBarClocks();
+
+  ViewMan::getInstance()->updateView(this, &barRegion);
+
+  timers->setTimerD(this, 1, 4); // only set the getridofbar timer if not ffwd/fbwd
+  timers->setTimerD(this, 2, 0, 200000000);
+}
+
+void VRadioRec::timercall(int clientReference)
+{
+  switch(clientReference)
+  {
+    case 1:
+    {
+      // Remove bar
+      removeBar();
+      break;
+    }
+    case 2:
+    {
+      // Update clock
+      if (!barShowing) break;
+      drawBarClocks();
+      ViewMan::getInstance()->updateView(this, &barRegion);
+      timers->setTimerD(this, 2, 0, 200000000);
+      break;
+    }
+  }
+}
+
+void VRadioRec::drawBarClocks()
+{
+  Log* logger = Log::getInstance();
+  logger->log("VRadioRec", Log::DEBUG, "Draw bar clocks");
+
+  // Draw RTC
+  // Blank the area first
+  rectangle(barRegion.x + 624, barRegion.y + 12, 60, 30, barBlue);
+  char timeString[20];
+  time_t t;
+  time(&t);
+  struct tm* tms = localtime(&t);
+  strftime(timeString, 19, "%H:%M", tms);
+  drawText(timeString, barRegion.x + 624, barRegion.y + 12, Colour::LIGHTTEXT);
+
+  // Draw clocks
+
+  rectangle(clocksRegion, barBlue);
+
+/*
+  ULONG currentFrameNum = player->getCurrentFrameNum();
+  ULONG lengthFrames;
+  if (myRec->recInfo->timerEnd > time(NULL))
+  {
+    // chasing playback
+    // Work out an approximate length in frames (good to 1s...)
+    lengthFrames = (myRec->recInfo->timerEnd - myRec->recInfo->timerStart) * video->getFPS();
+  }
+  else
+  {
+//    lengthFrames = player->getLengthFrames();
+    lengthFrames = 0;
+  }
+
+  hmsf currentFrameHMSF = video->framesToHMSF(currentFrameNum);
+  hmsf lengthHMSF = video->framesToHMSF(lengthFrames);
+
+  char buffer[100];
+  if (currentFrameNum >= lengthFrames)
+  {
+    strcpy(buffer, "-:--:-- / -:--:--");
+  }
+  else
+  {
+    SNPRINTF(buffer, 99, "%01i:%02i:%02i / %01i:%02i:%02i", currentFrameHMSF.hours, currentFrameHMSF.minutes, currentFrameHMSF.seconds, lengthHMSF.hours, lengthHMSF.minutes, lengthHMSF.seconds);
+    logger->log("VRadioRec", Log::DEBUG, buffer);
+  }
+*/
+
+  ULONG currentSeconds = player->getCurrentSeconds();
+  ULONG lengthSeconds = player->getLengthSeconds();
+  char buffer[100];
+
+  if (lengthSeconds && (currentSeconds < lengthSeconds))
+  {
+    ULONG dcurrentSeconds = currentSeconds;
+    ULONG dlengthSeconds = lengthSeconds;
+
+    ULONG currentHours = dcurrentSeconds / 3600;
+    dcurrentSeconds %= 3600;
+    ULONG currentMinutes = dcurrentSeconds / 60;
+    dcurrentSeconds %= 60;
+
+    ULONG lengthHours = dlengthSeconds / 3600;
+    dlengthSeconds %= 3600;
+    ULONG lengthMinutes = dlengthSeconds / 60;
+    dlengthSeconds %= 60;
+
+    SNPRINTF(buffer, 99, "%01lu:%02lu:%02lu / %01lu:%02lu:%02lu", currentHours, currentMinutes, dcurrentSeconds, lengthHours, lengthMinutes, dlengthSeconds);
+    logger->log("VRadioRec", Log::DEBUG, buffer);
+  }
+  else
+  {
+    strcpy(buffer, "-:--:-- / -:--:--");
+  }
+
+  drawText(buffer, clocksRegion.x, clocksRegion.y, Colour::LIGHTTEXT);
+
+
+
+
+  // Draw progress bar
+  int progBarXbase = barRegion.x + 300;
+
+  rectangle(barRegion.x + progBarXbase, barRegion.y + 12, 310, 24, Colour::LIGHTTEXT);
+  rectangle(barRegion.x + progBarXbase + 2, barRegion.y + 14, 306, 20, barBlue);
+
+  if (currentSeconds > lengthSeconds) return;
+  if (lengthSeconds == 0) return;
+
+  // Draw yellow portion
+  int progressWidth = 302 * currentSeconds / lengthSeconds;
+  rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 16, progressWidth, 16, Colour::SELECTHIGHLIGHT);
+/*
+
+  if (myRec->recInfo->timerEnd > time(NULL)) // if chasing
+  {
+    int nrWidth = (int)(302 * ((double)(lengthFrames - 0) / lengthFrames)); // 0 inserted instead of getlengthframes
+
+    Log::getInstance()->log("GVASDF", Log::DEBUG, "Length Frames: %lu", lengthFrames);
+//    Log::getInstance()->log("GVASDF", Log::DEBUG, "Player lf: %lu", player->getLengthFrames());
+    Log::getInstance()->log("GVASDF", Log::DEBUG, "NR WDITH: %i", nrWidth);
+    rectangle(barRegion.x + progBarXbase + 4 + 302 - nrWidth, barRegion.y + 16, nrWidth, 16, Colour::RED);
+  }
+*/
+
+  logger->log("VRadioRec", Log::DEBUG, "blips");
+
+  // Now calc position for start margin blips
+  int posPix;
+
+  posPix = 302 * startMargin / lengthSeconds;
+  logger->log("VRadioRec", Log::DEBUG, "posPix %i", posPix);
+
+  rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 - 2, 2, 2, Colour::LIGHTTEXT);
+  rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 + 24, 2, 2, Colour::LIGHTTEXT);
+
+  posPix = 302 * (lengthSeconds - endMargin) / lengthSeconds;
+
+  rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 - 2, 2, 2, Colour::LIGHTTEXT);
+  rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 + 24, 2, 2, Colour::LIGHTTEXT);
+}
+
+void VRadioRec::removeBar()
+{
+  if (!barShowing) return;
+  timers->cancelTimer(this, 2);
+  barShowing = false;
+  rectangle(barRegion, Colour::BLACK);
+  ViewMan::getInstance()->updateView(this, &barRegion);
+}
diff --git a/vradiorec.h b/vradiorec.h
new file mode 100644 (file)
index 0000000..d7f9aa6
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+    Copyright 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#ifndef VRADIOREC_H
+#define VRADIOREC_H
+
+#include <stdio.h>
+
+#include "video.h"
+#include "view.h"
+#include "playerradio.h"
+#include "vdr.h"
+#include "recording.h"
+#include "command.h"
+#include "colour.h"
+#include "osd.h"
+#include "timers.h"
+#include "timerreceiver.h"
+#include "message.h"
+
+class Timers;
+
+class VRadioRec : public View, public TimerReceiver
+{
+  public:
+    VRadioRec(Recording* rec);
+    ~VRadioRec();
+    void draw();
+    int handleCommand(int command);
+    void go();
+
+    void timercall(int clientReference);
+    void processMessage(Message* m);
+
+  private:
+    VDR* vdr;
+    Video* video;
+    Timers* timers;
+    PlayerRadio* player;
+    Recording* myRec;
+
+    Colour barBlue;
+
+    bool playing;
+    bool barShowing;
+    bool stickyBar;
+
+    void doBar(int action);
+    void drawBarClocks();
+    void stopPlay();
+    void removeBar();
+    Region barRegion;
+    Region clocksRegion;
+
+    UINT startMargin;
+    UINT endMargin;
+};
+
+#endif
index 822de8b3679f71efd0ff86281165fa571946bf47..c3edd7bc9a99ac9db419b41dec822d57d232f333 100644 (file)
@@ -297,11 +297,22 @@ int VRecordingList::doPlay(bool resume)
   if (toPlay)
   {
     toPlay->loadRecInfo();
-    VVideoRec* vidrec = new VVideoRec(toPlay);
-    vidrec->draw();
-    viewman->add(vidrec);
-    viewman->updateView(vidrec);
-    vidrec->go(resume);
+    if (toPlay->recInfo->hasVideo())
+    {
+      VVideoRec* vidrec = new VVideoRec(toPlay);
+      vidrec->draw();
+      viewman->add(vidrec);
+      viewman->updateView(vidrec);
+      vidrec->go(resume);
+    }
+    else
+    {
+      VRadioRec* radrec = new VRadioRec(toPlay);
+      radrec->draw();
+      viewman->add(radrec);
+      viewman->updateView(radrec);
+      radrec->go();
+    }
     return 1;
   }
   // should not get to here
index 007c8e93b2014cbf672240a86da101b70f8f5986..b5355cc250b82971d3d8753599d8dc81322f5179 100644 (file)
@@ -38,6 +38,7 @@
 #include "vrecordingmenu.h"
 #include "vdr.h"
 #include "vvideorec.h"
+#include "vradiorec.h"
 #include "colour.h"
 #include "video.h"
 #include "i18n.h"
index bebf4e9c83967e2b110acb8f65d49d5c8db9ddc3..0259dee8bcc948930fb79886ffc8d1e73d12f2c6 100644 (file)
@@ -23,6 +23,7 @@
 
 #include <stdio.h>
 
+#include "video.h"
 #include "view.h"
 #include "player.h"
 #include "vdr.h"