void pool_close(POOL_T *pPool, int wait_for_ret) { TIME_VAL tv0 = timer_GetTime(); if(!pPool) { return; } if(pPool->pInUse) { pPool->destroy_onempty = 1; if(wait_for_ret) { LOG(X_DEBUG("pool_close %s waiting for resources to be returned"), (pPool->descr ? pPool->descr : "")); while(pPool->pInUse) { if(wait_for_ret > 0 && (timer_GetTime() - tv0) / TIME_VAL_MS > wait_for_ret) { LOG(X_WARNING("pool_close %s aborting wait for resources to be returned"), (pPool->descr ? pPool->descr : "")); break; } usleep(50000); } LOG(X_DEBUG("pool_close %s done waiting for resources to be returned"), (pPool->descr ? pPool->descr : "")); } else { LOG(X_DEBUG("pool_close %s delaying deallocation until resources returned"), (pPool->descr ? pPool->descr : "")); return; } } pool_free(pPool); }
int sys_mem_usage(SYS_USAGE_CTXT_T *pCtxt) { int rc = 0; TIME_VAL tmnow; if(!pCtxt || !pCtxt->pcurM) { return -1; } tmnow = timer_GetTime(); // // Return if it has not yet been SYS_MEM_MIN_INTERVAL_MS since the last snapshot // if(pCtxt->memsnapshot.tvsnapshot != 0 && ((tmnow - pCtxt->memsnapshot.tvsnapshot) / TIME_VAL_MS) < SYS_MEM_MIN_INTERVAL_MS) { LOG(X_DEBUGV("Memory snapshot not ready")); return 0; } // // Take a running snapshot of the cpu counters // if((rc = mem_snapshot(&pCtxt->memsnapshot, tmnow)) < 0) { return rc; } memcpy(pCtxt->pcurM, &pCtxt->memsnapshot, sizeof(MEM_SNAPSHOT_T)); return 1; }
enum STREAM_NET_ADVFR_RC stream_net_check_time(TIME_VAL *ptvStart, uint64_t *pframeId, unsigned int clockHz, unsigned int frameDeltaHz, uint64_t *pPtsOut, int64_t *pPtsOffset) { TIME_VAL tvNow = timer_GetTime(); double tvElapsed, tvEquiv; uint64_t us; if(*pframeId == 0) { *ptvStart = tvNow; } else if((tvElapsed = (double)(tvElapsed = (tvNow - *ptvStart))/1000) < (tvEquiv = (double)1000 * *pframeId * frameDeltaHz / clockHz)) { us = (tvEquiv - tvElapsed) * 1000; //fprintf(stderr, "streamnet_check_time notavail elapsed:%lldms, frameId:%lld, equiv of %.3f ms (%.3f, %.3f %llu us) %d/%dHz\n", (tvNow - *ptvStart)/1000, *pframeId, (double)1000 * *pframeId / clockHz, tvElapsed, tvEquiv, us, clockHz, frameDeltaHz); if(us > 2000) { usleep(MIN(5000, us - 2000)); } return STREAM_NET_ADVFR_RC_NOTAVAIL; } if(pPtsOut) { *pPtsOut = (uint64_t) 90000.0f * ((double) *pframeId * frameDeltaHz / clockHz); if(pPtsOffset) { if(*pPtsOffset < 0 && -1 * *pPtsOffset > *pPtsOut) { *pPtsOut = 0; } else { (*pPtsOut) += *pPtsOffset; } } //fprintf(stderr, "stream_net_check_time PTS:%.3f, frameId:%llu, clockHz:%uHz, deltaHz:%dHz, elapsed:%lldms, equiv of %.3fms %d/%dHz\n", PTSF(*pPtsOut), *pframeId, clockHz, frameDeltaHz, (tvNow - *ptvStart)/1000, (double) tvEquiv, clockHz, frameDeltaHz); } (*pframeId)++; return STREAM_NET_ADVFR_RC_OK; }
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; }
int rtmp_auth_cache_store(RTMP_AUTH_PERSIST_STORAGE_T *pcachedAuth, const RTMP_AUTH_PERSIST_T *pElem) { int rc = -1; unsigned int sz = 0; TIME_VAL tm; RTMP_AUTH_PERSIST_NODE_T *p, *p_prev = NULL; RTMP_AUTH_PERSIST_NODE_T *pinsert = NULL; RTMP_AUTH_PERSIST_NODE_T *poldest = NULL; if(pElem->opaque[0] == '\0') { return -1; } VSX_DEBUG_RTMP( LOG(X_DEBUG("RTMP auth cache storing opaque: '%s', salt: '%s', challenge: '%s'"), pElem->opaque, pElem->salt, pElem->challenge); ); tm = timer_GetTime(); pthread_mutex_lock(&pcachedAuth->mtx); p = pcachedAuth->phead; while(p) { if(p->tm == 0 || !strcmp(pElem->opaque, p->s.opaque) || p->tm + (RTMP_AUTH_CACHE_LIFETIME_SEC * TIME_VAL_US) < tm) { pinsert = p; break; } if(!poldest || poldest->tm > p->tm) { poldest = p; }
int stream_udp(STREAM_XMIT_NODE_T *pList, double durationSec) { STREAM_XMIT_NODE_T *pStream; COLLECT_STREAM_PKTDATA_T collectPkt; int triedXmit; unsigned int idxDest; TIME_VAL tmstart, tm1, tmnow; TIME_VAL tmprevbwdescr; int rc; int sz; int szPkt; unsigned int szData; const unsigned char *pData; unsigned int szDataPayload; const unsigned char *pDataPayload; uint64_t totBytes = 0; unsigned int totPkts = 0; unsigned int bytes = 0; unsigned int pkts = 0; unsigned int bytes2 = 0; unsigned int pkts2 = 0; //char dstStr[64]; #ifndef WIN32 unsigned int countIterations = 0; #endif // WIN32 if(!pList #if defined(VSX_HAVE_LICENSE) || !pList->pLic #endif // VSX_HAVE_LICENSE ) { return -1; } //snprintf(dstStr, sizeof(dstStr), "%s:%d", inet_ntoa(pList->pRtpMulti->pdests[0].saDsts.sin_addr), // ntohs(pList->pRtpMulti->pdests[0].saDsts.sin_port)); tmstart = tmnow = timer_GetTime(); tm1 = tmstart; tmprevbwdescr = tmstart; while(*pList->prunning == STREAMER_STATE_RUNNING && !g_proc_exit) { pStream = pList; triedXmit = 0; while(pStream) { if((rc = pStream->cbCanSend(pStream->pCbData)) > 0) { pData = stream_rtp_data(pStream->pRtpMulti); szData = stream_rtp_datalen(pStream->pRtpMulti); if(szData >= RTP_HEADER_LEN) { pDataPayload = pData + RTP_HEADER_LEN; szDataPayload = szData - RTP_HEADER_LEN; } else { pDataPayload = NULL; szDataPayload = 0; } if(pStream->pXmitAction->do_stream) { triedXmit = 1; totPkts++; pkts++; pkts2++; if(pStream->pXmitAction->do_output) { szPkt = 0; for(idxDest = 0; idxDest < pStream->pRtpMulti->numDests; idxDest++) { if(pStream->pXmitDestRc[idxDest] != 0) { continue; } if(pStream->rawSend) { if(pktgen_Queue(pData, szData) != 0) { pStream->pXmitDestRc[idxDest] = -1; sz = -1; } else { sz = szData; } } else { // // Check and send an rtcp sender report // if(*pStream->pfrtcp_sr_intervalsec > 0 && (tmnow - pStream->pRtpMulti->pdests[idxDest].tmLastRtcpSr) / TIME_VAL_MS > *pStream->pfrtcp_sr_intervalsec * 1000) { sendRtcpSr(pStream, idxDest); pStream->pRtpMulti->pdests[idxDest].tmLastRtcpSr = tmnow; } if((sz = sendPktUdpRtp(pStream, idxDest, pData, szData)) < 0) { pStream->pXmitDestRc[idxDest] = sz; } } if(szPkt == 0 && sz > 0) { szPkt = sz; } } // end of for // Exit if there are no good transmitters in the list if(pStream->pXmitAction->do_output && szPkt == 0) { return -1; } } else { // if do_output szPkt = szData; } if(!pStream->pXmitAction->do_output_rtphdr && szPkt > RTP_HEADER_LEN) { szPkt -= RTP_HEADER_LEN; } totBytes += szPkt; bytes += szPkt; // // Add the packet data to any outbound avclive subscribers // // TODO: do not hardcode szData > RTP_HEADER_LEN rtp hdr len if(pStream->pLiveQ && pStream->pLiveQ->numActive > 0 && szDataPayload > 0) { pthread_mutex_lock(&pStream->pLiveQ->mtx); for(sz = 0; (unsigned int) sz < pStream->pLiveQ->max; sz++) { if(pStream->pLiveQ->pQs[sz]) { pktqueue_addpkt(pStream->pLiveQ->pQs[sz], pDataPayload, szDataPayload, NULL, 0); } } pthread_mutex_unlock(&pStream->pLiveQ->mtx); } bytes2 += szPkt; } else { // preserve rtp sequence number during 'live pause' //pStream->pRtp->m_pRtp->sequence_num = // htons(htons(pStream->pRtp->m_pRtp->sequence_num) - 1); //During 'live pause', update the last seq # //if(pStream->pXmitAction->prior_do_stream && pStream->prtp_sequence_at_end) { // *pStream->prtp_sequence_at_end = pStream->pRtp->m_pRtp->sequence_num; //} //fprintf(stderr, "not streaming\n"); } // // Record output stream // if(pStream->pXmitAction->do_record_post && pStream->pXmitCbs->cbRecord && pStream->pXmitCbs->pCbRecordData) { memset(&collectPkt, 0, sizeof(collectPkt)); collectPkt.payload.pData = (unsigned char *) pDataPayload; PKTCAPLEN(collectPkt.payload) = szDataPayload; if((sz = pStream->pXmitCbs->cbRecord(pStream->pXmitCbs->pCbRecordData, &collectPkt)) < 0) { return -1; } if(triedXmit == 0) { triedXmit = 1; } } // // Call post processing function, such as http live streaming // callback to segment and package output ts files // if(pStream->pXmitAction->do_httplive && pStream->pXmitCbs->cbPostProc && pStream->pXmitCbs->pCbPostProcData && pStream->pXmitAction->do_stream) { if((sz = pStream->pXmitCbs->cbPostProc(pStream->pXmitCbs->pCbPostProcData, pDataPayload, szDataPayload)) < 0) { return -1; } if(triedXmit == 0) { triedXmit = 1; } } //if(pStream->pXmitAction->do_stream != pStream->pXmitAction->prior_do_stream) { // pStream->pXmitAction->prior_do_stream = pStream->pXmitAction->do_stream; //} pStream->pRtpMulti->payloadLen = 0; if((rc = pStream->cbPreparePkt(pStream->pCbData)) < 0) { return -1; } //fprintf(stderr, "streamer prepare pkt returned %d\n", rc); } else if(rc < 0) { LOG(X_DEBUG("Stream ending, sent: %"LL64"u bytes %u pkts"), totBytes, totPkts); return -1; } else { //fprintf(stderr, "streamer cansend rc:%d\n", rc); } pStream = pStream->pNext; } // while(pStream) pktgen_SendQueued(); if(triedXmit == 0) { #ifdef WIN32 //sl1 = timer_GetTime(); //Sleep(1) may sleep past its short bedtime on win32, even from a thread w/ SCHED_RR //However, sleep(0) uses alot more cpu slices //TODO: make this a WaitForSingleObject for any registered pktqueue writers if(pList->pSleepQ) { //TODO: this does not return if a pkt has been queued and no subsequent pkt arrives pktqueue_waitforunreaddata(pList->pSleepQ); //pthread_cond_wait(pList->pCond, pList->pMtxCond); } else { Sleep(1); } } else { // On windows Sleep 0 relinquishes execution for any waiting threads Sleep(0); #else // WIN32 VSX_DEBUG2(tmnow = timer_GetTime()) usleep(1000); countIterations = 0; VSX_DEBUGLOG3("stream_udp slept for %lld ns\n", timer_GetTime() - tmnow); } else { if(countIterations++ > 10000) { // During continuous xmit, sleep to prevent unresponsive system usleep(1); countIterations = 0; } #endif // WIN32 } tmnow = timer_GetTime(); if(pList->pBwDescr && (tmnow / TIME_VAL_US) > (tmprevbwdescr / TIME_VAL_US) + 1) { pList->pBwDescr->intervalMs = (float)(tmnow - tmprevbwdescr)/ TIME_VAL_MS; pList->pBwDescr->pkts = pkts2; pList->pBwDescr->bytes = bytes2; TV_FROM_TIMEVAL(pList->pBwDescr->updateTv, tmnow); //pList->pBwDescr->updateTv.tv_sec = tmnow / TIME_VAL_US; //pList->pBwDescr->updateTv.tv_usec = tmnow % TIME_VAL_US; bytes2 = 0; pkts2 = 0; tmprevbwdescr = tmnow; } if(durationSec > 0 && tmnow > tmstart + (durationSec * TIME_VAL_US)) { LOG(X_DEBUG("Stream duration %.1f sec limit reached"), durationSec); *pList->prunning = STREAMER_STATE_FINISHED; } #if defined (VSX_HAVE_LICENSE) // Check if stream time is limited if(!(pList->pLic->capabilities & LIC_CAP_STREAM_TIME_UNLIMITED)) { if(tmnow > tmstart + (STREAM_LIMIT_SEC * TIME_VAL_US)) { LOG(X_INFO("Stream time limited. Stopping stream transmission after %d sec"), (int) (tmnow - tmstart) / TIME_VAL_US); *pList->prunning = STREAMER_STATE_FINISHED; if(!(g_proc_exit_flags & PROC_EXIT_FLAG_NO_EXIT_ON_STREAM_TIME_LIMITED)) { g_proc_exit = 1; } } } #endif // VSX_HAVE_LICENSE #if defined(LITE_VERSION) if(tmnow > tmstart + (STREAM_LIMIT_LITE_SEC * TIME_VAL_US)) { LOG(X_INFO("Stream time limited. Stopping stream transmission after %d sec"), (int) (tmnow - tmstart) / TIME_VAL_US); *pList->prunning = STREAMER_STATE_FINISHED; if(!(g_proc_exit_flags & PROC_EXIT_FLAG_NO_EXIT_ON_STREAM_TIME_LIMITED)) { g_proc_exit = 1; } } #endif // (LITE_VERSION) /* if(0 && pList->verbosity > 1 && tv2.tv_sec > tv1.tv_sec+3) { elapsedMs0 = ((tv2.tv_sec - tv0.tv_sec) * 1000) + ((tv2.tv_usec - tv0.tv_usec) /1000); elapsedMs1 = ((tv2.tv_sec - tv1.tv_sec) * 1000) + ((tv2.tv_usec - tv1.tv_usec) /1000); fprintf(stdout, "%u", elapsedMs0/1000); if(durationSec != 0) { fprintf(stdout, "/%.1f", durationSec); } fprintf(stdout, " sec, %s %.1fKb/s %.1fpkts/s (total: %u pkts, %.1fKB, %.1fKb/s)", dstStr, (double)(bytes / 128.0f / ((double)elapsedMs1/1000.0f)), (double)(pkts/ ((double)elapsedMs1/1000.0f)), totPkts, (double)totBytes/1024.0f, (double)(totBytes / 128.0f / ((double)elapsedMs0/1000.0f))); fprintf(stdout, "\n"); bytes = 0; pkts = 0; tv1.tv_sec = tv2.tv_sec; tv1.tv_usec = tv2.tv_usec; } */ }
static void logger_write_file(LOG_PROPERTIES_T *pLogProperties, int sev, const char *msg, va_list *pvlist) { struct tm *ptm = NULL; char strtime[64]; char strpid[64]; char strsev[30]; char strspace[2]; const TIME_VAL tmNowUs = timer_GetTime(); const time_t tmNow = tmNowUs / TIME_VAL_US; //const time_t tmNow = time(NULL); if(!pLogProperties->g_fp || !(pLogProperties->g_log_flags & LOG_FLAG_USEFILEOUTPUT) || sev == 0 || (sev > 0 && sev > pLogProperties->g_log_level) || (sev < 0 && sev < (-1 * pLogProperties->g_log_level))) { return; } if((pLogProperties->g_log_flags & LOG_OUTPUT_PRINT_DATE) && (ptm = (struct tm *) localtime(&tmNow)) != NULL) { strftime(strtime, sizeof(strtime) -1, "%b %d %H:%M:%S", ptm); strtime[sizeof(strtime) - 1] = '\0'; } else { strtime[0] = '\0'; } strpid[0] = '\0'; print_tag(pLogProperties, strpid, sizeof(strpid)); if(pLogProperties->g_log_flags & LOG_OUTPUT_PRINT_SEV) { snprintf(strsev, sizeof(strsev) - 1, "%s ", logger_getSeverityStr((unsigned short) sev)); strsev[sizeof(strsev) - 1] = '\0'; } else { strsev[0] = '\0'; } if(strtime[0] != '\0' || (strpid[0] != '\0' && strsev[0] != '\0')) { strspace[0] = ' '; strspace[1] = '\0'; } else { strspace[0] = '\0'; } // Log to output file fprintf(pLogProperties->g_fp, "%s.%.3llu %s%s%s", strtime, tmNowUs/TIME_VAL_MS%TIME_VAL_MS, strsev, strpid, strspace); vfprintf(pLogProperties->g_fp, msg, *pvlist); // flush output if(pLogProperties->g_log_flags & LOG_FLAG_FLUSHOUTPUT) { if(pLogProperties->g_fp != NULL && (pLogProperties->g_log_flags & LOG_FLAG_USEFILEOUTPUT)) { fflush(pLogProperties->g_fp); } } return; }
static int readFramesFromDev(CAP_DEV_CFG_T *pCapDevCfg) { int rc = 0; TIME_VAL tv0, tv, tvSleep, tvStart; TIME_VAL tvElapsed = 0; TIME_VAL tvMark = 0; unsigned int frameIdx = 0; COLLECT_STREAM_PKTDATA_T pktdata; CAP_DEV_CTXT_T capDevCtxt; CAPTURE_FILTER_T *pFilter = &pCapDevCfg->pCapCfg->pcommon->filt.filters[pCapDevCfg->idxFilter]; //BMP_FILE_T bmp; //memset(&bmp, 0, sizeof(bmp)); memset(&capDevCtxt, 0, sizeof(capDevCtxt)); capDevCtxt.dev = pCapDevCfg->pCapCfg->pcommon->localAddrs[pCapDevCfg->idxFilter]; //capDevCtxt.dev = "./rawfmt.bmp"; if(readFramesSetup(pFilter, &capDevCtxt.frame) < 0) { return -1; } //TODO: seperate out and maintain better throttle mechanism tv0 = tvStart = timer_GetTime(); while(!g_proc_exit && pCapDevCfg->pCapCfg->running == 0) { // // Reading too slow, reset time reference // if(tvElapsed > tvMark + 300000) { LOG(X_WARNING("Resetting read time for %s after %llu ms > %llu ms (%.3ffps x %ufr)"), capDevCtxt.dev, tvElapsed / TIME_VAL_MS, (TIME_VAL)((1000.0f / pFilter->fps) * frameIdx), pFilter->fps, frameIdx); tv0 = timer_GetTime(); frameIdx = 0; } do { tv = timer_GetTime(); tvElapsed = (tv - tv0); tvMark = (frameIdx / pFilter->fps) * TIME_VAL_US; if(tvElapsed < tvMark) { if((tvSleep = (tvMark - tvElapsed)) < 1000) { tvSleep = 1; } //fprintf(stderr, "sleeping %lld ns\n", tvSleep); usleep(tvSleep); tv = timer_GetTime(); tvElapsed = (tv - tv0); } } while(tvElapsed < tvMark); //if(frameIdx > 50) usleep(50000); //fprintf(stderr, "reading frame %d supposed to be:%lld at:%lld\n", frameIdx, (TIME_VAL) (frameIdx*1000.0f/pFilter->fps),(timer_GetTime() - tv0)/ TIME_VAL_MS); if((rc = readFrameFromDev(&capDevCtxt)) < 0) { break; } /* if((rc = bmp_create(&bmp, pFilter->mediaType, pFilter->width, pFilter->height, capDevCtxt.frame.pData)) < 0) { LOG(X_ERROR("Failed to create bmp %d x %d for format %d"), pFilter->width, pFilter->height, pFilter->mediaType); break; } PKTLEN32(pktdata.payload) = BMP_GET_LEN(&bmp); pktdata.payload.pData = BMP_GET_DATA(&bmp); */ pktdata.tv.tv_sec = (tv - tvStart) / TIME_VAL_US; pktdata.tv.tv_usec = (tv - tvStart) % TIME_VAL_US; PKTLEN32(pktdata.payload) = capDevCtxt.frame.len; pktdata.payload.pData = capDevCtxt.frame.pData; //fprintf(stderr, "got %d bmp %d from %s %llu\n", capDevCtxt.frame.len, BMP_GET_LEN(&bmp), capDevCtxt.dev, tv - tvStart); pCapDevCfg->pStream->cbOnPkt(&pCapDevCfg->pStreamCbData->sp[0], (const COLLECT_STREAM_PKTDATA_T *) &pktdata); frameIdx++; } if(capDevCtxt.fd > 0) { close(capDevCtxt.fd); } free(capDevCtxt.frame.pBuf); //bmp_destroy(&bmp); return rc; }
int sys_cpu_usage(SYS_USAGE_CTXT_T *pCtxt) { int rc = 0; CPU_SNAPSHOT_T *pSnapshots[2]; CPU_SNAPSHOT_T diff; TIME_VAL tmnow; if(!pCtxt || !pCtxt->pcurC) { return -1; } if(pCtxt->cpusnapshots[0].tvsnapshot == 0) { pSnapshots[0] = &pCtxt->cpusnapshots[0]; pSnapshots[1] = &pCtxt->cpusnapshots[1]; } else { pSnapshots[1] = &pCtxt->cpusnapshots[0]; pSnapshots[0] = &pCtxt->cpusnapshots[1]; } tmnow = timer_GetTime(); // // Return if it has not yet been SYS_CPU_MIN_INTERVAL_MS since the last snapshot // if(pSnapshots[1]->tvsnapshot != 0 && ((tmnow - pSnapshots[1]->tvsnapshot) / TIME_VAL_MS) < SYS_CPU_MIN_INTERVAL_MS) { LOG(X_DEBUGV("CPU snapshots not ready")); return 0; } // // Take a running snapshot of the cpu counters // if((rc = cpu_snapshot(pSnapshots[0])) < 0) { return rc; } pSnapshots[0]->tvsnapshot = tmnow; if(pSnapshots[1]->tvsnapshot == 0) { // // We've only been called once and don't have at least 2 snapshots // return 0; } // // Make sure the counters have been updated since the last snapshot // if((diff.tmtot = pSnapshots[0]->tmtot - pSnapshots[1]->tmtot) == 0) { LOG(X_ERROR("CPU tmtot 0 (%Lf - %Lf)"), pSnapshots[0]->tmtot, pSnapshots[1]->tmtot); return 0; } diff.tmuser = pSnapshots[0]->tmuser - pSnapshots[1]->tmuser; diff.tmnice = pSnapshots[0]->tmnice - pSnapshots[1]->tmnice; diff.tmsys = pSnapshots[0]->tmsys - pSnapshots[1]->tmsys; diff.tmidle = pSnapshots[0]->tmidle - pSnapshots[1]->tmidle; pCtxt->pcurC->percentuser = get_percentage(diff.tmuser, &diff); pCtxt->pcurC->percentnice = get_percentage(diff.tmnice, &diff); pCtxt->pcurC->percentsys = get_percentage(diff.tmsys, &diff); pCtxt->pcurC->percentidle = get_percentage(diff.tmidle, &diff); pSnapshots[1]->tvsnapshot = 0; return 1; }
int streamxmit_onRTCPNACK(STREAM_RTP_DEST_T *pDest, const RTCP_PKT_RTPFB_NACK_T *pNack) { STREAMXMIT_PKT_HDR_T *pktHdr; TIME_VAL tvNow; uint16_t nackSeqNumStart; uint16_t nackBlp; unsigned int seqNumOffset; unsigned int idxWr; unsigned int count = 0; int doRetransmission = 0; STREAM_XMIT_QUEUE_T *pAsyncQ = NULL; if(!pDest || !pNack) { return -1; } else if(!(pAsyncQ = &pDest->asyncQ) || !pAsyncQ->doRtcpNack || !pAsyncQ->pQ) { return 0; } //TODO: compute incoming NACK rate to outgoing RTP rate for ABR algorithm tvNow = timer_GetTime(); nackSeqNumStart = htons(pNack->pid); nackBlp = htons(pNack->blp); pthread_mutex_lock(&pAsyncQ->mtx); //LOG(X_DEBUG("streamxmit_onRTCPNACK nackSeqNumStart:%d"), nackSeqNumStart); //fprintf(stderr, "treamxmit_onRTCPNACK nackSeqNumStart... nackSeqNumStart:%d\n", nackSeqNumStart); pktqueue_dump(pDest->asyncQ.pQ, pktqueue_cb_streamxmit_dump_pkthdr); // // If the highest possible NACK sequence num came before the first packet of the keyframe of the current GOP, // then disregard it because we are assuming the keyframe is an IDR. // if(pAsyncQ->haveLastKeyframeSeqNumStart && nackSeqNumStart + 16 < pAsyncQ->lastKeyframeSeqNumStart) { pthread_mutex_unlock(&pAsyncQ->mtx); LOG(X_DEBUG("RTP NACK for sequence: %d ignored because it pertains to a prior GOP sequence:%d"), nackSeqNumStart, pDest->asyncQ.lastKeyframeSeqNumStart); return 0; } // // Iterate the stored packet list backwards starting with the most recently // transmitted packet // idxWr = pAsyncQ->pQ->idxWr; //LOG(X_DEBUG("streamxmit_onRTCPNACK starting at idxWr:%d / %d"), idxWr-1, pAsyncQ->pQ->cfg.maxPkts); while(count++ < pAsyncQ->pQ->cfg.maxPkts) { if(idxWr > 0) { idxWr--; } else { idxWr = pAsyncQ->pQ->cfg.maxPkts - 1; } if(!(pAsyncQ->pQ->pkts[idxWr].flags & PKTQUEUE_FLAG_HAVEPKTDATA)) { // // The write queue slot is empty // //LOG(X_DEBUG("streamxmit_onRTCPNACK idxWr:%d / %d is empty.. break"), idxWr, pAsyncQ->pQ->cfg.maxPkts); break; } else if((pktHdr = (STREAMXMIT_PKT_HDR_T *) pAsyncQ->pQ->pkts[idxWr].xtra.pQUserData) && !pktHdr->rtcp) { if(pktHdr->seqNum < nackSeqNumStart) { // // 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 // //LOG(X_DEBUG("streamxmit_onRTCPNACK idxWr:%d / %d is empty.. break.. seqNuM:%d came before nackSeqNumStart:%d"), idxWr, pAsyncQ->pQ->cfg.maxPkts, pktHdr->seqNum, nackSeqNumStart); break; } else if(pktHdr->tvXmit + (pAsyncQ->nackHistoryMs * TIME_VAL_MS) < tvNow) { // // The queue slot packet time is too old and beyond our threshold for RTP retransmission // //LOG(X_DEBUG("streamxmit_onRTCPNACK idxWr:%d / %d is empty.. seqNuM:%d nack too old to honor"), idxWr, pAsyncQ->pQ->cfg.maxPkts, pktHdr->seqNum); if(pktHdr->doRetransmit >= 0) { LOG(X_DEBUG("RTP NACK for sequence: %d ignored because it has age %d ms > %d ms"), nackSeqNumStart, pAsyncQ->nackHistoryMs, (tvNow - pktHdr->tvXmit) / TIME_VAL_MS); } pktHdr->doRetransmit = -1; break; //TODO: handle seq roll... should not be a big problem here... } else if(pktHdr->doRetransmit >= 0 && pktHdr->seqNum <= nackSeqNumStart + 16) { //LOG(X_DEBUG("streamxmit_onRTCPNACK idxWr:%d / %d seqNum:%d, nackSeqNumStart:%d is within range. seqNumOffset:%d, nackBlp:0x%x"), idxWr, pAsyncQ->pQ->cfg.maxPkts, pktHdr->seqNum, nackSeqNumStart, (pktHdr->seqNum - nackSeqNumStart), nackBlp); if((seqNumOffset = pktHdr->seqNum - nackSeqNumStart) == 0 || (nackBlp & (1 << (seqNumOffset - 1)))) { // // The packet sequence number matches a packet which is being negatively-acknowledged so mark // it for retransmission // if(pktHdr->doRetransmit >= 0 && (!pAsyncQ->haveLastKeyframeSeqNumStart || pktHdr->seqNum >= pAsyncQ->lastKeyframeSeqNumStart)) { LOG(X_DEBUGV("RTP NACK marked pt:%d sequence:%d for retransmission"), pDest->pRtpMulti->init.pt, pktHdr->seqNum); if(pktHdr->doRetransmit == 0) { if(pDest->pstreamStats) { stream_abr_notifyBitrate(pDest->pstreamStats, &pDest->streamStatsMtx, STREAM_ABR_UPDATE_REASON_NACK_REQ, .5f); } } pktHdr->doRetransmit++; } else { LOG(X_DEBUG("RTP NACK not marking pt:%d sequence:%d for retransmission"), pDest->pRtpMulti->init.pt, pktHdr->seqNum); } doRetransmission = 1; } } } } if(doRetransmission) { if(!pAsyncQ->haveRequestedRetransmission) { pAsyncQ->haveRequestedRetransmission = 1; // // Signal the output retransmission thread // vsxlib_cond_signal(&pDest->pRtpMulti->asyncRtpCond.cond, &pDest->pRtpMulti->asyncRtpCond.mtx); } } pthread_mutex_unlock(&pAsyncQ->mtx); return 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; }
int streamxmit_sendto(STREAM_RTP_DEST_T *pDest, const unsigned char *data, unsigned int len, int rtcp, int keyframe, int drop) { int rc = 0; NETIO_SOCK_T *pnetsock = NULL; const unsigned char *pData = data; PKTQ_EXTRADATA_T qXtra; STREAMXMIT_PKT_HDR_T pktHdr; unsigned char encBuf[PACKETGEN_PKT_UDP_DATA_SZ]; // // If we're using DTLS-SRTP over audio/video mux (on same RTP port) then we need to choose // the right socket which has completed the DTLS handshake // if(rtcp) { pnetsock = STREAM_RTCP_PNETIOSOCK(*pDest); } else { pnetsock = STREAM_RTP_PNETIOSOCK(*pDest); } //LOG(X_DEBUG("STREAMXMIT_SENDTO len:%d, rtcp:%d, drop:%d, pDest: 0x%x, srtp:0x%x, pSrtpShared: 0x%x, 0x%x, lenK:%d"), len, rtcp, drop, pDest, &pDest->srtps[rtcp ? 1 : 0], pDest->srtps[rtcp ? 1 : 0].pSrtpShared, SRTP_CTXT_PTR(&pDest->srtps[rtcp ? 1 : 0]), SRTP_CTXT_PTR(&pDest->srtps[rtcp ? 1 : 0])->k.lenKey);LOGHEX_DEBUG(data, MIN(16, len)); if((rc = srtp_dtls_protect(pnetsock, data, len, encBuf, sizeof(encBuf), (const struct sockaddr *) (rtcp ? &pDest->saDstsRtcp : &pDest->saDsts), pDest->srtps[rtcp ? 1 : 0].pSrtpShared, rtcp ? SENDTO_PKT_TYPE_RTCP : SENDTO_PKT_TYPE_RTP)) < 0) { if(rc == -2) { // DTLS handshake not complete return 0; } else { return -1; } } else if(rc > 0) { pData = encBuf; len = rc; } // // Queue the packet for async sending // if(pDest->asyncQ.pQ && (pDest->asyncQ.doAsyncXmit || (!rtcp && pDest->asyncQ.doRtcpNack))) { memset(&qXtra, 0, sizeof(qXtra)); memset(&pktHdr, 0, sizeof(pktHdr)); pktHdr.flags = 0; pktHdr.tvCreate = timer_GetTime(); if(!pDest->asyncQ.doAsyncXmit) { pktHdr.tvXmit = pktHdr.tvCreate; } if(!(pktHdr.rtcp = rtcp)) { pktHdr.seqNum = htons(pDest->pRtpMulti->pRtp->sequence_num); pktHdr.timeStamp = htonl(pDest->pRtpMulti->pRtp->timeStamp); pktHdr.keyframe = keyframe; } qXtra.pQUserData = &pktHdr; pthread_mutex_lock(&pDest->asyncQ.mtx); if(!pDest->asyncQ.isCurPktKeyframe && keyframe) { pDest->asyncQ.haveLastKeyframeSeqNumStart = 1; pDest->asyncQ.lastKeyframeSeqNumStart = pktHdr.seqNum; //LOG(X_DEBUG("NACK - GOP start at packet %d"), pktHdr.seqNum); } if(pktqueue_addpkt(pDest->asyncQ.pQ, pData, len, &qXtra, (!pDest->asyncQ.isCurPktKeyframe && keyframe ? 1 : 0)) != PKTQUEUE_RC_OK) { pthread_mutex_unlock(&pDest->asyncQ.mtx); return -1; } //LOG(X_DEBUG("NACK q rd:[%d]/%d, wr:%d"), pDest->asyncQ.pQ->idxRd, pDest->asyncQ.pQ->cfg.maxPkts, pDest->asyncQ.pQ->idxWr); pDest->asyncQ.isCurPktKeyframe = keyframe; pthread_mutex_unlock(&pDest->asyncQ.mtx); rc = len; //pktqueue_dump(pDest->asyncQ.pQ, pktqueue_cb_streamxmit_dump_pkthdr); } if(!pDest->asyncQ.doAsyncXmit && !drop) { if((rc = srtp_sendto(pnetsock, (void *) pData, len, 0, (const struct sockaddr *) (rtcp ? &pDest->saDstsRtcp : &pDest->saDsts), NULL, rtcp ? SENDTO_PKT_TYPE_RTCP : SENDTO_PKT_TYPE_RTP, 1)) < 0) { return -1; } } else if(drop) { rc = len; } if(pDest->pstreamStats) { stream_stats_addPktSample(pDest->pstreamStats, &pDest->streamStatsMtx, len, !rtcp); } return rc; }
int http_flv_recvloop(CAP_ASYNC_DESCR_T *pCfg, unsigned int offset, int sz, FILE_OFFSET_T contentLen, unsigned char *pbuf, unsigned int szbuf, CAPTURE_STREAM_T *pStream) { CAP_HTTP_FLV_T ctxt; FLV_TAG_HDR_T *pTagHdr; unsigned char *pdata; unsigned int ts0 = 0; unsigned int ts; unsigned int tsoffset = 0; int havets0 = 0; TIME_VAL tm0, tm; unsigned int msElapsed; int do_sleep; int rc; memset(&ctxt, 0, sizeof(ctxt)); ctxt.common.pCfg = pCfg; ctxt.common.pdata = pbuf; ctxt.common.dataoffset = offset; ctxt.common.datasz = offset + sz; ctxt.pStream = pStream; //if(sz > 0) { // LOG(X_WARNING("Partial FLV HTTP Content-Header read offset %d not implemented. (offset:%d)"), sz, offset); //} if(net_setsocknonblock(NETIOSOCK_FD(pCfg->pSockList->netsockets[0]), 1) < 0) { return -1; } if(http_flv_init(&ctxt) < 0) { return -1; } // // Read 'FLV' 9 byte header // if(!(pdata = read_net_flv(&ctxt, 9))) { http_flv_close(&ctxt); return -1; } memcpy(&ctxt.flvhdr, pdata, 9); if(!(ctxt.flvhdr.type[0] == 'F' && ctxt.flvhdr.type[1] == 'L' && ctxt.flvhdr.type[2] == 'V' && ctxt.flvhdr.ver == FLV_HDR_VER_1)) { LOG(X_ERROR("Invalid HTTP FLV start sequence 0x%x 0x%x 0x%x 0x%x"), pdata[0], pdata[1], pdata[2], pdata[3]); http_flv_close(&ctxt); return -1; } if((FLV_HDR_HAVE_VIDEO((&ctxt.flvhdr)) && !ctxt.client.pQVid) || (FLV_HDR_HAVE_AUDIO((&ctxt.flvhdr)) && !ctxt.client.pQAud)) { LOG(X_ERROR("FLV capture input queue(s) not propery configured: video: %d, audio: %d"), ctxt.client.pQVid ? 1 : 0, ctxt.client.pQAud ? 1 : 0); http_flv_close(&ctxt); return -1; } tm0 = timer_GetTime(); if(pCfg->pcommon->caprealtime) { LOG(X_DEBUG("Treating capture as real-time.")); } do { if(!(pTagHdr = (FLV_TAG_HDR_T *) read_net_flv(&ctxt, FLV_TAG_HDR_SZ))) { return -1; } ctxt.taghdr.szprev = htonl(pTagHdr->szprev); ctxt.taghdr.type = pTagHdr->type; memcpy(&ctxt.taghdr.size32, &pTagHdr->size, 3); ctxt.taghdr.size32 = htonl(ctxt.taghdr.size32 << 8); memcpy(&ctxt.taghdr.timestamp32, &pTagHdr->timestamp, 3); ctxt.taghdr.timestamp32 = htonl(ctxt.taghdr.timestamp32 << 8); ctxt.taghdr.timestamp32 |= (pTagHdr->tsext << 24); // // For live content, initial timestamp may not always begin at 0 // if(!havets0) { if((ts0 = ctxt.taghdr.timestamp32) > 0) { havets0 = 1; } } else if(ctxt.taghdr.timestamp32 < ts0) { LOG(X_WARNING("HTTP FLV timestamp anamoly ts:%u, initial ts:%u (offset:%u)"), ctxt.taghdr.timestamp32, ts0, tsoffset); tsoffset += ts0; ts0 = ctxt.taghdr.timestamp32; } ts = ctxt.taghdr.timestamp32 - ts0 + tsoffset; //fprintf(stderr, "flv hdr type:0x%x sz:%d ts:%u(ts0:%u+%u) pkt_ts:%u\n", ctxt.taghdr.type, ctxt.taghdr.size32, ts, ts0, tsoffset, ctxt.taghdr.timestamp32); if(!(pdata = read_net_flv(&ctxt, ctxt.taghdr.size32))) { return -1; } // // For non-live content, throttle download rate inline with timestamps for real-time // restreaming // if(pCfg->pcommon->caprealtime) { do { do_sleep = 0; tm = timer_GetTime(); msElapsed = (unsigned int) (tm - tm0) / TIME_VAL_MS; //fprintf(stderr, "msElapsed:%d ts:%d %lld %lld\n", msElapsed, ctxt.taghdr.timestamp32, tm, tm0); if(msElapsed < ts) { if((do_sleep = ts - msElapsed) > 500) { do_sleep = 500; } //fprintf(stderr, "zzzz %d ms\n", do_sleep); usleep(do_sleep * 1000); } } while(do_sleep); } rc = 0; switch(ctxt.taghdr.type) { case FLV_TAG_VIDEODATA: rc = rtmp_handle_vidpkt(&ctxt.client, pdata, ctxt.taghdr.size32, ts); break; case FLV_TAG_AUDIODATA: rc = rtmp_handle_audpkt(&ctxt.client, pdata, ctxt.taghdr.size32, ts); case FLV_TAG_SCRIPTDATA: break; default: rc = -1; break; } if(rc < 0) { LOG(X_ERROR("Failed to process FLV packet type: 0x%x, len:%d"), ctxt.taghdr.type, ctxt.taghdr.size32); break; } } while(pCfg->running == 0 && (contentLen == 0 || ctxt.client.ctxt.bytesRead < contentLen) && !g_proc_exit); http_flv_close(&ctxt); return ctxt.client.ctxt.bytesRead; }
int streamer_run_tonet(STREAM_XMIT_NODE_T *pList, double durationSec) { STREAM_XMIT_NODE_T *pStream; int triedXmit; TIME_VAL tmstartOutput = 0; TIME_VAL tmstart, tmnow; int haveTmstartOutput = 0; int rc; #if defined(VSX_HAVE_TURN) int pendingTurnRelays = 1; int pendingTurnRelays0; NET_PROGRESS_T progTurn; memset(&progTurn, 0, sizeof(progTurn)); #else // (VSX_HAVE_TURN) int pendingTurnRelays = 0; #endif // (VSX_HAVE_TURN) #if defined(VSX_HAVE_SSL_DTLS) TIME_VAL tmstartRtpHandshakeDone = 0; int giveupRtcpHandshakes = 0; int pendingDtlsRtpHandshakes = 1; NET_PROGRESS_T progRtp; NET_PROGRESS_T progRtcp; DTLS_TIMEOUT_CFG_T dtlsTimeouts; setDtlsTimeouts(&dtlsTimeouts, pList); memset(&progRtp, 0, sizeof(progRtp)); memset(&progRtcp, 0, sizeof(progRtcp)); #else int pendingDtlsRtpHandshakes = 0; #endif // (VSX_HAVE_SSL_DTLS) #ifndef WIN32 unsigned int countIterations = 0; #endif // WIN32 if(!pList #if defined(VSX_HAVE_LICENSE) || !pList->pLic #endif // VSX_HAVE_LICENSE ) { return -1; } tmstart = tmnow = timer_GetTime(); while(*pList->prunning == STREAMER_STATE_RUNNING && !g_proc_exit) { pStream = pList; triedXmit = 0; while(pStream) { #if defined(VSX_HAVE_TURN) pendingTurnRelays0 = pendingTurnRelays; if(pendingTurnRelays > 0 && (pendingTurnRelays = do_turn_relays(pStream, &progTurn)) < 0) { LOG(X_ERROR("Giving up waiting for output transmission after TURN relay setup failure")); return -1; } if(pendingTurnRelays == 0 && pendingTurnRelays0 != pendingTurnRelays && progTurn.numTotal > 0) { LOG(X_INFO("Starting output transmission after creating TURN relay(s)")); } //LOG(X_DEBUG("pendingTurnRelays:%d, %d/%d, numerror:%d"), pendingTurnRelays, progTurn.numCompleted, progTurn.numTotal, progTurn.numError); #endif // (VSX_HAVE_TURN) #if defined(VSX_HAVE_SSL_DTLS) //if(pStream->pRtpMulti->pStreamerCfg->xcode.vid.pip.active) //LOG(X_DEBUG("loop... pendingDtlsRtpHandshakes:%d, rtp:%d - %d, rtcp:%d - %d, giveupRtcpHandshakes:%d, tm:%lld ms"), pendingDtlsRtpHandshakes, progRtp.numTotal, progRtp.numCompleted, progRtcp.numTotal, progRtcp.numCompleted, giveupRtcpHandshakes, (timer_GetTime() - tmstart) / TIME_VAL_MS); //LOG(X_DEBUG("streamer2: pendingTurnRelays;%d, completed:%d/%d, pendingDtlsRtpHandshakes:%d"), pendingTurnRelays, progTurn.numCompleted, progTurn.numTotal, pendingDtlsRtpHandshakes); if(pendingTurnRelays == 0 && pendingDtlsRtpHandshakes > 0 && (pendingDtlsRtpHandshakes = do_dtls_handshakes(pStream, tmstart, &tmstartRtpHandshakeDone, &progRtp, &progRtcp, &dtlsTimeouts)) < 0) { return -1; } else if(pendingDtlsRtpHandshakes == 0 && pendingTurnRelays == 0) { if(!haveTmstartOutput) { tmstartOutput = timer_GetTime(); haveTmstartOutput = 1; } if(progRtcp.numTotal - progRtcp.numCompleted > 0 && !giveupRtcpHandshakes) { if(tmstartRtpHandshakeDone == 0) { tmstartRtpHandshakeDone = timer_GetTime(); } if(((tmnow = timer_GetTime()) - tmstartRtpHandshakeDone) / TIME_VAL_MS > dtlsTimeouts.handshakeRtcpAdditionalGiveupMs) { LOG(X_ERROR("Aborting DTLS RTCP handshaking after %lld ms"), (tmnow - tmstart) / TIME_VAL_MS); giveupRtcpHandshakes = 1; } else { do_dtls_handshakes(pStream, tmstart, &tmstartRtpHandshakeDone, NULL, &progRtcp, &dtlsTimeouts); } //LOG(X_DEBUG("loop... calling do_dtls_handshakes Rtcp.numDtls:%d - Rtcp.numDtlsCompleted"), progRtcp.numTotal, progRtcp.numCompleted); } #endif // (VSX_HAVE_SSL_DTLS) //if(pStream->pRtpMulti->pStreamerCfg->xcode.vid.pip.active) //LOG(X_DEBUG("Calling cbCanSend")); if((rc = pStream->cbCanSend(pStream->pCbData)) > 0) { if((rc = pStream->cbPreparePkt(pStream->pCbData)) < 0) { return -1; } else if(rc > 0) { triedXmit = 1; } } else if(rc == -2) { // all programs have ended return 0; } else if(rc < 0) { return -1; } else { //fprintf(stderr, "streamer cansend rc:%d\n", rc); } #if defined(VSX_HAVE_SSL_DTLS) } #endif // (VSX_HAVE_SSL_DTLS) pStream = pStream->pNext; } // while(pStream) if(triedXmit == 0) { #ifdef WIN32 //sl1 = timer_GetTime(); //Sleep(1) may sleep past its short bedtime on win32, even from a thread w/ SCHED_RR //However, sleep(0) uses alot more cpu slices //TODO: make this a WaitForSingleObject for any registered pktqueue writers if(pList->pSleepQ) { //TODO: this does not return if a pkt has been queued and no subsequent pkt arrives pktqueue_waitforunreaddata(pList->pSleepQ); } else { Sleep(1); } } else { // On windows Sleep 0 relinquishes execution for any waiting threads Sleep(0); #else // WIN32 VSX_DEBUG2(tmnow = timer_GetTime()); //fprintf(stderr, "usleep...\n"); if(pendingDtlsRtpHandshakes > 0 || pendingTurnRelays > 0) { usleep(10000); } else { usleep(1000); } //fprintf(stderr, "usleep done...\n"); countIterations = 0; VSX_DEBUG_STREAMAV( LOG(X_DEBUGV("stream_readframes slept for %lld ns"), timer_GetTime() - tmnow)); //fprintf(stderr, "%lld --woke up from streamer2 zzz...\n", timer_GetTime() / TIME_VAL_MS); } else { //fprintf(stderr, "stream_readframes not sleeping count:%d tried:%d\n", countIterations, triedXmit); //fprintf(stderr, "no usleep...%d\n", countIterations); if(countIterations++ > 10000) { // During continuous xmit, sleep to prevent unresponsive system usleep(1); countIterations = 0; } #endif // WIN32 } tmnow = timer_GetTime(); #if defined(TEST_KILL) //static int raiseSig=0; if(!raiseSig && haveTmstartOutput && tmnow > tmstartOutput + (4* TIME_VAL_US)) { raise(SIGINT); } static int raiseSig=0; if(!raiseSig && haveTmstartOutput && tmnow > tmstartOutput + (19* TIME_VAL_US)) { g_proc_exit=1;if(g_proc_exit_cond){pthread_cond_broadcast(g_proc_exit_cond);} } #endif // TEST_KILL if(haveTmstartOutput && durationSec > 0 && tmnow > tmstartOutput + (durationSec * TIME_VAL_US)) { LOG(X_DEBUG("Stream duration %.1f sec limit reached"), durationSec); //*pList->prunning = STREAMER_STATE_FINISHED; return 0; } #if defined(VSX_HAVE_LICENSE) // Check if stream time is limited if(!(pList->pLic->capabilities & LIC_CAP_STREAM_TIME_UNLIMITED)) { if(haveTmstartOutput && tmnow > tmstartOutput + (STREAM_LIMIT_SEC * TIME_VAL_US)) { LOG(X_INFO("Stream time limited. Stopping stream transmission after %d sec"), (int) (tmnow - tmstartOutput) / TIME_VAL_US); *pList->prunning = STREAMER_STATE_FINISHED; if(!(g_proc_exit_flags & PROC_EXIT_FLAG_NO_EXIT_ON_STREAM_TIME_LIMITED)) { g_proc_exit = 1; } } } #endif // VSX_HAVE_LICENSE /* LOG(X_DEBUG("pList: 0x%x, pip.active:%d, overlay.havePip:%d"), pList, pList->pRtpMulti->pStreamerCfg->xcode.vid.pip.active, pList->pRtpMulti->pStreamerCfg->xcode.vid.overlay.havePip); if(pList->pRtpMulti->pStreamerCfg->xcode.vid.pip.active && haveTmstartOutput && tmnow > tmstartOutput + (5 * TIME_VAL_US)) { *pList->prunning = STREAMER_STATE_FINISHED; if(!(g_proc_exit_flags & PROC_EXIT_FLAG_NO_EXIT_ON_STREAM_TIME_LIMITED)) { g_proc_exit = 1; } } */ #if defined(LITE_VERSION) if(haveTmstartOutput && tmnow > tmstartOutput + (STREAM_LIMIT_LITE_SEC * TIME_VAL_US)) { LOG(X_INFO("Stream time limited. Stopping stream transmission after %d sec"), (int) (tmnow - tmstartOutput) / TIME_VAL_US); *pList->prunning = STREAMER_STATE_FINISHED; if(!(g_proc_exit_flags & PROC_EXIT_FLAG_NO_EXIT_ON_STREAM_TIME_LIMITED)) { g_proc_exit = 1; } } #endif // (LITE_VERSION) }
static int do_dtls_handshakes(STREAM_XMIT_NODE_T *pStream, TIME_VAL tmstart, TIME_VAL *ptmstartRtpHandshakeDone, NET_PROGRESS_T *progRtp, NET_PROGRESS_T *progRtcp, const DTLS_TIMEOUT_CFG_T *pdtlsTimeouts) { int idxDest, idxDestPrior; STREAM_RTP_DEST_T *pDest = NULL; STREAM_RTP_DEST_T *pDestPrior = NULL; STREAM_RTP_MULTI_T *pRtpMulti = NULL; STREAM_RTP_MULTI_T *pRtpMultiPrior = NULL; int sameRtpSock, sameRtcpSock; TIME_VAL tmnow; //LOG(X_DEBUG("do_dtls_handshakes")); if(progRtp) { memset(progRtp, 0, sizeof(NET_PROGRESS_T)); } if(progRtcp) { memset(progRtcp, 0, sizeof(NET_PROGRESS_T)); } pRtpMulti = pStream->pRtpMulti; while(pRtpMulti) { if(g_proc_exit) { return -1; } else if(*pStream->prunning != STREAMER_STATE_RUNNING) { continue; } // // Check if the RTP socket is pending creation by the capture_socket thread // if(progRtp && pRtpMulti->pStreamerCfg->sharedCtxt.state == STREAMER_SHARED_STATE_PENDING_CREATE) { progRtp->numTotal++; continue; } for(idxDest = 0; idxDest < pRtpMulti->numDests; idxDest++) { if(!(pDest = &pRtpMulti->pdests[idxDest]) || !pDest->isactive) { continue; } sameRtpSock = sameRtcpSock = 0; if(pRtpMultiPrior) { // // Check RTP video/audio multiplexing over the same port // for(idxDestPrior = 0; idxDestPrior < pRtpMultiPrior->numDests; idxDestPrior++) { if(!(pDestPrior = &pRtpMultiPrior->pdests[idxDestPrior]) || !pDestPrior->isactive) { continue; } if(pDestPrior->saDsts.sin_port == pDest->saDsts.sin_port) { sameRtpSock = 1; } if(pDestPrior->saDstsRtcp.sin_port == pDest->saDstsRtcp.sin_port) { sameRtcpSock = 1; } } } //if(pDest->pRtpMulti->pStreamerCfg->xcode.vid.pip.active) //LOG(X_DEBUG("SAMERTPSOCK:%D, SAMERTCPSOCK:%d"), sameRtpSock, sameRtcpSock); if(progRtp && (STREAM_RTP_PNETIOSOCK(*pDest)->flags & NETIO_FLAG_SSL_DTLS) && !sameRtpSock && STREAM_RTP_FD(*pDest) != INVALID_SOCKET) { //if(pDest->pRtpMulti->pStreamerCfg->xcode.vid.pip.active) //LOG(X_DEBUG("DO_DTLS_HS RTP pnetsock.state: %d, flags:0x%x"), STREAM_RTP_PNETIOSOCK(*pDest)->ssl.state, STREAM_RTP_PNETIOSOCK(*pDest)->flags); do_dtls_handshake(STREAM_RTP_PNETIOSOCK(*pDest), &pDest->saDsts, progRtp); } if(progRtcp && STREAM_RTP_FD(*pDest) != STREAM_RTCP_FD(*pDest) && (STREAM_RTCP_PNETIOSOCK(*pDest)->flags & NETIO_FLAG_SSL_DTLS) && !sameRtcpSock && STREAM_RTCP_FD(*pDest) != INVALID_SOCKET) { //if(pDest->pRtpMulti->pStreamerCfg->xcode.vid.pip.active) //LOG(X_DEBUG("DO_DTLS_HS RCTP pnetsock.state: %d, flags:0x%x"), STREAM_RTCP_PNETIOSOCK(*pDest)->ssl.state, STREAM_RTCP_PNETIOSOCK(*pDest)->flags); do_dtls_handshake(STREAM_RTCP_PNETIOSOCK(*pDest), &pDest->saDstsRtcp, progRtcp); } } // end of for(idxDest.. pRtpMultiPrior = pRtpMulti; pRtpMulti = pRtpMulti->pnext; } // end of while(pRtpMulti... //LOG(X_DEBUG("streamer_do_dtls_handshake done rtp-numDtls:%d, rtp-numDtlsCompleted:%d, rtcp-numDtls:%d, rtcp-numDtlsCompleted:%d"), progRtp ? progRtp->numTotal : -1, progRtp ? progRtp->numCompleted : -1, progRtcp->numTotal, progRtcp->numCompleted); if(!progRtp) { return 0; } if(progRtp->numError > 0) { return -1; } else if(progRtp->numCompleted == progRtp->numTotal) { if(*ptmstartRtpHandshakeDone == 0) { *ptmstartRtpHandshakeDone = timer_GetTime(); } if(progRtcp->numTotal - progRtcp->numCompleted > 0 && ((tmnow = timer_GetTime()) - *ptmstartRtpHandshakeDone) / TIME_VAL_MS < pdtlsTimeouts->handshakeRtcpAdditionalMs) { return progRtcp->numTotal - progRtcp->numCompleted; } else { return 0; } } else { if(((tmnow = timer_GetTime()) - tmstart) / TIME_VAL_MS > pdtlsTimeouts->handshakeRtpTimeoutMs) { LOG(X_ERROR("Aborting DTLS RTP handshake(s) after %lld ms. %d/%d completed"), (tmnow - tmstart) / TIME_VAL_MS, progRtp->numCompleted, progRtp->numTotal); return -1; } else { return progRtp->numTotal - progRtp->numCompleted; } } }
static void stream_monitor_proc(void *pArg) { MONITOR_START_WRAP_T startWrap; STREAM_STATS_MONITOR_T *pMonitor = NULL; FILE *fp = NULL; unsigned int intervalMs; TIME_VAL tvNext, tv; memcpy(&startWrap, pArg, sizeof(startWrap)); pMonitor = startWrap.pMonitor; logutil_tid_add(pthread_self(), startWrap.tid_tag); pMonitor->runMonitor = 1; if((intervalMs = pMonitor->dumpIntervalMs) <= 0) { intervalMs = STREAM_STATS_DUMP_MS; } // // Set the output file path // if(pMonitor->statfilepath) { if(!strcasecmp(pMonitor->statfilepath, "stdout")) { fp = stdout; } else if(!strcasecmp(pMonitor->statfilepath, "stderr")) { fp = stderr; } else if(!(fp = fopen(pMonitor->statfilepath, "w"))) { LOG(X_ERROR("Failed top open stats file %s for writing"), pMonitor->statfilepath); pMonitor->runMonitor = -1; } } else { //fp = stdout; } if(pMonitor->runMonitor == 1) { tvNext = timer_GetTime() + (intervalMs * TIME_VAL_MS); LOG(X_INFO("Started %sstream output monitor %s%s"), pMonitor->pAbr ? "ABR " : "", pMonitor->statfilepath ? "to " : "", pMonitor->statfilepath ? pMonitor->statfilepath : ""); } while(pMonitor->runMonitor == 1 && !g_proc_exit) { if((tv = timer_GetTime()) >= tvNext) { stream_monitor_dump(fp, pMonitor); tvNext += (intervalMs * TIME_VAL_MS); } usleep(100000); } if(fp) { fclose(fp); } pMonitor->runMonitor = -1; pthread_mutex_destroy(&pMonitor->mtx); pMonitor->active = -1; logutil_tid_remove(pthread_self()); }