// Called from openavbTLThreadFn() which is started from openavbTLRun() void openavbTLRunListener(tl_state_t *pTLState) { AVB_TRACE_ENTRY(AVB_TRACE_TL); if (!pTLState) { AVB_LOG_ERROR("Invalid TLState"); AVB_TRACE_EXIT(AVB_TRACE_TL); return; } openavb_tl_cfg_t *pCfg = &pTLState->cfg; pTLState->pPvtListenerData = calloc(1, sizeof(listener_data_t)); if (!pTLState->pPvtListenerData) { AVB_LOG_WARNING("Failed to allocate listener data."); return; } AVBStreamID_t streamID; memcpy(streamID.addr, pCfg->stream_addr.mac, ETH_ALEN); streamID.uniqueID = pCfg->stream_uid; AVB_LOGF_INFO("Attach "STREAMID_FORMAT, STREAMID_ARGS(&streamID)); // Create Stats Mutex { MUTEX_ATTR_HANDLE(mta); MUTEX_ATTR_INIT(mta); MUTEX_ATTR_SET_TYPE(mta, MUTEX_ATTR_TYPE_DEFAULT); MUTEX_ATTR_SET_NAME(mta, "TLStatsMutex"); MUTEX_CREATE_ERR(); MUTEX_CREATE(pTLState->statsMutex, mta); MUTEX_LOG_ERR("Could not create/initialize 'TLStatsMutex' mutex"); } // Tell endpoint to listen for our stream. // If there is a talker, we'll get callback (above.) pTLState->bConnected = openavbTLRunListenerInit(pTLState->endpointHandle, &streamID); if (pTLState->bConnected) { bool bServiceIPC; // Do until we are stopped or loose connection to endpoint while (pTLState->bRunning && pTLState->bConnected) { // Listen for an RX frame (or just sleep if not streaming) bServiceIPC = listenerDoStream(pTLState); if (bServiceIPC) { // Look for messages from endpoint. Don't block (timeout=0) if (!openavbEptClntService(pTLState->endpointHandle, 0)) { AVB_LOGF_WARNING("Lost connection to endpoint "STREAMID_FORMAT, STREAMID_ARGS(&streamID)); pTLState->bConnected = FALSE; pTLState->endpointHandle = 0; } } } // Stop streaming listenerStopStream(pTLState); { MUTEX_CREATE_ERR(); MUTEX_DESTROY(pTLState->statsMutex); // Destroy Stats Mutex MUTEX_LOG_ERR("Error destroying mutex"); } // withdraw our listener attach if (pTLState->bConnected) openavbEptClntStopStream(pTLState->endpointHandle, &streamID); } else { AVB_LOGF_WARNING("Failed to connect to endpoint "STREAMID_FORMAT, STREAMID_ARGS(&streamID)); } if (pTLState->pPvtListenerData) { free(pTLState->pPvtListenerData); pTLState->pPvtListenerData = NULL; } AVB_TRACE_EXIT(AVB_TRACE_TL); }
bool startAvdecc(const char* ifname, const char **inifiles, int numfiles) { AVB_TRACE_ENTRY(AVB_TRACE_AVDECC); LOG_EAVB_CORE_VERSION(); // Ensure that we're running as root // (need to be root to use raw sockets) uid_t euid = geteuid(); if (euid != (uid_t)0) { fprintf(stderr, "Error: needs to run as root\n\n"); goto error; } // Get the AVDECC configuration memset(&gAvdeccCfg, 0, sizeof(openavb_avdecc_cfg_t)); openavbReadAvdeccConfig(DEFAULT_AVDECC_INI_FILE, &gAvdeccCfg); // Determine which interface to use. if (ifname) { strncpy(gAvdeccCfg.ifname, ifname, sizeof(gAvdeccCfg.ifname)); } else if (gAvdeccCfg.ifname[0] == '\0') { AVB_LOG_ERROR("No interface specified. Use the -I flag, or add one to " DEFAULT_AVDECC_INI_FILE "."); goto error; } // Read the information from the supplied INI files. openavb_tl_data_cfg_t * prevStream = NULL, * newStream; U32 i1; for (i1 = 0; i1 < numfiles; i1++) { char iniFile[1024]; snprintf(iniFile, sizeof(iniFile), "%s", inifiles[i1]); // Create a new item with this INI information. newStream = malloc(sizeof(openavb_tl_data_cfg_t)); if (!newStream) { AVB_LOG_ERROR("Out of memory"); goto error; } memset(newStream, 0, sizeof(openavb_tl_data_cfg_t)); if (!openavbReadTlDataIniFile(iniFile, newStream)) { AVB_LOGF_ERROR("Error reading ini file: %s", inifiles[i1]); goto error; } // Append this item to the list of items. if (!prevStream) { // First item. streamList = newStream; } else { // Subsequent item. prevStream->next = newStream; } prevStream = newStream; } /* Run AVDECC in its own thread. */ avdeccRunning = TRUE; avdeccInitSucceeded = FALSE; int err = pthread_create(&avdeccServerHandle, NULL, avdeccServerThread, NULL); if (err) { AVB_LOGF_ERROR("Failed to start AVDECC thread: %s", strerror(err)); goto error; } /* Wait a while to see if the thread was able to start AVDECC. */ int i; for (i = 0; avdeccRunning && !avdeccInitSucceeded && i < 5000; ++i) { SLEEP_MSEC(1); } if (!avdeccInitSucceeded) { goto error; } AVB_TRACE_EXIT(AVB_TRACE_AVDECC); return true; error: AVB_TRACE_EXIT(AVB_TRACE_AVDECC); return false; }
// callback function - called for each name/value pair by ini parsing library static int openavbIniCfgCallback(void *user, const char *tlSection, const char *name, const char *value) { AVB_TRACE_ENTRY(AVB_TRACE_AVDECC); openavb_tl_data_cfg_t *pCfg = (openavb_tl_data_cfg_t *)user; AVB_LOGF_DEBUG("name=[%s] value=[%s]", name, value); bool valOK = FALSE; char *pEnd; if (MATCH(name, "role")) { if (MATCH(value, "talker")) { pCfg->role = AVB_ROLE_TALKER; valOK = TRUE; } else if (MATCH(value, "listener")) { pCfg->role = AVB_ROLE_LISTENER; valOK = TRUE; } } else if (MATCH(name, "initial_state")) { if (MATCH(value, "running")) { pCfg->initial_state = TL_INIT_STATE_RUNNING; valOK = TRUE; } else if (MATCH(value, "stopped")) { pCfg->initial_state = TL_INIT_STATE_STOPPED; valOK = TRUE; } } else if (MATCH(name, "dest_addr")) { valOK = parse_mac(value, &pCfg->dest_addr); } else if (MATCH(name, "stream_addr")) { valOK = parse_mac(value, &pCfg->stream_addr); } else if (MATCH(name, "stream_uid")) { errno = 0; pCfg->stream_uid = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && pCfg->stream_uid <= UINT16_MAX) valOK = TRUE; } else if (MATCH(name, "max_interval_frames")) { errno = 0; pCfg->max_interval_frames = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && pCfg->max_interval_frames <= UINT16_MAX) valOK = TRUE; } else if (MATCH(name, "max_frame_size")) { errno = 0; pCfg->max_frame_size = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && pCfg->max_interval_frames <= UINT16_MAX) valOK = TRUE; } else if (MATCH(name, "sr_class")) { if (strlen(value) == 1) { if (tolower(value[0]) == 'a') { pCfg->sr_class = SR_CLASS_A; valOK = TRUE; } else if (tolower(value[0]) == 'b') { pCfg->sr_class = SR_CLASS_B; valOK = TRUE; } } } else if (MATCH(name, "sr_rank")) { if (strlen(value) == 1) { if (value[0] == '1') { pCfg->sr_rank = SR_RANK_REGULAR; valOK = TRUE; } else if (value[0] == '0') { pCfg->sr_rank = SR_RANK_EMERGENCY; valOK = TRUE; } } } else if (MATCH(name, "max_transit_usec")) { errno = 0; pCfg->max_transit_usec = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && pCfg->max_transit_usec <= UINT32_MAX) valOK = TRUE; } else if (MATCH(name, "max_transmit_deficit_usec")) { errno = 0; pCfg->max_transmit_deficit_usec = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && pCfg->max_transmit_deficit_usec <= UINT32_MAX) valOK = TRUE; } else if (MATCH(name, "internal_latency")) { errno = 0; pCfg->internal_latency = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && pCfg->internal_latency <= UINT32_MAX) valOK = TRUE; } else if (MATCH(name, "batch_factor")) { errno = 0; pCfg->batch_factor = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && pCfg->batch_factor > 0 && pCfg->batch_factor <= INT32_MAX) valOK = TRUE; } else if (MATCH(name, "max_stale")) { errno = 0; pCfg->max_stale = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && pCfg->max_stale >= 0 && pCfg->max_stale <= INT32_MAX) valOK = TRUE; } else if (MATCH(name, "raw_tx_buffers")) { errno = 0; pCfg->raw_tx_buffers = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && pCfg->raw_tx_buffers <= UINT32_MAX) valOK = TRUE; } else if (MATCH(name, "raw_rx_buffers")) { errno = 0; pCfg->raw_rx_buffers = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && pCfg->raw_rx_buffers <= UINT32_MAX) valOK = TRUE; } else if (MATCH(name, "report_seconds")) { errno = 0; pCfg->report_seconds = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && (int)pCfg->report_seconds >= 0 && pCfg->report_seconds <= INT32_MAX) valOK = TRUE; } else if (MATCH(name, "start_paused")) { // ignore this item - tl_host doesn't use it because // it pauses before reading any of its streams. errno = 0; long tmp; tmp = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && tmp >= 0 && tmp <= 1) { pCfg->start_paused = (tmp == 1); valOK = TRUE; } } else if (MATCH(name, "vlan_id")) { errno = 0; long tmp; tmp = strtol(value, &pEnd, 0); // vlanID is 12 bit field if (*pEnd == '\0' && errno == 0 && tmp >= 0x0 && tmp <= 0xFFF) { pCfg->vlan_id = tmp; valOK = TRUE; } } else if (MATCH(name, "fixed_timestamp")) { errno = 0; long tmp; tmp = strtol(value, &pEnd, 0); if (*pEnd == '\0' && errno == 0) { pCfg->fixed_timestamp = tmp; valOK = TRUE; } } else if (MATCH(name, "spin_wait")) { errno = 0; long tmp; tmp = strtol(value, &pEnd, 0); if (*pEnd == '\0' && errno == 0) { pCfg->spin_wait = (tmp == 1); valOK = TRUE; } } else if (MATCH(name, "tx_blocking_in_intf")) { errno = 0; long tmp; tmp = strtol(value, &pEnd, 0); if (*pEnd == '\0' && errno == 0) { pCfg->tx_blocking_in_intf = tmp; valOK = TRUE; } } else if (MATCH(name, "thread_rt_priority")) { errno = 0; long tmp; tmp = strtol(value, &pEnd, 0); if (*pEnd == '\0' && errno == 0) { pCfg->thread_rt_priority = tmp; valOK = TRUE; } } else if (MATCH(name, "thread_affinity")) { errno = 0; unsigned long tmp; tmp = strtoul(value, &pEnd, 0); if (*pEnd == '\0' && errno == 0) { pCfg->thread_affinity = tmp; valOK = TRUE; } } else if (MATCH(name, "friendly_name")) { strncpy(pCfg->friendly_name, value, FRIENDLY_NAME_SIZE - 1); valOK = TRUE; } else if (MATCH(name, "current_sampling_rate")) { errno = 0; pCfg->current_sampling_rate = strtol(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0 && pCfg->current_sampling_rate <= UINT32_MAX) valOK = TRUE; } else if (MATCH(name, "sampling_rates")) { errno = 0; memset(pCfg->sampling_rates,0,sizeof(pCfg->sampling_rates)); char *rate, *copy; copy = strdup(value); rate = strtok(copy,","); int i = 0,break_flag = 0; while (rate != NULL ) { pCfg->sampling_rates[i] = strtol(rate,&pEnd, 10); if (*pEnd != '\0' || errno != 0 || pCfg->sampling_rates[i] > UINT32_MAX) { break_flag = 1; break; } rate = strtok(NULL,","); i++; } if (break_flag != 1) { valOK = TRUE; pCfg->sampling_rates_count = i; } } else if (strcmp(name, "intf_nv_audio_rate") == 0) { long int val = strtol(value, &pEnd, 10); if (val >= AVB_AUDIO_RATE_8KHZ && val <= AVB_AUDIO_RATE_192KHZ) { pCfg->audioRate = val; valOK = TRUE; } else { AVB_LOG_ERROR("Invalid audio rate configured for intf_nv_audio_rate."); pCfg->audioRate = AVB_AUDIO_RATE_44_1KHZ; } } else if (strcmp(name, "intf_nv_audio_bit_depth") == 0) { long int val = strtol(value, &pEnd, 10); if (val >= AVB_AUDIO_BIT_DEPTH_1BIT && val <= AVB_AUDIO_BIT_DEPTH_64BIT) { pCfg->audioBitDepth = val; valOK = TRUE; } else { AVB_LOG_ERROR("Invalid audio type configured for intf_nv_audio_bits."); pCfg->audioBitDepth = AVB_AUDIO_BIT_DEPTH_24BIT; } } else if (strcmp(name, "intf_nv_audio_channels") == 0) { long int val = strtol(value, &pEnd, 10); if (val >= AVB_AUDIO_CHANNELS_1 && val <= AVB_AUDIO_CHANNELS_8) { pCfg->audioChannels = val; valOK = TRUE; } else { AVB_LOG_ERROR("Invalid audio channels configured for intf_nv_audio_channels."); pCfg->audioChannels = AVB_AUDIO_CHANNELS_2; } } else if (MATCH(name, "map_fn")) { errno = 0; memset(pCfg->map_fn,0,sizeof(pCfg->map_fn)); strncpy(pCfg->map_fn,value,sizeof(pCfg->map_fn)-1); valOK = TRUE; } else { // Ignored item. AVB_LOGF_DEBUG("Unhandled configuration item: name=%s, value=%s", name, value); // Don't abort for this item. valOK = TRUE; } if (!valOK) { // bad value AVB_LOGF_ERROR("Invalid value: name=%s, value=%s", name, value); return 0; } AVB_TRACE_EXIT(AVB_TRACE_AVDECC); return 1; // OK }
static inline bool listenerDoStream(tl_state_t *pTLState) { AVB_TRACE_ENTRY(AVB_TRACE_TL); if (!pTLState) { AVB_LOG_ERROR("Invalid TLState"); AVB_TRACE_EXIT(AVB_TRACE_TL); return FALSE; } openavb_tl_cfg_t *pCfg = &pTLState->cfg; listener_data_t *pListenerData = pTLState->pPvtListenerData; bool bRet = FALSE; if (pTLState->bStreaming) { U64 nowNS; pListenerData->nReportCalls++; // Try to receive a frame if (IS_OPENAVB_SUCCESS(openavbAvtpRx(pListenerData->avtpHandle))) { pListenerData->nReportFrames++; } CLOCK_GETTIME64(OPENAVB_TIMER_CLOCK, &nowNS); if (pCfg->report_seconds > 0) { if (nowNS > pListenerData->nextReportNS) { U64 lost = openavbAvtpLost(pListenerData->avtpHandle); U64 bytes = openavbAvtpBytes(pListenerData->avtpHandle); U32 rxbuf = openavbAvtpRxBufferLevel(pListenerData->avtpHandle); U32 mqbuf = openavbMediaQCountItems(pTLState->pMediaQ, TRUE); U32 mqrdy = openavbMediaQCountItems(pTLState->pMediaQ, FALSE); AVB_LOGRT_INFO(LOG_RT_BEGIN, LOG_RT_ITEM, FALSE, "RX UID:%d, ", LOG_RT_DATATYPE_U16, &pListenerData->streamID.uniqueID); AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "calls=%ld, ", LOG_RT_DATATYPE_U32, &pListenerData->nReportCalls); AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "frames=%ld, ", LOG_RT_DATATYPE_U32, &pListenerData->nReportFrames); AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "lost=%lld, ", LOG_RT_DATATYPE_U64, &lost); AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "bytes=%lld, ", LOG_RT_DATATYPE_U64, &bytes); AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "rxbuf=%d, ", LOG_RT_DATATYPE_U32, &rxbuf); AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "mqbuf=%d, ", LOG_RT_DATATYPE_U32, &mqbuf); AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, LOG_RT_END, "mqrdy=%d", LOG_RT_DATATYPE_U32, &mqrdy); openavbListenerAddStat(pTLState, TL_STAT_RX_CALLS, pListenerData->nReportCalls); openavbListenerAddStat(pTLState, TL_STAT_RX_FRAMES, pListenerData->nReportFrames); openavbListenerAddStat(pTLState, TL_STAT_RX_LOST, lost); openavbListenerAddStat(pTLState, TL_STAT_RX_BYTES, bytes); pListenerData->nReportCalls = 0; pListenerData->nReportFrames = 0; pListenerData->nextReportNS += (pCfg->report_seconds * NANOSECONDS_PER_SECOND); } } if (nowNS > pListenerData->nextSecondNS) { pListenerData->nextSecondNS += NANOSECONDS_PER_SECOND; bRet = TRUE; } } else { SLEEP(1); bRet = TRUE; } AVB_TRACE_EXIT(AVB_TRACE_TL); return bRet; }
int avbEndpointLoop(void) { AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); int retVal = -1; openavbRC rc = OPENAVB_SUCCESS; do { if (!x_cfg.bypassAsCapableCheck && (startPTP() < 0)) { // make sure ptp, a separate process, starts and is using the same interface as endpoint AVB_LOG_ERROR("PTP failed to start - Exiting"); break; } else if(x_cfg.bypassAsCapableCheck) { AVB_LOG_WARNING(" "); AVB_LOG_WARNING("Configuration 'gptp_asCapable_not_required = 1' is set."); AVB_LOG_WARNING("This configuration bypasses the requirement for gPTP"); AVB_LOG_WARNING("and openavb_gptp is not started automatically."); AVB_LOG_WARNING("An appropriate ptp MUST be started separately."); AVB_LOG_WARNING("Any network which does not use ptp to synchronize time"); AVB_LOG_WARNING("on each and every network device is NOT an AVB network."); AVB_LOG_WARNING("Such a network WILL NOT FUNCTION PROPERLY."); AVB_LOG_WARNING(" "); } x_streamList = NULL; if (!openavbQmgrInitialize(x_cfg.fqtss_mode, x_cfg.ifindex, x_cfg.ifname, x_cfg.mtu, x_cfg.link_kbit, x_cfg.nsr_kbit)) { AVB_LOG_ERROR("Failed to initialize QMgr"); break; } if (!openavbMaapInitialize(x_cfg.ifname, x_cfg.maapPort, &(x_cfg.maap_preferred), maapRestartCallback)) { AVB_LOG_ERROR("Failed to initialize MAAP"); openavbQmgrFinalize(); break; } if (!openavbShaperInitialize(x_cfg.ifname, x_cfg.shaperPort)) { AVB_LOG_ERROR("Failed to initialize Shaper"); openavbMaapFinalize(); openavbQmgrFinalize(); break; } if(!x_cfg.noSrp) { // Initialize SRP rc = openavbSrpInitialize(strmAttachCb, strmRegCb, x_cfg.ifname, x_cfg.link_kbit, x_cfg.bypassAsCapableCheck); } else { rc = OPENAVB_SUCCESS; AVB_LOG_WARNING(" "); AVB_LOG_WARNING("Configuration 'preconfigured = 1' is set."); AVB_LOG_WARNING("SRP is disabled. Streams MUST be configured manually"); AVB_LOG_WARNING("on each and every device in the network, without exception."); AVB_LOG_WARNING("AN AVB NETWORK WILL NOT FUNCTION AS EXPECTED UNLESS ALL"); AVB_LOG_WARNING("STREAMS ARE PROPERLY CONFIGURED ON ALL NETWORK DEVICES."); AVB_LOG_WARNING(" "); } if (!IS_OPENAVB_SUCCESS(rc)) { AVB_LOG_ERROR("Failed to initialize SRP"); openavbShaperFinalize(); openavbMaapFinalize(); openavbQmgrFinalize(); break; } if (openavbEndpointServerOpen()) { retVal = 0; while (endpointRunning) { openavbEptSrvrService(); } openavbEndpointServerClose(); } if(!x_cfg.noSrp) { // Shutdown SRP openavbSrpShutdown(); } openavbShaperFinalize(); openavbMaapFinalize(); openavbQmgrFinalize(); } while (0); if (!x_cfg.bypassAsCapableCheck && (stopPTP() < 0)) { AVB_LOG_WARNING("Failed to execute PTP stop command: killall -s SIGINT openavb_gptp"); } AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return retVal; }
/* Talker client registers a stream */ bool openavbEptSrvrRegisterStream(int h, AVBStreamID_t *streamID, U8 destAddr[], AVBTSpec_t *tSpec, U8 srClass, U8 srRank, U32 latency) { openavbRC rc = OPENAVB_SUCCESS; AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); clientStream_t *ps = findStream(streamID); if (ps && ps->clientHandle != h) { AVB_LOGF_ERROR("Error registering talker; multiple clients for stream %d", streamID->uniqueID); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return FALSE; } ps = addStream(h, streamID); if (!ps) { AVB_LOGF_ERROR("Error registering talker; unable to add client stream %d", streamID->uniqueID); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return FALSE; } ps->role = clientTalker; ps->tSpec = *tSpec; ps->srClass = (SRClassIdx_t)srClass; ps->srRank = srRank; ps->latency = latency; ps->fwmark = INVALID_FWMARK; if (memcmp(ps->destAddr, destAddr, ETH_ALEN) == 0) { // no client-supplied address, use MAAP struct ether_addr addr; ps->hndMaap = openavbMaapAllocate(1, &addr); if (ps->hndMaap) { memcpy(ps->destAddr, addr.ether_addr_octet, ETH_ALEN); strmAttachCb((void*)ps, openavbSrp_LDSt_Stream_Info); // Inform talker about MAAP } else { AVB_LOG_ERROR("Error registering talker: MAAP failed to allocate MAC address"); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); delStream(ps); return FALSE; } } else { // client-supplied destination MAC address memcpy(ps->destAddr, destAddr, ETH_ALEN); ps->hndMaap = NULL; } // Do SRP talker register AVB_LOGF_DEBUG("REGISTER: ps=%p, streamID=%d, tspec=%d,%d, srClass=%d, srRank=%d, latency=%d, da="ETH_FORMAT"", ps, streamID->uniqueID, tSpec->maxFrameSize, tSpec->maxIntervalFrames, ps->srClass, ps->srRank, ps->latency, ETH_OCTETS(ps->destAddr)); if(x_cfg.noSrp) { // we are operating in a mode supporting preconfigured streams; SRP is not in use, // so, as a proxy for SRP, which would normally make this call after establishing // the stream, call the callback from here strmAttachCb((void*)ps, openavbSrp_LDSt_Ready); } else { // normal SRP operation rc = openavbSrpRegisterStream((void*)ps, &ps->streamID, ps->destAddr, &ps->tSpec, ps->srClass, ps->srRank, ps->latency); if (!IS_OPENAVB_SUCCESS(rc)) { if (ps->hndMaap) openavbMaapRelease(ps->hndMaap); delStream(ps); } } openavbEndPtLogAllStaticStreams(); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return IS_OPENAVB_SUCCESS(rc); }
static int cfgCallback(void *user, const char *section, const char *name, const char *value) { AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); if (!user || !section || !name || !value) { AVB_LOG_ERROR("Config: invalid arguments passed to callback"); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return 0; } openavb_endpoint_cfg_t *pCfg = (openavb_endpoint_cfg_t*)user; AVB_LOGF_DEBUG("name=[%s] value=[%s]", name, value); bool valOK = FALSE; char *pEnd; if (MATCH(section, "network")) { if (MATCH(name, "ifname")) { if_info_t ifinfo; if (openavbCheckInterface(value, &ifinfo)) { strncpy(pCfg->ifname, value, IFNAMSIZ - 1); memcpy(pCfg->ifmac, &ifinfo.mac, ETH_ALEN); pCfg->ifindex = ifinfo.index; pCfg->mtu = ifinfo.mtu; valOK = TRUE; } } else if (MATCH(name, "link_kbit")) { errno = 0; pCfg->link_kbit = strtoul(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0) valOK = TRUE; } else if (MATCH(name, "nsr_kbit")) { errno = 0; pCfg->nsr_kbit = strtoul(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0) valOK = TRUE; } else { // unmatched item, fail AVB_LOGF_ERROR("Unrecognized configuration item: section=%s, name=%s", section, name); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return 0; } } else if (MATCH(section, "ptp")) { if (MATCH(name, "start_options")) { pCfg->ptp_start_opts = strdup(value); valOK = TRUE; } else { // unmatched item, fail AVB_LOGF_ERROR("Unrecognized configuration item: section=%s, name=%s", section, name); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return 0; } } else if (MATCH(section, "fqtss")) { if (MATCH(name, "mode")) { errno = 0; pCfg->fqtss_mode = strtoul(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0) valOK = TRUE; } else { // unmatched item, fail AVB_LOGF_ERROR("Unrecognized configuration item: section=%s, name=%s", section, name); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return 0; } } else if (MATCH(section, "srp")) { if (MATCH(name, "preconfigured")) { errno = 0; unsigned temp = strtoul(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0) { valOK = TRUE; if (temp == 1) pCfg->noSrp = TRUE; else pCfg->noSrp = FALSE; } } else if (MATCH(name, "gptp_asCapable_not_required")) { errno = 0; unsigned temp = strtoul(value, &pEnd, 10); if (*pEnd == '\0' && errno == 0) { valOK = TRUE; if (temp == 1) pCfg->bypassAsCapableCheck = TRUE; else pCfg->bypassAsCapableCheck = FALSE; } } else { // unmatched item, fail AVB_LOGF_ERROR("Unrecognized configuration item: section=%s, name=%s", section, name); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return 0; } } else { // unmatched item, fail AVB_LOGF_ERROR("Unrecognized configuration item: section=%s, name=%s", section, name); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return 0; } if (!valOK) { cfgValErr(section, name, value); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return 0; } AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return 1; // OK }
/* SRP tells us about a listener peer (Listener Ready or Failed) */ openavbRC strmAttachCb(void* pv, openavbSrpLsnrDeclSubtype_t lsnrDecl) { AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); openavbRC rc = OPENAVB_FAILURE; clientStream_t *ps = (clientStream_t*)pv; AVB_LOGF_INFO("SRP talker callback uid=%d: lsnrDecl=%x", ps->streamID.uniqueID, lsnrDecl); if (lsnrDecl == openavbSrp_LDSt_Ready || lsnrDecl == openavbSrp_LDSt_Ready_Failed) { // Somebody is listening - get ready to stream if (ps->fwmark != INVALID_FWMARK) { AVB_LOG_DEBUG("attach callback: already setup queues"); rc = OPENAVB_SUCCESS; } else { AVB_LOG_DEBUG("Attach callback: setting up queues for streaming"); rc = openavbSrpGetClassParams(ps->srClass, &ps->priority, &ps->vlanID, &ps->classRate); if (IS_OPENAVB_SUCCESS(rc)) { ps->fwmark = openavbQmgrAddStream(ps->srClass, ps->classRate, ps->tSpec.maxIntervalFrames, ps->tSpec.maxFrameSize); if (ps->fwmark == INVALID_FWMARK) { AVB_LOG_ERROR("Error in attach callback: unable to setup stream queues"); rc = OPENAVB_FAILURE; } else { rc = OPENAVB_SUCCESS; } } else { AVB_LOG_ERROR("Error in attach callback: unable to get class params"); rc = OPENAVB_FAILURE; } } } else { // Nobody listening if (ps->fwmark != INVALID_FWMARK) { AVB_LOG_DEBUG("Attach callback: tearing down queues"); openavbQmgrRemoveStream(ps->fwmark); ps->fwmark = INVALID_FWMARK; } rc = OPENAVB_SUCCESS; } if (IS_OPENAVB_SUCCESS(rc)) { openavbEptSrvrNotifyTlkrOfSrpCb(ps->clientHandle, &ps->streamID, x_cfg.ifname, ps->destAddr, lsnrDecl, ps->srClass, ps->classRate, ps->vlanID, ps->priority, ps->fwmark); rc = OPENAVB_SUCCESS; } AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return rc; }
// This callback is called when acting as a listener. bool openavbIntfViewerRxCB(media_q_t *pMediaQ) { AVB_TRACE_ENTRY(AVB_TRACE_INTF_DETAIL); if (pMediaQ) { pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; if (!pPvtData) { AVB_LOG_ERROR("Private interface module data not allocated."); return FALSE; } media_q_item_t *pMediaQItem = openavbMediaQTailLock(pMediaQ, pPvtData->ignoreTimestamp); if (pMediaQItem) { // The skip countdown allow the viewer modes to set a number of packets to ignore // after logging to reduce or eliminate the logging from affecting the stats. if (pPvtData->skipCountdown) pPvtData->skipCountdown--; if (pMediaQItem->dataLen && !pPvtData->skipCountdown) { pPvtData->servicedCount++; if (pPvtData->viewType == VIEWER_MODE_DETAIL) { U32 avtpTimestamp; U64 avtpTimestampTime; bool avtpTimestampValid; U32 nowTimestamp; U64 nowTimestampTime; bool nowTimestampValid; U64 nowTime; S32 lateNS = 0; U64 gapNS = 0; avtpTimestamp = openavbAvtpTimeGetAvtpTimestamp(pMediaQItem->pAvtpTime); avtpTimestampTime = openavbAvtpTimeGetAvtpTimeNS(pMediaQItem->pAvtpTime); avtpTimestampValid = openavbAvtpTimeTimestampIsValid(pMediaQItem->pAvtpTime); openavbAvtpTimeSetToWallTime(pMediaQItem->pAvtpTime); nowTimestamp = openavbAvtpTimeGetAvtpTimestamp(pMediaQItem->pAvtpTime); nowTimestampTime = openavbAvtpTimeGetAvtpTimeNS(pMediaQItem->pAvtpTime); nowTimestampValid = openavbAvtpTimeTimestampIsValid(pMediaQItem->pAvtpTime); CLOCK_GETTIME64(OPENAVB_CLOCK_REALTIME, &nowTime); if (avtpTimestampValid && nowTimestampValid) { lateNS = nowTimestampTime - avtpTimestampTime; if (lateNS > pPvtData->maxLateNS) { pPvtData->maxLateNS = lateNS; } pPvtData->accumLateNS += lateNS; if (pPvtData->servicedCount > 1) { gapNS = nowTime - pPvtData->prevNowTime; if (gapNS > pPvtData->maxGapNS) { pPvtData->maxGapNS = gapNS; } pPvtData->accumGapNS += gapNS; } pPvtData->prevNowTime = nowTime; if ((pPvtData->servicedCount % pPvtData->viewInterval) == 0) { S32 lateAvg = pPvtData->accumLateNS / pPvtData->servicedCount; S32 gapAvg = pPvtData->accumGapNS / (pPvtData->servicedCount - 1); AVB_LOGRT_INFO(LOG_RT_BEGIN, LOG_RT_ITEM, LOG_RT_END, "****************************", LOG_RT_DATATYPE_CONST_STR, NULL); AVB_LOGRT_INFO(LOG_RT_BEGIN, LOG_RT_ITEM, LOG_RT_END, "Packets: %u", LOG_RT_DATATYPE_U32, &pPvtData->servicedCount); AVB_LOGRT_INFO(LOG_RT_BEGIN, LOG_RT_ITEM, LOG_RT_END, "AVTP Timestamp: %u NS", LOG_RT_DATATYPE_U32, &avtpTimestamp); AVB_LOGRT_INFO(LOG_RT_BEGIN, LOG_RT_ITEM, LOG_RT_END, "Now Timestamp: %u NS", LOG_RT_DATATYPE_U32, &nowTimestamp); AVB_LOGRT_INFO(LOG_RT_BEGIN, LOG_RT_ITEM, LOG_RT_END, "Late: %d NS", LOG_RT_DATATYPE_S32, &lateNS); AVB_LOGRT_INFO(LOG_RT_BEGIN, LOG_RT_ITEM, LOG_RT_END, "Late Avg: %d NS", LOG_RT_DATATYPE_S32, &lateAvg); AVB_LOGRT_INFO(LOG_RT_BEGIN, LOG_RT_ITEM, LOG_RT_END, "Late Max: %d NS", LOG_RT_DATATYPE_S32, &pPvtData->maxLateNS); AVB_LOGRT_INFO(LOG_RT_BEGIN, LOG_RT_ITEM, LOG_RT_END, "Gap: %u NS", LOG_RT_DATATYPE_U32, &gapNS); AVB_LOGRT_INFO(LOG_RT_BEGIN, LOG_RT_ITEM, LOG_RT_END, "Gap Avg: %u NS", LOG_RT_DATATYPE_U32, &gapAvg); AVB_LOGRT_INFO(LOG_RT_BEGIN, LOG_RT_ITEM, LOG_RT_END, "Gap Max: %u NS", LOG_RT_DATATYPE_U32, &pPvtData->maxGapNS); AVB_LOGRT_INFO(LOG_RT_BEGIN, LOG_RT_ITEM, LOG_RT_END, "Data length: %u", LOG_RT_DATATYPE_U32, &pMediaQItem->dataLen); pPvtData->accumLateNS = 0; pPvtData->maxLateNS = 0; pPvtData->accumGapNS = 0; pPvtData->maxGapNS = 0; pPvtData->prevNowTime = 0; pPvtData->servicedCount = 0; pPvtData->skipCountdown = 10; } } } else if (pPvtData->viewType == VIEWER_MODE_MAPPING_AWARE) { } else if (pPvtData->viewType == VIEWER_MODE_AVTP_TIMESTAMP) { U64 avtpTimestampTime; bool avtpTimestampValid; S32 deltaNS = 0; avtpTimestampTime = openavbAvtpTimeGetAvtpTimeNS(pMediaQItem->pAvtpTime); avtpTimestampValid = openavbAvtpTimeTimestampIsValid(pMediaQItem->pAvtpTime); if (avtpTimestampValid) { if (pPvtData->servicedCount > 1) { deltaNS = avtpTimestampTime - pPvtData->prevAvtpTimestampTime; if (deltaNS > pPvtData->maxAvtpDeltaNS) { pPvtData->maxAvtpDeltaNS = deltaNS; } pPvtData->accumAvtpDeltaNS += deltaNS; if (pPvtData->avgForJitter != 0) { S32 deltaJitter = pPvtData->avgForJitter - deltaNS; if (deltaJitter < 0) deltaJitter = -deltaJitter; pPvtData->jitter += (1.0/16.0) * ((float)deltaJitter - pPvtData->jitter); } } pPvtData->prevAvtpTimestampTime = avtpTimestampTime; if ((pPvtData->servicedCount % pPvtData->viewInterval) == 0) { S32 deltaAvg = pPvtData->accumAvtpDeltaNS / (pPvtData->servicedCount - 1); U32 jitter = (U32)(pPvtData->jitter); AVB_LOGRT_INFO(LOG_RT_BEGIN, LOG_RT_ITEM, FALSE, "AVTP Timestamp Delta: %d NS ", LOG_RT_DATATYPE_S32, &deltaNS); AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "AVTP Timestamp Delta Avg: %d NS ", LOG_RT_DATATYPE_S32, &deltaAvg); AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "AVTP Timestamp Delta Max: %d NS ", LOG_RT_DATATYPE_S32, &pPvtData->maxAvtpDeltaNS); AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, LOG_RT_END, "Jitter: %d", LOG_RT_DATATYPE_S32, &jitter); pPvtData->accumAvtpDeltaNS = 0; pPvtData->maxAvtpDeltaNS = 0; pPvtData->servicedCount = 0; pPvtData->prevAvtpTimestampTime = 0; pPvtData->skipCountdown = 10; pPvtData->jitter = 0.0; pPvtData->avgForJitter = deltaAvg; } } } else if (pPvtData->viewType == VIEWER_MODE_LATENCY) { U64 avtpTimestampTime; bool avtpTimestampValid; U64 nowTimestampTime; bool nowTimestampValid; S32 lateNS = 0; avtpTimestampTime = openavbAvtpTimeGetAvtpTimeNS(pMediaQItem->pAvtpTime); avtpTimestampValid = openavbAvtpTimeTimestampIsValid(pMediaQItem->pAvtpTime); openavbAvtpTimeSetToWallTime(pMediaQItem->pAvtpTime); nowTimestampTime = openavbAvtpTimeGetAvtpTimeNS(pMediaQItem->pAvtpTime); nowTimestampValid = openavbAvtpTimeTimestampIsValid(pMediaQItem->pAvtpTime); if (avtpTimestampValid && nowTimestampValid) { lateNS = nowTimestampTime - avtpTimestampTime; if (lateNS > pPvtData->maxLateNS) { pPvtData->maxLateNS = lateNS; } pPvtData->accumLateNS += lateNS; if (pPvtData->avgForJitter != 0) { S32 lateJitter = pPvtData->avgForJitter - lateNS; if (lateJitter < 0) lateJitter = -lateJitter; pPvtData->jitter += (1.0/16.0) * ((float)lateJitter - pPvtData->jitter); } if ((pPvtData->servicedCount % pPvtData->viewInterval) == 0) { S32 lateAvg = pPvtData->accumLateNS / pPvtData->servicedCount; U32 jitter = (U32)(pPvtData->jitter); AVB_LOGRT_INFO(LOG_RT_BEGIN, LOG_RT_ITEM, FALSE, "Latency: %d NS ", LOG_RT_DATATYPE_S32, &lateNS); AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "Latency Avg: %d NS ", LOG_RT_DATATYPE_S32, &lateAvg); AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "Latency Max: %d NS ", LOG_RT_DATATYPE_S32, &pPvtData->maxLateNS); AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, LOG_RT_END, "Jitter: %d", LOG_RT_DATATYPE_S32, &jitter); pPvtData->accumLateNS = 0; pPvtData->maxLateNS = 0; pPvtData->servicedCount = 0; pPvtData->skipCountdown = 10; pPvtData->jitter = 0.0; pPvtData->avgForJitter = lateAvg; } } } else if (pPvtData->viewType == VIEWER_MODE_SELECTIVE_TIMESTAMP) { } else if (pPvtData->viewType == VIEWER_MODE_LATE) { U64 avtpTimestampTime; bool avtpTimestampValid; U64 nowTimestampTime; bool nowTimestampValid; S32 lateNS = 0; avtpTimestampTime = openavbAvtpTimeGetAvtpTimeNS(pMediaQItem->pAvtpTime); avtpTimestampValid = openavbAvtpTimeTimestampIsValid(pMediaQItem->pAvtpTime); openavbAvtpTimeSetToWallTime(pMediaQItem->pAvtpTime); nowTimestampTime = openavbAvtpTimeGetAvtpTimeNS(pMediaQItem->pAvtpTime); nowTimestampValid = openavbAvtpTimeTimestampIsValid(pMediaQItem->pAvtpTime); if (avtpTimestampValid && nowTimestampValid) { lateNS = nowTimestampTime - avtpTimestampTime; if (lateNS > pPvtData->maxLateNS) { pPvtData->maxLateNS = lateNS; } pPvtData->accumLateNS += lateNS; if (pPvtData->avgForJitter != 0) { S32 lateJitter = pPvtData->avgForJitter - lateNS; if (lateJitter < 0) lateJitter = -lateJitter; pPvtData->jitter += (1.0/16.0) * ((float)lateJitter - pPvtData->jitter); } if ((pPvtData->servicedCount % pPvtData->viewInterval) == 0) { S32 lateAvg = pPvtData->accumLateNS / pPvtData->servicedCount; U32 jitter = (U32)(pPvtData->jitter); AVB_LOGRT_INFO(LOG_RT_BEGIN, LOG_RT_ITEM, FALSE, "Late: %d NS ", LOG_RT_DATATYPE_S32, &lateNS); AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "Late Avg: %d NS ", LOG_RT_DATATYPE_S32, &lateAvg); AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "Late Max: %d NS ", LOG_RT_DATATYPE_S32, &pPvtData->maxLateNS); AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, LOG_RT_END, "Jitter: %d", LOG_RT_DATATYPE_S32, &jitter); pPvtData->accumLateNS = 0; pPvtData->maxLateNS = 0; pPvtData->servicedCount = 0; pPvtData->skipCountdown = 10; pPvtData->jitter = 0.0; pPvtData->avgForJitter = lateAvg; } } } else if (pPvtData->viewType == VIEWER_MODE_GAP) { U64 nowTime; U64 gapNS = 0; CLOCK_GETTIME64(OPENAVB_CLOCK_REALTIME, &nowTime); if (pPvtData->servicedCount > 1) { gapNS = nowTime - pPvtData->prevNowTime; if (gapNS > pPvtData->maxGapNS) { pPvtData->maxGapNS = gapNS; } pPvtData->accumGapNS += gapNS; if (pPvtData->avgForJitter != 0) { S32 gapJitter = pPvtData->avgForJitter - gapNS; if (gapJitter < 0) gapJitter = -gapJitter; pPvtData->jitter += (1.0/16.0) * ((float)gapJitter - pPvtData->jitter); } } pPvtData->prevNowTime = nowTime; if ((pPvtData->servicedCount % pPvtData->viewInterval) == 0) { S32 gapAvg = pPvtData->accumGapNS / (pPvtData->servicedCount - 1); U32 jitter = (U32)(pPvtData->jitter); AVB_LOGRT_INFO(LOG_RT_BEGIN, LOG_RT_ITEM, FALSE, "Gap: %d NS ", LOG_RT_DATATYPE_S32, &gapNS); AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "Gap Avg: %d NS ", LOG_RT_DATATYPE_S32, &gapAvg); AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "Gap Max: %d NS ", LOG_RT_DATATYPE_S32, &pPvtData->maxGapNS); AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, LOG_RT_END, "Jitter: %d", LOG_RT_DATATYPE_S32, &jitter); pPvtData->accumGapNS = 0; pPvtData->maxGapNS = 0; pPvtData->prevNowTime = 0; pPvtData->servicedCount = 0; pPvtData->skipCountdown = 10; pPvtData->jitter = 0.0; pPvtData->avgForJitter = gapAvg; } } } openavbMediaQTailPull(pMediaQ); } } AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); return TRUE; }
bool talkerStartStream(tl_state_t *pTLState) { AVB_TRACE_ENTRY(AVB_TRACE_TL); if (!pTLState) { AVB_LOG_ERROR("Invalid TLState"); AVB_TRACE_EXIT(AVB_TRACE_TL); return FALSE; } openavb_tl_cfg_t *pCfg = &pTLState->cfg; talker_data_t *pTalkerData = pTLState->pPvtTalkerData; assert(!pTLState->bStreaming); pTalkerData->wakeFrames = pCfg->max_interval_frames * pCfg->batch_factor; // Set a max_transmit_deficit_usec default if (pCfg->max_transmit_deficit_usec == 0) pCfg->max_transmit_deficit_usec = 50000; openavbRC rc = openavbAvtpTxInit(pTLState->pMediaQ, &pCfg->map_cb, &pCfg->intf_cb, pTalkerData->ifname, &pTalkerData->streamID, pTalkerData->destAddr, pCfg->max_transit_usec, pTalkerData->fwmark, pTalkerData->vlanID, pTalkerData->vlanPCP, pTalkerData->wakeFrames * pCfg->raw_tx_buffers, &pTalkerData->avtpHandle); if (IS_OPENAVB_FAILURE(rc)) { AVB_LOG_ERROR("Failed to create AVTP stream"); AVB_TRACE_EXIT(AVB_TRACE_TL); return FALSE; } avtp_stream_t *pStream = (avtp_stream_t *)(pTalkerData->avtpHandle); if (!pStream->pMapCB->map_transmit_interval_cb(pTLState->pMediaQ)) { pTalkerData->wakeRate = pTalkerData->classRate / pCfg->batch_factor; } else { // Override the class observation interval with the one provided by the mapping module. pTalkerData->wakeRate = pStream->pMapCB->map_transmit_interval_cb(pTLState->pMediaQ) / pCfg->batch_factor; } pTalkerData->sleepUsec = MICROSECONDS_PER_SECOND / pTalkerData->wakeRate; pTalkerData->intervalNS = NANOSECONDS_PER_SECOND / pTalkerData->wakeRate; U32 SRKbps = ((unsigned long)pTalkerData->classRate * (unsigned long)pCfg->max_interval_frames * (unsigned long)pStream->frameLen * 8L) / 1000; U32 DataKbps = ((unsigned long)pTalkerData->wakeRate * (unsigned long)pCfg->max_interval_frames * (unsigned long)pStream->frameLen * 8L) / 1000; AVB_LOGF_INFO(STREAMID_FORMAT", sr-rate=%lu, data-rate=%lu, frames=%u, size=%u, batch=%u, sleep=%" PRId64 ", sr-Kbps=%d, data-Kbps=%d", STREAMID_ARGS(&pTalkerData->streamID), (unsigned long)(pTalkerData->classRate), (unsigned long)(pTalkerData->wakeRate), pTalkerData->tSpec.maxIntervalFrames, pTalkerData->tSpec.maxFrameSize, pCfg->batch_factor, pTalkerData->intervalNS / 1000, SRKbps, DataKbps); // number of intervals per report pTalkerData->wakesPerReport = pCfg->report_seconds * NANOSECONDS_PER_SECOND / pTalkerData->intervalNS; // counts of intervals and frames between reports pTalkerData->cntFrames = 0; pTalkerData->cntWakes = 0; // setup the initial times U64 nowNS; CLOCK_GETTIME64(OPENAVB_TIMER_CLOCK, &nowNS); // Align clock : allows for some performance gain nowNS = ((nowNS + (pTalkerData->intervalNS)) / pTalkerData->intervalNS) * pTalkerData->intervalNS; pTalkerData->nextReportNS = nowNS + (pCfg->report_seconds * NANOSECONDS_PER_SECOND); pTalkerData->nextSecondNS = nowNS + NANOSECONDS_PER_SECOND; pTalkerData->nextCycleNS = nowNS + pTalkerData->intervalNS; // Clear stats openavbTalkerClearStats(pTLState); // we're good to go! pTLState->bStreaming = TRUE; AVB_TRACE_EXIT(AVB_TRACE_TL); return TRUE; }
// Called from openavbTLThreadFn() which is started from openavbTLRun() void openavbTLRunTalker(tl_state_t *pTLState) { AVB_TRACE_ENTRY(AVB_TRACE_TL); if (!pTLState) { AVB_LOG_ERROR("Invalid TLState"); AVB_TRACE_EXIT(AVB_TRACE_TL); return; } pTLState->pPvtTalkerData = calloc(1, sizeof(talker_data_t)); if (!pTLState->pPvtTalkerData) { AVB_LOG_WARNING("Failed to allocate talker data."); return; } // Create Stats Mutex { MUTEX_ATTR_HANDLE(mta); MUTEX_ATTR_INIT(mta); MUTEX_ATTR_SET_TYPE(mta, MUTEX_ATTR_TYPE_DEFAULT); MUTEX_ATTR_SET_NAME(mta, "TLStatsMutex"); MUTEX_CREATE_ERR(); MUTEX_CREATE(pTLState->statsMutex, mta); MUTEX_LOG_ERR("Could not create/initialize 'TLStatsMutex' mutex"); } /* If using endpoint register talker, else register with tpsec */ pTLState->bConnected = openavbTLRunTalkerInit(pTLState); if (pTLState->bConnected) { bool bServiceIPC; // Do until we are stopped or loose connection to endpoint while (pTLState->bRunning && pTLState->bConnected) { // Talk (or just sleep if not streaming.) bServiceIPC = talkerDoStream(pTLState); // TalkerDoStream() returns TRUE once per second, // so that we can service our IPC at that low rate. if (bServiceIPC) { // Look for messages from endpoint. Don't block (timeout=0) if (!openavbEptClntService(pTLState->endpointHandle, 0)) { AVB_LOGF_WARNING("Lost connection to endpoint, will retry "STREAMID_FORMAT, STREAMID_ARGS(&(((talker_data_t *)pTLState->pPvtTalkerData)->streamID))); pTLState->bConnected = FALSE; pTLState->endpointHandle = 0; } } } // Stop streaming talkerStopStream(pTLState); { MUTEX_CREATE_ERR(); MUTEX_DESTROY(pTLState->statsMutex); // Destroy Stats Mutex MUTEX_LOG_ERR("Error destroying mutex"); } // withdraw our talker registration if (pTLState->bConnected) openavbEptClntStopStream(pTLState->endpointHandle, &(((talker_data_t *)pTLState->pPvtTalkerData)->streamID)); openavbTLRunTalkerFinish(pTLState); } else { AVB_LOGF_WARNING("Failed to connect to endpoint"STREAMID_FORMAT, STREAMID_ARGS(&(((talker_data_t *)pTLState->pPvtTalkerData)->streamID))); } if (pTLState->pPvtTalkerData) { free(pTLState->pPvtTalkerData); pTLState->pPvtTalkerData = NULL; } AVB_TRACE_EXIT(AVB_TRACE_TL); }
static inline bool talkerDoStream(tl_state_t *pTLState) { AVB_TRACE_ENTRY(AVB_TRACE_TL); if (!pTLState) { AVB_LOG_ERROR("Invalid TLState"); AVB_TRACE_EXIT(AVB_TRACE_TL); return FALSE; } openavb_tl_cfg_t *pCfg = &pTLState->cfg; talker_data_t *pTalkerData = pTLState->pPvtTalkerData; bool bRet = FALSE; if (pTLState->bStreaming) { U64 nowNS; if (!pCfg->tx_blocking_in_intf) { // sleep until the next interval SLEEP_UNTIL_NSEC(pTalkerData->nextCycleNS); //AVB_DBG_INTERVAL(8000, TRUE); // send the frames for this interval int i; for (i = pTalkerData->wakeFrames; i > 0; i--) { if (IS_OPENAVB_SUCCESS(openavbAvtpTx(pTalkerData->avtpHandle, i == 1, pCfg->tx_blocking_in_intf))) pTalkerData->cntFrames++; else break; } } else { // Interface module block option if (IS_OPENAVB_SUCCESS(openavbAvtpTx(pTalkerData->avtpHandle, TRUE, pCfg->tx_blocking_in_intf))) pTalkerData->cntFrames++; } if (pTalkerData->cntWakes++ % pTalkerData->wakeRate == 0) { // time to service the endpoint IPC bRet = TRUE; } CLOCK_GETTIME64(OPENAVB_TIMER_CLOCK, &nowNS); if (pCfg->report_seconds > 0) { if (nowNS > pTalkerData->nextReportNS) { S32 late = pTalkerData->wakesPerReport - pTalkerData->cntWakes; U64 bytes = openavbAvtpBytes(pTalkerData->avtpHandle); if (late < 0) late = 0; U32 txbuf = openavbAvtpTxBufferLevel(pTalkerData->avtpHandle); U32 mqbuf = openavbMediaQCountItems(pTLState->pMediaQ, TRUE); AVB_LOGRT_INFO(LOG_RT_BEGIN, LOG_RT_ITEM, FALSE, "TX UID:%d, ", LOG_RT_DATATYPE_U16, &pTalkerData->streamID.uniqueID); AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "calls=%ld, ", LOG_RT_DATATYPE_U32, &pTalkerData->cntWakes); AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "frames=%ld, ", LOG_RT_DATATYPE_U32, &pTalkerData->cntFrames); AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "late=%d, ", LOG_RT_DATATYPE_U32, &late); AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "bytes=%lld, ", LOG_RT_DATATYPE_U64, &bytes); AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, FALSE, "txbuf=%d, ", LOG_RT_DATATYPE_U32, &txbuf); AVB_LOGRT_INFO(FALSE, LOG_RT_ITEM, LOG_RT_END, "mqbuf=%d, ", LOG_RT_DATATYPE_U32, &mqbuf); openavbTalkerAddStat(pTLState, TL_STAT_TX_CALLS, pTalkerData->cntWakes); openavbTalkerAddStat(pTLState, TL_STAT_TX_FRAMES, pTalkerData->cntFrames); openavbTalkerAddStat(pTLState, TL_STAT_TX_LATE, late); openavbTalkerAddStat(pTLState, TL_STAT_TX_BYTES, bytes); pTalkerData->cntFrames = 0; pTalkerData->cntWakes = 0; pTalkerData->nextReportNS = nowNS + (pCfg->report_seconds * NANOSECONDS_PER_SECOND); } } if (nowNS > pTalkerData->nextSecondNS) { pTalkerData->nextSecondNS += NANOSECONDS_PER_SECOND; bRet = TRUE; } if (!pCfg->tx_blocking_in_intf) { pTalkerData->nextCycleNS += pTalkerData->intervalNS; if ((pTalkerData->nextCycleNS + (pCfg->max_transmit_deficit_usec * 1000)) < nowNS) { // Hit max deficit time. Something must be wrong. Reset the cycle timer. // Align clock : allows for some performance gain nowNS = ((nowNS + (pTalkerData->intervalNS)) / pTalkerData->intervalNS) * pTalkerData->intervalNS; pTalkerData->nextCycleNS = nowNS + pTalkerData->intervalNS; } } } else { SLEEP(1); // time to service the endpoint IPC bRet = TRUE; } AVB_TRACE_EXIT(AVB_TRACE_TL); return bRet; }
/********************************************** * main */ int main(int argc, char *argv[]) { AVB_TRACE_ENTRY(AVB_TRACE_HOST); int iniIdx = 0; char *programName; char *optIfnameGlobal = NULL; programName = strrchr(argv[0], '/'); programName = programName ? programName + 1 : argv[0]; if (argc < 2) { openavbTlHostUsage(programName); exit(-1); } tl_handle_t *tlHandleList = NULL; int i1; // Process command line bool optDone = FALSE; while (!optDone) { int opt = getopt(argc, argv, "hI:"); if (opt != EOF) { switch (opt) { case 'I': optIfnameGlobal = strdup(optarg); break; case 'h': default: openavbTlHostUsage(programName); exit(-1); } } else { optDone = TRUE; } } osalAVBInitialize(optIfnameGlobal); iniIdx = optind; U32 tlCount = argc - iniIdx; if (!openavbTLInitialize(tlCount)) { AVB_LOG_ERROR("Unable to initialize talker listener library"); osalAVBFinalize(); exit(-1); } // Setup signal handler // We catch SIGINT and shutdown cleanly bool err; struct sigaction sa; sa.sa_handler = openavbTLSigHandler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; err = sigaction(SIGINT, &sa, NULL); if (err) { AVB_LOG_ERROR("Failed to setup SIGINT handler"); osalAVBFinalize(); exit(-1); } err = sigaction(SIGUSR1, &sa, NULL); if (err) { AVB_LOG_ERROR("Failed to setup SIGUSR1 handler"); osalAVBFinalize(); exit(-1); } registerStaticMapModule(openavbMapPipeInitialize); registerStaticMapModule(openavbMapAVTPAudioInitialize); registerStaticMapModule(openavbMapCtrlInitialize); registerStaticMapModule(openavbMapH264Initialize); registerStaticMapModule(openavbMapMjpegInitialize); registerStaticMapModule(openavbMapMpeg2tsInitialize); registerStaticMapModule(openavbMapNullInitialize); registerStaticMapModule(openavbMapUncmpAudioInitialize); registerStaticIntfModule(openavbIntfEchoInitialize); registerStaticIntfModule(openavbIntfCtrlInitialize); registerStaticIntfModule(openavbIntfLoggerInitialize); registerStaticIntfModule(openavbIntfNullInitialize); //registerStaticIntfModule(openavbIntfToneGenInitialize); registerStaticIntfModule(openavbIntfViewerInitialize); registerStaticIntfModule(openavbIntfAlsaInitialize); registerStaticIntfModule(openavbIntfMjpegGstInitialize); registerStaticIntfModule(openavbIntfMpeg2tsFileInitialize); registerStaticIntfModule(openavbIntfMpeg2tsGstInitialize); registerStaticIntfModule(openavbIntfWavFileInitialize); registerStaticIntfModule(openavbIntfH264RtpGstInitialize); tlHandleList = calloc(1, sizeof(tl_handle_t) * tlCount); // Open all streams for (i1 = 0; i1 < tlCount; i1++) { tlHandleList[i1] = openavbTLOpen(); } // Parse ini and configure all streams for (i1 = 0; i1 < tlCount; i1++) { openavb_tl_cfg_t cfg; openavb_tl_cfg_name_value_t NVCfg; char iniFile[1024]; snprintf(iniFile, sizeof(iniFile), "%s", argv[i1 + iniIdx]); if (optIfnameGlobal && !strcasestr(iniFile, ",ifname=")) { snprintf(iniFile + strlen(iniFile), sizeof(iniFile), ",ifname=%s", optIfnameGlobal); } openavbTLInitCfg(&cfg); memset(&NVCfg, 0, sizeof(NVCfg)); if (!openavbTLReadIniFileOsal(tlHandleList[i1], iniFile, &cfg, &NVCfg)) { AVB_LOGF_ERROR("Error reading ini file: %s\n", argv[i1 + 1]); osalAVBFinalize(); exit(-1); } if (!openavbTLConfigure(tlHandleList[i1], &cfg, &NVCfg)) { AVB_LOGF_ERROR("Error configuring: %s\n", argv[i1 + 1]); osalAVBFinalize(); exit(-1); } int i2; for (i2 = 0; i2 < NVCfg.nLibCfgItems; i2++) { free(NVCfg.libCfgNames[i2]); free(NVCfg.libCfgValues[i2]); } } #ifdef AVB_FEATURE_GSTREAMER // If we're supporting the interface modules which use GStreamer, // initialize GStreamer here to avoid errors. gst_init(0, NULL); #endif for (i1 = 0; i1 < tlCount; i1++) { openavbTLRun(tlHandleList[i1]); } while (bRunning) { sleep(1); } for (i1 = 0; i1 < tlCount; i1++) { openavbTLStop(tlHandleList[i1]); } for (i1 = 0; i1 < tlCount; i1++) { openavbTLClose(tlHandleList[i1]); } openavbTLCleanup(); #ifdef AVB_FEATURE_GSTREAMER // If we're supporting the interface modules which use GStreamer, // De-initialize GStreamer to clean up resources. gst_deinit(); #endif osalAVBFinalize(); AVB_TRACE_EXIT(AVB_TRACE_HOST); exit(0); }