Example #1
0
void thread_func_wrapper(void *pArg) {
  THREAD_FUNC_WRAPPER_ARG_T wrap;
  char tmp[128];
  int rc = 0;

  memcpy(&wrap, pArg, sizeof(THREAD_FUNC_WRAPPER_ARG_T));
  ((THREAD_FUNC_WRAPPER_ARG_T *)pArg)->flags = 0;

  logutil_tid_add(pthread_self(), wrap.tid_tag);

  //pthread_cond_broadcast(((THREAD_FUNC_WRAPPER_ARG_T *) pArg)->pcond);

  //fprintf(stderr, "%u THREAD_FUNC pConn:0x%x inuse:%d\n", pthread_self(), wrap.pConn, wrap.pConn->pool.inuse);

  //
  // Handle any SSL handshaking on the connection thread, to avoid blocking the accept loop
  //
  if((wrap.pConn->sd.netsocket.flags & NETIO_FLAG_SSL_TLS)) {

    if((rc = netio_acceptssl(&wrap.pConn->sd.netsocket)) < 0) {
      LOG(X_ERROR("Closing non-SSL connection on port %d from %s:%d"), htons(INET_PORT(wrap.pConn->pListenCfg->sa)), 
          FORMAT_NETADDR(wrap.pConn->sd.sa, tmp, sizeof(tmp)), htons(INET_PORT(wrap.pConn->sd.sa)));
    }
  }

  if(rc == 0) {
    //
    // Call the wrapper's thread procedure
    //
    wrap.thread_func(wrap.pConn);
  }

  //fprintf(stderr, "%d THREAD_FUNC DONE pConn:0x%x inuse:%d\n", pthread_self(), wrap.pConn, wrap.pConn->pool.inuse);

  netio_closesocket(&wrap.pConn->sd.netsocket);
  pool_return(wrap.pConnPool, &wrap.pConn->pool);

  logutil_tid_remove(pthread_self());

  //fprintf(stderr, "%u THREAD_FUNC DONE pConn:0x%x inuse:%d\n", pthread_self(), wrap.pConn, wrap.pConn->pool.inuse);

}
Example #2
0
int srv_rtsp_proc(CLIENT_CONN_T *pConn, const unsigned char *prebuf, unsigned int prebufsz) {
  int rc = 0;
  RTSP_REQ_CTXT_T rtspCtxt;
  char tmps[2][128];

  if(!pConn) {
    return -1;
  }

  if(!(pConn->pListenCfg->urlCapabilities & URL_CAP_RTSPLIVE)) {
    LOG(X_ERROR("Listener %s:%d not enabled for rtsp%s stream to %s:%d"),
          FORMAT_NETADDR(pConn->pListenCfg->sa, tmps[1], sizeof(tmps[1])), ntohs(INET_PORT(pConn->pListenCfg->sa)),
          (pConn->sd.netsocket.flags & NETIO_FLAG_SSL_TLS) ? "s" : "",
          FORMAT_NETADDR(pConn->sd.sa, tmps[0], sizeof(tmps[0])), ntohs(INET_PORT(pConn->sd.sa)));
    return -1;
  }

  memset(&rtspCtxt, 0, sizeof(rtspCtxt));
  rtspCtxt.pSd = &pConn->sd;
  rtspCtxt.pListenCfg = pConn->pListenCfg;

  if(pConn->pStreamerCfg0 && pConn->pStreamerCfg0->running >= 0) {
    rtspCtxt.pStreamerCfg = (STREAMER_CFG_T *) pConn->pStreamerCfg0;
  } else if(pConn->pStreamerCfg1 && pConn->pStreamerCfg1->running >= 0) {
    rtspCtxt.pStreamerCfg = (STREAMER_CFG_T *) pConn->pStreamerCfg1;
  }

  rtspCtxt.prebufdata = (unsigned char *) prebuf;
  rtspCtxt.prebufsz = prebufsz;

  if(rtspCtxt.pStreamerCfg && ((STREAMER_CFG_T *)rtspCtxt.pStreamerCfg)->pRtspSessions) {
    rtsp_handle_conn(&rtspCtxt);
  }

  netio_closesocket(&pConn->sd.netsocket);

  LOG(X_DEBUG("RTSP connection thread ended %s:%d"), FORMAT_NETADDR(pConn->sd.sa, tmps[0], sizeof(tmps[0])),
                                                     ntohs(INET_PORT(pConn->sd.sa)));
  return rc;
}
Example #3
0
int restrict_hostallowed(struct addrinfo *inf)
{
	struct addrinfo *iter;

#if ALLOW_ALL
	return 1;
#endif

#define INET_PORT(addr) ((struct sockaddr_in *)addr)->sin_port

	for(iter = ips; iter; iter = iter->ai_next){

		/*
		 * ports must be the same for memcmp
		 * _major_ fixme right here
		 * if the server ever switches to IPv6
		 */
		INET_PORT(iter->ai_addr) = INET_PORT(inf->ai_addr);

		if(!memcmp(inf->ai_addr, iter->ai_addr, iter->ai_addrlen))
			return 1;
	}
	return 0;
}
Example #4
0
int capture_parseLocalAddr(const char *localAddrStr, SOCKET_LIST_T *pSockList) {

  int numPorts = 0;
  struct sockaddr_storage localAddr;
  unsigned short localPorts[SOCKET_LIST_MAX];
  size_t idx;
  int rc = -1;

  if(!localAddrStr || !pSockList) {
    return -1; 
  }

  memset(pSockList, 0, sizeof(SOCKET_LIST_T));
  memset(&localAddr, 0, sizeof(localAddr));

  if((numPorts = parseIpPortStr(localAddrStr, &localAddr, localPorts, SOCKET_LIST_MAX, 1)) > 0) {

    for(idx = 0; idx < sizeof(pSockList->netsockets) / sizeof(pSockList->netsockets[0]); idx++) {

      memset(&pSockList->salist[idx], 0, sizeof(pSockList->salist[idx]));
      NETIOSOCK_FD(pSockList->netsockets[idx]) = INVALID_SOCKET;  

      if(idx < (size_t) numPorts) {
        memcpy(&pSockList->salist[idx], &localAddr, sizeof(pSockList->salist[idx]));
        INET_PORT(pSockList->salist[idx]) = htons(localPorts[idx]);
 
        //if(rtcp) {
        //  memcpy(&pSockList->salistRtcp[idx], &pSockList->salist[idx], sizeof(pSockList->salistRtcp[idx]));
        //  pSockList->salistRtcp[idx].sin_port =
        //                RTCP_PORT_FROM_RTP(htons(pSockList->salistRtcp[idx].sin_port));
        //}
      }
    }
    pSockList->numSockets = numPorts;

    rc = 1;
  } else if(numPorts == 0) {

    // The given localAddrStr string could be a name of a local interface
    rc = 0;
  } 

  return rc;
}
Example #5
0
int capture_getdestFromStr(const char *str,
                           struct sockaddr_storage *psa,
                           const char **ppuri,
                           char *hostbuf,
                           uint16_t dfltPort) {

  char buf[CAPTURE_HTTP_HOSTBUF_MAXLEN];
  unsigned int sz = 0;
  unsigned int idx;
  const char *p;
  const char *p2 = NULL;
  int have_onlyport = 1;
  int rc = 0;

  if(!str) {
    return -1;
  }

  if((p = strstr(str, "://"))) {
    str = p + 3; 
  }

  p = str;

  if((p2 = strstr(p, ":")) || (p2 = strstr(p, "/"))) {
    sz = p2 - p;
  } else {
    sz = strlen(str);
  }

  for(idx = 0; idx < sz; idx++) {
    if(p[idx] < '0' || p[idx] > '9') {
      have_onlyport = 0;
      break;
    }
  }
  if(have_onlyport) {
    sz = 0;
    p2 = p;
  }

  if(hostbuf) {
    hostbuf[0] = '\0';
  }

  if(psa && sz > 0) {

    if(sz >= sizeof(buf)) {
      sz = sizeof(buf) - 1;
    }
    memcpy(buf, p, sz);
    buf[sz] = '\0';

    if(hostbuf) {
      memcpy(hostbuf, buf, sz + 1);
    }

    memset(psa, 0, sizeof(struct sockaddr_in));
    if((rc = net_resolvehost(buf, psa)) != 0) {
      return rc;
    }
/*
    psa->sin_family = AF_INET;
    if((psa->sin_addr.s_addr = net_resolvehost4(buf)) == INADDR_NONE) {
      return -1;
    }
*/

  }

  if(p2 && (*p2 == ':' || have_onlyport)) {

    if(*p2 == ':') {
      p2++;
    }
    p = p2;
    if((p2 = strstr(p, "/"))) {
      sz = p2 - p;
    } else {
      sz = strlen(p);
    }

    if(psa) {
      if(sz >= sizeof(buf)) {
        sz = sizeof(buf) - 1;
      }
      memcpy(buf, p, sz);
      buf[sz] = '\0';

      if((PINET_PORT(psa)= htons(atoi(buf))) == 0) {
        LOG(X_ERROR("Invalid destination port '%s' in '%s'"), buf, str);
        return -1;
      }
    }
  } else {
    INET_PORT(*psa) = htons(dfltPort);
  }

  if(ppuri) {
    *ppuri = p2;
  }

  //LOG(X_DEBUG("GETDESTFROMSTR str: '%s', have_onlyport: %d, hostbuf: '%s', uri: '%s', inet_ntop: '%s', port: %d"), str, have_onlyport, hostbuf, ppuri ? *ppuri : "", psa ? INET_NTOP(*psa, buf, sizeof(buf)) : "", psa ? htons(PINET_PORT(psa)) : -1);
  return 0;
}
Example #6
0
int srvlisten_loop(SRV_LISTENER_CFG_T *pListenCfg, void *thread_func) {

  int salen;
  THREAD_FUNC_WRAPPER_ARG_T wrapArg;
  CLIENT_CONN_T *pConn;
  SOCKET_DESCR_T sdclient;
  int rc = -1;
  const char *s;
  //pthread_cond_t cond;
  pthread_mutex_t mtx;
  TIME_VAL tv0, tv1;
  char tmp[128];

#if defined(__APPLE__) 
  int sockopt = 0;
#endif // __APPLE__

  if(!pListenCfg || !pListenCfg->pnetsockSrv || !pListenCfg->pConnPool || !thread_func || 
     pListenCfg->pConnPool->numElements <= 0) {
    return -1;
  }

  //memset(&saSrv, 0, sizeof(saSrv));
  memset(&sdclient.netsocket, 0, sizeof(sdclient.netsocket));
  //salen = sizeof(saSrv);

  //if(getsockname(pListenCfg->pnetsockSrv->sock, (struct sockaddr *) &saSrv,  (socklen_t *) &salen) != 0) {
  //  LOG(X_ERROR("getsockopt failed on server socket"));
  //}

  pthread_mutex_init(&mtx, NULL);
  //pthread_cond_init(&cond, NULL);

  while(!g_proc_exit) {

    salen = sizeof(sdclient.sa);
    if((NETIOSOCK_FD(sdclient.netsocket) = 
                 accept(PNETIOSOCK_FD(pListenCfg->pnetsockSrv), (struct sockaddr *) &sdclient.sa, 
                                      (socklen_t *) &salen)) == INVALID_SOCKET) {
      if(!g_proc_exit) {
        LOG(X_ERROR("%saccept failed on %s:%d"), 
             ((pListenCfg->pnetsockSrv->flags & NETIO_FLAG_SSL_TLS) ? "SSL " : ""),
                 //inet_ntoa(saSrv.sin_addr), ntohs(saSrv.sin_port));
                 FORMAT_NETADDR(pListenCfg->sa, tmp, sizeof(tmp)), ntohs(INET_PORT(pListenCfg->sa)));
      }
      break;
    }

    if(g_proc_exit) {
      break;
    }

    sdclient.netsocket.flags = pListenCfg->pnetsockSrv->flags;

    //
    // Find an available client thread to process the client request
    //
    if((pConn = (CLIENT_CONN_T *) pool_get(pListenCfg->pConnPool)) == NULL) {
      LOG(X_WARNING("No available connection for %s:%d (max:%d) on port %d"), 
           FORMAT_NETADDR(sdclient.sa, tmp, sizeof(tmp)), ntohs(INET_PORT(sdclient.sa)), 
           pListenCfg->pConnPool->numElements, ntohs(INET_PORT(pListenCfg->sa)));

      netio_closesocket(&sdclient.netsocket);
      continue;
    }

#if defined(__APPLE__) 
    sockopt = 1;
    if(setsockopt(NETIOSOCK_FD(sdclient.netsocket), SOL_SOCKET, SO_NOSIGPIPE,
                 (char*) &sockopt, sizeof(sockopt)) != 0) {
      LOG(X_ERROR("Failed to set SO_NOSIGPIPE"));
    }
#endif // __APPLE__

    //pConn->psrvsaListen = &saSrv;
    //memcpy(&pConn->srvsaListen, &saSrv, sizeof(pConn->srvsaListen));

    LOG(X_DEBUG("Accepted connection on port %d from %s:%d"), htons(INET_PORT(pListenCfg->sa)), 
        FORMAT_NETADDR(sdclient.sa, tmp, sizeof(tmp)), htons(INET_PORT(sdclient.sa)));

    pthread_attr_init(&pConn->attr);
    pthread_attr_setdetachstate(&pConn->attr, PTHREAD_CREATE_DETACHED);
    memset(&pConn->sd.netsocket, 0, sizeof(pConn->sd.netsocket));
    NETIO_SET(pConn->sd.netsocket, sdclient.netsocket);
    memcpy(&pConn->sd.sa, &sdclient.sa, INET_SIZE(sdclient));
    pConn->pListenCfg = pListenCfg;
    NETIOSOCK_FD(sdclient.netsocket) = INVALID_SOCKET;

    wrapArg.thread_func = thread_func;
    wrapArg.pConnPool = pListenCfg->pConnPool;
    wrapArg.pConn = pConn;
    wrapArg.flags = 1;
    wrapArg.tid_tag[0] = '\0';
    if((s = logutil_tid_lookup(pthread_self(), 0)) && s[0] != '\0') {
      snprintf(wrapArg.tid_tag, sizeof(wrapArg.tid_tag), "%s-%u", s, pConn->pool.id);
    }
    //wrapArg.pcond = &cond;

  //fprintf(stderr, "%d CALLING wrap: 0x%x pConn:0x%x\n", pthread_self(), &wrapArg, wrapArg.pConn);

    if((rc = pthread_create(&pConn->ptd,
                    &pConn->attr,
                    (void *) thread_func_wrapper,
                    (void *) &wrapArg)) != 0) {
      LOG(X_ERROR("Unable to create connection handler thread on port %d from %s:%d (%d %s)"), 
          htons(INET_PORT(pListenCfg->sa)), FORMAT_NETADDR(sdclient.sa, tmp, sizeof(tmp)),
          htons(INET_PORT(sdclient.sa)), rc, strerror(rc));
      netio_closesocket(&pConn->sd.netsocket);
      pool_return(pListenCfg->pConnPool, &pConn->pool);
      wrapArg.flags = 0;
      //pthread_cond_broadcast(&cond);
      break;
    }

    pthread_attr_destroy(&pConn->attr);

    //
    // be careful not to reuse the same wrapArg instance 
    // since the stack variable arguments could get
    // overwritten by the next loop iteration, before the thread proc is 
    // invoked
    //
    //fprintf(stderr, "wait start\n");
    tv0 = timer_GetTime();
    //if(wrapArg.flags == 1) {

      //
      // It seems that calling pthread_cond_wait here to check if the thread creation is
      // complete is not completely reliable and portable, so we do the lame way 
      // of sleeping and polling.
      //
      //pthread_cond_wait(&cond, &mtx);

      while(wrapArg.flags == 1) {
        usleep(100);
        if(((tv1 = timer_GetTime()) - tv0) / TIME_VAL_MS > 1000) {
          LOG(X_WARNING("Abandoning wait for connection thread start on port %d from %s:%d"),
               htons(INET_PORT(pListenCfg->sa)),
              FORMAT_NETADDR(pListenCfg->sa, tmp, sizeof(tmp)), ntohs(INET_PORT(pListenCfg->sa)));
          break;
        } 
      }
      //fprintf(stderr, "THREAD STARTED AFTER %lld ns\n", (timer_GetTime() - tv0));
    //}

    //fprintf(stderr, "wait done\n");

    //while(wrapArg.flag == 1) {
    //  usleep(10000); 
    //}

  }

  //pthread_cond_destroy(&cond);
  pthread_mutex_destroy(&mtx);

  return rc;
}
Example #7
0
static void srvlisten_media_proc(void *pArg) {

  SRV_LISTENER_CFG_T *pListenCfg = (SRV_LISTENER_CFG_T *) pArg;
  NETIO_SOCK_T netsocksrv;
  struct sockaddr_storage sa;
  CLIENT_CONN_T *pConnTmp;
  char tmp[128];
  char bufses[32];
  const int backlog = NET_BACKLOG_DEFAULT;
  unsigned int tsMax = 0;
  unsigned int flvMax = 0;
  unsigned int mkvMax = 0;
  unsigned int rtmpMax = 0;
  unsigned int rtspMax = 0;
  int haveAuth = 0;
  int haveRtmpAuth = 0;
  int haveRtspAuth = 0;
  int haveToken = 0;
  int rc = 0;

  logutil_tid_add(pthread_self(), pListenCfg->tid_tag);

  if((pListenCfg->urlCapabilities & (URL_CAP_TSLIVE | URL_CAP_TSHTTPLIVE | URL_CAP_FLVLIVE |
                                     URL_CAP_MKVLIVE | URL_CAP_LIVE | URL_CAP_STATUS | URL_CAP_MOOFLIVE |
                                     URL_CAP_PIP | URL_CAP_CONFIG | URL_CAP_BROADCAST |
                                     URL_CAP_RTMPLIVE | URL_CAP_RTMPTLIVE | URL_CAP_RTSPLIVE)) == 0) {

    LOG(X_WARNING("Server listener exiting because no capabilities enabled on %s:%d"),
         FORMAT_NETADDR(pListenCfg->sa, tmp, sizeof(tmp)), ntohs(INET_PORT(pListenCfg->sa)));
    logutil_tid_remove(pthread_self());
    return;
  } else if(!(pConnTmp = (CLIENT_CONN_T *) pListenCfg->pConnPool->pElements)) {
    return;
  }

  memset(&netsocksrv, 0, sizeof(netsocksrv));
  memcpy(&sa, &pListenCfg->sa, sizeof(pListenCfg->sa));
  netsocksrv.flags = pListenCfg->netflags;

  if((NETIOSOCK_FD(netsocksrv) = net_listen((const struct sockaddr *) &sa, backlog)) == INVALID_SOCKET) {
    logutil_tid_remove(pthread_self());
    return;
  }

  pthread_mutex_lock(&pListenCfg->mtx);
  pListenCfg->pnetsockSrv = &netsocksrv;
  pthread_mutex_unlock(&pListenCfg->mtx);

  if(pListenCfg->pAuthStore && IS_AUTH_CREDENTIALS_SET(pListenCfg->pAuthStore)) {
    haveAuth = 1;
  }
  if(pListenCfg->pAuthTokenId && pListenCfg->pAuthTokenId[0] != '\0') {
    haveToken = 1;
  }

  tsMax = pConnTmp->pStreamerCfg0->liveQs[0].max;
  flvMax = pConnTmp->pStreamerCfg0->action.liveFmts.out[STREAMER_OUTFMT_IDX_FLV].max;
  mkvMax = pConnTmp->pStreamerCfg0->action.liveFmts.out[STREAMER_OUTFMT_IDX_MKV].max;
  rtmpMax = pConnTmp->pStreamerCfg0->action.liveFmts.out[STREAMER_OUTFMT_IDX_RTMP].max;
  rtspMax = pConnTmp->pStreamerCfg0->pRtspSessions->max;

  if(HAVE_URL_CAP_RTMP(pListenCfg->urlCapabilities)) {

    if(IS_AUTH_CREDENTIALS_SET(&pConnTmp->pStreamerCfg0->creds[STREAMER_AUTH_IDX_RTMP].stores[pListenCfg->idxCfg])) {
      //
      // RTMP server streaming credentials not implemented 
      //
      //haveRtmpAuth = 1;
    }

    LOG(X_INFO("rtmp %s%s available at "URL_RTMP_FMT_STR"%s max:%d"),
       ((pListenCfg->netflags & NETIO_FLAG_PLAINTEXT) && (pListenCfg->netflags & NETIO_FLAG_SSL_TLS) ) ? "(SSL) " :
       ((pListenCfg->netflags & NETIO_FLAG_SSL_TLS) ? "(SSL only) " : ""),
           ((pListenCfg->urlCapabilities & URL_CAP_RTMPTLIVE) && !(pListenCfg->urlCapabilities & URL_CAP_RTMPLIVE) ?
            "(tunneled only) " : ((pListenCfg->urlCapabilities & URL_CAP_RTMPTLIVE) ? "(tunneled) " : "")),
           URL_PROTO_FMT2_ARGS(
               (pListenCfg->netflags & NETIO_FLAG_SSL_TLS),
                 FORMAT_NETADDR(sa, tmp, sizeof(tmp))), ntohs(INET_PORT(sa)),
           (haveRtmpAuth ? " (Using auth)" : ""), rtmpMax);
  }

  if(pListenCfg->urlCapabilities & URL_CAP_RTSPLIVE) {

    if(IS_AUTH_CREDENTIALS_SET(&pConnTmp->pStreamerCfg0->creds[STREAMER_AUTH_IDX_RTSP].stores[pListenCfg->idxCfg])) {
      haveRtspAuth = 1;
    }

    if(pListenCfg->pCfg->prtspsessiontimeout && *pListenCfg->pCfg->prtspsessiontimeout != 0) {
      snprintf(bufses, sizeof(bufses), ", timeout:%u sec", *pListenCfg->pCfg->prtspsessiontimeout);
    } else {
      bufses[0] = '\0';
    }

    LOG(X_INFO("rtsp %s available at "URL_RTSP_FMT_STR"%s max:%d%s"),
       ((pListenCfg->netflags & NETIO_FLAG_PLAINTEXT) && (pListenCfg->netflags & NETIO_FLAG_SSL_TLS) ) ? "(SSL) " :
       ((pListenCfg->netflags & NETIO_FLAG_SSL_TLS) ? "(SSL only) " : ""),
             URL_PROTO_FMT2_ARGS(
                 (pListenCfg->netflags & NETIO_FLAG_SSL_TLS),
                   FORMAT_NETADDR(sa, tmp, sizeof(tmp))), ntohs(INET_PORT(sa)),
             (haveRtspAuth ? " (Using auth)" : ""), rtspMax, bufses);
  }

  if((pListenCfg->urlCapabilities & URL_CAP_ROOTHTML)) {
    LOG(X_INFO("broadcast interface available at "URL_HTTP_FMT_STR"%s%s%s max:%d"), 
           URL_HTTP_FMT_ARGS2(pListenCfg, FORMAT_NETADDR(sa, tmp, sizeof(tmp))), VSX_ROOT_URL,
           (haveAuth ? " (Using auth)" : ""),
           (pListenCfg->pCfg->cfgShared.livepwd ? " (Using password)" : ""), pListenCfg->max);
  }

  if((pListenCfg->urlCapabilities & URL_CAP_LIVE)) {
    LOG(X_INFO("live available at "URL_HTTP_FMT_STR"%s%s%s%s max:%d"), 
           URL_HTTP_FMT_ARGS2(pListenCfg, FORMAT_NETADDR(sa, tmp, sizeof(tmp))), VSX_LIVE_URL,
           (haveAuth ? " (Using auth)" : ""),
           (haveToken ? " (Using token)" : ""),
           (pListenCfg->pCfg->cfgShared.livepwd ? " (Using password)" : ""), pListenCfg->max);
  }

  if((pListenCfg->urlCapabilities & URL_CAP_TSLIVE)) {
    LOG(X_INFO("tslive available at "URL_HTTP_FMT_STR"%s%s%s%s max:%d"), 
           URL_HTTP_FMT_ARGS2(pListenCfg, FORMAT_NETADDR(sa, tmp, sizeof(tmp))), VSX_TSLIVE_URL,
           (haveAuth ? " (Using auth)" : ""),
           (haveToken ? " (Using token)" : ""),
           (pListenCfg->pCfg->cfgShared.livepwd ? " (Using password)" : ""), tsMax);
  }

  if((pListenCfg->urlCapabilities & URL_CAP_TSHTTPLIVE)) {
    LOG(X_INFO("httplive available at "URL_HTTP_FMT_STR"%s%s%s%s max:%d"), 
           URL_HTTP_FMT_ARGS2(pListenCfg, FORMAT_NETADDR(sa, tmp, sizeof(tmp))), VSX_HTTPLIVE_URL,
           (haveAuth ? " (Using auth)" : ""),
           (haveToken ? " (Using token)" : ""),
           (pListenCfg->pCfg->cfgShared.livepwd ? " (Using password)" : ""), pListenCfg->max);
  }

  if((pListenCfg->urlCapabilities & URL_CAP_FLVLIVE)) {
    LOG(X_INFO("flvlive available at "URL_HTTP_FMT_STR"%s%s%s%s max:%d"), 
           URL_HTTP_FMT_ARGS2(pListenCfg, FORMAT_NETADDR(sa, tmp, sizeof(tmp))), VSX_FLVLIVE_URL,
           (haveAuth ? " (Using auth)" : ""),
           (haveToken ? " (Using token)" : ""),
           (pListenCfg->pCfg->cfgShared.livepwd ? " (Using password)" : ""), flvMax);
  }

  if((pListenCfg->urlCapabilities & URL_CAP_MKVLIVE)) {
    LOG(X_INFO("mkvlive available at "URL_HTTP_FMT_STR"%s%s%s%s max:%d"), 
           URL_HTTP_FMT_ARGS2(pListenCfg, FORMAT_NETADDR(sa, tmp, sizeof(tmp))), VSX_MKVLIVE_URL,
           (haveAuth ? " (Using auth)" : ""),
           (haveToken ? " (Using token)" : ""),
           (pListenCfg->pCfg->cfgShared.livepwd ? " (Using password)" : ""), mkvMax);
  }

  if((pListenCfg->urlCapabilities & URL_CAP_MOOFLIVE)) {
    LOG(X_INFO("dash available at "URL_HTTP_FMT_STR"%s%s%s%s max:%d"), 
           URL_HTTP_FMT_ARGS2(pListenCfg, FORMAT_NETADDR(sa, tmp, sizeof(tmp))), VSX_DASH_URL,
           (haveAuth ? " (Using auth)" : ""),
           (haveToken ? " (Using token)" : ""),
           (pListenCfg->pCfg->cfgShared.livepwd ? " (Using password)" : ""), pListenCfg->max);
  }

  if((pListenCfg->urlCapabilities & URL_CAP_STATUS)) {
    LOG(X_INFO("status available at "URL_HTTP_FMT_STR"%s%s%s"), 
           URL_HTTP_FMT_ARGS2(pListenCfg, FORMAT_NETADDR(sa, tmp, sizeof(tmp))), VSX_STATUS_URL,
            (haveAuth ? " (Using auth)" : ""),
           (pListenCfg->pCfg->cfgShared.livepwd ? " (Using password)" : ""));
  }

  if((pListenCfg->urlCapabilities & URL_CAP_PIP)) {
    LOG(X_INFO("pip available at "URL_HTTP_FMT_STR"%s%s%s"), 
           URL_HTTP_FMT_ARGS2(pListenCfg, FORMAT_NETADDR(sa, tmp, sizeof(tmp))), VSX_PIP_URL,
            (haveAuth ? " (Using auth)" : ""),
           (pListenCfg->pCfg->cfgShared.livepwd ? " (Using password)" : ""));
  }

  if((pListenCfg->urlCapabilities & URL_CAP_CONFIG)) {
    LOG(X_INFO("config available at "URL_HTTP_FMT_STR"%s%s%s"), 
           URL_HTTP_FMT_ARGS2(pListenCfg, FORMAT_NETADDR(sa, tmp, sizeof(tmp))), VSX_CONFIG_URL,
            (haveAuth ? " (Using auth)" : ""),
           (pListenCfg->pCfg->cfgShared.livepwd ? " (Using password)" : ""));
  }

  //fprintf(stderr, "LISTENER THREAD %s %s:%d active:%d cap: 0x%x\n", pListenCfg->netflags & NETIO_FLAG_SSL_TLS ? "SSL" : "", inet_ntoa(sa.sin_addr), ntohs(sa.sin_port), pListenCfg->active, pListenCfg->urlCapabilities);

  //
  // Service any client connections on the live listening port
  //
  rc = srvlisten_loop(pListenCfg, srv_cmd_proc);

  pthread_mutex_lock(&pListenCfg->mtx);
  pListenCfg->pnetsockSrv = NULL; 
  netio_closesocket(&netsocksrv);
  pthread_mutex_unlock(&pListenCfg->mtx);

  LOG(X_WARNING("HTTP listener thread exiting with code: %d"), rc);

  logutil_tid_remove(pthread_self());

  return;
}
Example #8
0
int srv_rtmp_proc(CLIENT_CONN_T *pConn, const unsigned char *prebuf, unsigned int prebufsz, int istunneled) {
  int rc = 0;
  STREAMER_CFG_T *pStreamerCfg = NULL;
  STREAMER_OUTFMT_T *pLiveFmt = NULL;
  STREAM_STATS_T *pstats = NULL;
  RTMP_CTXT_T rtmpCtxt;
  unsigned int numQFull = 0;
  char tmps[2][128];
  OUTFMT_CFG_T *pOutFmt = NULL;

  if(!pConn) {
    return -1;
  }

  if(!HAVE_URL_CAP_RTMP(pConn->pListenCfg->urlCapabilities)) {
    LOG(X_ERROR("Listener %s:%d not enabled for rtmp%s%s stream to %s:%d"), 
          FORMAT_NETADDR(pConn->pListenCfg->sa, tmps[1], sizeof(tmps[1])), ntohs(INET_PORT(pConn->pListenCfg->sa)),
          istunneled > 0 ? "t" : "",      
          (pConn->sd.netsocket.flags & NETIO_FLAG_SSL_TLS) ? "s" : "", 
          FORMAT_NETADDR(pConn->sd.sa, tmps[0], sizeof(tmps[0])), ntohs(INET_PORT(pConn->sd.sa)));
    return -1;
  }

  pStreamerCfg = GET_STREAMER_FROM_CONN(pConn);

  if(pStreamerCfg && pStreamerCfg->action.liveFmts.out[STREAMER_OUTFMT_IDX_RTMP].do_outfmt) {

    pLiveFmt = &pStreamerCfg->action.liveFmts.out[STREAMER_OUTFMT_IDX_RTMP];

    if(pStreamerCfg->pMonitor && pStreamerCfg->pMonitor->active) {
      if(!(pstats = stream_monitor_createattach(pStreamerCfg->pMonitor,
             (const struct sockaddr *) &pConn->sd.sa, STREAM_METHOD_RTMP, STREAM_MONITOR_ABR_NONE))) {
      }
    }

    //
    // Add a livefmt cb
    //
    pOutFmt = outfmt_setCb(pLiveFmt, rtmp_addFrame, &rtmpCtxt, &pLiveFmt->qCfg, pstats, 
                           1, pStreamerCfg->frameThin, &numQFull);

  } 

  if(pOutFmt) {

    memset(&rtmpCtxt, 0, sizeof(rtmpCtxt));

    rtmp_init(&rtmpCtxt, MAX(pLiveFmt->qCfg.maxPktLen, pLiveFmt->qCfg.growMaxPktLen));
    rtmpCtxt.pSd = &pConn->sd;
    rtmpCtxt.novid = pStreamerCfg->novid;
    rtmpCtxt.noaud = pStreamerCfg->noaud;
    rtmpCtxt.av.vid.pStreamerCfg = pStreamerCfg;
    rtmpCtxt.av.aud.pStreamerCfg = pStreamerCfg;
    rtmpCtxt.pAuthTokenId = pConn->pListenCfg->pAuthTokenId;
    if(pstats) {
      rtmpCtxt.pStreamMethod = &pstats->method;
    }
    rtmpCtxt.pOutFmt = pOutFmt;
    rtmpCtxt.prebufdata = (unsigned char *) prebuf;
    rtmpCtxt.prebufsz = prebufsz;

    if(!(pConn->pListenCfg->urlCapabilities & URL_CAP_RTMPLIVE)) {
      rtmpCtxt.donotunnel = 1;
    }
    if((pConn->pListenCfg->urlCapabilities & URL_CAP_RTMPTLIVE)) {
      rtmpCtxt.dohttptunnel = 1;
    }

    //
    // Unpause the outfmt callback mechanism now that rtmp_init was called
    //
    outfmt_pause(pOutFmt, 0);

    LOG(X_INFO("Starting rtmp%s%s stream[%d] %d/%d to %s:%d"), 
          istunneled > 0 ? "t" : "",      
          (pConn->sd.netsocket.flags & NETIO_FLAG_SSL_TLS) ? "s" : "", pOutFmt->cbCtxt.idx, numQFull + 1, 
          pLiveFmt->max, FORMAT_NETADDR(pConn->sd.sa, tmps[0], sizeof(tmps[0])), ntohs(INET_PORT(pConn->sd.sa)));

    rtmp_handle_conn(&rtmpCtxt);

    LOG(X_INFO("Ending rtmp%s%s stream[%d] to %s:%d"), 
         istunneled > 0 ? "t" : "",      
         (pConn->sd.netsocket.flags & NETIO_FLAG_SSL_TLS) ? "s" : "", pOutFmt->cbCtxt.idx, 
         FORMAT_NETADDR(pConn->sd.sa, tmps[0], sizeof(tmps[0])), ntohs(INET_PORT(pConn->sd.sa)));

    //
    // Remove the livefmt cb
    //
    outfmt_removeCb(pOutFmt);

    rtmp_close(&rtmpCtxt);

  } else {

    if(pstats) {
      //
      // Destroy automatically detaches the stats from the monitor linked list
      //
      stream_stats_destroy(&pstats, NULL);
    }

    LOG(X_WARNING("No rtmp resource available (max:%d) for %s:%d"), 
        (pLiveFmt ? pLiveFmt->max : 0),
        FORMAT_NETADDR(pConn->sd.sa, tmps[0], sizeof(tmps[0])), ntohs(INET_PORT(pConn->sd.sa)));

    rc = -1;
  }

  netio_closesocket(&pConn->sd.netsocket);

  LOG(X_DEBUG("RTMP connection thread ended %s:%d"), FORMAT_NETADDR(pConn->sd.sa, tmps[0], sizeof(tmps[0])), 
                                                     ntohs(INET_PORT(pConn->sd.sa)));

  return rc;
}
Example #9
0
int rtspsrv_init(STREAM_RTSP_SESSIONS_T *pRtsp) {

  pthread_t ptdMonitor;
  struct sockaddr_storage sa;
  pthread_attr_t attrMonitor;
  RTSP_MONITOR_CTXT_T startCtxt;
  const char *s;

  if(!pRtsp || pRtsp->max <= 0) {
    return -1;
  }

  if(pRtsp->psessions) {
    avc_free((void *) &pRtsp->psessions);
  }

  destroy_rtspgetsessions(pRtsp);

  if(!(pRtsp->psessions = (RTSP_SESSION_T *)
                          avc_calloc(pRtsp->max, sizeof(RTSP_SESSION_T)))) {
    return -1;
  }

  pRtsp->numRtspGetSessions = pRtsp->max * 2;
  if(!(pRtsp->pRtspGetSessionsBuf = (RTSP_HTTP_SESSION_T *)
                          avc_calloc(pRtsp->numRtspGetSessions, sizeof(RTSP_HTTP_SESSION_T)))) {
    avc_free((void *) &pRtsp->psessions);
    pRtsp->numRtspGetSessions = 0;
    return -1;
  }

  pthread_mutex_init(&pRtsp->mtx, NULL);
 
  //
  // If all UDP / RTP sockets are bound to the same port then establish
  // the listener of this port prior to any RTSP interaction because some app
  // gateways may send some UDP polling data to the base port - and returning
  // an ICMP port unreachable would prevent such app gateways from allocating
  // UDP proxy ports
  //
  pRtsp->sockStaticLocalPort = INVALID_SOCKET;
  if(pRtsp->staticLocalPort > 0) {
    memset(&sa, 0, sizeof(sa));
    sa.ss_family = AF_INET;
    ((struct sockaddr_in *) &sa)->sin_addr.s_addr = INADDR_ANY;
    INET_PORT(sa) = htons(pRtsp->staticLocalPort);
    if((pRtsp->sockStaticLocalPort = net_opensocket(SOCK_DGRAM, 0, 0, (struct sockaddr *) &sa)) == INVALID_SOCKET) {
      LOG(X_ERROR("Failed to open RTSP static local RTP port %d"), pRtsp->staticLocalPort);
    } else {
      if(net_setsocknonblock(pRtsp->sockStaticLocalPort, 1) < 0) {
      LOG(X_ERROR("Failed to listen on RTSP static local RTP port %d"), pRtsp->staticLocalPort);
        net_closesocket(&pRtsp->sockStaticLocalPort);
      }
    }
    //if(pRtsp->sockStaticLocalPort != INVALID_SOCKET) {
    //  sain.sin_addr.s_addr = inet_addr("127.0.0.1");
    //  rc = sendto(pRtsp->sockStaticLocalPort, &sain, 1, 0, (struct sockaddr *) &sain, sizeof(sain));
    //  fprintf(stderr, "SENDTO:%d\n", rc);
    //}
  }

  //
  // Parse any CSV of quoted User-Agent matches which should try to force TCP interleaved mode
  //
  pRtsp->rtspForceTcpUAList.count = 0;
  if(pRtsp->rtspForceTcpUAList.str) {
    strutil_parse_csv(cbparse_entry_rtspua, pRtsp, pRtsp->rtspForceTcpUAList.str);
  }

  pRtsp->runMonitor = 2;
  memset(&startCtxt, 0, sizeof(startCtxt));
  startCtxt.pRtsp = pRtsp;
  if((s = logutil_tid_lookup(pthread_self(), 0)) && s[0] != '\0') {
    snprintf(startCtxt.tid_tag, sizeof(startCtxt.tid_tag), "%s-rtspmon", s);
  }
  pthread_attr_init(&attrMonitor);
  pthread_attr_setdetachstate(&attrMonitor, PTHREAD_CREATE_DETACHED);

  if(pthread_create(&ptdMonitor,
                    &attrMonitor,
                    (void *) rtsp_monitor_proc,
                    (void *) &startCtxt) != 0) {
    LOG(X_ERROR("Unable to create RTP monitor thread"));
    pRtsp->runMonitor = 0;
    if(pRtsp->psessions) {
      avc_free((void *) &pRtsp->psessions);
    }
    destroy_rtspgetsessions(pRtsp);
    pthread_mutex_destroy(&pRtsp->mtx);
  }

  while(pRtsp->runMonitor != 1 && pRtsp->runMonitor != -1) {
    usleep(5000);
  }

  return 0;
}
Example #10
0
static int streamxmit_retransmitRtp(STREAM_RTP_DEST_T *pDest) {
  STREAMXMIT_PKT_HDR_T *pktHdr;
  unsigned int idxRd;
  unsigned int count = 0;
  int lastWasExpired = 0;
  int rc;
  int haveRequestedRetransmission = 0;
  const PKTQUEUE_PKT_T *pQPkt = NULL;
  STREAM_XMIT_QUEUE_T *pAsyncQ = &pDest->asyncQ;
  TIME_VAL tvNow;
  struct timeval tv;
  float kbps;
  char tmp[128];

  tvNow = timer_GetTime();
  TV_FROM_TIMEVAL(tv, tvNow);

  pthread_mutex_lock(&pAsyncQ->mtx);
  idxRd = pAsyncQ->pQ->idxRd;
  //LOG(X_DEBUG("NACK streamxmit_iterate ... idxRd:%d, idxWr:%d / %d"), pAsyncQ->pQ->idxRd, pAsyncQ->pQ->idxWr, pAsyncQ->pQ->cfg.maxPkts);
  while(count++ < pAsyncQ->pQ->cfg.maxPkts) {

    pQPkt = &pAsyncQ->pQ->pkts[idxRd];

    if(!(pQPkt->flags & PKTQUEUE_FLAG_HAVEPKTDATA)) {
      //LOG(X_DEBUG("NACK streamxmit_iterate break at qid[%d]"), idxRd);
      break;
    }
    //LOG(X_DEBUG("NACK streamxmit_iterate check qid[%d] seqno:%d, %lld ms ago"), idxRd, ((STREAMXMIT_PKT_HDR_T *) pQPkt->xtra.pQUserData)->seqNum, (tvNow - ((STREAMXMIT_PKT_HDR_T *) pQPkt->xtra.pQUserData)->tvxmit)/1000);

    lastWasExpired = 0;

    if(!(pktHdr = (STREAMXMIT_PKT_HDR_T *) pQPkt->xtra.pQUserData)) {

      pAsyncQ->pQ->idxRd = idxRd;
      fprintf(stderr, "streamxmit_itertate should never get here... idxRd:%d\n", idxRd);

    } else if(pktHdr->tvXmit + (pAsyncQ->nackHistoryMs * TIME_VAL_MS) < tvNow) {
      //
      // The queue slot RTP sequence number came before the NACK starting sequence number
      // so the current queue content is before the scope of the NACK
      //
      if(pktHdr->doRetransmit > 0) {
        pktHdr->doRetransmit = -1;

        if(pDest->pstreamStats) {
          stream_abr_notifyBitrate(pDest->pstreamStats, &pDest->streamStatsMtx, 
                                   STREAM_ABR_UPDATE_REASON_NACK_AGED, .5f);
        }

      }
      pAsyncQ->pQ->idxRd = idxRd;
      pAsyncQ->pQ->pkts[idxRd].flags = 0;
      lastWasExpired = 1;
      //LOG(X_DEBUG("NACK streamxmit_iterate old here marked to -1 ... idxRd:%d, seqNum:%d"), idxRd, pktHdr->seqNum);

    } else if(pktHdr->doRetransmit > 0 && !pktHdr->rtcp) {

      if(pktHdr->tvLastRetransmit == 0 || 
         pktHdr->tvLastRetransmit + (RTP_RETRANSMIT_INTERVAL_MS * TIME_VAL_MS) < tvNow) {

        //
        // Ensure that the retransmission will not exceed our retransmission bitrate limit
        //
        if(pAsyncQ->retransmissionKbpsMax > 0 &&
          burstmeter_updateCounters(&pAsyncQ->retransmissionMeter, &tv) == 0 &&
          (kbps = (burstmeter_getBitrateBps(&pAsyncQ->retransmissionMeter)/THROUGHPUT_BYTES_IN_KILO_F)) >= pAsyncQ->retransmissionKbpsMax) {

          LOG(X_WARNING("RTP NACK retransmission bitrate %.3f >= %.3f  throttled pt:%d sequence:%d to %s:%d, for original %d ms ago."), 
                     kbps, pAsyncQ->retransmissionKbpsMax, pDest->pRtpMulti->init.pt, pktHdr->seqNum, 
                     FORMAT_NETADDR(pDest->saDstsRtcp, tmp, sizeof(tmp)), ntohs(INET_PORT(pDest->saDstsRtcp)),
                     (tvNow - pktHdr->tvXmit) / TIME_VAL_MS);

          if(pDest->pstreamStats) {
            stream_abr_notifyBitrate(pDest->pstreamStats, &pDest->streamStatsMtx,
                                     STREAM_ABR_UPDATE_REASON_NACK_THROTTLED, .5f);
          }

          pktHdr->doRetransmit = 0;
          pktHdr->tvLastRetransmit = tvNow; 

        } else {

          burstmeter_AddSample(&pAsyncQ->retransmissionMeter, pQPkt->len, &tv);

          //LOG(X_DEBUG("RTP NACK Retransmission bitrate: %.3f %s (%.3f bps)"),  (float) pAsyncQ->retransmissionMeter.meter.rangeMs/1000, burstmeter_printThroughputStr(buf, sizeof(buf), &pAsyncQ->retransmissionMeter), (float) burstmeter_getBitrateBps(&pAsyncQ->retransmissionMeter));

          //
          // Retransmit the RTP packet
          //
          LOG(X_DEBUG("RTP NACK retransmitting pt:%d sequence:%d, len:%d to %s:%d, original sent %d ms ago.  "
                      "Rate: %.3f pkts/s, %.3f b/s"), 
                    pDest->pRtpMulti->init.pt, pktHdr->seqNum, pQPkt->len, 
                    FORMAT_NETADDR(pDest->saDstsRtcp, tmp, sizeof(tmp)), ntohs(INET_PORT(pDest->saDstsRtcp)), 
                    (tvNow - pktHdr->tvXmit) / TIME_VAL_MS,
                    (float) burstmeter_getPacketratePs(&pAsyncQ->retransmissionMeter),
                    (float) burstmeter_getBitrateBps(&pAsyncQ->retransmissionMeter));

          if((rc = srtp_sendto(STREAM_RTP_PNETIOSOCK(*pDest), (void *) pQPkt->pData, pQPkt->len, 0,
                              (const struct sockaddr *) &pDest->saDsts, NULL, SENDTO_PKT_TYPE_RTP, 1)) < 0) {
          
            pktHdr->doRetransmit = -1;
          } else {
            pktHdr->doRetransmit = 0;
          }

          pktHdr->tvLastRetransmit = tvNow; 
          if(pDest->pstreamStats) {
            stream_abr_notifyBitrate(pDest->pstreamStats, &pDest->streamStatsMtx,
                                     STREAM_ABR_UPDATE_REASON_NACK_RETRANSMITTED, .5f);
          }

        }

      } else {
        haveRequestedRetransmission = 1;
      }
   
    }

    if(++idxRd >= pAsyncQ->pQ->cfg.maxPkts) {
      idxRd = 0;
    }
    if(lastWasExpired) {
      pAsyncQ->pQ->idxRd = idxRd;
    } 

  } // end of while...

  pAsyncQ->haveRequestedRetransmission = haveRequestedRetransmission;

  pthread_mutex_unlock(&pAsyncQ->mtx);

  return haveRequestedRetransmission;
}
Example #11
0
int srv_ctrl_pip(CLIENT_CONN_T *pConn) {
  int rc = 0; 
  int rcTmp;
  enum HTTP_STATUS statusCode = HTTP_STATUS_OK;
  const char *parg;
  PIP_CFG_T pipCfg;
  IXCODE_VIDEO_CROP_T crop;
  IXCODE_PIP_MOTION_T *pipMotion = NULL;
  int idx = 0;
  int pip_id = 0;
  int do_stop = 0;
  int do_start = 0;
  int do_update = 0;
  int do_status = 0;
  int do_crop = 0;
  STUN_POLICY_T stunPolicy;
  int activePipIds[MAX_PIPS];
  const char *do_pipconf = NULL;
  unsigned int idxResp;
  char tmp[128];
  char buf[1024];
  char respExtra[512];
  STREAMER_CFG_T *pCfg = NULL;
  char *strxcodebuf = NULL;

  LOG(X_DEBUG("Received PIP command %s%s from %s:%d"), pConn->httpReq.puri,  
      http_req_dump_uri(&pConn->httpReq, buf, sizeof(buf)), FORMAT_NETADDR(pConn->sd.sa, tmp, sizeof(tmp)), 
      htons(INET_PORT(pConn->sd.sa)));

  buf[0] = '\0';
  respExtra[0] = '\0';

  if(pConn->pStreamerCfg0 && pConn->pStreamerCfg0->running >= 0) {
    pCfg = (STREAMER_CFG_T *) pConn->pStreamerCfg0;
  } else if(pConn->pStreamerCfg1 && pConn->pStreamerCfg1->running >= 0) {
    pCfg = (STREAMER_CFG_T *) pConn->pStreamerCfg1;
  }

  if(pCfg) {

    memset(&crop, 0, sizeof(crop));

    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, PIP_KEY_PAD_TOP))) {
      crop.padTop = atoi(parg);
      do_crop = 1;
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, PIP_KEY_PAD_BOTTOM))) {
      crop.padBottom = atoi(parg);
      do_crop = 1;
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, PIP_KEY_PAD_LEFT))) {
      crop.padLeft = atoi(parg);
      do_crop = 1;
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, PIP_KEY_PAD_RGB))) {
      strutil_read_rgb(parg, crop.padRGB);
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, PIP_KEY_PAD_RIGHT))) {
      crop.padRight = atoi(parg);
      do_crop = 1;
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, PIP_KEY_CROP_TOP))) {
      crop.cropTop = atoi(parg);
      do_crop = 1;
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, PIP_KEY_CROP_BOTTOM))) {
      crop.cropBottom = atoi(parg);
      do_crop = 1;
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, PIP_KEY_CROP_LEFT))) {
      crop.cropLeft = atoi(parg);
      do_crop = 1;
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, PIP_KEY_CROP_RIGHT))) {
      crop.cropRight = atoi(parg);
      do_crop = 1;
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, PIP_KEY_PAD_ASPECT_RATIO))) {
      crop.padAspectR = atoi(parg);
      do_crop = 1;
    }

    if(do_crop) {
      if((rc = xcode_set_crop_pad(&crop, &pCfg->xcode.vid.out[0], crop.padAspectR, 
                                  pCfg->xcode.vid.out[0].resOutH, pCfg->xcode.vid.out[0].resOutV, 0, 0)) < 0) {
        LOG(X_ERROR("Invalid picture pad / crop dimensions"));
      } else {
        //TODO: updating these params does not work via xcode ipc
        //TODO: this should be atomic w/ respect to xcoder 
        memcpy(&pCfg->xcode.vid.out[0].crop, &crop, sizeof(pCfg->xcode.vid.out[0].crop));
      }
    }

    memset(&pipCfg, 0, sizeof(pipCfg));
    pipCfg.rtpPktzMode = -1;
    pipCfg.dtls_handshake_server = -1;

    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "pipstatus"))) {
      do_status = 1;
      pip_id = atoi(parg);
    } else if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "pipstop"))) {
      do_stop = 1;
      pip_id = atoi(parg);
    } else if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "pipstart"))) {
      if(atoi(parg) > 0) {
        do_start = 1;
      }
    } else if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "pipupdate"))) {
      // start a PIP which was previously started with 'delaystart' parameter
      if((pip_id = atoi(parg)) > 0) {
        do_update = 1;
      }
    }

    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "pipconf"))) {
      do_pipconf = parg;
    }

    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "pipxcode")) ||
       (parg = conf_find_keyval(pConn->httpReq.uriPairs, "xcode"))) {
      pipCfg.strxcode = parg;
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "pip")) ||
       (parg = conf_find_keyval(pConn->httpReq.uriPairs, "in")) ||
       (parg = conf_find_keyval(pConn->httpReq.uriPairs, "input"))) {
      pipCfg.input = parg;
    }

    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "out")) ||
       (parg = conf_find_keyval(pConn->httpReq.uriPairs, "output"))) {
      pipCfg.output = parg;
//pipCfg.output="dtlssrtp://127.0.0.1:5000,5008";
//pipCfg.output="srtp://127.0.0.1:5000,5000";
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "tslive"))) {
      pipCfg.tsliveaddr = parg;
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "rtppayloadtype"))) {
      pipCfg.output_rtpptstr = parg;
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "rtpssrc"))) {
      pipCfg.output_rtpssrcsstr = parg;
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "mtu"))) {
      pipCfg.mtu = atoi(parg);
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "rtppktzmode"))) {
      pipCfg.rtpPktzMode = atoi(parg);
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "rtcpports"))) {
      pipCfg.rtcpPorts = parg;
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "srtpkey"))) {
      pipCfg.srtpKeysBase64 = parg;
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "dtls"))) {
      pipCfg.use_dtls = atoi(parg);
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "dtlssrtp"))) {
      pipCfg.use_dtls = pipCfg.use_srtp = atoi(parg);
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "dtlsserver"))) {
      pipCfg.dtls_handshake_server = atoi(parg);
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "dtlsclient"))) {
      pipCfg.dtls_handshake_server = !atoi(parg);
    }
    //if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "dtlsoutserverkey"))) {
    //  pipCfg.dtls_xmit_serverkey = atoi(parg);
   // }
    //if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "dtlsinserverkey")) ||
    //   (parg = conf_find_keyval(pConn->httpReq.uriPairs, "dtlsserverkey"))) {
    //  pipCfg.dtls_capture_serverkey = atoi(parg);
    //}
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "stunrespond"))) {
      if((stunPolicy = atoi(parg)) != STUN_POLICY_ENABLED) {
        stunPolicy = STUN_POLICY_NONE;
      }
      if((pipCfg.stunBindingResponse = stunPolicy) != STUN_POLICY_NONE) {
        pipCfg.stunRespUseSDPIceufrag = 1;
      }
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "stunrequestuser"))) {
      pipCfg.stunReqUsername = parg;
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "stunrequestpass"))) {
      pipCfg.stunReqPass = parg;
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "stunrequestrealm"))) {
      pipCfg.stunReqRealm = parg;
    }
    if(pipCfg.stunBindingRequest == STUN_POLICY_NONE && (pipCfg.stunReqUsername || pipCfg.stunReqPass || pipCfg.stunReqRealm)) {
      pipCfg.stunBindingRequest = STUN_POLICY_XMIT_IF_RCVD;
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "stunrequest"))) {
      stunPolicy = atoi(parg);
      pipCfg.stunBindingRequest = stunPolicy;
    }

    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "turnuser"))) {
      pipCfg.turnCfg.turnUsername = parg; 
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "turnpass"))) {
      pipCfg.turnCfg.turnPass = parg; 
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "turnrealm"))) {
      pipCfg.turnCfg.turnRealm = parg; 
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "turnserver"))) {
      pipCfg.turnCfg.turnServer = parg; 
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "turnpeer"))) {
      pipCfg.turnCfg.turnPeerRelay = parg; 
    }

    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "turnsdp"))) {
      pipCfg.turnCfg.turnSdpOutPath = parg; 
    }

    //if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "turnout"))) {
    //  pipCfg.turnCfg.turnOutput = parg; 
    //} else if(pipCfg.turnCfg.turnServer) {
    //  pipCfg.turnCfg.turnOutput = pipCfg.turnCfg.turnServer;
    //}

    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "delaystart"))) {
      pipCfg.delayed_output = atoi(parg);
    }

    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "abrself"))) {
      pipCfg.abrSelf = atoi(parg);
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "abrauto")) ||
       (parg = conf_find_keyval(pConn->httpReq.uriPairs, "abr"))) {
      pipCfg.abrAuto = atoi(parg);
    }

    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "piplayout")) ||
       (parg = conf_find_keyval(pConn->httpReq.uriPairs, "layout"))) {
      pipCfg.layoutType = pip_str2layout(parg);
    }

    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "pipx")) ||
       (parg = conf_find_keyval(pConn->httpReq.uriPairs, "pipxleft")) ||
       (parg = conf_find_keyval(pConn->httpReq.uriPairs, "pipxright"))) {
      pipCfg.posx = atoi(parg);
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "pipy")) ||
       (parg = conf_find_keyval(pConn->httpReq.uriPairs, "pipytop")) ||
       (parg = conf_find_keyval(pConn->httpReq.uriPairs, "pipybottom"))) {
      pipCfg.posy = atoi(parg);
    }

    // PIP_KEY_ZORDER "zorder"
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "pipzorder")) ||
       (parg = conf_find_keyval(pConn->httpReq.uriPairs, "zorder"))) {
      pipCfg.zorder = atoi(parg);
    }

    // PIP_KEY_NOAUDIO
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "pipnoaudio")) ||
       (parg = conf_find_keyval(pConn->httpReq.uriPairs, "noaud")) ||
       (parg = conf_find_keyval(pConn->httpReq.uriPairs, "noaudio"))) {
      pipCfg.noaud = 1;
    }

    // PIP_KEY_NOVIDEO
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "pipnovideo")) ||
       (parg = conf_find_keyval(pConn->httpReq.uriPairs, "novid")) ||
       (parg = conf_find_keyval(pConn->httpReq.uriPairs, "novideo"))) {
      pipCfg.novid = 1;
    }

    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "duplicate"))) {
      pipCfg.allowDuplicate = 1;
    }

    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "startchime"))) {
      pipCfg.startChimeFile = parg;
    }

    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "stopchime"))) {
      pipCfg.stopChimeFile = parg;
    }

    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "rtpretransmit"))) {
      pipCfg.nackRtpRetransmitVideo = atoi(parg);
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "rembxmitmaxrate"))) {
      pipCfg.apprembRtcpSendVideoMaxRateBps =  (unsigned int) strutil_read_numeric(optarg, 0, 1000, 0);
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "rembxmitminrate"))) {
      pipCfg.apprembRtcpSendVideoMinRateBps =  (unsigned int) strutil_read_numeric(optarg, 0, 1000, 0);
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "rembxmitforce"))) {
      pipCfg.apprembRtcpSendVideoForceAdjustment = atoi(parg);
    }

    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "pipalphamax"))) {
      pipCfg.alphamax_min1 = atoi(parg) + 1;
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "pipalphamin"))) {
      pipCfg.alphamin_min1 = atoi(parg) + 1;
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "pipbefore"))) {
      if(atoi(parg) > 0) {
        pipCfg.flags |= PIP_FLAGS_INSERT_BEFORE_SCALE;
      }
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "pipxright"))) {
      pipCfg.flags |= PIP_FLAGS_POSITION_RIGHT;
    }
    if((parg = conf_find_keyval(pConn->httpReq.uriPairs, "pipybottom"))) {
      pipCfg.flags |= PIP_FLAGS_POSITION_BOTTOM;
    }

    //
    // Read the PIP configuration from a fiel
    //
    if(do_pipconf) {
      pipCfg.cfgfile = do_pipconf;

      //if((idx = pip_getIndexById(pCfg,  pip_id, 1)) >= 0 && idx < MAX_PIPS && pCfg->pStorageBuf) {
      //  strxcodebuf = ((STREAM_STORAGE_T *) pCfg->pStorageBuf)->pipxcodeStrbuf[idx];

      if(pCfg->pStorageBuf) {
        //TODO: if we're all going to use the same buffer (temporarily) we need some locking here...
        strxcodebuf = ((STREAM_STORAGE_T *) pCfg->pStorageBuf)->pipxcodeStrbuf[0];
      }

      if((pipMotion = pip_read_conf(&pipCfg, pCfg->status.pathFile, strxcodebuf))) {
        do_start = 1;
        do_stop = 0;
      } else {

      }
    }

    if(do_status) {

      if(pip_id == -1) {
        if((rc = pip_getActiveIds(pCfg, activePipIds, 1)) > 0) {
          idxResp = 0;
          if((rcTmp = snprintf(respExtra, sizeof(respExtra), "&ids=")) > 0) {
            idxResp += rcTmp;
          }
          for(idx = 0; idx < rc; idx++) {
            if((rcTmp = snprintf(&respExtra[idxResp], sizeof(respExtra) - idxResp, "%s%d", 
                                 idx > 0 ? "," : "", activePipIds[idx])) > 0) { 
              idxResp += rcTmp;
            }
          }
        }
      } else if((idx = pip_getIndexById(pCfg, pip_id, 1)) >= 0) {
        rc = pip_id;
      } else {
        rc = -1;
      }

    } else if(do_update) {

      if((idx = pip_getIndexById(pCfg, pip_id, 1)) >= 0) {
        rc = pip_update(&pipCfg, pCfg, pip_id);
      } else {
        do_update = 0;
      }

    } else if(do_stop) {

      if((rc = pip_stop(pCfg, pip_id)) < 0) {
        LOG(X_ERROR("Failed to stop PIP[%d]"), pip_id);
      }

    } else if(do_start && rc >= 0) {

      rc = pip_start(&pipCfg, pCfg, pipMotion);

    }

    //
    // The html body response
    //
    snprintf(buf, sizeof(buf), "code=%d%s", rc, respExtra[0] != '\0' ? respExtra : ""); 

  } else {
    rc = -1;
  }
 
  if(rc < 0) {
    statusCode = HTTP_STATUS_SERVERERROR;
  }

  rc = http_resp_send(&pConn->sd, &pConn->httpReq, statusCode, (unsigned char *) buf, strlen(buf));

  LOG(X_DEBUG("Sent PIP response '%s' to %s:%d"), buf, FORMAT_NETADDR(pConn->sd.sa, tmp, sizeof(tmp)), 
                                                  htons(INET_PORT(pConn->sd.sa)));

  return rc;
}