Exemple #1
0
/**
 * Create timeshift buffer
 *
 * max_period of buffer in seconds (0 = unlimited)
 * max_size   of buffer in bytes   (0 = unlimited)
 */
streaming_target_t *timeshift_create
  (streaming_target_t *out, time_t max_time)
{
  char buf[512];
  timeshift_t *ts = calloc(1, sizeof(timeshift_t));

  /* Must hold global lock */
  lock_assert(&global_lock);

  /* Create directories */
  if (timeshift_filemgr_makedirs(timeshift_index, buf, sizeof(buf)))
    return NULL;

  /* Setup structure */
  TAILQ_INIT(&ts->files);
  ts->output     = out;
  ts->path       = strdup(buf);
  ts->max_time   = max_time;
  ts->state      = TS_INIT;
  ts->full       = 0;
  ts->vididx     = -1;
  ts->id         = timeshift_index;
  ts->ondemand   = timeshift_ondemand;
  pthread_mutex_init(&ts->rdwr_mutex, NULL);
  pthread_mutex_init(&ts->state_mutex, NULL);

  /* Initialise output */
  tvh_pipe(O_NONBLOCK, &ts->rd_pipe);

  /* Initialise input */
  streaming_queue_init(&ts->wr_queue, 0);
  streaming_target_init(&ts->input, timeshift_input, ts, 0);
  pthread_create(&ts->wr_thread, NULL, timeshift_writer, ts);
  pthread_create(&ts->rd_thread, NULL, timeshift_reader, ts);

  /* Update index */
  timeshift_index++;

  return &ts->input;
}
Exemple #2
0
/*
 * Get current / new file
 */
timeshift_file_t *timeshift_filemgr_get ( timeshift_t *ts, int create )
{
  int fd;
  struct timespec tp;
  timeshift_file_t *tsf_tl, *tsf_hd, *tsf_tmp;
  timeshift_index_data_t *ti;
  char path[512];
  time_t time;

  /* Return last file */
  if (!create)
    return timeshift_filemgr_newest(ts);

  /* No space */
  if (ts->full)
    return NULL;

  /* Store to file */
  clock_gettime(CLOCK_MONOTONIC_COARSE, &tp);
  time   = tp.tv_sec / TIMESHIFT_FILE_PERIOD;
  tsf_tl = TAILQ_LAST(&ts->files, timeshift_file_list);
  if (!tsf_tl || tsf_tl->time != time) {
    tsf_hd = TAILQ_FIRST(&ts->files);

    /* Close existing */
    if (tsf_tl && tsf_tl->fd != -1)
      timeshift_filemgr_close(tsf_tl);

    /* Check period */
    if (ts->max_time && tsf_hd && tsf_tl) {
      time_t d = (tsf_tl->time - tsf_hd->time) * TIMESHIFT_FILE_PERIOD;
      if (d > (ts->max_time+5)) {
        if (!tsf_hd->refcount) {
          timeshift_filemgr_remove(ts, tsf_hd, 0);
          tsf_hd = NULL;
        } else {
          tvhlog(LOG_DEBUG, "timeshift", "ts %d buffer full", ts->id);
          ts->full = 1;
        }
      }
    }

    /* Check size */
    if (!timeshift_unlimited_size &&
        atomic_pre_add_u64(&timeshift_total_size, 0) >= timeshift_max_size) {

      /* Remove the last file (if we can) */
      if (tsf_hd && !tsf_hd->refcount) {
        timeshift_filemgr_remove(ts, tsf_hd, 0);

      /* Full */
      } else {
        tvhlog(LOG_DEBUG, "timeshift", "ts %d buffer full", ts->id);
        ts->full = 1;
      }
    }
      
    /* Create new file */
    tsf_tmp = NULL;
    if (!ts->full) {

      /* Create directories */
      if (!ts->path) {
        if (timeshift_filemgr_makedirs(ts->id, path, sizeof(path)))
          return NULL;
        ts->path = strdup(path);
      }

      /* Create File */
      snprintf(path, sizeof(path), "%s/tvh-%"PRItime_t, ts->path, time);
      tvhtrace("timeshift", "ts %d create file %s", ts->id, path);
      if ((fd = open(path, O_WRONLY | O_CREAT, 0600)) > 0) {
        tsf_tmp = calloc(1, sizeof(timeshift_file_t));
        tsf_tmp->time     = time;
        tsf_tmp->fd       = fd;
        tsf_tmp->path     = strdup(path);
        tsf_tmp->refcount = 0;
        tsf_tmp->last     = getmonoclock();
        TAILQ_INIT(&tsf_tmp->iframes);
        TAILQ_INIT(&tsf_tmp->sstart);
        TAILQ_INSERT_TAIL(&ts->files, tsf_tmp, link);

        /* Copy across last start message */
        if (tsf_tl && (ti = TAILQ_LAST(&tsf_tl->sstart, timeshift_index_data_list))) {
          tvhtrace("timeshift", "ts %d copy smt_start to new file",
                   ts->id);
          timeshift_index_data_t *ti2 = calloc(1, sizeof(timeshift_index_data_t));
          ti2->data = streaming_msg_clone(ti->data);
          TAILQ_INSERT_TAIL(&tsf_tmp->sstart, ti2, link);
        }
      }
    }
    tsf_tl = tsf_tmp;
  }

  if (tsf_tl)
    tsf_tl->refcount++;
  return tsf_tl;
}
/*
 * Get current / new file
 */
