Beispiel #1
0
void WebViewGraphicsBased::enableFrameRateMeasurement()
{
    m_measureFps = true;
    m_lastConsultTime = m_startTime = QTime::currentTime();
    QTimer* updateTimer = new QTimer(this);
    updateTimer->setInterval(1000);
    connect(updateTimer, SIGNAL(timeout()), this, SLOT(updateFrameRate()));
    updateTimer->start();
}
Beispiel #2
0
void
CMainFrame::OnTimer(UINT_PTR nIDEvent) 
{      
   const CFlyCapDoc* pDoc = (CFlyCapDoc*)GetActiveDocument();      
   if( pDoc != NULL )
   {            
      updateFrameRate();
      updateImageInfo();
   }
   CFrameWnd::OnTimer(nIDEvent);
}
Beispiel #3
0
WebViewGraphicsBased::WebViewGraphicsBased(QWidget* parent)
    : QGraphicsView(parent)
    , m_item(new GraphicsWebView)
    , m_numPaintsTotal(0)
    , m_numPaintsSinceLastMeasure(0)
    , m_measureFps(false)
    , m_resizesToContents(false)
    , m_machine(0)
{
    setScene(new QGraphicsScene(this));
    scene()->addItem(m_item);

    setFrameShape(QFrame::NoFrame);
    setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

    m_updateTimer = new QTimer(this);
    m_updateTimer->setInterval(1000);
    connect(m_updateTimer, SIGNAL(timeout()), this, SLOT(updateFrameRate()));
}
void Simulation::doSimulationStep()
{
  ++simulationStep;
  simulatedTime += scene->stepLength;

  scene->updateActuators();

  collisions = contactPoints = 0;

  dSpaceCollide2((dGeomID)staticSpace, (dGeomID)movableSpace, this, (dNearCallback*)&staticCollisionWithSpaceCallback);
  if(scene->detectBodyCollisions)
    dSpaceCollide(movableSpace, this, (dNearCallback*)&staticCollisionSpaceWithSpaceCallback);

  if(scene->useQuickSolver && (simulationStep % scene->quickSolverSkip) == 0)
    dWorldQuickStep(physicalWorld, scene->stepLength);
  else
    dWorldStep(physicalWorld, scene->stepLength);
  dJointGroupEmpty(contactGroup);

  updateFrameRate();
}
CameraPub::CameraPub()
  : Display()
  , camera_trigger_name_("camera_trigger")
  , nh_()
  , new_caminfo_(false)
  , force_render_(false)
  , trigger_activated_(false)
  , last_image_publication_time_(0)
  , caminfo_ok_(false)
  , video_publisher_(0)
{
  topic_property_ = new RosTopicProperty("Image Topic", "",
      QString::fromStdString(ros::message_traits::datatype<sensor_msgs::Image>()),
      "sensor_msgs::Image topic to publish to.", this, SLOT(updateTopic()));

  namespace_property_ = new StringProperty("Display namespace", "",
      "Namespace for this display.", this, SLOT(updateDisplayNamespace()));

  camera_info_property_ = new RosTopicProperty("Camera Info Topic", "",
      QString::fromStdString(ros::message_traits::datatype<sensor_msgs::CameraInfo>()),
      "sensor_msgs::CameraInfo topic to subscribe to.", this, SLOT(updateTopic()));

  queue_size_property_ = new IntProperty( "Queue Size", 2,
      "Advanced: set the size of the incoming message queue.  Increasing this "
      "is useful if your incoming TF data is delayed significantly from your"
      " image data, but it can greatly increase memory usage if the messages are big.",
                                          this, SLOT(updateQueueSize()));
  queue_size_property_->setMin(1);

  frame_rate_property_ = new FloatProperty("Frame Rate", -1,
      "Sets target frame rate. Set to < 0 for maximum speed, set to 0 to stop, you can "
      "trigger single images with the /rviz_camera_trigger service.",
                                           this, SLOT(updateFrameRate()));
  frame_rate_property_->setMin(-1);

  background_color_property_ = new ColorProperty("Background Color", Qt::black,
      "Sets background color, values from 0.0 to 1.0.",
                                           this, SLOT(updateBackgroundColor()));
}
CameraView::CameraView(Camera * camera, QWidget *parent, int tabIdx):
    QWidget(parent),
    ui(new Ui::CameraView){

    _cam = camera;
    _tabIdx = tabIdx;

    connect(_cam, SIGNAL(CTAFrameUpdatedSignal()), this, SLOT(showCTAFrame()));
    connect(_cam, SIGNAL(ATCFrameUpdatedSignal()), this, SLOT(showATCFrame()));
    connect(_cam, SIGNAL(ATCRecFrameUpdatedSignal()), this, SLOT(showATCRecFrame()));
    connect(_cam, SIGNAL(frameRateUpdatedSignal()), this, SLOT(updateFrameRate()));
    //***NBS***//
    connect(_cam, SIGNAL(residualEnergyUpdatedSignal()), this, SLOT(updateResidualEnergy()));
    //***NBS***//
    connect(_cam,SIGNAL(curBandwidthChangedSignal(int,double)),this,SLOT(updateBandwidthSlot(int,double)));
    connect (_cam,SIGNAL(recognitionCompletedSignal(QString,QString)),SLOT(updateRecognitionResultSlot(QString,QString)));

    ui->setupUi(this);

    ui->label_image->setText("");

}
Beispiel #7
0
bool EventStream::loadEventData( int event_id )
{
    static char sql[ZM_SQL_MED_BUFSIZ];

    snprintf( sql, sizeof(sql), "select M.Id, M.Name, E.Frames, unix_timestamp( StartTime ) as StartTimestamp, max(F.Delta)-min(F.Delta) as Duration from Events as E inner join Monitors as M on E.MonitorId = M.Id inner join Frames as F on E.Id = F.EventId where E.Id = %d group by E.Id", event_id );

    if ( mysql_query( &dbconn, sql ) )
    {
        Error( "Can't run query: %s", mysql_error( &dbconn ) );
        exit( mysql_errno( &dbconn ) );
    }

    MYSQL_RES *result = mysql_store_result( &dbconn );
    if ( !result )
    {
        Error( "Can't use query result: %s", mysql_error( &dbconn ) );
        exit( mysql_errno( &dbconn ) );
    }

    if ( !mysql_num_rows( result ) )
    {
        Fatal( "Unable to load event %d, not found in DB", event_id );
    }

    MYSQL_ROW dbrow = mysql_fetch_row( result );

    if ( mysql_errno( &dbconn ) )
    {
        Error( "Can't fetch row: %s", mysql_error( &dbconn ) );
        exit( mysql_errno( &dbconn ) );
    }

    delete event_data;
    event_data = new EventData;
    event_data->event_id = event_id;
    event_data->monitor_id = atoi( dbrow[0] );
    event_data->start_time = atoi(dbrow[3]);
    if ( config.use_deep_storage )
    {
        struct tm *event_time = localtime( &event_data->start_time );
        if ( config.dir_events[0] == '/' )
            snprintf( event_data->path, sizeof(event_data->path), "%s/%ld/%02d/%02d/%02d/%02d/%02d/%02d", config.dir_events, event_data->monitor_id, event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday, event_time->tm_hour, event_time->tm_min, event_time->tm_sec );
        else
            snprintf( event_data->path, sizeof(event_data->path), "%s/%s/%ld/%02d/%02d/%02d/%02d/%02d/%02d", staticConfig.PATH_WEB.c_str(), config.dir_events, event_data->monitor_id, event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday, event_time->tm_hour, event_time->tm_min, event_time->tm_sec );
    }
    else
    {
        if ( config.dir_events[0] == '/' )
            snprintf( event_data->path, sizeof(event_data->path), "%s/%ld/%ld", config.dir_events, event_data->monitor_id, event_data->event_id );
        else
            snprintf( event_data->path, sizeof(event_data->path), "%s/%s/%ld/%ld", staticConfig.PATH_WEB.c_str(), config.dir_events, event_data->monitor_id, event_data->event_id );
    }
    event_data->frame_count = dbrow[2] == NULL ? 0 : atoi(dbrow[2]);
    event_data->duration = atof(dbrow[4]);

    updateFrameRate( (double)event_data->frame_count/event_data->duration );

    mysql_free_result( result );

    snprintf( sql, sizeof(sql), "select FrameId, unix_timestamp( `TimeStamp` ), Delta from Frames where EventId = %d order by FrameId asc", event_id );
    if ( mysql_query( &dbconn, sql ) )
    {
        Error( "Can't run query: %s", mysql_error( &dbconn ) );
        exit( mysql_errno( &dbconn ) );
    }

    result = mysql_store_result( &dbconn );
    if ( !result )
    {
        Error( "Can't use query result: %s", mysql_error( &dbconn ) );
        exit( mysql_errno( &dbconn ) );
    }

    event_data->n_frames = mysql_num_rows( result );

    event_data->frames = new FrameData[event_data->frame_count];
    int id, last_id = 0;
    time_t timestamp, last_timestamp = event_data->start_time;
    double delta, last_delta = 0.0;
    while ( ( dbrow = mysql_fetch_row( result ) ) )
    {
        id = atoi(dbrow[0]);
        timestamp = atoi(dbrow[1]);
        delta = atof(dbrow[2]);
        int id_diff = id - last_id;
        double frame_delta = (delta-last_delta)/id_diff;
        if ( id_diff > 1 )
        {
            for ( int i = last_id+1; i < id; i++ )
            {
                event_data->frames[i-1].timestamp = (time_t)(last_timestamp + ((i-last_id)*frame_delta));
                event_data->frames[i-1].offset = (time_t)(event_data->frames[i-1].timestamp-event_data->start_time);
                event_data->frames[i-1].delta = frame_delta;
                event_data->frames[i-1].in_db = false;
            }
        }
        event_data->frames[id-1].timestamp = timestamp;
        event_data->frames[id-1].offset = (time_t)(event_data->frames[id-1].timestamp-event_data->start_time);
        event_data->frames[id-1].delta = id>1?frame_delta:0.0;
        event_data->frames[id-1].in_db = true;
        last_id = id;
        last_delta = delta;
        last_timestamp = timestamp;
    }
    if ( mysql_errno( &dbconn ) )
    {
        Error( "Can't fetch row: %s", mysql_error( &dbconn ) );
        exit( mysql_errno( &dbconn ) );
    }

    //for ( int i = 0; i < 250; i++ )
    //{
        //Info( "%d -> %d @ %f (%d)", i+1, event_data->frames[i].timestamp, event_data->frames[i].delta, event_data->frames[i].in_db );
    //}

    mysql_free_result( result );

    if ( forceEventChange || mode == MODE_ALL_GAPLESS )
    {
        if ( replay_rate > 0 )
            curr_stream_time = event_data->frames[0].timestamp;
        else
            curr_stream_time = event_data->frames[event_data->frame_count-1].timestamp;
    }
    Debug( 2, "Event:%ld, Frames:%ld, Duration: %.2f", event_data->event_id, event_data->frame_count, event_data->duration );

    return( true );
}
void MonitorStream::processCommand(const CmdMsg *msg) {
  Debug( 2, "Got message, type %d, msg %d", msg->msg_type, msg->msg_data[0] );
  // Check for incoming command
  switch( (MsgCommand)msg->msg_data[0] ) {
    case CMD_PAUSE :
      Debug(1, "Got PAUSE command");
      paused = true;
      delayed = true;
      last_frame_sent = TV_2_FLOAT(now);
      break;
    case CMD_PLAY :
      Debug(1, "Got PLAY command");
      if ( paused ) {
        paused = false;
        delayed = true;
      }
      replay_rate = ZM_RATE_BASE;
      break;
    case CMD_VARPLAY :
      Debug(1, "Got VARPLAY command");
      if ( paused ) {
        paused = false;
        delayed = true;
      }
      replay_rate = ntohs(((unsigned char)msg->msg_data[2]<<8)|(unsigned char)msg->msg_data[1])-32768;
      break;
    case CMD_STOP :
      Debug(1, "Got STOP command");
      paused = false;
      delayed = false;
      break;
    case CMD_FASTFWD :
      Debug(1, "Got FAST FWD command");
      if ( paused ) {
        paused = false;
        delayed = true;
      }
      // Set play rate
      switch ( replay_rate ) {
        case 2 * ZM_RATE_BASE :
          replay_rate = 5 * ZM_RATE_BASE;
          break;
        case 5 * ZM_RATE_BASE :
          replay_rate = 10 * ZM_RATE_BASE;
          break;
        case 10 * ZM_RATE_BASE :
          replay_rate = 25 * ZM_RATE_BASE;
          break;
        case 25 * ZM_RATE_BASE :
        case 50 * ZM_RATE_BASE :
          replay_rate = 50 * ZM_RATE_BASE;
          break;
        default :
          replay_rate = 2 * ZM_RATE_BASE;
          break;
      }
      break;
    case CMD_SLOWFWD :
      Debug( 1, "Got SLOW FWD command" );
      paused = true;
      delayed = true;
      replay_rate = ZM_RATE_BASE;
      step = 1;
      break;
    case CMD_SLOWREV :
      Debug( 1, "Got SLOW REV command" );
      paused = true;
      delayed = true;
      replay_rate = ZM_RATE_BASE;
      step = -1;
      break;
    case CMD_FASTREV :
      Debug( 1, "Got FAST REV command" );
      if ( paused ) {
        paused = false;
        delayed = true;
      }
      // Set play rate
      switch ( replay_rate ) {
        case -2 * ZM_RATE_BASE :
          replay_rate = -5 * ZM_RATE_BASE;
          break;
        case -5 * ZM_RATE_BASE :
          replay_rate = -10 * ZM_RATE_BASE;
          break;
        case -10 * ZM_RATE_BASE :
          replay_rate = -25 * ZM_RATE_BASE;
          break;
        case -25 * ZM_RATE_BASE :
        case -50 * ZM_RATE_BASE :
          replay_rate = -50 * ZM_RATE_BASE;
          break;
        default :
          replay_rate = -2 * ZM_RATE_BASE;
          break;
      }
      break;
    case CMD_ZOOMIN :
      x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2];
      y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4];
      Debug( 1, "Got ZOOM IN command, to %d,%d", x, y );
      switch ( zoom ) {
        case 100:
          zoom = 150;
          break;
        case 150:
          zoom = 200;
          break;
        case 200:
          zoom = 300;
          break;
        case 300:
          zoom = 400;
          break;
        case 400:
        default :
          zoom = 500;
          break;
      }
      break;
    case CMD_ZOOMOUT :
      Debug( 1, "Got ZOOM OUT command" );
      switch ( zoom ) {
        case 500:
          zoom = 400;
          break;
        case 400:
          zoom = 300;
          break;
        case 300:
          zoom = 200;
          break;
        case 200:
          zoom = 150;
          break;
        case 150:
        default :
          zoom = 100;
          break;
      }
      break;
    case CMD_PAN :
      x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2];
      y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4];
      Debug(1, "Got PAN command, to %d,%d", x, y);
      break;
    case CMD_SCALE :
      scale = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2];
      Debug(1, "Got SCALE command, to %d", scale);
      break;
    case CMD_QUIT :
      Info("User initiated exit - CMD_QUIT");
      break;
    case CMD_QUERY :
      Debug(1, "Got QUERY command, sending STATUS");
      break;
    default :
      Error("Got unexpected command %d", msg->msg_data[0]);
      break;
  } // end switch command

  struct {
    int id;
    int state;
    double fps;
    int buffer_level;
    int rate;
    double delay;
    int zoom;
    bool delayed;
    bool paused;
    bool enabled;
    bool forced;
  } status_data;

  status_data.id = monitor->Id();
  status_data.fps = monitor->GetFPS();
  status_data.state = monitor->shared_data->state;
  if ( playback_buffer > 0 )
    status_data.buffer_level = (MOD_ADD( (temp_write_index-temp_read_index), 0, temp_image_buffer_count )*100)/temp_image_buffer_count;
  else
    status_data.buffer_level = 0;
  status_data.delayed = delayed;
  status_data.paused = paused;
  status_data.rate = replay_rate;
  status_data.delay = TV_2_FLOAT( now ) - TV_2_FLOAT( last_frame_timestamp );
  status_data.zoom = zoom;
  //status_data.enabled = monitor->shared_data->active;
  status_data.enabled = monitor->trigger_data->trigger_state!=Monitor::TRIGGER_OFF;
  status_data.forced = monitor->trigger_data->trigger_state==Monitor::TRIGGER_ON;
  Debug(2, "Buffer Level:%d, Delayed:%d, Paused:%d, Rate:%d, delay:%.3f, Zoom:%d, Enabled:%d Forced:%d", 
    status_data.buffer_level,
    status_data.delayed,
    status_data.paused,
    status_data.rate,
    status_data.delay,
    status_data.zoom,
    status_data.enabled,
    status_data.forced
  );

  DataMsg status_msg;
  status_msg.msg_type = MSG_DATA_WATCH;
  memcpy(&status_msg.msg_data, &status_data, sizeof(status_data));
  int nbytes = 0;
  if ( (nbytes = sendto(sd, &status_msg, sizeof(status_msg), MSG_DONTWAIT, (sockaddr *)&rem_addr, sizeof(rem_addr))) < 0 ) {
    //if ( errno != EAGAIN )
    {
      Error( "Can't sendto on sd %d: %s", sd, strerror(errno) );
      //exit( -1 );
    }
  }
  Debug(2, "Number of bytes sent to (%s): (%d)", rem_addr.sun_path, nbytes);

  // quit after sending a status, if this was a quit request
  if ( (MsgCommand)msg->msg_data[0]==CMD_QUIT ) {
    Debug(2,"Quitting");
    exit(0);
  }

  Debug(2,"Updating framerate");
  updateFrameRate(monitor->GetFPS());
} // end void MonitorStream::processCommand(const CmdMsg *msg)
void MonitorStream::runStream() {
  if ( type == STREAM_SINGLE ) {
    // Not yet migrated over to stream class
    SingleImage(scale);
    return;
  }

  openComms();

  if ( !checkInitialised() ) {
    Error("Not initialized");
    return;
  }

  updateFrameRate(monitor->GetFPS());

  if ( type == STREAM_JPEG )
    fputs("Content-Type: multipart/x-mixed-replace;boundary=ZoneMinderFrame\r\n\r\n", stdout);

  // point to end which is theoretically not a valid value because all indexes are % image_buffer_count
  unsigned int last_read_index = monitor->image_buffer_count; 

  time_t stream_start_time;
  time(&stream_start_time);

  frame_count = 0;

  temp_image_buffer = 0;
  temp_image_buffer_count = playback_buffer;
  temp_read_index = temp_image_buffer_count;
  temp_write_index = temp_image_buffer_count;

  std::string swap_path;
  bool buffered_playback = false;

  // Last image and timestamp when paused, will be resent occasionally to prevent timeout
  Image *paused_image = NULL;
  struct timeval paused_timestamp;

  // 15 is the max length for the swap path suffix, /zmswap-whatever, assuming max 6 digits for monitor id
  const int max_swap_len_suffix = 15; 

  int swap_path_length = staticConfig.PATH_SWAP.length() + 1; // +1 for NULL terminator
  int subfolder1_length = snprintf(NULL, 0, "/zmswap-m%d", monitor->Id()) + 1;
  int subfolder2_length = snprintf(NULL, 0, "/zmswap-q%06d", connkey) + 1;
  int total_swap_path_length = swap_path_length + subfolder1_length + subfolder2_length;

  if ( connkey && ( playback_buffer > 0 ) ) {

    if ( total_swap_path_length + max_swap_len_suffix > PATH_MAX ) {
      Error("Swap Path is too long. %d > %d ", total_swap_path_length+max_swap_len_suffix, PATH_MAX);
    } else {
      swap_path = staticConfig.PATH_SWAP;

      Debug( 3, "Checking swap path folder: %s", swap_path.c_str() );
      if ( checkSwapPath(swap_path.c_str(), true) ) {
        swap_path += stringtf("/zmswap-m%d", monitor->Id());

        Debug(4, "Checking swap path subfolder: %s", swap_path.c_str());
        if ( checkSwapPath(swap_path.c_str(), true) ) {
          swap_path += stringtf("/zmswap-q%06d", connkey);

          Debug(4, "Checking swap path subfolder: %s", swap_path.c_str());
          if ( checkSwapPath(swap_path.c_str(), true) ) {
            buffered_playback = true;
          }
        }
      }

      if ( !buffered_playback ) {
        Error("Unable to validate swap image path, disabling buffered playback");
      } else {
        Debug(2, "Assigning temporary buffer");
        temp_image_buffer = new SwapImage[temp_image_buffer_count];
        memset( temp_image_buffer, 0, sizeof(*temp_image_buffer)*temp_image_buffer_count );
        Debug( 2, "Assigned temporary buffer" );
      }
    }
  } else {
    Debug(2, "Not using playback_buffer");
  } // end if connkey  & playback_buffer

  float max_secs_since_last_sent_frame = 10.0; //should be > keep alive amount (5 secs)
  while ( !zm_terminate ) {
    bool got_command = false;
    if ( feof(stdout) ) {
      Debug(2,"feof stdout");
      break;
    } else if ( ferror(stdout) ) {
      Debug(2,"ferror stdout");
      break;
    } else if ( !monitor->ShmValid() ) {
      Debug(2,"monitor not valid.... maybe we should wait until it comes back.");
      break;
    }

    gettimeofday(&now, NULL);

    bool was_paused = paused;
    if ( connkey ) {
      while(checkCommandQueue()) {
Debug(2, "Have checking command Queue for connkey: %d", connkey );
        got_command = true;
      }
      // Update modified time of the socket .lock file so that we can tell which ones are stale.
      if ( now.tv_sec - last_comm_update.tv_sec > 3600 ) {
        touch(sock_path_lock);
        last_comm_update = now;
      }

    }

    if ( paused ) {
      if ( !was_paused ) {
        int index = monitor->shared_data->last_write_index % monitor->image_buffer_count;
        Debug(1,"Saving paused image from index %d",index);
        paused_image = new Image( *monitor->image_buffer[index].image );
        paused_timestamp = *(monitor->image_buffer[index].timestamp);
      }
    } else if ( paused_image ) {
      Debug(1,"Clearing paused_image");
      delete paused_image;
      paused_image = NULL;
    }

    if ( buffered_playback && delayed ) {
      if ( temp_read_index == temp_write_index ) {
        // Go back to live viewing
        Debug( 1, "Exceeded temporary streaming buffer" );
        // Clear paused flag
        paused = false;
        // Clear delayed_play flag
        delayed = false;
        replay_rate = ZM_RATE_BASE;
      } else {
        if ( !paused ) {
          int temp_index = MOD_ADD(temp_read_index, 0, temp_image_buffer_count);
          //Debug( 3, "tri: %d, ti: %d", temp_read_index, temp_index );
          SwapImage *swap_image = &temp_image_buffer[temp_index];

          if ( !swap_image->valid ) {
            paused = true;
            delayed = true;
            temp_read_index = MOD_ADD(temp_read_index, (replay_rate>=0?-1:1), temp_image_buffer_count);
          } else {
            //Debug( 3, "siT: %f, lfT: %f", TV_2_FLOAT( swap_image->timestamp ), TV_2_FLOAT( last_frame_timestamp ) );
            double expected_delta_time = ((TV_2_FLOAT( swap_image->timestamp ) - TV_2_FLOAT( last_frame_timestamp )) * ZM_RATE_BASE)/replay_rate;
            double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent;

            //Debug( 3, "eDT: %.3lf, aDT: %.3f, lFS:%.3f, NOW:%.3f", expected_delta_time, actual_delta_time, last_frame_sent, TV_2_FLOAT( now ) );
            // If the next frame is due
            if ( actual_delta_time > expected_delta_time ) {
              //Debug( 2, "eDT: %.3lf, aDT: %.3f", expected_delta_time, actual_delta_time );
              if ( temp_index%frame_mod == 0 ) {
                Debug( 2, "Sending delayed frame %d", temp_index );
                // Send the next frame
                if ( ! sendFrame(temp_image_buffer[temp_index].file_name, &temp_image_buffer[temp_index].timestamp) )
                  zm_terminate = true;
                memcpy(&last_frame_timestamp, &(swap_image->timestamp), sizeof(last_frame_timestamp));
                //frame_sent = true;
              }
              temp_read_index = MOD_ADD(temp_read_index, (replay_rate>0?1:-1), temp_image_buffer_count);
            }
          }
        } else if ( step != 0 ) {
          temp_read_index = MOD_ADD( temp_read_index, (step>0?1:-1), temp_image_buffer_count );

          SwapImage *swap_image = &temp_image_buffer[temp_read_index];

          // Send the next frame
          if ( !sendFrame( temp_image_buffer[temp_read_index].file_name, &temp_image_buffer[temp_read_index].timestamp ) )
            zm_terminate = true;
          memcpy( &last_frame_timestamp, &(swap_image->timestamp), sizeof(last_frame_timestamp) );
          //frame_sent = true;
          step = 0;
        } else {
          //paused?
          int temp_index = MOD_ADD(temp_read_index, 0, temp_image_buffer_count);

           double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent;
           if ( got_command || actual_delta_time > 5 ) {
            // Send keepalive
            Debug( 2, "Sending keepalive frame %d", temp_index );
            // Send the next frame
            if ( !sendFrame( temp_image_buffer[temp_index].file_name, &temp_image_buffer[temp_index].timestamp ) )
              zm_terminate = true;
            //frame_sent = true;
          }
        } // end if (!paused) or step or paused
      } // end if have exceeded buffer or not

      if ( temp_read_index == temp_write_index ) {
        // Go back to live viewing
        Warning( "Rewound over write index, resuming live play" );
        // Clear paused flag
        paused = false;
        // Clear delayed_play flag
        delayed = false;
        replay_rate = ZM_RATE_BASE;
      }
    } // end if ( buffered_playback && delayed )

    if ( last_read_index != monitor->shared_data->last_write_index ) {
      // have a new image to send
      int index = monitor->shared_data->last_write_index % monitor->image_buffer_count; // % shouldn't be neccessary
      last_read_index = monitor->shared_data->last_write_index;
      Debug( 2, "index: %d: frame_mod: %d frame count: %d paused(%d) delayed(%d)", index, frame_mod, frame_count, paused, delayed );
      if ( (frame_mod == 1) || ((frame_count%frame_mod) == 0) ) {
        if ( !paused && !delayed ) {
          // Send the next frame
          Monitor::Snapshot *snap = &monitor->image_buffer[index];

          Debug(2, "sending Frame.");
          if ( !sendFrame(snap->image, snap->timestamp) ) {
            Debug(2, "sendFrame failed, quiting.");
            zm_terminate = true;
          }
          // Perhaps we should use NOW instead. 
          memcpy(&last_frame_timestamp, snap->timestamp, sizeof(last_frame_timestamp));
          //frame_sent = true;

          temp_read_index = temp_write_index;
        } else {
          double actual_delta_time = TV_2_FLOAT(now) - last_frame_sent;
          if ( actual_delta_time > 5 ) {
            if ( paused_image ) {
              // Send keepalive
              Debug(2, "Sending keepalive frame ");
              // Send the next frame
              if ( !sendFrame(paused_image, &paused_timestamp) )
                zm_terminate = true;
            } else {
              Debug(2, "Would have sent keepalive frame, but had no paused_image ");
            }
           }
        }
      } // end if should send frame

      if ( buffered_playback && !paused ) {
        if ( monitor->shared_data->valid ) {
          if ( monitor->image_buffer[index].timestamp->tv_sec ) {
            int temp_index = temp_write_index%temp_image_buffer_count;
            Debug(2, "Storing frame %d", temp_index);
            if ( !temp_image_buffer[temp_index].valid ) {
              snprintf( temp_image_buffer[temp_index].file_name, sizeof(temp_image_buffer[0].file_name), "%s/zmswap-i%05d.jpg", swap_path.c_str(), temp_index );
              temp_image_buffer[temp_index].valid = true;
            }
            memcpy( &(temp_image_buffer[temp_index].timestamp), monitor->image_buffer[index].timestamp, sizeof(temp_image_buffer[0].timestamp) );
            monitor->image_buffer[index].image->WriteJpeg( temp_image_buffer[temp_index].file_name, config.jpeg_file_quality );
            temp_write_index = MOD_ADD( temp_write_index, 1, temp_image_buffer_count );
            if ( temp_write_index == temp_read_index ) {
              // Go back to live viewing
              Warning( "Exceeded temporary buffer, resuming live play" );
              paused = false;
              delayed = false;
              replay_rate = ZM_RATE_BASE;
            }
          } else {
            Warning( "Unable to store frame as timestamp invalid" );
          }
        } else {
          Warning( "Unable to store frame as shared memory invalid" );
        }
      } // end if buffered playback
      frame_count++;
    } else {
      Debug(4,"Waiting for capture last_write_index=%u", monitor->shared_data->last_write_index);
    } // end if ( (unsigned int)last_read_index != monitor->shared_data->last_write_index ) 

    unsigned long sleep_time = (unsigned long)((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*abs(replay_rate*2)));
    Debug(4, "Sleeping for (%d)", sleep_time);
    usleep(sleep_time);
    if ( ttl ) {
      if ( (now.tv_sec - stream_start_time) > ttl ) {
        Debug(2, "now(%d) - start(%d) > ttl(%d) break", now.tv_sec, stream_start_time, ttl);
        break;
      }
    }
    if ( ! last_frame_sent ) {
      // If we didn't capture above, because frame_mod was bad? Then last_frame_sent will not have a value.
      last_frame_sent = now.tv_sec;
      Warning( "no last_frame_sent.  Shouldn't happen. frame_mod was (%d) frame_count (%d) ", frame_mod, frame_count );
    } else if ( (!paused) && ( (TV_2_FLOAT( now ) - last_frame_sent) > max_secs_since_last_sent_frame ) ) {
      Error( "Terminating, last frame sent time %f secs more than maximum of %f", TV_2_FLOAT( now ) - last_frame_sent, max_secs_since_last_sent_frame );
      break;
    }
  } // end while

  if ( buffered_playback ) {
    Debug(1, "Cleaning swap files from %s", swap_path.c_str());
    struct stat stat_buf;
    if ( stat(swap_path.c_str(), &stat_buf) < 0 ) {
      if ( errno != ENOENT ) {
        Error("Can't stat '%s': %s", swap_path.c_str(), strerror(errno));
      }
    } else if ( !S_ISDIR(stat_buf.st_mode) ) {
      Error("Swap image path '%s' is not a directory", swap_path.c_str());
    } else {
      char glob_pattern[PATH_MAX] = "";

      snprintf(glob_pattern, sizeof(glob_pattern), "%s/*.*", swap_path.c_str());
      glob_t pglob;
      int glob_status = glob(glob_pattern, 0, 0, &pglob);
      if ( glob_status != 0 ) {
        if ( glob_status < 0 ) {
          Error("Can't glob '%s': %s", glob_pattern, strerror(errno));
        } else {
          Debug(1, "Can't glob '%s': %d", glob_pattern, glob_status);
        }
      } else {
        for ( unsigned int i = 0; i < pglob.gl_pathc; i++ ) {
          if ( unlink(pglob.gl_pathv[i]) < 0 ) {
            Error("Can't unlink '%s': %s", pglob.gl_pathv[i], strerror(errno));
          }
        }
      }
      globfree( &pglob );
      if ( rmdir(swap_path.c_str()) < 0 ) {
        Error( "Can't rmdir '%s': %s", swap_path.c_str(), strerror(errno) );
      }
    } // end if checking for swap_path
  } // end if buffered_playback

  closeComms();
} // end MonitorStream::runStream