Beispiel #1
0
static void *
spawn_pipe_thread(void *aux)
{
  tvhpoll_event_t ev[2];
  tvhpoll_t *efd = tvhpoll_create(2);
  int nfds;

  memset(ev, 0, sizeof(ev));
  ev[0].events   = TVHPOLL_IN;
  ev[0].fd       = spawn_pipe_info.rd;
  ev[0].data.ptr = &spawn_pipe_info;
  ev[1].events   = TVHPOLL_IN;
  ev[1].fd       = spawn_pipe_error.rd;
  ev[1].data.ptr = &spawn_pipe_error;
  tvhpoll_add(efd, ev, 2);

  while (spawn_pipe_running) {

    nfds = tvhpoll_wait(efd, ev, 2, 500);

    if (nfds > 0) {
      spawn_pipe_read(&spawn_pipe_info, &spawn_info_buf, LOG_INFO);
      spawn_pipe_read(&spawn_pipe_error, &spawn_error_buf, LOG_ERR);
    }
    spawn_reaper();

  }

  tvhpoll_destroy(efd);
  return NULL;
}
Beispiel #2
0
void
tcp_server_init(int opt_ipv6)
{
  pthread_t tid;

  if(opt_ipv6)
    tcp_preferred_address_family = AF_INET6;

  tcp_server_poll = tvhpoll_create(10);
  pthread_create(&tid, NULL, tcp_server_loop, NULL);
}
Beispiel #3
0
/*
 * Timeshift thread
 */