timeshift_file_t *timeshift_filemgr_get ( timeshift_t *ts, int create )
{
  int fd;
  struct timespec tp;
  timeshift_file_t *tsf_tl, *tsf_hd, *tsf_tmp;
  timeshift_index_data_t *ti;
  char path[PATH_MAX];
  time_t time;

  /* Return last file */
  if (!create)
    return timeshift_filemgr_newest(ts);

  /* No space */
  if (ts->full)
    return NULL;

  /* Store to file */
  clock_gettime(CLOCK_MONOTONIC_COARSE, &tp);
  time   = tp.tv_sec / TIMESHIFT_FILE_PERIOD;
  tsf_tl = TAILQ_LAST(&ts->files, timeshift_file_list);
  if (!tsf_tl || tsf_tl->time != time ||
      (tsf_tl->ram && tsf_tl->woff >= timeshift_conf.ram_segment_size)) {
    tsf_hd = TAILQ_FIRST(&ts->files);

    /* Close existing */
    if (tsf_tl)
      timeshift_filemgr_close(tsf_tl);

    /* Check period */
    if (!timeshift_conf.unlimited_period &&
        ts->max_time && tsf_hd && tsf_tl) {
      time_t d = (tsf_tl->time - tsf_hd->time) * TIMESHIFT_FILE_PERIOD;
      if (d > (ts->max_time+5)) {
        if (!tsf_hd->refcount) {
          timeshift_filemgr_remove(ts, tsf_hd, 0);
          tsf_hd = NULL;
        } else {
          tvhlog(LOG_DEBUG, "timeshift", "ts %d buffer full", ts->id);
          ts->full = 1;
        }
      }
    }

    /* Check size */
    if (!timeshift_conf.unlimited_size &&
        atomic_pre_add_u64(&timeshift_conf.total_size, 0) >= timeshift_conf.max_size) {

      /* Remove the last file (if we can) */
      if (tsf_hd && !tsf_hd->refcount) {
        timeshift_filemgr_remove(ts, tsf_hd, 0);

      /* Full */
      } else {
        tvhlog(LOG_DEBUG, "timeshift", "ts %d buffer full", ts->id);
        ts->full = 1;
      }
    }

    /* Create new file */
    tsf_tmp = NULL;
    if (!ts->full) {

      tvhtrace("timeshift", "ts %d RAM total %"PRId64" requested %"PRId64" segment %"PRId64,
                   ts->id, atomic_pre_add_u64(&timeshift_total_ram_size, 0),
                   timeshift_conf.ram_size, timeshift_conf.ram_segment_size);
      if (timeshift_conf.ram_size >= 8*1024*1024 &&
          atomic_pre_add_u64(&timeshift_total_ram_size, 0) <
            timeshift_conf.ram_size + (timeshift_conf.ram_segment_size / 2)) {
        tsf_tmp = timeshift_filemgr_file_init(ts, time);
        tsf_tmp->ram_size = MIN(16*1024*1024, timeshift_conf.ram_segment_size);
        tsf_tmp->ram = malloc(tsf_tmp->ram_size);
        if (!tsf_tmp->ram) {
          free(tsf_tmp);
          tsf_tmp = NULL;
        } else {
          tvhtrace("timeshift", "ts %d create RAM segment with %"PRId64" bytes (time %li)",
                   ts->id, tsf_tmp->ram_size, (long)time);
        }
      }
      
      if (!tsf_tmp && !timeshift_conf.ram_only) {
        /* Create directories */
        if (!ts->path) {
          if (timeshift_filemgr_makedirs(ts->id, path, sizeof(path)))
            return NULL;
          ts->path = strdup(path);
        }

        /* Create File */
        snprintf(path, sizeof(path), "%s/tvh-%"PRItime_t, ts->path, time);
        tvhtrace("timeshift", "ts %d create file %s", ts->id, path);
        if ((fd = open(path, O_WRONLY | O_CREAT, 0600)) > 0) {
          tsf_tmp = timeshift_filemgr_file_init(ts, time);
          tsf_tmp->wfd = fd;
          tsf_tmp->path = strdup(path);
        }
      }

      if (tsf_tmp) {
        /* Copy across last start message */
        if (tsf_tl && (ti = TAILQ_LAST(&tsf_tl->sstart, timeshift_index_data_list))) {
          tvhtrace("timeshift", "ts %d copy smt_start to new file",
                   ts->id);
          timeshift_index_data_t *ti2 = calloc(1, sizeof(timeshift_index_data_t));
          ti2->data = streaming_msg_clone(ti->data);
          TAILQ_INSERT_TAIL(&tsf_tmp->sstart, ti2, link);
        }
      }
    }
    tsf_tl = tsf_tmp;
  }

  if (tsf_tl)
    tsf_tl->refcount++;
  return tsf_tl;
}