static int waitfordata(STREAM_AV_DATA_T *pData) { PKTQUEUE_T *pQ = (PKTQUEUE_T *) pData->pDataSrc->pCbData; STREAM_AV_DATA_T *pDataComplement = NULL; uint32_t qWrIdx; uint32_t qWrIdxComplement = 0; int rc = 1; qWrIdx = pktqueue_havepkt(pQ); //fprintf(stderr, "waitfordata called for %s qwridx:%d/%d\n", pData == &((STREAM_PES_T *)pData->pPes)->vid ? "vid" : "aud", qWrIdx, pData->lastQWrIdx); if(qWrIdx != 0 && qWrIdx != pData->lastQWrIdx) { return 1; } pDataComplement = getComplement(pData); if(pDataComplement && ((PKTQUEUE_T *) pDataComplement->pDataSrc->pCbData)->haveRdr) { qWrIdxComplement = pktqueue_havepkt((PKTQUEUE_T *) pDataComplement->pDataSrc->pCbData); //fprintf(stderr, "waitfordata complement %s qwridx:%d/%d\n", pDataComplement == &pPes->vid ? "vid" : "aud", qWrIdxComplement, pDataComplement->lastQWrIdx); if(qWrIdxComplement != 0 && qWrIdxComplement != pDataComplement->lastQWrIdx) { pDataComplement->lastQWrIdx = qWrIdxComplement; return 1; } } VSX_DEBUGLOG3("waiting for data %s qwridx:%d/%d, comp qwridx:%d\n", pData == &((STREAM_PES_T *)pData->pPes)->vid ? "vid" : "aud", qWrIdx, pData->lastQWrIdx, qWrIdxComplement); rc = pktqueue_waitforunreaddata(pQ); VSX_DEBUGLOG3("waiting for data %s done:%d\n", &((STREAM_PES_T *)pData->pPes)->vid ? "vid" : "aud", rc); return rc; }
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; } */ }
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) }