Esempio n. 1
0
/*
 * Connected
 */
static int
iptv_rtsp_header ( http_client_t *hc )
{
  iptv_mux_t *im = hc->hc_aux;
  rtsp_priv_t *rp;
  int r;

  if (im == NULL) {
    /* teardown (or teardown timeout) */
    if (hc->hc_cmd == RTSP_CMD_TEARDOWN) {
      pthread_mutex_lock(&global_lock);
      mtimer_arm_rel(&hc->hc_close_timer, iptv_rtsp_close_cb, hc, 0);
      pthread_mutex_unlock(&global_lock);
    }
    return 0;
  }

  if (hc->hc_cmd == RTSP_CMD_GET_PARAMETER && hc->hc_code != HTTP_STATUS_OK) {
    tvhtrace("iptv", "GET_PARAMETER command returned invalid error code %d for '%s', "
        "fall back to OPTIONS in keep alive loop.", hc->hc_code, im->mm_iptv_url_raw);
    hc->hc_rtsp_keep_alive_cmd = RTSP_CMD_OPTIONS;
    return 0;
  }

  if (hc->hc_code != HTTP_STATUS_OK) {
    tvherror("iptv", "invalid error code %d for '%s'", hc->hc_code, im->mm_iptv_url_raw);
    return 0;
  }

  rp = im->im_data;

  switch (hc->hc_cmd) {
  case RTSP_CMD_SETUP:
    r = rtsp_setup_decode(hc, 0);
    if (r >= 0) {
      rtsp_play(hc, rp->path, rp->query);
      rp->play = 1;
    }
    break;
  case RTSP_CMD_PLAY:
    // Now let's set peer port for RTCP
    // Use the HTTP host for sending RTCP reports, NOT the hc_rtp_dest (which is where the stream is sent)
    if (udp_connect(rp->rtcp_info->connection, "rtcp", hc->hc_host, hc->hc_rtcp_server_port)) {
        tvhlog(LOG_WARNING, "rtsp", "Can't connect to remote, RTCP receiver reports won't be sent");
    }
    hc->hc_cmd = HTTP_CMD_NONE;
    pthread_mutex_lock(&global_lock);
    iptv_input_mux_started(hc->hc_aux);
    mtimer_arm_rel(&rp->alive_timer, iptv_rtsp_alive_cb, im,
                   sec2mono(MAX(1, (hc->hc_rtp_timeout / 2) - 1)));
    pthread_mutex_unlock(&global_lock);
    break;
  default:
    break;
  }

  return 0;
}
Esempio n. 2
0
static void *
mtimer_thread(void *aux)
{
  mtimer_t *mti;
  mti_callback_t *cb;
  int64_t now, next;
#if ENABLE_GTIMER_CHECK
  int64_t mtm;
  const char *id;
  const char *fcn;
#endif

  while (tvheadend_is_running()) {
    now = mdispatch_clock_update();

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

    next = now + sec2mono(3600);

    while((mti = LIST_FIRST(&mtimers)) != NULL) {
      
      if (mti->mti_expire > now) {
        next = mti->mti_expire;
        break;
      }

#if ENABLE_GTIMER_CHECK
      mtm = getmonoclock();
      id = mti->mti_id;
      fcn = mti->mti_fcn;
#endif
      cb = mti->mti_callback;

      LIST_REMOVE(mti, mti_link);
      mti->mti_callback = NULL;

      cb(mti->mti_opaque);

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

    /* Periodic updates */
    if (next > mtimer_periodic)
      next = mtimer_periodic;

    /* Wait */
    tvh_cond_timedwait(&mtimer_cond, &global_lock, next);
    pthread_mutex_unlock(&global_lock);
  }
  
  return NULL;
}
Esempio n. 3
0
static void *
mtimer_thread(void *aux)
{
  mtimer_t *mti;
  mti_callback_t *cb;
  int64_t now, next;
  const char *id;

  pthread_mutex_lock(&global_lock);
  while (tvheadend_is_running() && atomic_get(&tvheadend_mainloop) == 0)
    tvh_cond_wait(&mtimer_cond, &global_lock);
  pthread_mutex_unlock(&global_lock);

  while (tvheadend_is_running()) {
    now = mdispatch_clock_update();

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

    next = now + sec2mono(3600);

    while((mti = LIST_FIRST(&mtimers)) != NULL) {

      if (mti->mti_expire > now) {
        next = mti->mti_expire;
        break;
      }

#if ENABLE_GTIMER_CHECK
      id = mti->mti_id;
#else
      id = NULL;
#endif
      tprofile_start(&mtimer_profile, id);

      cb = mti->mti_callback;

      LIST_REMOVE(mti, mti_link);
      mti->mti_callback = NULL;

      cb(mti->mti_opaque);

      tprofile_finish(&mtimer_profile);
    }

    /* Periodic updates */
    if (next > mtimer_periodic)
      next = mtimer_periodic;

    /* Wait */
    tvh_cond_timedwait(&mtimer_cond, &global_lock, next);
    pthread_mutex_unlock(&global_lock);
  }

  return NULL;
}
Esempio n. 4
0
void
mpegts_network_scan_timer_cb ( void *p )
{
  mpegts_network_t *mn = p;
  mpegts_mux_t *mm, *nxt = NULL;
  int r;

  /* Process Q */
  for (mm = TAILQ_FIRST(&mn->mn_scan_pend); mm != NULL; mm = nxt) {
    nxt = TAILQ_NEXT(mm, mm_scan_link);
    assert(mm->mm_scan_state == MM_SCAN_STATE_PEND || mm->mm_scan_state == MM_SCAN_STATE_ACTIVE);

    /* Don't try to subscribe already tuned muxes */
    if (mm->mm_active) continue;

    /* Attempt to tune */
    r = mpegts_mux_subscribe(mm, NULL, "scan", mm->mm_scan_weight,
                             mm->mm_scan_flags |
                             SUBSCRIPTION_ONESHOT |
                             SUBSCRIPTION_TABLES);

    /* Started */
    if (!r) {
      assert(mm->mm_scan_state == MM_SCAN_STATE_ACTIVE);
      continue;
    }
    assert(mm->mm_scan_state == MM_SCAN_STATE_PEND);

    /* No free tuners - stop */
    if (r == SM_CODE_NO_FREE_ADAPTER || r == SM_CODE_NO_ADAPTERS)
      break;

    /* No valid tuners (subtly different, might be able to tuner a later
     * mux)
     */
    if (r == SM_CODE_NO_VALID_ADAPTER && mm->mm_is_enabled(mm))
      continue;

    /* Failed */
    TAILQ_REMOVE(&mn->mn_scan_pend, mm, mm_scan_link);
    if (mm->mm_scan_result != MM_SCAN_FAIL) {
      mm->mm_scan_result = MM_SCAN_FAIL;
      idnode_changed(&mm->mm_id);
    }
    mm->mm_scan_state  = MM_SCAN_STATE_IDLE;
    mm->mm_scan_weight = 0;
    mpegts_network_scan_notify(mm);
  }

  /* Re-arm timer. Really this is just a safety measure as we'd normally
   * expect the timer to be forcefully triggered on finish of a mux scan
   */
  mtimer_arm_rel(&mn->mn_scan_timer, mpegts_network_scan_timer_cb, mn, sec2mono(120));
}
Esempio n. 5
0
/*
 * Read thread
 */
static void *
iptv_file_thread ( void *aux )
{
  iptv_mux_t *im = aux;
  file_priv_t *fp = im->im_data;
  ssize_t r;
  int fd = fp->fd, pause = 0;
  char buf[32*1024];
  off_t off = 0;
  int64_t mono;
  int e;

#if defined(PLATFORM_DARWIN)
  fcntl(fd, F_NOCACHE, 1);
#endif
  pthread_mutex_lock(&iptv_lock);
  while (!fp->shutdown && fd > 0) {
    while (!fp->shutdown && pause) {
      mono = mclk() + sec2mono(1);
      do {
        e = tvh_cond_timedwait(&fp->cond, &iptv_lock, mono);
        if (e == ETIMEDOUT)
          break;
      } while (ERRNO_AGAIN(e));
    }
    if (fp->shutdown)
      break;
    pause = 0;
    pthread_mutex_unlock(&iptv_lock);
    r = read(fd, buf, sizeof(buf));
    pthread_mutex_lock(&iptv_lock);
    if (r == 0)
      break;
    if (r < 0) {
      if (ERRNO_AGAIN(errno))
        continue;
      break;
    }
    sbuf_append(&im->mm_iptv_buffer, buf, r);
    if (iptv_input_recv_packets(im, r) == 1)
      pause = 1;
#ifndef PLATFORM_DARWIN
#if !ENABLE_ANDROID
    posix_fadvise(fd, off, r, POSIX_FADV_DONTNEED);
#endif
#endif
    off += r;
  }
  pthread_mutex_unlock(&iptv_lock);
  return NULL;
}
Esempio n. 6
0
static void
epggrab_ota_kick ( int delay )
{
  /* next round is pending? queue rest of ota muxes */
  if (epggrab_ota_pending_flag) {
    epggrab_ota_pending_flag = 0;
    epggrab_ota_requeue();
  }

  if (TAILQ_EMPTY(&epggrab_ota_pending))
    return;

  mtimer_arm_rel(&epggrab_ota_kick_timer, epggrab_ota_kick_cb, NULL, sec2mono(delay));
}
Esempio n. 7
0
/*
 * Alive timeout
 */
static void
iptv_rtsp_alive_cb ( void *aux )
{
  iptv_mux_t *im = aux;
  rtsp_priv_t *rp = im->im_data;
  if(rp->hc->hc_rtsp_keep_alive_cmd == RTSP_CMD_GET_PARAMETER)
    rtsp_get_parameter(rp->hc, "position");
  else if(rp->hc->hc_rtsp_keep_alive_cmd == RTSP_CMD_OPTIONS)
    rtsp_send(rp->hc, RTSP_CMD_OPTIONS, rp->path, rp->query, NULL);
  else
    return;
  mtimer_arm_rel(&rp->alive_timer, iptv_rtsp_alive_cb, im,
                 sec2mono(MAX(1, (rp->hc->hc_rtp_timeout / 2) - 1)));
}
Esempio n. 8
0
void
mpegts_network_scan_queue_add
  ( mpegts_mux_t *mm, int weight, int flags, int delay )
{
  int reload = 0;
  char buf[256], buf2[256];;
  mpegts_network_t *mn = mm->mm_network;

  if (!mm->mm_is_enabled(mm)) return;

  if (weight <= 0) return;

  if (weight > mm->mm_scan_weight) {
    mm->mm_scan_weight = weight;
    reload             = 1;
  }

  /* Already active */
  if (mm->mm_scan_state == MM_SCAN_STATE_ACTIVE)
    return;

  /* Remove entry (or ignore) */
  if (mm->mm_scan_state == MM_SCAN_STATE_PEND) {
    if (!reload)
      return;
    TAILQ_REMOVE(&mn->mn_scan_pend, mm, mm_scan_link);
  }

  mpegts_mux_nice_name(mm, buf, sizeof(buf));
  mn->mn_display_name(mn, buf2, sizeof(buf2));
  tvhdebug(LS_MPEGTS, "%s - adding mux %s to scan queue weight %d flags %04X",
           buf2, buf, weight, flags);

  /* Add new entry */
  mm->mm_scan_state  = MM_SCAN_STATE_PEND;
  mm->mm_scan_flags |= flags;
  if (mm->mm_scan_flags == 0)
    mm->mm_scan_flags = SUBSCRIPTION_IDLESCAN;
  TAILQ_INSERT_SORTED_R(&mn->mn_scan_pend, mpegts_mux_queue,
                        mm, mm_scan_link, mm_cmp);
  mtimer_arm_rel(&mn->mn_scan_timer, mpegts_network_scan_timer_cb, mn, sec2mono(delay));
  mpegts_network_scan_notify(mm);
}
Esempio n. 9
0
/* Finished */
static inline void
mpegts_network_scan_mux_done0
  ( mpegts_mux_t *mm, mpegts_mux_scan_result_t result, int weight )
{
  mpegts_network_t *mn = mm->mm_network;
  mpegts_mux_scan_state_t state = mm->mm_scan_state;

  /* prevent double del: */
  /*   mpegts_mux_stop -> mpegts_network_scan_mux_cancel */
  mm->mm_scan_state = MM_SCAN_STATE_IDLE;
  mpegts_mux_unsubscribe_by_name(mm, "scan");
  mm->mm_scan_state = state;

  if (state == MM_SCAN_STATE_PEND) {
    if (weight || mn->mn_idlescan) {
      if (!weight)
        mm->mm_scan_weight = SUBSCRIPTION_PRIO_SCAN_IDLE;
      TAILQ_REMOVE(&mn->mn_scan_pend, mm, mm_scan_link);
      TAILQ_INSERT_SORTED_R(&mn->mn_scan_pend, mpegts_mux_queue,
                            mm, mm_scan_link, mm_cmp);
      mtimer_arm_rel(&mn->mn_scan_timer, mpegts_network_scan_timer_cb, mn, sec2mono(10));
      weight = 0;
    } else {
      mpegts_network_scan_queue_del(mm);
    }
  } else {
    if (!weight && mn->mn_idlescan)
      weight = SUBSCRIPTION_PRIO_SCAN_IDLE;
    mpegts_network_scan_queue_del(mm);
  }

  if (result != MM_SCAN_NONE && mm->mm_scan_result != result) {
    mm->mm_scan_result = result;
    idnode_changed(&mm->mm_id);
  }

  /* Re-enable? */
  if (weight > 0)
    mpegts_network_scan_queue_add(mm, weight, mm->mm_scan_flags, 10);
}
Esempio n. 10
0
int
tvh_write(int fd, const void *buf, size_t len)
{
  int64_t limit = mclk() + sec2mono(25);
  ssize_t c;

  while (len) {
    c = write(fd, buf, len);
    if (c < 0) {
      if (ERRNO_AGAIN(errno)) {
        if (mclk() > limit)
          break;
        tvh_safe_usleep(100);
        continue;
      }
      break;
    }
    len -= c;
    buf += c;
  }

  return len ? 1 : 0;
}
Esempio n. 11
0
static void *
tvhdhomerun_device_discovery_thread( void *aux )
{
  struct hdhomerun_discover_device_t result_list[MAX_HDHOMERUN_DEVICES];
  int numDevices, brk;

  while (tvheadend_is_running()) {

    numDevices =
      hdhomerun_discover_find_devices_custom(0,
                                             HDHOMERUN_DEVICE_TYPE_TUNER,
                                             HDHOMERUN_DEVICE_ID_WILDCARD,
                                             result_list,
                                             MAX_HDHOMERUN_DEVICES);

    if (numDevices > 0) {
      while (numDevices > 0 ) {
        numDevices--;
        struct hdhomerun_discover_device_t* cDev = &result_list[numDevices];
        if ( cDev->device_type == HDHOMERUN_DEVICE_TYPE_TUNER ) {
          pthread_mutex_lock(&global_lock);
          tvhdhomerun_device_t *existing = tvhdhomerun_device_find(cDev->device_id);
          if ( tvheadend_is_running() ) {
            if ( !existing ) {
              tvhinfo(LS_TVHDHOMERUN,"Found HDHomerun device %08x with %d tuners",
                      cDev->device_id, cDev->tuner_count);
              tvhdhomerun_device_create(cDev);
            } else if ( ((struct sockaddr_in *)&existing->hd_info.ip_address)->sin_addr.s_addr !=
                     htonl(cDev->ip_addr) ) {
              struct sockaddr_storage detected_dev_addr;
              memset(&detected_dev_addr, 0, sizeof(detected_dev_addr));
              detected_dev_addr.ss_family = AF_INET;
              ((struct sockaddr_in *)&detected_dev_addr)->sin_addr.s_addr = htonl(cDev->ip_addr);

              char existing_ip[64];
              tcp_get_str_from_ip(&existing->hd_info.ip_address, existing_ip, sizeof(existing_ip));

              char detected_ip[64];
              tcp_get_str_from_ip(&detected_dev_addr, detected_ip, sizeof(detected_ip));

              tvhinfo(LS_TVHDHOMERUN,"HDHomerun device %08x switched IPs from %s to %s, updating",
                     cDev->device_id, existing_ip, detected_ip);
              tvhdhomerun_device_destroy(existing);
              tvhdhomerun_device_create(cDev);
            }
          }
          pthread_mutex_unlock(&global_lock);
        }
      }
    }

    pthread_mutex_lock(&tvhdhomerun_discovery_lock);
    brk = 0;
    if (tvheadend_is_running()) {
      brk = tvh_cond_timedwait(&tvhdhomerun_discovery_cond,
                               &tvhdhomerun_discovery_lock,
                               mclk() + sec2mono(15));
      brk = !ERRNO_AGAIN(brk) && brk != ETIMEDOUT;
    }
    pthread_mutex_unlock(&tvhdhomerun_discovery_lock);
    if (brk)
      break;
  }

  return NULL;
}
Esempio n. 12
0
/*
 * 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 */
    pthread_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;
      }
      pthread_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) {
        pthread_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) {
          pthread_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;
      }

    }

    pthread_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;
}
Esempio n. 13
0
int
h264_decode_slice_header(elementary_stream_t *st, bitstream_t *bs, int *pkttype,
			 int *isfield)
{
  h264_private_t *p;
  h264_pps_t *pps;
  h264_sps_t *sps;
  uint32_t slice_type, pps_id, width, height, v;
  uint64_t d;

  *pkttype = 0;
  *isfield = 0;

  if ((p = st->es_priv) == NULL)
    return -1;

  read_golomb_ue(bs); /* first_mb_in_slice */
  slice_type = read_golomb_ue(bs);

  if (slice_type > 4)
    slice_type -= 5;  /* Fixed slice type per frame */

  pps_id = read_golomb_ue(bs);
  if (pps_id >= MAX_PPS_COUNT)
    return -1;
  pps = &p->pps[pps_id];
  if (!pps->valid)
    return -1;
  sps = &p->sps[pps->sps_id];
  if (!sps->valid)
    return -1;

  if (!sps->max_frame_num_bits)
    return -1;

  switch(slice_type) {
  case 0:
    *pkttype = PKT_P_FRAME;
    break;
  case 1:
    *pkttype = PKT_B_FRAME;
    break;
  case 2:
    *pkttype = PKT_I_FRAME;
    break;
  default:
    return -1;
  }

  skip_bits(bs, sps->max_frame_num_bits);

  if (!sps->mbs_only_flag)
    if (read_bits1(bs)) {
      skip_bits1(bs); // bottom field
      *isfield = 1;
    }

  d = 0;
  if (sps->time_scale)
    d = 180000 * (uint64_t)sps->units_in_tick / (uint64_t)sps->time_scale;
  if (d == 0 && st->es_frame_duration < 2 && p->start + sec2mono(4) < mclk()) {
    tvhwarn("parser", "H264 stream has not timing information, using 30fps");
    d = 3000; /* 90000/30 = 3000 : 30fps */
  }

#if 0
  if (sps->cbpsize)
    st->es_vbv_size = sps->cbpsize;
#endif

  width  = sps->width;
  height = sps->height * (2 - sps->mbs_only_flag);

  if (width && height && d)
    parser_set_stream_vparam(st, width, height, d);

  if (sps->aspect_num && sps->aspect_den) {
    width  *= sps->aspect_num;
    height *= sps->aspect_den;
    if (width && height) {
      v = gcdU32(width, height);
      st->es_aspect_num = width / v;
      st->es_aspect_den = height / v;
    }
  } else {
    st->es_aspect_num = 0;
    st->es_aspect_den = 1;
  }

  return 0;
}