Exemple #1
0
static void
mainloop(void)
{
  gtimer_t *gti;
  gti_callback_t *cb;
  struct timespec ts;

  while(tvheadend_running) {
    clock_gettime(CLOCK_REALTIME, &ts);

    /* 1sec stuff */
    if (ts.tv_sec > dispatch_clock) {
      dispatch_clock = ts.tv_sec;

      spawn_reaper(); /* reap spawned processes */

      comet_flush(); /* Flush idle comet mailboxes */
    }

    /* Global timers */
    pthread_mutex_lock(&global_lock);

    // TODO: there is a risk that if timers re-insert themselves to
    //       the top of the list with a 0 offset we could loop indefinitely
    
#if 0
    tvhdebug("gtimer", "now %ld.%09ld", ts.tv_sec, ts.tv_nsec);
    LIST_FOREACH(gti, &gtimers, gti_link)
      tvhdebug("gtimer", "  gti %p expire %ld.%08ld",
               gti, gti->gti_expire.tv_sec, gti->gti_expire.tv_nsec);
#endif

    while((gti = LIST_FIRST(&gtimers)) != NULL) {
      
      if ((gti->gti_expire.tv_sec > ts.tv_sec) ||
          ((gti->gti_expire.tv_sec == ts.tv_sec) &&
           (gti->gti_expire.tv_nsec > ts.tv_nsec))) {
        ts = gti->gti_expire;
        break;
      }

      cb = gti->gti_callback;
      //tvhdebug("gtimer", "%p callback", gti);

      LIST_REMOVE(gti, gti_link);
      gti->gti_callback = NULL;

      cb(gti->gti_opaque);
    }

    /* Bound wait */
    if ((LIST_FIRST(&gtimers) == NULL) || (ts.tv_sec > (dispatch_clock + 1))) {
      ts.tv_sec  = dispatch_clock + 1;
      ts.tv_nsec = 0;
    }

    /* Wait */
    //tvhdebug("gtimer", "wait till %ld.%09ld", ts.tv_sec, ts.tv_nsec);
    pthread_cond_timedwait(&gtimer_cond, &global_lock, &ts);
    pthread_mutex_unlock(&global_lock);
  }
}
Exemple #2
0
static void _xmltv_load_grabbers ( void )
{
  int outlen;
  size_t i, p, n;
  char *outbuf;
  char name[1000];
  char *tmp, *tmp2 = NULL, *path;

  /* Load data */
  outlen = spawn_and_store_stdout(XMLTV_FIND, NULL, &outbuf);

  /* Process */
  if ( outlen > 0 ) {
    p = n = i = 0;
    while ( i < outlen ) {
      if ( outbuf[i] == '\n' || outbuf[i] == '\0' ) {
        outbuf[i] = '\0';
        sprintf(name, "XMLTV: %s", &outbuf[n]);
        epggrab_module_int_create(NULL, &outbuf[p], name, 3, &outbuf[p],
                                NULL, _xmltv_parse, NULL, NULL);
        p = n = i + 1;
      } else if ( outbuf[i] == '|' ) {
        outbuf[i] = '\0';
        n = i + 1;
      }
      i++;
    }
    free(outbuf);

  /* Internal search */
  } else if ((tmp = getenv("PATH"))) {
    tvhdebug("epggrab", "using internal grab search");
    char bin[256];
    char desc[] = "--description";
    char *argv[] = {
      NULL,
      desc,
      NULL
    };
    path = strdup(tmp);
    tmp  = strtok_r(path, ":", &tmp2);
    while (tmp) {
      DIR *dir;
      struct dirent *de;
      struct stat st;
      if ((dir = opendir(tmp))) {
        while ((de = readdir(dir))) {
          if (strstr(de->d_name, XMLTV_GRAB) != de->d_name) continue;
          snprintf(bin, sizeof(bin), "%s/%s", tmp, de->d_name);
          if (epggrab_module_find_by_id(bin)) continue;
          if (stat(bin, &st)) continue;
          if (!(st.st_mode & S_IEXEC)) continue;
          if (!S_ISREG(st.st_mode)) continue;
          if ((outlen = spawn_and_store_stdout(bin, argv, &outbuf)) > 0) {
            if (outbuf[outlen-1] == '\n') outbuf[outlen-1] = '\0';
            snprintf(name, sizeof(name), "XMLTV: %s", outbuf);
            epggrab_module_int_create(NULL, bin, name, 3, bin,
                                      NULL, _xmltv_parse, NULL, NULL);
            free(outbuf);
          }
        }
        closedir(dir);
      }
      tmp = strtok_r(NULL, ":", &tmp2);
    }
    free(path);
  }
}
/*
 * Timeshift thread
 */
