static int capture_cbQueueRawDevFrame(void *pArg, const void *pData, uint32_t szData) { int rc = 0; CAP_QUEUERAW_FRAME_DATA_T *pFrameData = (CAP_QUEUERAW_FRAME_DATA_T *) pArg; PKTQ_EXTRADATA_T xtra; xtra.tm.pts = ((uint64_t)pFrameData->pPktData->tv.tv_sec * 90000) + ((double)pFrameData->pPktData->tv.tv_usec / TIME_VAL_US * 90000); xtra.tm.dts = 0; //xtra.flags = CAPTURE_SP_FLAG_KEYFRAME; xtra.pQUserData = NULL; //fprintf(stderr, "capture_cbWriteRawDevFrame len:%d %.3f(%llu) tv:%u:%u \n", szData, PTSF(xtra.tm.pts), xtra.tm.pts, pFrameData->pPktData->tv.tv_sec, pFrameData->pPktData->tv.tv_usec); rc = pktqueue_addpkt(pFrameData->pSp->pCapAction->pQueue, pFrameData->pPktData->payload.pData, PKTLEN32(pFrameData->pPktData->payload), &xtra, 1); 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 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; }
static int capture_send_dummy_frames(CAP_SEND_DUMMY_FRAMES_CTXT_T *pCtxt) { int rc = 0; CAPTURE_CBDATA_SP_T *pSp; CAPTURE_STREAM_T *pStream; PKTQ_EXTRADATA_T xtra; uint64_t pts; enum STREAM_NET_ADVFR_RC timeRc; unsigned int idx; unsigned char buf[16384]; uint64_t pts0[CAPTURE_MAX_FILTERS]; uint64_t frameIds[CAPTURE_MAX_FILTERS]; unsigned int frameSz[CAPTURE_MAX_FILTERS]; float fps[CAPTURE_MAX_FILTERS]; TIME_VAL tvStart[CAPTURE_MAX_FILTERS]; int is_init[CAPTURE_MAX_FILTERS]; int haveSendOnly = 0; CAPTURE_FILTER_T *pFilters; unsigned int numFilters; CAP_ASYNC_DESCR_T *pCfg = pCtxt->pCfg; CAPTURE_STATE_T *pState = pCtxt->pState; buf[0] = 0x00; memset(frameIds, 0, sizeof(frameIds)); memset(is_init, 0, sizeof(is_init)); memset(fps, 0, sizeof(fps)); memset(frameSz, 0, sizeof(frameSz)); memset(pts0, 0, sizeof(pts0)); memset(buf, 0, sizeof(buf)); pFilters = pState->filt.filters; //pFilters = pCfg->pcommon->filters; numFilters = pState->filt.numFilters; LOG(X_DEBUG("Started dummy input frame processor")); while(pCfg->running == STREAMER_STATE_RUNNING && g_proc_exit == 0 && *pCtxt->prunning == STREAMER_STATE_RUNNING) { haveSendOnly = 0; for(idx = 0; idx < numFilters; idx++) { if(pCfg->pStreamerCfg->cfgrtp.xmitType != SDP_XMIT_TYPE_SENDONLY && pFilters[idx].xmitType != SDP_XMIT_TYPE_SENDONLY) { // // Clear the xcode decode input flag // setNoDecode(&pFilters[idx], pCfg->pStreamerCfg, 0); if(is_init[idx]) { // // Restore the xcode input file type // if(codectype_isVid(pFilters[idx].mediaType)) { pCfg->pStreamerCfg->xcode.vid.common.cfgFileTypeIn = pFilters[idx].mediaType; } else if(codectype_isAud(pFilters[idx].mediaType)) { pCfg->pStreamerCfg->xcode.aud.common.cfgFileTypeIn = pFilters[idx].mediaType; } pthread_mutex_lock(pCtxt->pmtx); if((pStream = findStream(pState, &pFilters[idx]))) { // // Clear the RTCP BYE reception flag since some clients may have sent an RTCP BYE when going on hold. // //pStream->haveRtcpBye = 0; capture_delete_stream(pState, pStream); } pthread_mutex_unlock(pCtxt->pmtx); is_init[idx] = 0; } continue; } else if(!is_init[idx]) { init_filter(pCfg, &pFilters[idx], &fps[idx], &frameSz[idx]); if(frameSz[idx] > sizeof(buf)) { LOG(X_ERROR("Capture dummy stream frame size %d exceeds %d"), frameSz[idx], sizeof(buf)); return -1; } pthread_mutex_lock(pCtxt->pmtx); if(!(pStream = on_new_stream(pState, &pFilters[idx], &pts0[idx]))) { pthread_mutex_unlock(pCtxt->pmtx); return -1; } pthread_mutex_unlock(pCtxt->pmtx); is_init[idx] = 1; } haveSendOnly = 1; pthread_mutex_lock(pCtxt->pmtx); if(!(pStream = findStream(pState, &pFilters[idx])) || !(pSp = pStream->pCbUserData)) { LOG(X_ERROR("Unable to find dummy stream for filter media type: %d"), pFilters[idx].mediaType); pthread_mutex_unlock(pCtxt->pmtx); return -1; } pthread_mutex_unlock(pCtxt->pmtx); // // Set the xcode decode input flag // setNoDecode(&pFilters[idx], pCfg->pStreamerCfg, 1); // // Add the dummy frame to the input capture queue. The dummy frame timing is used to // drive the stream frame processor. // memset(&xtra, 0, sizeof(xtra)); //xtra.flags = CAPTURE_SP_FLAG_KEYFRAME; if((timeRc = stream_net_check_time(&tvStart[idx], &frameIds[idx], fps[idx], 1, &pts, NULL)) == STREAM_NET_ADVFR_RC_OK) { xtra.tm.pts = pts + pts0[idx]; //fprintf(stderr, "ADD_DUMMY_FRAME [%d] fps:%.3f, clockHz:%d pts:%.3f (%llu) (relative pts:%.3f)\n", idx, fps[idx], pSp->pStream->clockHz, PTSF(xtra.tm.pts), xtra.tm.pts, PTSF(pts)); rc = pktqueue_addpkt(pSp->pCapAction->pQueue, buf, frameSz[idx], &xtra, 1); //capture_addCompleteFrameToQ(pSp, ts); } } if(!haveSendOnly) { usleep(20000); } } LOG(X_DEBUG("Finished dummy input frame processor")); return 0; }