void *timeshift_reader ( void *p )
{
  timeshift_t *ts = p;
  int nfds, end, run = 1, wait = -1;
  timeshift_file_t *cur_file = NULL;
  int cur_speed = 100, keyframe_mode = 0;
  int64_t pause_time = 0, play_time = 0, last_time = 0;
  int64_t now, deliver, skip_time = 0;
  streaming_message_t *sm = NULL, *ctrl = NULL;
  timeshift_index_iframe_t *tsi = NULL;
  streaming_skip_t *skip = NULL;
  time_t last_status = 0;
  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;
    now       = getmonoclock();

    /* 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("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->ondemand && (speed < 0))
            speed = cur_file ? speed : 0;

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

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

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

              /* Set position */
              } else {
                tvhlog(LOG_DEBUG, "timeshift", "ts %d enter timeshift mode",
                       ts->id);
                timeshift_writer_flush(ts);
                pthread_mutex_lock(&ts->rdwr_mutex);
                if ((cur_file    = timeshift_filemgr_get(ts, 1))) {
                  cur_file->roff = cur_file->size;
                  pause_time     = cur_file->last;
                  last_time      = pause_time;
                }
                pthread_mutex_unlock(&ts->rdwr_mutex);
              }

            /* Buffer playback */
            } else if (ts->state == TS_PLAY) {
              pause_time = last_time;

            /* Paused */
            } else {
            }

            /* Check keyframe mode */
            keyframe      = (speed < 0) || (speed > 400);
            if (keyframe != keyframe_mode) {
              tvhlog(LOG_DEBUG, "timeshift", "using keyframe mode? %s",
                     keyframe ? "yes" : "no");
              keyframe_mode = keyframe;
              if (keyframe) {
                tsi = NULL;
              }
            }

            /* Update */
            play_time  = getmonoclock();
            cur_speed  = speed;
            if (speed != 100 || ts->state != TS_LIVE)
              ts->state = speed == 0 ? TS_PAUSE : TS_PLAY;
            tvhlog(LOG_DEBUG, "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) {
                  pthread_mutex_lock(&ts->rdwr_mutex);
                  timeshift_filemgr_flush(ts, NULL);
                  ts->full = 0;
                  pthread_mutex_unlock(&ts->rdwr_mutex);
                }

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

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

            case SMT_SKIP_ABS_TIME:
              if (ts->pts_delta == 0) {
                tvhlog(LOG_ERR, "timeshift", "ts %d abs skip not possible no PTS delta", ts->id);
                skip = NULL;
                break;
              }
              /* -fallthrough */
            case SMT_SKIP_REL_TIME:

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

              /* Live playback (stage1) */
              if (ts->state == TS_LIVE) {
                pthread_mutex_lock(&ts->rdwr_mutex);
                if ((cur_file    = timeshift_filemgr_get(ts, !ts->ondemand))) {
                  cur_file->roff = cur_file->size;
                  last_time      = cur_file->last;
                } else {
                  tvhlog(LOG_ERR, "timeshift", "ts %d failed to get current file", ts->id);
                  skip = NULL;
                }
                pthread_mutex_unlock(&ts->rdwr_mutex);
              }

              /* May have failed */
              if (skip) {
                skip_time += (skip->type == SMT_SKIP_ABS_TIME) ? ts->pts_delta : last_time;
                tvhlog(LOG_DEBUG, "timeshift", "ts %d skip last_time %"PRId64" pts_delta %"PRId64,
                       ts->id, skip_time - ts->pts_delta, ts->pts_delta);

               /* Live (stage2) */
                if (ts->state == TS_LIVE) {
                  if (skip_time >= now) {
                    tvhlog(LOG_DEBUG, "timeshift", "ts %d skip ignored, already live", ts->id);
                    skip = NULL;
                  } else {
                    ts->state = TS_PLAY;
                  }
                }
              }

              /* OK */
              if (skip) {

                /* Adjust time */
                play_time  = now;
                pause_time = skip_time;
                tsi        = NULL;

                /* Clear existing packet */
                if (sm)
                  streaming_msg_free(sm);
                sm = NULL;
              }
              break;
            default:
              tvhlog(LOG_ERR, "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;
        }
      }
    }

    /* Status message */
    if (now >= (last_status + 1000000)) {
      streaming_message_t *tsm;
      timeshift_status_t *status;
      timeshift_index_iframe_t *fst, *lst;
      status = calloc(1, sizeof(timeshift_status_t));
      fst    = _timeshift_first_frame(ts);
      lst    = _timeshift_last_frame(ts);
      status->full  = ts->full;
      status->shift = ts->state <= TS_LIVE ? 0 : ts_rescale_i(now - last_time, 1000000);
      if (lst && fst && lst != fst && ts->pts_delta != PTS_UNSET) {
        status->pts_start = ts_rescale_i(fst->time - ts->pts_delta, 1000000);
        status->pts_end   = ts_rescale_i(lst->time - ts->pts_delta, 1000000);
      } else {
        status->pts_start = PTS_UNSET;
        status->pts_end   = PTS_UNSET;
      }
      tsm = streaming_msg_create_data(SMT_TIMESHIFT_STATUS, status);
      streaming_target_deliver2(ts->output, tsm);
      last_status = now;
    }

    /* Done */
    if (!run || !cur_file || ((ts->state != TS_PLAY && !skip))) {
      pthread_mutex_unlock(&ts->state_mutex);
      continue;
    }

    /* Calculate delivery time */
    deliver = (now - play_time) + TIMESHIFT_PLAY_BUF;
    deliver = (deliver * cur_speed) / 100;
    deliver = (deliver + pause_time);

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

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

        /* Time */
        if (!skip)
          req_time = last_time + ((cur_speed < 0) ? -1 : 1);
        else
          req_time = skip_time;
        tvhlog(LOG_DEBUG, "timeshift", "ts %d skip to %"PRId64" from %"PRId64,
               ts->id, req_time - ts->pts_delta, last_time - ts->pts_delta);

        /* Find */
        pthread_mutex_lock(&ts->rdwr_mutex);
        end = _timeshift_skip(ts, req_time, last_time,
                              cur_file, &tsf, &tsi);
        pthread_mutex_unlock(&ts->rdwr_mutex);
        if (tsi)
          tvhlog(LOG_DEBUG, "timeshift", "ts %d skip found pkt @ %"PRId64,
                 ts->id, tsi->time - ts->pts_delta);

        /* File changed (close) */
        if ((tsf != cur_file) && cur_file && cur_file->rfd >= 0) {
          close(cur_file->rfd);
          cur_file->rfd = -1;
        }

        /* Position */
        if (cur_file)
          cur_file->refcount--;
        if ((cur_file = tsf) != NULL) {
          if (tsi)
            cur_file->roff = tsi->pos;
          else
            cur_file->roff = 0;
        }
      }

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

    /* Send skip response */
    if (skip) {
      if (sm && sm->sm_type == SMT_PACKET) {
        th_pkt_t *pkt = sm->sm_data;
        skip->time = pkt->pkt_pts;
        skip->type = SMT_SKIP_ABS_TIME;
        tvhlog(LOG_DEBUG, "timeshift", "ts %d skip to pts %"PRId64" ok, time %"PRId64,
               ts->id, ts_rescale(skip->time, 1000000), sm->sm_time - ts->pts_delta);
      } else {
        /* Report error */
        skip->type = SMT_SKIP_ERROR;
        skip       = NULL;
        tvhlog(LOG_DEBUG, "timeshift", "ts %d skip failed (%d)", ts->id, sm ? sm->sm_type : -1);
      }
      streaming_target_deliver2(ts->output, ctrl);
      ctrl = NULL;
    }

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

      if (sm->sm_type == SMT_PACKET && tvhtrace_enabled()) {
        th_pkt_t *pkt = sm->sm_data;
        tvhtrace("timeshift",
                 "ts %d pkt out - stream %d type %c pts %10"PRId64
                 " dts %10"PRId64 " dur %10d len %6zu time %14"PRItime_t,
                 ts->id,
                 pkt->pkt_componentindex,
                 pkt_frametype_to_char(pkt->pkt_frametype),
                 ts_rescale(pkt->pkt_pts, 1000000),
                 ts_rescale(pkt->pkt_dts, 1000000),
                 pkt->pkt_duration,
                 pktbuf_len(pkt->pkt_payload), sm->sm_time - ts->pts_delta);
      }
      last_time = sm->sm_time;
      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("timeshift", "ts %d wait %d",
               ts->id, wait);
    }

    /* Terminate */
    if (!cur_file || end != 0) {
      if (!end)
        end = (cur_speed > 0) ? 1 : -1;

      /* Back to live (unless buffer is full) */
      if (end == 1 && !ts->full) {
        tvhlog(LOG_DEBUG, "timeshift", "ts %d eob revert to live mode", ts->id);
        ts->state = TS_LIVE;
        cur_speed = 100;
        ctrl      = streaming_msg_create_code(SMT_SPEED, cur_speed);
        streaming_target_deliver2(ts->output, ctrl);
        ctrl      = NULL;

        /* Flush timeshift buffer to live */
        if (_timeshift_flush_to_live(ts, &cur_file, &sm, &wait) == -1)
          break;

        /* Close file (if open) */
        if (cur_file && cur_file->rfd >= 0) {
          close(cur_file->rfd);
          cur_file->rfd = -1;
        }

        /* Flush ALL files */
        if (ts->ondemand)
          timeshift_filemgr_flush(ts, NULL);

      /* Pause */
      } else {
        if (cur_speed <= 0) {
          cur_speed = 0;
          ts->state = TS_PAUSE;
        } else {
          cur_speed = 100;
          ts->state = TS_PLAY;
          play_time = now;
        }
        tvhlog(LOG_DEBUG, "timeshift", "ts %d sob speed %d", ts->id, cur_speed);
        pause_time = last_time;
        ctrl       = streaming_msg_create_code(SMT_SPEED, cur_speed);
        streaming_target_deliver2(ts->output, ctrl);
        ctrl       = NULL;
      }

    /* Flush unwanted */
    } else if (ts->ondemand && cur_file) {
      pthread_mutex_lock(&ts->rdwr_mutex);
      timeshift_filemgr_flush(ts, cur_file);
      pthread_mutex_unlock(&ts->rdwr_mutex);
    }

    pthread_mutex_unlock(&ts->state_mutex);
  }

  /* Cleanup */
  tvhpoll_destroy(pd);
  if (cur_file && cur_file->rfd >= 0) {
    close(cur_file->rfd);
    cur_file->rfd = -1;
  }
  if (sm)       streaming_msg_free(sm);
  if (ctrl)     streaming_msg_free(ctrl);
  tvhtrace("timeshift", "ts %d exit reader thread", ts->id);

  return NULL;
}
Beispiel #4
0
int
tcp_connect(const char *hostname, int port, const char *bindaddr,
            char *errbuf, size_t errbufsize, int timeout)
{
  int fd, r, res, err;
  struct addrinfo *ai;
  char portstr[6];
  socklen_t errlen = sizeof(err);

  snprintf(portstr, 6, "%u", port);
  res = getaddrinfo(hostname, portstr, NULL, &ai);
  
  if (res != 0) {
    snprintf(errbuf, errbufsize, "%s", gai_strerror(res));
    return -1;
  }

  fd = tvh_socket(ai->ai_family, SOCK_STREAM, 0);
  if(fd == -1) {
    snprintf(errbuf, errbufsize, "Unable to create socket: %s",
	     strerror(errno));
    freeaddrinfo(ai);
    return -1;
  }

  /**
   * Switch to nonblocking
   */
  fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);

  if (ai->ai_family == AF_INET || ai->ai_family == AF_INET6) {
    if (bindaddr && bindaddr[0] != '\0') {
      struct sockaddr_storage ip;
      memset(&ip, 0, sizeof(ip));
      ip.ss_family = ai->ai_family;
      if (inet_pton(AF_INET, bindaddr, IP_IN_ADDR(ip)) <= 0 ||
          bind(fd, (struct sockaddr *)&ip, IP_IN_ADDRLEN(ip)) < 0) {
        snprintf(errbuf, errbufsize, "Cannot bind to IPv%s addr '%s'",
                                     ai->ai_family == AF_INET6 ? "6" : "4",
                                     bindaddr);
        freeaddrinfo(ai);
        return -1;
      }
    }
  } else {
    snprintf(errbuf, errbufsize, "Invalid protocol family");
    freeaddrinfo(ai);
    return -1;
  }

  r = connect(fd, ai->ai_addr, ai->ai_addrlen);
  freeaddrinfo(ai);

  if(r == -1) {
    /* timeout < 0 - do not wait at all */
    if(errno == EINPROGRESS && timeout < 0) {
      err = 0;
    } else if(errno == EINPROGRESS) {
      tvhpoll_event_t ev;
      tvhpoll_t *efd;

      efd = tvhpoll_create(1);
      memset(&ev, 0, sizeof(ev));
      ev.events   = TVHPOLL_OUT;
      ev.fd       = fd;
      ev.data.ptr = &fd;
      tvhpoll_add(efd, &ev, 1);

      /* minimal timeout is one second */
      if (timeout < 1)
        timeout = 0;

      while (1) {
        if (!tvheadend_running) {
          errbuf[0] = '\0';
          tvhpoll_destroy(efd);
          close(fd);
          return -1;
        }

        r = tvhpoll_wait(efd, &ev, 1, timeout * 1000);
        if (r > 0)
          break;
        
        if (r == 0) { /* Timeout */
          snprintf(errbuf, errbufsize, "Connection attempt timed out");
          tvhpoll_destroy(efd);
          close(fd);
          return -1;
        }
      
        if (!ERRNO_AGAIN(errno)) {
          snprintf(errbuf, errbufsize, "poll() error: %s", strerror(errno));
          tvhpoll_destroy(efd);
          close(fd);
          return -1;
        }
      }
      
      tvhpoll_destroy(efd);
      getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&err, &errlen);
    } else {
      err = errno;
    }
  } else {
    err = 0;
  }

  if(err != 0) {
    snprintf(errbuf, errbufsize, "%s", strerror(err));
    close(fd);
    return -1;
  }
  
  fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK);


  /* Set the keep-alive active */
  err = 1;
  setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&err, errlen);

  return fd;
}
Beispiel #5
0
/*
 *  Discovery thread
 */
