int cbOnPkt_silk(void *pUserData, const COLLECT_STREAM_PKTDATA_T *pPkt) { CAPTURE_CBDATA_SP_T *pSp = (CAPTURE_CBDATA_SP_T *) pUserData; const unsigned char *pData = pPkt ? pPkt->payload.pData : NULL; unsigned int len = pPkt ? PKTCAPLEN(pPkt->payload) : 0; CAPTURE_STORE_CBDATA_T cbData; int queueFr = 0; int rc = 0; if(pSp == NULL) { return -1; } else if(!pPkt || !pPkt->payload.pData) { // Packet was lost LOG(X_WARNING("RTP SILK lost packet, last seq:%u, last ts:%u"), pSp->lastPktSeq, pSp->lastPktTs); pSp->spFlags |= (CAPTURE_SP_FLAG_DAMAGEDFRAME | CAPTURE_SP_FLAG_PREVLOST); return 0; } VSX_DEBUG_RTP( LOG(X_DEBUG("RTP - rtp-recv-silk len:%d seq:%u ts:%u(%.3f) ts0:%u(%.3f, dlta:%.3f), mrk:%d ssrc:0x%x " "flags:0x%x pQ:0x%x"), PKTCAPLEN(pPkt->payload), pPkt->u.rtp.seq, pPkt->u.rtp.ts, PTSF(pPkt->u.rtp.ts), pSp->pStream->ts0, (pSp->pStream->clockHz > 0 ? ((double)pSp->pStream->ts0/pSp->pStream->clockHz) : 0), (pSp->pStream->clockHz > 0 ? ((double)(pPkt->u.rtp.ts - pSp->pStream->ts0)/pSp->pStream->clockHz) : 0), (pPkt->u.rtp.marksz & PKTDATA_RTP_MASK_MARKER)?1:0, pSp->pStream->hdr.key.ssrc, pSp->spFlags, pSp->pCapAction && pSp->pCapAction->pQueue ? 1 : 0); if(pPkt && PKTCAPLEN(pPkt->payload) > 0) { LOGHEX_DEBUG(pPkt->payload.pData, MIN(16, PKTCAPLEN(pPkt->payload))); } );
int cbOnPkt_vp8(void *pUserData, const COLLECT_STREAM_PKTDATA_T *pPkt) { int rc = 0; CAPTURE_CBDATA_SP_T *pSp = (CAPTURE_CBDATA_SP_T *) pUserData; CAPTURE_STORE_CBDATA_T cbData; int queueFr = 0; if(pSp == NULL) { return -1; } else if(!pPkt || !pPkt->payload.pData) { // Packet was lost LOG(X_WARNING("RTP VP8 lost packet, last seq:%u, last ts:%u"), pSp->lastPktSeq, pSp->lastPktTs); pSp->spFlags |= (CAPTURE_SP_FLAG_DAMAGEDFRAME | CAPTURE_SP_FLAG_PREVLOST); return 0; } VSX_DEBUG( //VSX_DEBUGLOG_TIME LOG(X_DEBUG("rtp-recv-vp8 len:%d seq:%u ts:%u(%.3f) ts0:%u(%.3f, dlta:%.3f), mrk:%d ssrc:0x%x " "flags:0x%x pQ:0x%x"), PKTCAPLEN(pPkt->payload), pPkt->u.rtp.seq, pPkt->u.rtp.ts, PTSF(pPkt->u.rtp.ts), pSp->pStream->ts0, (pSp->pStream->clockHz > 0 ? ((double)pSp->pStream->ts0/pSp->pStream->clockHz) : 0), (pSp->pStream->clockHz > 0 ? ((double)(pPkt->u.rtp.ts - pSp->pStream->ts0)/pSp->pStream->clockHz) : 0), (pPkt->u.rtp.marksz & PKTDATA_RTP_MASK_MARKER)?1:0, pSp->pStream->hdr.key.ssrc, pSp->spFlags, pSp->pCapAction && pSp->pCapAction->pQueue ? 1 : 0); if(pPkt && PKTCAPLEN(pPkt->payload) > 0) { LOGHEX_DEBUG(pPkt->payload.pData, MIN(16, PKTCAPLEN(pPkt->payload))); } //fprintf(stderr, "tmpFrame.buf.idx:%d\n", pSp->pCapAction->tmpFrameBuf.idx); );
int cbOnPkt_aac(void *pUserData, const COLLECT_STREAM_PKTDATA_T *pPkt) { CAPTURE_CBDATA_SP_T *pSp = (CAPTURE_CBDATA_SP_T *) pUserData; const unsigned char *pData = pPkt ? pPkt->payload.pData : NULL; unsigned int len = pPkt ? PKTCAPLEN(pPkt->payload) : 0; unsigned char adtsHdr[8]; const ESDS_DECODER_CFG_T *pDecoderCfg; unsigned int numAuFrames; unsigned int idx; unsigned int payloadLen; unsigned int fragmentLen; unsigned int payloadStartIdx; uint32_t ts; CAPTURE_STORE_CBDATA_T cbData; int queueFr = 0; int rc = 0; // // TODO: pPkt ts, seq, ssrc, etc... are only set for RTP, if capture config is read // from SDP file - and goes through stream_net_av. (streaming capture eonly) // capture for recording and stream_net_pes uses deprecated way of queing raw // network data for later depacketization, which calls this cb from the queue reader // if(pSp == NULL) { return -1; } else if(pData == NULL) { // Packet was lost LOG(X_WARNING("Lost AAC-hbr RTP packet")); pSp->spFlags &= ~CAPTURE_SP_FLAG_INFRAGMENT; return 0; } VSX_DEBUG_RTP( LOG(X_DEBUG("RTP - rtp-recv-aac len:%d seq:%u ts:%u(%.3f) ts0:%u(%.3f, dlta:%.3f), mrk:%d ssrc:0x%x " "flags:0x%x pQ:0x%x"), PKTCAPLEN(pPkt->payload), pPkt->u.rtp.seq, pPkt->u.rtp.ts, PTSF(pPkt->u.rtp.ts), pSp->pStream->ts0, (pSp->pStream->clockHz > 0 ? ((double)pSp->pStream->ts0/pSp->pStream->clockHz) : 0), (pSp->pStream->clockHz > 0 ? ((double)(pPkt->u.rtp.ts - pSp->pStream->ts0)/pSp->pStream->clockHz) : 0), (pPkt->u.rtp.marksz & PKTDATA_RTP_MASK_MARKER)?1:0, pSp->pStream->hdr.key.ssrc, pSp->spFlags, pSp->pCapAction && pSp->pCapAction->pQueue ? 1 : 0); if(pPkt) { LOGHEX_DEBUG(pPkt->payload.pData, MIN(16, PKTCAPLEN(pPkt->payload))); } );
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 int processPkt(CAPTURE_CBDATA_SP_T *pSp, const COLLECT_STREAM_PKTDATA_T *pPkt, CAPTURE_STORE_CBDATA_T *pCbData) { int rc = 0; const unsigned char *pData = pPkt->payload.pData; unsigned int len = PKTCAPLEN(pPkt->payload); unsigned int extHdrIdx = 0; unsigned int picId = 0; unsigned int partitionId; //fprintf(stderr, "pkt-len:%d ", len); avc_dumpHex(stderr, pPkt->payload.pData, MIN(16, PKTCAPLEN(pPkt->payload)), 0); // // http://www.potaroo.net/ietf/all-ids/draft-ietf-payload-vp8-08.txt // if(len < 1) { LOG(X_ERROR("RTP VP8 insufficient packet length %d, ssrc: 0x%x, seq:%u, ts:%u"), len, pSp->pStream->hdr.key.ssrc, pPkt->u.rtp.seq, pPkt->u.rtp.ts); pSp->spFlags |= (CAPTURE_SP_FLAG_DAMAGEDFRAME); return -1; } if(pSp->lastPktTs != pPkt->u.rtp.ts) { memset(&pSp->u.vp8, 0, sizeof(pSp->u.vp8)); } if((partitionId = (pData[0] & VP8_PARTITIONS_MASK)) >= VP8_PARTITIONS_COUNT) { LOG(X_ERROR("RTP VP8 invalid partition id %d, ssrc: 0x%x, seq:%u, ts:%u"), partitionId, pSp->pStream->hdr.key.ssrc, pPkt->u.rtp.seq, pPkt->u.rtp.ts); return -1; } if((pSp->spFlags & CAPTURE_SP_FLAG_PREVLOST) && (pData[0] & VP8_PD_START_PARTITION) && partitionId > 0 && pSp->u.vp8.partitions[partitionId - 1].byteIdx == 0) { LOG(X_ERROR("RTP VP8 start of partition[%d], but partition[%d] has %d bytes, %s, ssrc: 0x%x, seq:%u, ts:%u"), partitionId, partitionId - 1, pSp->u.vp8.partitions[partitionId - 1].byteIdx, (pSp->spFlags & CAPTURE_SP_FLAG_PREVLOST) ? " prior pkt lost" : "", pSp->pStream->hdr.key.ssrc, pPkt->u.rtp.seq, pPkt->u.rtp.ts), pSp->spFlags |= (CAPTURE_SP_FLAG_DAMAGEDFRAME | CAPTURE_SP_FLAG_DROPFRAME); return -1; } else if((pData[0] & VP8_PD_START_PARTITION) && pSp->u.vp8.partitions[partitionId].byteIdx > 0) { LOG(X_ERROR("RTP VP8 partition bit set for continuation of partition[%d] byte[%d]%s, ssrc: 0x%x, seq:%u, ts:%u"), partitionId, pSp->u.vp8.partitions[partitionId].byteIdx, (pSp->spFlags & CAPTURE_SP_FLAG_PREVLOST) ? " prior pkt lost" : "", pSp->pStream->hdr.key.ssrc, pPkt->u.rtp.seq, pPkt->u.rtp.ts), pSp->spFlags |= (CAPTURE_SP_FLAG_DAMAGEDFRAME | CAPTURE_SP_FLAG_DROPFRAME); return -1; } else if(!(pData[0] & VP8_PD_START_PARTITION) && pSp->pCapAction->tmpFrameBuf.idx == 0) { LOG(X_ERROR("RTP VP8 partition bit not set for presumed start of partition[%d] byte[%d]%s, ssrc: 0x%x, seq:%u, ts:%u"), partitionId, pSp->u.vp8.partitions[partitionId].byteIdx, (pSp->spFlags & CAPTURE_SP_FLAG_PREVLOST) ? " prior pkt lost" : "", pSp->pStream->hdr.key.ssrc, pPkt->u.rtp.seq, pPkt->u.rtp.ts), pSp->spFlags |= (CAPTURE_SP_FLAG_DAMAGEDFRAME | CAPTURE_SP_FLAG_DROPFRAME); return -1; } if(pData[0] & VP8_PD_RESERVED_BIT) { LOG(X_ERROR("RTP VP8 reserved bit should not be set, ssrc: 0x%x, seq:%u, ts:%u"), pSp->pStream->hdr.key.ssrc, pPkt->u.rtp.seq, pPkt->u.rtp.ts); pSp->spFlags |= (CAPTURE_SP_FLAG_DAMAGEDFRAME); return -1; } if(pData[0] & VP8_PD_EXTENDED_CTRL) { if(len < 2) { LOG(X_ERROR("RTP VP8 insufficient packet length %d, ssrc: 0x%x, seq:%u, ts:%u"), len, pSp->pStream->hdr.key.ssrc, pPkt->u.rtp.seq, pPkt->u.rtp.ts); pSp->spFlags |= (CAPTURE_SP_FLAG_DAMAGEDFRAME); return -1; } extHdrIdx++; if(pData[1] & VP8_PD_X_START_PICID_PRESENT) { extHdrIdx++; if(pData[2] & VP8_PD_I_START_PICID_16BIT) { extHdrIdx++; picId = htons(((pData[2] & 0x7f) << 8)| pData[3]); } else { picId = pData[2] & 0x7f; } } if(pData[1] & VP8_PD_X_START_TLOPICIDX_PRESENT) { extHdrIdx++; } if((pData[1] & VP8_PD_X_START_TID_PRESENT) || (pData[1] & VP8_PD_X_START_TID_KEYIDX_PRESENT)) { extHdrIdx++; } } extHdrIdx++; if(len < extHdrIdx) { LOG(X_ERROR("RTP VP8 insufficient packet length %d, ssrc: 0x%x, seq:%u, ts:%u"), len, pSp->pStream->hdr.key.ssrc, pPkt->u.rtp.seq, pPkt->u.rtp.ts); return -1; } if(pSp->pCapAction->tmpFrameBuf.idx == 0 && (pData[0] & VP8_PD_START_PARTITION)) { if(VP8_ISKEYFRAME(&pData[extHdrIdx])) { //fprintf(stderr, "setting haveKeyFrame to 1 spFlags:0x%x\n\n", pSp->spFlags); pSp->spFlags |= CAPTURE_SP_FLAG_KEYFRAME; //LOG(X_DEBUG("cap-vp8 RTP pts:%.3f, seq:%u KEYFRAME"), (float) pPkt->u.rtp.ts / 90000, pPkt->u.rtp.seq); } else { //LOG(X_DEBUG("cap-vp8 RTP pts:%.3f, seq:%u"), (float) pPkt->u.rtp.ts / 90000, pPkt->u.rtp.seq); } } //TODO: handle RTP padding pSp->u.vp8.partitions[partitionId].byteIdx += (len - extHdrIdx); //LOG(X_DEBUG("vp8 storing data tmpFrame.buf.idx:%d, pktlen:%d,exthdridx:%d"), pSp->pCapAction->tmpFrameBuf.idx, len, extHdrIdx); LOGHEX_DEBUG(pData, MIN(16, PKTCAPLEN(pPkt->payload))); rc = pCbData->cbStoreData(pCbData->pCbDataArg, &pData[extHdrIdx], len - extHdrIdx); return rc; }