STREAM_STATS_T *stream_monitor_createattach(STREAM_STATS_MONITOR_T *pMonitor, const struct sockaddr_in *psaRemote, STREAM_METHOD_T method, STREAM_MONITOR_ABR_TYPE_T abrEnabled) { unsigned int numWr = 1; unsigned int numRd = 1; STREAM_STATS_T *pStats; if(!pMonitor || !pMonitor->active || !psaRemote) { return NULL; } // // If this monitor is for use with Adaptive Bitrate control then we don't care about stream // data sources which do not account for the bitrate we intend to control. // if(pMonitor->pAbr && abrEnabled == STREAM_MONITOR_ABR_NONE) { return NULL; } if(abrEnabled == STREAM_MONITOR_ABR_ENABLED) { LOG(X_DEBUG("ABR is enabled for output -> %s://%s:%d"), devtype_methodstr(method), inet_ntoa(psaRemote->sin_addr), htons(psaRemote->sin_port)); } if(method == STREAM_METHOD_UDP) { // // payload over UDP w/o RTP header (MPEG-2 ts) // numRd = 0; } else if(method == STREAM_METHOD_UDP_RTP) { // // We try to account RTP and RTCP output independently // numWr = 2; // // For RTP output there is no TCP flow based in/out queing disparity // numRd = 0; } if(!(pStats = stream_stats_create(psaRemote, numWr, numRd, pMonitor->rangeMs1, pMonitor->rangeMs2))) { return NULL; } pStats->method = method; pStats->abrEnabled = abrEnabled; if(stream_monitor_attach(pMonitor, pStats) < 0) { stream_stats_destroy(&pStats, NULL); } return pStats; }
static STREAM_STATS_T *stream_stats_create(const struct sockaddr_in *psaRemote, unsigned int numWr, unsigned int numRd, int rangeMs1, int rangeMs2) { STREAM_STATS_T *pStats = NULL; unsigned int idx; unsigned int periodMs; int rangeMs; int rc = 0; if(!(pStats = (STREAM_STATS_T *) avc_calloc(1, sizeof(STREAM_STATS_T)))) { return NULL; } pStats->active = 1; for(idx = 0; idx < THROUGHPUT_STATS_BURSTRATES_MAX; idx++) { rangeMs = idx == 0 ? rangeMs1 : rangeMs2; if(rangeMs <= 0) { continue; } periodMs = rangeMs / 10; rangeMs = periodMs * 10; if(rc >= 0 && numWr > 0) { // This is used for UDP / RTP output or general TCP based flow queue writing rc = burstmeter_init(&pStats->throughput_rt[0].bitratesWr[idx], periodMs, rangeMs); } if(rc >= 0 && numWr > 1) { // This is used for UDP / RTCP output rc = burstmeter_init(&pStats->throughput_rt[1].bitratesWr[idx], periodMs, rangeMs); } if(rc >= 0 && numRd > 0) { // This is used for general TCP based flow queue reading rc = burstmeter_init(&pStats->throughput_rt[0].bitratesRd[idx], periodMs, rangeMs); } if(rc < 0) { stream_stats_destroy(&pStats, NULL); return NULL; } } if(psaRemote) { memcpy(&pStats->saRemote, psaRemote, sizeof(pStats->saRemote)); } pStats->numWr = numWr; pStats->numRd = numRd; pthread_mutex_init(&pStats->mtx, NULL); return pStats; }
int srv_rtmp_proc(CLIENT_CONN_T *pConn, const unsigned char *prebuf, unsigned int prebufsz, int istunneled) { int rc = 0; STREAMER_CFG_T *pStreamerCfg = NULL; STREAMER_OUTFMT_T *pLiveFmt = NULL; STREAM_STATS_T *pstats = NULL; RTMP_CTXT_T rtmpCtxt; unsigned int numQFull = 0; char tmps[2][128]; OUTFMT_CFG_T *pOutFmt = NULL; if(!pConn) { return -1; } if(!HAVE_URL_CAP_RTMP(pConn->pListenCfg->urlCapabilities)) { LOG(X_ERROR("Listener %s:%d not enabled for rtmp%s%s stream to %s:%d"), FORMAT_NETADDR(pConn->pListenCfg->sa, tmps[1], sizeof(tmps[1])), ntohs(INET_PORT(pConn->pListenCfg->sa)), istunneled > 0 ? "t" : "", (pConn->sd.netsocket.flags & NETIO_FLAG_SSL_TLS) ? "s" : "", FORMAT_NETADDR(pConn->sd.sa, tmps[0], sizeof(tmps[0])), ntohs(INET_PORT(pConn->sd.sa))); return -1; } pStreamerCfg = GET_STREAMER_FROM_CONN(pConn); if(pStreamerCfg && pStreamerCfg->action.liveFmts.out[STREAMER_OUTFMT_IDX_RTMP].do_outfmt) { pLiveFmt = &pStreamerCfg->action.liveFmts.out[STREAMER_OUTFMT_IDX_RTMP]; if(pStreamerCfg->pMonitor && pStreamerCfg->pMonitor->active) { if(!(pstats = stream_monitor_createattach(pStreamerCfg->pMonitor, (const struct sockaddr *) &pConn->sd.sa, STREAM_METHOD_RTMP, STREAM_MONITOR_ABR_NONE))) { } } // // Add a livefmt cb // pOutFmt = outfmt_setCb(pLiveFmt, rtmp_addFrame, &rtmpCtxt, &pLiveFmt->qCfg, pstats, 1, pStreamerCfg->frameThin, &numQFull); } if(pOutFmt) { memset(&rtmpCtxt, 0, sizeof(rtmpCtxt)); rtmp_init(&rtmpCtxt, MAX(pLiveFmt->qCfg.maxPktLen, pLiveFmt->qCfg.growMaxPktLen)); rtmpCtxt.pSd = &pConn->sd; rtmpCtxt.novid = pStreamerCfg->novid; rtmpCtxt.noaud = pStreamerCfg->noaud; rtmpCtxt.av.vid.pStreamerCfg = pStreamerCfg; rtmpCtxt.av.aud.pStreamerCfg = pStreamerCfg; rtmpCtxt.pAuthTokenId = pConn->pListenCfg->pAuthTokenId; if(pstats) { rtmpCtxt.pStreamMethod = &pstats->method; } rtmpCtxt.pOutFmt = pOutFmt; rtmpCtxt.prebufdata = (unsigned char *) prebuf; rtmpCtxt.prebufsz = prebufsz; if(!(pConn->pListenCfg->urlCapabilities & URL_CAP_RTMPLIVE)) { rtmpCtxt.donotunnel = 1; } if((pConn->pListenCfg->urlCapabilities & URL_CAP_RTMPTLIVE)) { rtmpCtxt.dohttptunnel = 1; } // // Unpause the outfmt callback mechanism now that rtmp_init was called // outfmt_pause(pOutFmt, 0); LOG(X_INFO("Starting rtmp%s%s stream[%d] %d/%d to %s:%d"), istunneled > 0 ? "t" : "", (pConn->sd.netsocket.flags & NETIO_FLAG_SSL_TLS) ? "s" : "", pOutFmt->cbCtxt.idx, numQFull + 1, pLiveFmt->max, FORMAT_NETADDR(pConn->sd.sa, tmps[0], sizeof(tmps[0])), ntohs(INET_PORT(pConn->sd.sa))); rtmp_handle_conn(&rtmpCtxt); LOG(X_INFO("Ending rtmp%s%s stream[%d] to %s:%d"), istunneled > 0 ? "t" : "", (pConn->sd.netsocket.flags & NETIO_FLAG_SSL_TLS) ? "s" : "", pOutFmt->cbCtxt.idx, FORMAT_NETADDR(pConn->sd.sa, tmps[0], sizeof(tmps[0])), ntohs(INET_PORT(pConn->sd.sa))); // // Remove the livefmt cb // outfmt_removeCb(pOutFmt); rtmp_close(&rtmpCtxt); } else { if(pstats) { // // Destroy automatically detaches the stats from the monitor linked list // stream_stats_destroy(&pstats, NULL); } LOG(X_WARNING("No rtmp resource available (max:%d) for %s:%d"), (pLiveFmt ? pLiveFmt->max : 0), FORMAT_NETADDR(pConn->sd.sa, tmps[0], sizeof(tmps[0])), ntohs(INET_PORT(pConn->sd.sa))); rc = -1; } netio_closesocket(&pConn->sd.netsocket); LOG(X_DEBUG("RTMP connection thread ended %s:%d"), FORMAT_NETADDR(pConn->sd.sa, tmps[0], sizeof(tmps[0])), ntohs(INET_PORT(pConn->sd.sa))); return rc; }
static void srv_rtmp_proc(void *pfuncarg) { CLIENT_CONN_T *pConn = (CLIENT_CONN_T *) pfuncarg; STREAMER_CFG_T *pStreamerCfg = NULL; STREAMER_OUTFMT_T *pLiveFmt = NULL; STREAM_STATS_T *pstats = NULL; RTMP_CTXT_T rtmpCtxt; unsigned int numQFull = 0; char buf[SAFE_INET_NTOA_LEN_MAX]; OUTFMT_CFG_T *pOutFmt = NULL; pStreamerCfg = GET_STREAMER_FROM_CONN(pConn); if(pStreamerCfg && pStreamerCfg->action.liveFmts.out[STREAMER_OUTFMT_IDX_RTMP].do_outfmt) { pLiveFmt = &pStreamerCfg->action.liveFmts.out[STREAMER_OUTFMT_IDX_RTMP]; if(pStreamerCfg->pMonitor && pStreamerCfg->pMonitor->active) { if(!(pstats = stream_monitor_createattach(pStreamerCfg->pMonitor, &pConn->sd.sain, STREAM_METHOD_RTMP, STREAM_MONITOR_ABR_NONE))) { } } // // Add a livefmt cb // pOutFmt = outfmt_setCb(pLiveFmt, rtmp_addFrame, &rtmpCtxt, &pLiveFmt->qCfg, pstats, 1, pStreamerCfg->frameThin, &numQFull); } if(pOutFmt) { memset(&rtmpCtxt, 0, sizeof(rtmpCtxt)); rtmp_init(&rtmpCtxt, MAX(pLiveFmt->qCfg.maxPktLen, pLiveFmt->qCfg.growMaxPktLen)); rtmpCtxt.pSd = &pConn->sd; rtmpCtxt.novid = pStreamerCfg->novid; rtmpCtxt.noaud = pStreamerCfg->noaud; rtmpCtxt.av.vid.pStreamerCfg = pStreamerCfg; rtmpCtxt.av.aud.pStreamerCfg = pStreamerCfg; // // Unpause the outfmt callback mechanism now that rtmp_init was called // outfmt_pause(pOutFmt, 0); LOG(X_INFO("Starting rtmp stream[%d] %d/%d to %s:%d"), pOutFmt->cbCtxt.idx, numQFull + 1, pLiveFmt->max, net_inet_ntoa(pConn->sd.sain.sin_addr, buf), ntohs(pConn->sd.sain.sin_port)); rtmp_handle_conn(&rtmpCtxt); LOG(X_INFO("Ending rtmp stream[%d] to %s:%d"), pOutFmt->cbCtxt.idx, net_inet_ntoa(pConn->sd.sain.sin_addr, buf), ntohs(pConn->sd.sain.sin_port)); // // Remove the livefmt cb // outfmt_removeCb(pOutFmt); rtmp_close(&rtmpCtxt); } else { if(pstats) { // // Destroy automatically detaches the stats from the monitor linked list // stream_stats_destroy(&pstats, NULL); } LOG(X_WARNING("No rtmp resource available (max:%d) for %s:%d"), (pLiveFmt ? pLiveFmt->max : 0), net_inet_ntoa(pConn->sd.sain.sin_addr, buf), ntohs(pConn->sd.sain.sin_port)); } netio_closesocket(&pConn->sd.netsocket); LOG(X_DEBUG("RTMP connection thread ended %s:%d"), net_inet_ntoa(pConn->sd.sain.sin_addr, buf), ntohs(pConn->sd.sain.sin_port)); }