static void *
upnp_thread( void *aux )
{
  char *bindaddr = aux;
  tvhpoll_t *poll = tvhpoll_create(2);
  tvhpoll_event_t ev[2];
  upnp_data_t *data;
  udp_connection_t *multicast = NULL, *unicast = NULL;
  udp_connection_t *conn;
  unsigned char buf[16384];
  upnp_service_t *us;
  struct sockaddr_storage ip;
  socklen_t iplen;
  size_t size;
  int r, delay_ms;

  multicast = udp_bind(LS_UPNP, "upnp_thread_multicast",
                       "239.255.255.250", 1900, NULL,
                       NULL, 32*1024, 32*1024);
  if (multicast == NULL || multicast == UDP_FATAL_ERROR)
    goto error;
  unicast = udp_bind(LS_UPNP, "upnp_thread_unicast", bindaddr, 0, NULL,
                     NULL, 32*1024, 32*1024);
  if (unicast == NULL || unicast == UDP_FATAL_ERROR)
    goto error;

  memset(&ev, 0, sizeof(ev));
  ev[0].fd     = multicast->fd;
  ev[0].events = TVHPOLL_IN;
  ev[0].ptr    = multicast;
  ev[1].fd     = unicast->fd;
  ev[1].events = TVHPOLL_IN;
  ev[1].ptr    = unicast;
  tvhpoll_add(poll, ev, 2);

  delay_ms = 0;

  while (atomic_get(&upnp_running) && multicast->fd >= 0) {
    r = tvhpoll_wait(poll, ev, 2, delay_ms ?: 1000);
    if (r == 0) /* timeout */
      delay_ms = 0;

    while (r-- > 0) {
      if ((ev[r].events & TVHPOLL_IN) != 0) {
        conn = ev[r].ptr;
        iplen = sizeof(ip);
        size = recvfrom(conn->fd, buf, sizeof(buf), 0,
                                           (struct sockaddr *)&ip, &iplen);
        if (size > 0 && tvhtrace_enabled()) {
          char tbuf[256];
          inet_ntop(ip.ss_family, IP_IN_ADDR(ip), tbuf, sizeof(tbuf));
          tvhtrace(LS_UPNP, "%s - received data from %s:%hu [size=%zi]",
                   conn == multicast ? "multicast" : "unicast",
                   tbuf, (unsigned short) ntohs(IP_PORT(ip)), size);
          tvhlog_hexdump(LS_UPNP, buf, size);
        }
        /* TODO: a filter */
        TAILQ_FOREACH(us, &upnp_services, us_link)
          us->us_received(buf, size, conn, &ip);
      }
    }

    while (delay_ms == 0) {
      tvh_mutex_lock(&upnp_lock);
      data = TAILQ_FIRST(&upnp_data_write);
      if (data) {
        delay_ms = data->delay_ms;
        data->delay_ms = 0;
        if (!delay_ms) {
          TAILQ_REMOVE(&upnp_data_write, data, data_link);
        } else {
          data = NULL;
        }
      }
      tvh_mutex_unlock(&upnp_lock);
      if (data == NULL)
        break;
      upnp_dump_data(data);
      udp_write_queue(data->from_multicast ? multicast : unicast,
                      &data->queue, &data->storage);
      htsbuf_queue_flush(&data->queue);
      free(data);
      delay_ms = 0;
    }
  }

  /* flush the write queue (byebye messages) */
  while (1) {
    tvh_mutex_lock(&upnp_lock);
    data = TAILQ_FIRST(&upnp_data_write);
    if (data)
      TAILQ_REMOVE(&upnp_data_write, data, data_link);
    tvh_mutex_unlock(&upnp_lock);
    if (data == NULL)
      break;
    tvh_safe_usleep((long)data->delay_ms * 1000);
    upnp_dump_data(data);
    udp_write_queue(unicast, &data->queue, &data->storage);
    htsbuf_queue_flush(&data->queue);
    free(data);
  }

error:
  atomic_set(&upnp_running, 0);
  tvhpoll_destroy(poll);
  udp_close(unicast);
  udp_close(multicast);
  return NULL;
}
/*
 * 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;
}
Beispiel #7
0
/*
 *  Discovery thread
 */