void *timeshift_reader ( void *p )
{
  timeshift_t *ts = p;
  int nfds, end, run = 1, wait = -1, state;
  timeshift_seek_t *seek = &ts->seek;
  timeshift_file_t *tmp_file;
  int cur_speed = 100, keyframe_mode = 0;
  int64_t mono_now, mono_play_time = 0, mono_last_status = 0;
  int64_t deliver, deliver0, pause_time = 0, last_time = 0, skip_time = 0;
  int64_t i64;
  streaming_message_t *sm = NULL, *ctrl = NULL;
  streaming_skip_t *skip = NULL;
  tvhpoll_t *pd;
  tvhpoll_event_t ev = { 0 };

  pd = tvhpoll_create(1);
  ev.fd     = ts->rd_pipe.rd;
  ev.events = TVHPOLL_IN;
  tvhpoll_add(pd, &ev, 1);

  /* Output */
  while (run) {

    // Note: Previously we allowed unlimited wait, but we now must wake periodically
    //       to output status message
    if (wait < 0 || wait > 1000)
      wait = 1000;

    /* Wait for data */
    if(wait)
      nfds = tvhpoll_wait(pd, &ev, 1, wait);
    else
      nfds = 0;
    wait      = -1;
    end       = 0;
    skip      = NULL;
    mono_now  = getfastmonoclock();

    /* Control */
    tvh_mutex_lock(&ts->state_mutex);
    if (nfds == 1) {
      if (_read_msg(NULL, ts->rd_pipe.rd, &ctrl) > 0) {

        /* Exit */
        if (ctrl->sm_type == SMT_EXIT) {
          tvhtrace(LS_TIMESHIFT, "ts %d read exit request", ts->id);
          run = 0;
          streaming_msg_free(ctrl);
          ctrl = NULL;

        /* Speed */
        } else if (ctrl->sm_type == SMT_SPEED) {
          int speed = ctrl->sm_code;
          int keyframe;

          /* Bound it */
          if (speed > 3200)  speed = 3200;
          if (speed < -3200) speed = -3200;

          /* Ignore negative */
          if (!ts->dobuf && (speed < 0))
            speed = seek->file ? speed : 0;

          /* Process */
          if (cur_speed != speed) {

            /* Live playback */
            state = ts->state;
            if (state == TS_LIVE) {

              /* Reject */
              if (speed >= 100) {
                tvhdebug(LS_TIMESHIFT, "ts %d reject 1x+ in live mode", ts->id);
                speed = 100;

              /* Set position */
              } else {
                tvhdebug(LS_TIMESHIFT, "ts %d enter timeshift mode", ts->id);
                ts->dobuf = 1;
                _seek_reset(seek);
                tmp_file = timeshift_filemgr_newest(ts);
                if (tmp_file != NULL) {
                  i64 = tmp_file->last;
                  timeshift_file_put(tmp_file);
                } else {
                  i64 = ts->buf_time;
                }
                seek->file = timeshift_filemgr_get(ts, i64);
                if (seek->file != NULL) {
                  seek->file->roff = seek->file->size;
                  pause_time       = seek->file->last;
                  last_time        = pause_time;
                } else {
                  pause_time       = i64;
                  last_time        = pause_time;
                }
              }
            }

            /* Check keyframe mode */
            keyframe      = (speed < 0) || (speed > 400);
            if (keyframe != keyframe_mode) {
              tvhdebug(LS_TIMESHIFT, "using keyframe mode? %s", keyframe ? "yes" : "no");
              keyframe_mode = keyframe;
              if (keyframe)
                seek->frame = NULL;
            }

            /* Update */
            if (speed != 100 || state != TS_LIVE) {
              ts->state = speed == 0 ? TS_PAUSE : TS_PLAY;
              tvhtrace(LS_TIMESHIFT, "reader - set %s", speed == 0 ? "TS_PAUSE" : "TS_PLAY");
            }
            if ((ts->state == TS_PLAY && state != TS_PLAY) || (speed != cur_speed)) {
              mono_play_time = mono_now;
              tvhtrace(LS_TIMESHIFT, "update play time TS_LIVE - %"PRId64" play buffer from %"PRId64,
                       mono_now, pause_time);
              if (speed != cur_speed)
                pause_time = last_time;
            } else if (ts->state == TS_PAUSE && state != TS_PAUSE) {
              pause_time = last_time;
            }
            cur_speed = speed;
            tvhdebug(LS_TIMESHIFT, "ts %d change speed %d", ts->id, speed);
          }

          /* Send on the message */
          ctrl->sm_code = speed;
          streaming_target_deliver2(ts->output, ctrl);
          ctrl = NULL;

        /* Skip/Seek */
        } else if (ctrl->sm_type == SMT_SKIP) {
          skip = ctrl->sm_data;
          switch (skip->type) {
            case SMT_SKIP_LIVE:
              if (ts->state != TS_LIVE) {

                /* Reset */
                if (ts->full) {
                  timeshift_filemgr_flush(ts, NULL);
                  _seek_reset(seek);
                  ts->full = 0;
                }

                /* Release */
                if (sm)
                  streaming_msg_free(sm);

                /* Find end */
                skip_time = 0x7fffffffffffffffLL;
                // TODO: change this sometime!
              }
              break;

            case SMT_SKIP_ABS_TIME:
              /* -fallthrough */
            case SMT_SKIP_REL_TIME:

              /* Convert */
              skip_time = ts_rescale(skip->time, 1000000);
              tvhdebug(LS_TIMESHIFT, "ts %d skip %"PRId64" requested %"PRId64, ts->id, skip_time, skip->time);

              /* Live playback (stage1) */
              if (ts->state == TS_LIVE) {
                _seek_reset(seek);
                tmp_file = timeshift_filemgr_newest(ts);
                if (tmp_file) {
                  i64 = tmp_file->last;
                  timeshift_file_put(tmp_file);
                }
                if (tmp_file && (seek->file = timeshift_filemgr_get(ts, i64)) != NULL) {
                  seek->file->roff = seek->file->size;
                  last_time        = seek->file->last;
                } else {
                  last_time        = ts->buf_time;
                }
              }

              /* May have failed */
              if (skip->type == SMT_SKIP_REL_TIME)
                skip_time += last_time;
              tvhdebug(LS_TIMESHIFT, "ts %d skip time %"PRId64, ts->id, skip_time);

              /* Live (stage2) */
              if (ts->state == TS_LIVE) {
                if (skip_time >= ts->buf_time - TIMESHIFT_PLAY_BUF) {
                  tvhdebug(LS_TIMESHIFT, "ts %d skip ignored, already live", ts->id);
                  skip = NULL;
                } else {
                  ts->state = TS_PLAY;
                  ts->dobuf = 1;
                  tvhtrace(LS_TIMESHIFT, "reader - set TS_PLAY");
                }
              }

              /* OK */
              if (skip) {
                /* seek */
                seek->frame = NULL;
                end = _timeshift_do_skip(ts, skip_time, last_time, seek);
                if (seek->frame) {
                  pause_time = seek->frame->time;
                  tvhtrace(LS_TIMESHIFT, "ts %d skip - play buffer from %"PRId64" last_time %"PRId64,
                           ts->id, pause_time, last_time);

                  /* Adjust time */
                  if (mono_play_time != mono_now)
                    tvhtrace(LS_TIMESHIFT, "ts %d update play time skip - %"PRId64, ts->id, mono_now);
                  mono_play_time = mono_now;

                  /* Clear existing packet */
                  if (sm) {
                    streaming_msg_free(sm);
                    sm = NULL;
                  }
                } else {
                  skip = NULL;
                }
              }
              break;
            default:
              tvherror(LS_TIMESHIFT, "ts %d invalid/unsupported skip type: %d", ts->id, skip->type);
              skip = NULL;
              break;
          }

          /* Error */
          if (!skip) {
            ((streaming_skip_t*)ctrl->sm_data)->type = SMT_SKIP_ERROR;
            streaming_target_deliver2(ts->output, ctrl);
            ctrl = NULL;
          }

        /* Ignore */
        } else {
          streaming_msg_free(ctrl);
          ctrl = NULL;
        }
      }
    }


    /* Done */
    if (!run || !seek->file || ((ts->state != TS_PLAY && !skip))) {
      if (mono_now >= (mono_last_status + sec2mono(1))) {
        timeshift_status(ts, last_time);
        mono_last_status = mono_now;
      }
      tvh_mutex_unlock(&ts->state_mutex);
      continue;
    }

    /* Calculate delivery time */
    deliver0 = (mono_now - mono_play_time) + TIMESHIFT_PLAY_BUF;
    deliver = (deliver0 * cur_speed) / 100;
    deliver = (deliver + pause_time);
    tvhtrace(LS_TIMESHIFT, "speed %d now %"PRId64" play_time %"PRId64" deliver %"PRId64" deliver0 %"PRId64,
             cur_speed, mono_now, mono_play_time, deliver, deliver0);

    /* Determine next packet */
    if (!sm) {

      /* Rewind or Fast forward (i-frame only) */
      if (skip || keyframe_mode) {
        int64_t req_time;

        /* Time */
        if (!skip)
          req_time = last_time + ((cur_speed < 0) ? -1 : 1);
        else
          req_time = skip_time;

        end = _timeshift_do_skip(ts, req_time, last_time, seek);
      }

      /* Clear old message */
      if (sm) {
        streaming_msg_free(sm);
        sm = NULL;
      }

      /* Find packet */
      if (_timeshift_read(ts, seek, &sm, &wait) == -1) {
        tvh_mutex_unlock(&ts->state_mutex);
        break;
      }
    }

    /* Send skip response */
    if (skip) {
      if (sm) {
        /* Status message */
        skip->time = ts_rescale_inv(sm->sm_time, 1000000);
        skip->type = SMT_SKIP_ABS_TIME;
        tvhdebug(LS_TIMESHIFT, "ts %d skip to pts %"PRId64" ok", ts->id, sm->sm_time);
        /* Update timeshift status */
        timeshift_fill_status(ts, &skip->timeshift, sm->sm_time);
        mono_last_status = mono_now;
      } else {
        /* Report error */
        skip->type = SMT_SKIP_ERROR;
        skip       = NULL;
        tvhdebug(LS_TIMESHIFT, "ts %d skip failed (%d)", ts->id, sm ? sm->sm_type : -1);
      }
      streaming_target_deliver2(ts->output, ctrl);
    } else {
      streaming_msg_free(ctrl);
    }
    ctrl = NULL;

    /* Deliver */
    if (sm && (skip ||
               (((cur_speed < 0) && (sm->sm_time >= deliver)) ||
                ((cur_speed > 0) && (sm->sm_time <= deliver))))) {

      last_time = sm->sm_time;
      if (!skip && keyframe_mode) /* always send status on keyframe mode */
        timeshift_status(ts, last_time);
      timeshift_packet_log("out", ts, sm);
      streaming_target_deliver2(ts->output, sm);
      sm        = NULL;
      wait      = 0;

    } else if (sm) {

      if (cur_speed > 0)
        wait = (sm->sm_time - deliver) / 1000;
      else
        wait = (deliver - sm->sm_time) / 1000;
      if (wait == 0) wait = 1;
      tvhtrace(LS_TIMESHIFT, "ts %d wait %d speed %d sm_time %"PRId64" deliver %"PRId64,
               ts->id, wait, cur_speed, sm->sm_time, deliver);

    }

    /* Periodic timeshift status */
    if (mono_now >= (mono_last_status + sec2mono(1))) {
      timeshift_status(ts, last_time);
      mono_last_status = mono_now;
    }

    /* Terminate */
    if (!seek->file || end != 0) {

      /* Back to live (unless buffer is full) */
      if ((end == 1 && !ts->full) || !seek->file) {
        tvhdebug(LS_TIMESHIFT, "ts %d eob revert to live mode", ts->id);
        cur_speed = 100;
        ctrl      = streaming_msg_create_code(SMT_SPEED, cur_speed);
        streaming_target_deliver2(ts->output, ctrl);
        ctrl      = NULL;
        tvhtrace(LS_TIMESHIFT, "reader - set TS_LIVE");

        /* Flush timeshift buffer to live */
        if (_timeshift_flush_to_live(ts, seek, &wait) == -1) {
          tvh_mutex_unlock(&ts->state_mutex);
          break;
        }

        ts->state = TS_LIVE;

        /* Close file (if open) */
        _read_close(seek);

      /* Pause */
      } else {
        if (cur_speed <= 0) {
          cur_speed = 0;
          tvhtrace(LS_TIMESHIFT, "reader - set TS_PAUSE");
          ts->state = TS_PAUSE;
        } else {
          cur_speed = 100;
          tvhtrace(LS_TIMESHIFT, "reader - set TS_PLAY");
          if (ts->state != TS_PLAY) {
            ts->state = TS_PLAY;
            ts->dobuf = 1;
            if (mono_play_time != mono_now)
              tvhtrace(LS_TIMESHIFT, "update play time (pause) - %"PRId64, mono_now);
            mono_play_time = mono_now;
          }
        }
        tvhdebug(LS_TIMESHIFT, "ts %d sob speed %d last time %"PRId64, ts->id, cur_speed, last_time);
        pause_time = last_time;
        ctrl       = streaming_msg_create_code(SMT_SPEED, cur_speed);
        streaming_target_deliver2(ts->output, ctrl);
        ctrl       = NULL;
      }

    }

    tvh_mutex_unlock(&ts->state_mutex);
  }

  /* Cleanup */
  tvhpoll_destroy(pd);
  _read_close(seek);
  if (sm)       streaming_msg_free(sm);
  if (ctrl)     streaming_msg_free(ctrl);
  tvhtrace(LS_TIMESHIFT, "ts %d exit reader thread", ts->id);

  return NULL;
}
Exemple #4
0
static void
mainloop(void)
{
  gtimer_t *gti;
  gti_callback_t *cb;
  struct timespec ts;
#if ENABLE_GTIMER_CHECK
  int64_t mtm;
  const char *id;
  const char *fcn;
#endif

  while(tvheadend_running) {
    dispatch_clock_update(&ts);

    /* Global timers */
    pthread_mutex_lock(&global_lock);

    // TODO: there is a risk that if timers re-insert themselves to
    //       the top of the list with a 0 offset we could loop indefinitely
    
#if 0
    tvhdebug("gtimer", "now %ld.%09ld", ts.tv_sec, ts.tv_nsec);
    LIST_FOREACH(gti, &gtimers, gti_link)
      tvhdebug("gtimer", "  gti %p expire %ld.%08ld",
               gti, gti->gti_expire.tv_sec, gti->gti_expire.tv_nsec);
#endif

    while((gti = LIST_FIRST(&gtimers)) != NULL) {
      
      if ((gti->gti_expire.tv_sec > ts.tv_sec) ||
          ((gti->gti_expire.tv_sec == ts.tv_sec) &&
           (gti->gti_expire.tv_nsec > ts.tv_nsec))) {
        ts = gti->gti_expire;
        break;
      }

#if ENABLE_GTIMER_CHECK
      mtm = getmonoclock();
      id = gti->gti_id;
      fcn = gti->gti_fcn;
#endif
      cb = gti->gti_callback;

      LIST_REMOVE(gti, gti_link);
      gti->gti_callback = NULL;

      cb(gti->gti_opaque);

#if ENABLE_GTIMER_CHECK
      tvhtrace("gtimer", "%s:%s duration %"PRId64"ns", id, fcn, getmonoclock() - mtm);
#endif
    }

    /* Bound wait */
    if ((LIST_FIRST(&gtimers) == NULL) || (ts.tv_sec > (dispatch_clock + 1))) {
      ts.tv_sec  = dispatch_clock + 1;
      ts.tv_nsec = 0;
    }

    /* Wait */
    pthread_cond_timedwait(&gtimer_cond, &global_lock, &ts);
    pthread_mutex_unlock(&global_lock);
  }
}
Exemple #5
0
static void
mainloop(void)
{
  gtimer_t *gti;
  gti_callback_t *cb;
  time_t now;
  struct timespec ts;
#if ENABLE_GTIMER_CHECK
  int64_t mtm;
  const char *id;
  const char *fcn;
#endif

  while (tvheadend_is_running()) {
    now = gdispatch_clock_update();
    ts.tv_sec  = now + 3600;
    ts.tv_nsec = 0;

    /* Global timers */
    pthread_mutex_lock(&global_lock);

    // TODO: there is a risk that if timers re-insert themselves to
    //       the top of the list with a 0 offset we could loop indefinitely
    
#if 0
    tvhdebug("gtimer", "now %"PRItime_t, ts.tv_sec);
    LIST_FOREACH(gti, &gtimers, gti_link)
      tvhdebug("gtimer", "  gti %p expire %"PRItimet, gti, gti->gti_expire.tv_sec);
#endif

    while((gti = LIST_FIRST(&gtimers)) != NULL) {
      
      if (gti->gti_expire > now) {
        ts.tv_sec = gti->gti_expire;
        break;
      }

#if ENABLE_GTIMER_CHECK
      mtm = getmonoclock();
      id = gti->gti_id;
      fcn = gti->gti_fcn;
#endif
      cb = gti->gti_callback;

      LIST_REMOVE(gti, gti_link);
      gti->gti_callback = NULL;

      cb(gti->gti_opaque);

#if ENABLE_GTIMER_CHECK
      mtm = getmonoclock() - mtm;
      if (mtm > 10000)
        tvhtrace("gtimer", "%s:%s duration %"PRId64"us", id, fcn, mtm);
#endif
    }

    /* Wait */
    pthread_cond_timedwait(&gtimer_cond, &global_lock, &ts);
    pthread_mutex_unlock(&global_lock);
  }
}