static void *
upnp_thread( void *aux )
{
  char *bindaddr = aux;
  tvhpoll_t *poll = tvhpoll_create(2);
  tvhpoll_event_t ev[2];
  upnp_data_t *data;
  udp_connection_t *multicast = NULL, *unicast = NULL;
  udp_connection_t *conn;
  unsigned char buf[16384];
  upnp_service_t *us;
  struct sockaddr_storage ip;
  socklen_t iplen;
  size_t size;
  int r;

  multicast = udp_bind("upnp", "upnp_thread_multicast",
                       "239.255.255.250", 1900,
                       NULL, 32*1024);
  if (multicast == NULL || multicast == UDP_FATAL_ERROR)
    goto error;
  unicast = udp_bind("upnp", "upnp_thread_unicast", bindaddr, 0,
                     NULL, 32*1024);
  if (unicast == NULL || unicast == UDP_FATAL_ERROR)
    goto error;

  memset(&ev, 0, sizeof(ev));
  ev[0].fd       = multicast->fd;
  ev[0].events   = TVHPOLL_IN;
  ev[0].data.ptr = multicast;
  ev[1].fd       = unicast->fd;
  ev[1].events   = TVHPOLL_IN;
  ev[1].data.ptr = unicast;
  tvhpoll_add(poll, ev, 2);

  while (upnp_running && multicast->fd >= 0) {
    r = tvhpoll_wait(poll, ev, 2, 1000);

    while (r-- > 0) {
      if ((ev[r].events & TVHPOLL_IN) != 0) {
        conn = ev[r].data.ptr;
        iplen = sizeof(ip);
        size = recvfrom(conn->fd, buf, sizeof(buf), 0,
                                           (struct sockaddr *)&ip, &iplen);
#if ENABLE_TRACE
        if (size > 0) {
          char tbuf[256];
          inet_ntop(ip.ss_family, IP_IN_ADDR(ip), tbuf, sizeof(tbuf));
          tvhtrace("upnp", "%s - received data from %s:%hu [size=%zi]",
                   conn == multicast ? "multicast" : "unicast",
                   tbuf, (unsigned short) IP_PORT(ip), size);
          tvhlog_hexdump("upnp", buf, size);
        }
#endif
        /* TODO: a filter */
        TAILQ_FOREACH(us, &upnp_services, us_link)
          us->us_received(buf, size, conn, &ip);
      }
    }

    while (1) {
      pthread_mutex_lock(&upnp_lock);
      data = TAILQ_FIRST(&upnp_data_write);
      if (data)
        TAILQ_REMOVE(&upnp_data_write, data, data_link);
      pthread_mutex_unlock(&upnp_lock);
      if (data == NULL)
        break;
      udp_write_queue(unicast, &data->queue, &data->storage);
      htsbuf_queue_flush(&data->queue);
      free(data);
    }
  }

error:
  upnp_running = 0;
  tvhpoll_destroy(poll);
  udp_close(unicast);
  udp_close(multicast);
  return NULL;
}