/* Log information on all statically configured streams. * (Dynamically configured streams are logged by SRP.) */ void openavbEndPtLogAllStaticStreams(void) { bool hdrDone = FALSE; if(x_cfg.noSrp) { clientStream_t **lpp; for(lpp = &x_streamList; *lpp != NULL; lpp = &(*lpp)->next) { if ((*lpp)->clientHandle != AVB_ENDPOINT_HANDLE_INVALID) { if (!hdrDone) { AVB_LOG_INFO("Statically Configured Streams:"); AVB_LOG_INFO(" | SR | Destination | ----Max Frame(s)--- |"); AVB_LOG_INFO(" Role | Stream Id | Class | Address | Size | Per Interval |"); hdrDone = TRUE; } if ((*lpp)->role == clientTalker) { AVB_LOGF_INFO(" Talker | %02x:%02x:%02x:%02x:%02x:%02x - %4d | %c | %02x:%02x:%02x:%02x:%02x:%02x | %4u | %2u |", (*lpp)->streamID.addr[0], (*lpp)->streamID.addr[1], (*lpp)->streamID.addr[2], (*lpp)->streamID.addr[3], (*lpp)->streamID.addr[4], (*lpp)->streamID.addr[5], (*lpp)->streamID.uniqueID, AVB_CLASS_LABEL((*lpp)->srClass), (*lpp)->destAddr[0], (*lpp)->destAddr[1], (*lpp)->destAddr[2], (*lpp)->destAddr[3], (*lpp)->destAddr[4], (*lpp)->destAddr[5], (*lpp)->tSpec.maxFrameSize, (*lpp)->tSpec.maxIntervalFrames ); } else if ((*lpp)->role == clientListener) { AVB_LOGF_INFO(" Listener | %02x:%02x:%02x:%02x:%02x:%02x - %4d | - | --:--:--:--:--:-- | -- | -- |", (*lpp)->streamID.addr[0], (*lpp)->streamID.addr[1], (*lpp)->streamID.addr[2], (*lpp)->streamID.addr[3], (*lpp)->streamID.addr[4], (*lpp)->streamID.addr[5], (*lpp)->streamID.uniqueID ); } } } } }
/* SRP tells us about talker peer (Talker Registration or De-registration) */ openavbRC strmRegCb(void *pv, openavbSrpAttribType_t tlkrDecl, U8 destAddr[], AVBTSpec_t *tSpec, SRClassIdx_t srClass, U32 accumLatency, openavbSrpFailInfo_t *failInfo) { AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); clientStream_t *ps = (clientStream_t*)pv; AVB_LOGF_INFO("SRP listener callback uid=%d: tlkrDecl=%x", ps->streamID.uniqueID, tlkrDecl); openavbEptSrvrNotifyLstnrOfSrpCb(ps->clientHandle, &ps->streamID, x_cfg.ifname, destAddr, tlkrDecl, tSpec, srClass, accumLatency, failInfo); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return OPENAVB_SUCCESS; }
bool openavbTLRunListenerInit(int hnd, AVBStreamID_t *streamID) { tl_state_t *pTLState = TLHandleListGet(hnd); openavb_tl_cfg_t *pCfg = &pTLState->cfg; listener_data_t *pListenerData = pTLState->pPvtListenerData; strncpy(pListenerData->ifname, pCfg->ifname, IFNAMSIZ); memcpy(&pListenerData->streamID.addr, &pCfg->stream_addr.mac->ether_addr_octet, ETH_ALEN); pListenerData->streamID.uniqueID = pCfg->stream_uid; memcpy(&pListenerData->destAddr, &pCfg->dest_addr.mac->ether_addr_octet, ETH_ALEN); pListenerData->tSpec.maxIntervalFrames = pCfg->max_interval_frames; pListenerData->tSpec.maxFrameSize = pCfg->max_frame_size; AVB_LOGF_INFO("Dest Addr: "ETH_FORMAT, ETH_OCTETS(pListenerData->destAddr)); AVB_LOGF_INFO("Starting stream: "STREAMID_FORMAT, STREAMID_ARGS(streamID)); listenerStartStream(pTLState); return TRUE; }
bool openavbTLRunTalkerInit(tl_state_t *pTLState) { openavb_tl_cfg_t *pCfg = &pTLState->cfg; talker_data_t *pTalkerData = pTLState->pPvtTalkerData; AVBStreamID_t streamID; memset(&streamID, 0, sizeof(AVBStreamID_t)); if (pCfg->stream_addr.mac) memcpy(streamID.addr, pCfg->stream_addr.mac, ETH_ALEN); streamID.uniqueID = pCfg->stream_uid; unsigned int maxBitrate = 0; if (pCfg->intf_cb.intf_get_src_bitrate_cb != NULL) { maxBitrate = pCfg->intf_cb.intf_get_src_bitrate_cb(pTLState->pMediaQ); } if (maxBitrate > 0) { if (pCfg->map_cb.map_set_src_bitrate_cb != NULL) { pCfg->map_cb.map_set_src_bitrate_cb(pTLState->pMediaQ, maxBitrate); } if (pCfg->map_cb.map_get_max_interval_frames_cb != NULL) { unsigned int map_intv_frames = pCfg->map_cb.map_get_max_interval_frames_cb(pTLState->pMediaQ, pTLState->cfg.sr_class); pCfg->max_interval_frames = map_intv_frames > 0 ? map_intv_frames : pCfg->max_interval_frames; } } pTalkerData->tSpec.maxIntervalFrames = pCfg->max_interval_frames; // The TSpec frame size is the L2 payload - i.e. no Ethernet headers, VLAN, FCS, etc... pTalkerData->tSpec.maxFrameSize = pCfg->map_cb.map_max_data_size_cb(pTLState->pMediaQ); AVB_LOGF_INFO("Register "STREAMID_FORMAT": class: %c frame size: %d frame interval: %d", STREAMID_ARGS(&streamID), AVB_CLASS_LABEL(pCfg->sr_class), pTalkerData->tSpec.maxFrameSize, pTalkerData->tSpec.maxIntervalFrames); // Tell endpoint to register our stream. // SRP will send out talker declarations on the LAN. // If there are listeners, we'll get callback (above.) U32 transmitInterval = pTalkerData->classRate; if (pCfg->map_cb.map_transmit_interval_cb(pTLState->pMediaQ)) { // Override the class observation interval with the one provided by the mapping module. transmitInterval = pCfg->map_cb.map_transmit_interval_cb(pTLState->pMediaQ); } return (openavbEptClntRegisterStream(pTLState->endpointHandle, &streamID, pCfg->dest_addr.mac->ether_addr_octet, pCfg->backup_dest_addr_valid, // If we have a backup dest_addr, then the current one was forced and MAAP should not be used. &pTalkerData->tSpec, pCfg->sr_class, pCfg->sr_rank, pCfg->internal_latency, transmitInterval)); }
static bool openMapLib(tl_state_t *pTLState) { // OpenAVB using static mapping plugins therefore don't attempt to open a library #if 0 // Opening library if (pTLState->mapLib.libName) { AVB_LOGF_INFO("Attempting to open library: %s", pTLState->mapLib.libName); pTLState->mapLib.libHandle = dlopen(pTLState->mapLib.libName, RTLD_LAZY); if (!pTLState->mapLib.libHandle) { AVB_LOG_ERROR("Unable to open the mapping library."); return FALSE; } } #endif // Looking up function entry if (!pTLState->mapLib.funcName) { AVB_LOG_ERROR("Mapping initialize function not set."); return FALSE; } char *error; AVB_LOGF_INFO("Looking up symbol for function: %s", pTLState->mapLib.funcName); if (pTLState->mapLib.libHandle) { pTLState->cfg.pMapInitFn = dlsym(pTLState->mapLib.libHandle, pTLState->mapLib.funcName); } else { pTLState->cfg.pMapInitFn = dlsym(RTLD_DEFAULT, pTLState->mapLib.funcName); } if ((error = dlerror()) != NULL) { AVB_LOGF_ERROR("Mapping initialize function lookup error: %s.", error); return FALSE; } return TRUE; }
void talkerStopStream(tl_state_t *pTLState) { AVB_TRACE_ENTRY(AVB_TRACE_TL); if (!pTLState) { AVB_LOG_ERROR("Invalid TLState"); AVB_TRACE_EXIT(AVB_TRACE_TL); return; } talker_data_t *pTalkerData = pTLState->pPvtTalkerData; if (!pTalkerData) { AVB_LOG_ERROR("Invalid listener data"); AVB_TRACE_EXIT(AVB_TRACE_TL); return; } void *rawsock = NULL; if (pTalkerData->avtpHandle) { rawsock = ((avtp_stream_t*)pTalkerData->avtpHandle)->rawsock; } openavbTalkerAddStat(pTLState, TL_STAT_TX_CALLS, pTalkerData->cntWakes); openavbTalkerAddStat(pTLState, TL_STAT_TX_FRAMES, pTalkerData->cntFrames); // openavbTalkerAddStat(pTLState, TL_STAT_TX_LATE, 0); // Can't calulate at this time openavbTalkerAddStat(pTLState, TL_STAT_TX_BYTES, openavbAvtpBytes(pTalkerData->avtpHandle)); AVB_LOGF_INFO("TX "STREAMID_FORMAT", Totals: calls=%" PRIu64 ", frames=%" PRIu64 ", late=%" PRIu64 ", bytes=%" PRIu64 ", TXOutOfBuffs=%ld", STREAMID_ARGS(&pTalkerData->streamID), openavbTalkerGetStat(pTLState, TL_STAT_TX_CALLS), openavbTalkerGetStat(pTLState, TL_STAT_TX_FRAMES), openavbTalkerGetStat(pTLState, TL_STAT_TX_LATE), openavbTalkerGetStat(pTLState, TL_STAT_TX_BYTES), openavbRawsockGetTXOutOfBuffers(rawsock) ); if (pTLState->bStreaming) { openavbAvtpShutdown(pTalkerData->avtpHandle); pTLState->bStreaming = FALSE; } AVB_TRACE_EXIT(AVB_TRACE_TL); }
void listenerStopStream(tl_state_t *pTLState) { AVB_TRACE_ENTRY(AVB_TRACE_TL); if (!pTLState) { AVB_LOG_ERROR("Invalid TLState"); AVB_TRACE_EXIT(AVB_TRACE_TL); return; } listener_data_t *pListenerData = pTLState->pPvtListenerData; if (!pListenerData) { AVB_LOG_ERROR("Invalid listener data"); AVB_TRACE_EXIT(AVB_TRACE_TL); return; } openavbListenerAddStat(pTLState, TL_STAT_RX_CALLS, pListenerData->nReportCalls); openavbListenerAddStat(pTLState, TL_STAT_RX_FRAMES, pListenerData->nReportFrames); openavbListenerAddStat(pTLState, TL_STAT_RX_LOST, openavbAvtpLost(pListenerData->avtpHandle)); openavbListenerAddStat(pTLState, TL_STAT_RX_BYTES, openavbAvtpBytes(pListenerData->avtpHandle)); AVB_LOGF_INFO("RX "STREAMID_FORMAT", Totals: calls=%" PRIu64 ", frames=%" PRIu64 ", lost=%" PRIu64 ", bytes=%" PRIu64, STREAMID_ARGS(&pListenerData->streamID), openavbListenerGetStat(pTLState, TL_STAT_RX_CALLS), openavbListenerGetStat(pTLState, TL_STAT_RX_FRAMES), openavbListenerGetStat(pTLState, TL_STAT_RX_LOST), openavbListenerGetStat(pTLState, TL_STAT_RX_BYTES)); if (pTLState->bStreaming) { openavbAvtpShutdownListener(pListenerData->avtpHandle); pTLState->bStreaming = FALSE; } AVB_TRACE_EXIT(AVB_TRACE_TL); }
static gboolean bus_message(GstBus *bus, GstMessage *message, void *pv) { switch (GST_MESSAGE_TYPE(message)) { case GST_MESSAGE_ERROR: { GError *err = NULL; gchar *dbg_info = NULL; gst_message_parse_error(message, &err, &dbg_info); AVB_LOGF_ERROR("GStreamer ERROR message from element %s: %s", GST_OBJECT_NAME(message->src), err->message); AVB_LOGF_ERROR("Additional info: %s\n", (dbg_info) ? dbg_info : "none"); g_error_free(err); g_free(dbg_info); break; } case GST_MESSAGE_WARNING: { GError *err = NULL; gchar *dbg_info = NULL; gst_message_parse_warning(message, &err, &dbg_info); AVB_LOGF_WARNING("GStreamer WARNING message from element %s: %s", GST_OBJECT_NAME(message->src), err->message); AVB_LOGF_WARNING("Additional info: %s\n", (dbg_info) ? dbg_info : "none"); g_error_free(err); g_free(dbg_info); break; } case GST_MESSAGE_INFO: { GError *err = NULL; gchar *dbg_info = NULL; gst_message_parse_info(message, &err, &dbg_info); AVB_LOGF_ERROR("GStreamer INFO message from element %s: %s", GST_OBJECT_NAME(message->src), err->message); AVB_LOGF_ERROR("Additional info: %s\n", (dbg_info) ? dbg_info : "none"); g_error_free(err); g_free(dbg_info); break; } case GST_MESSAGE_STATE_CHANGED: { GstState old_state, new_state; gst_message_parse_state_changed(message, &old_state, &new_state, NULL); AVB_LOGF_DEBUG("Element %s changed state from %s to %s", GST_OBJECT_NAME(message->src), gst_element_state_get_name(old_state), gst_element_state_get_name(new_state)); break; } case GST_MESSAGE_STREAM_STATUS: { // not so valuable break; } case GST_MESSAGE_EOS: AVB_LOG_INFO("EOS received"); break; default: AVB_LOGF_INFO("GStreamer '%s' message from element %s", gst_message_type_get_name(GST_MESSAGE_TYPE(message)), GST_OBJECT_NAME(message->src)); break; } return TRUE; }
bool openavbAvdeccMsgSrvrHndlChangeNotificationFromClient(int avdeccMsgHandle, openavbAvdeccMsgStateType_t currentState) { AVB_TRACE_ENTRY(AVB_TRACE_AVDECC_MSG); avdecc_msg_state_t *pState = AvdeccMsgStateListGet(avdeccMsgHandle); if (!pState) { AVB_LOGF_ERROR("avdeccMsgHandle %d not valid", avdeccMsgHandle); AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); return false; } // Save the updated state. if (currentState != pState->lastReportedState) { openavbAvdeccMsgStateType_t previousState = pState->lastReportedState; pState->lastReportedState = currentState; AVB_LOGF_INFO("Notified of client %d state change from %s to %s (Last requested: %s)", avdeccMsgHandle, GetStateString(previousState), GetStateString(currentState), GetStateString(pState->lastRequestedState)); // If AVDECC did not yet set the client state, set the client state to the desired state. if (pState->lastRequestedState == OPENAVB_AVDECC_MSG_UNKNOWN) { if (pState->stream->initial_state == TL_INIT_STATE_RUNNING && pState->lastReportedState != OPENAVB_AVDECC_MSG_RUNNING) { // Have the client be running if the user explicitly requested it to be running. openavbAvdeccMsgSrvrChangeRequest(avdeccMsgHandle, OPENAVB_AVDECC_MSG_RUNNING); } else if (pState->stream->initial_state != TL_INIT_STATE_RUNNING && pState->lastReportedState == OPENAVB_AVDECC_MSG_RUNNING) { // Have the client not be running if the user didn't explicitly request it to be running. openavbAvdeccMsgSrvrChangeRequest(avdeccMsgHandle, OPENAVB_AVDECC_MSG_STOPPED); } else if (gAvdeccCfg.bFastConnectSupported && !(pState->bTalker) && pState->lastReportedState == OPENAVB_AVDECC_MSG_STOPPED) { // Listener started as not running, and is not configured to start in the running state. // See if we should do a fast connect using the saved state. openavb_tl_data_cfg_t *pCfg = pState->stream; if (pCfg) { U16 flags, talker_unique_id; U8 talker_entity_id[8], controller_entity_id[8]; bool bAvailable = openavbAvdeccGetSaveStateInfo(pCfg, &flags, &talker_unique_id, &talker_entity_id, &controller_entity_id); if (bAvailable) { openavbAcmpSMListenerSet_doFastConnect(pCfg, flags, talker_unique_id, talker_entity_id, controller_entity_id); } } } } else if (currentState == OPENAVB_AVDECC_MSG_STOPPED_UNEXPECTEDLY) { if (previousState != OPENAVB_AVDECC_MSG_STOPPED_UNEXPECTEDLY && pState->lastRequestedState == OPENAVB_AVDECC_MSG_RUNNING && gAvdeccCfg.bFastConnectSupported) { // The Talker disappeared. Use fast connect to assist with reconnecting if it reappears. openavb_tl_data_cfg_t *pCfg = pState->stream; if (pCfg) { U16 flags, talker_unique_id; U8 talker_entity_id[8], controller_entity_id[8]; bool bAvailable = openavbAvdeccGetSaveStateInfo(pCfg, &flags, &talker_unique_id, &talker_entity_id, &controller_entity_id); if (bAvailable) { // Set the timed-out status for the Listener's descriptor. // The next time the Talker advertises itself, openavbAcmpSMListenerSet_talkerTestFastConnect() should try to reconnect. openavb_aem_descriptor_stream_io_t *pDescriptor; U16 listenerUniqueId; for (listenerUniqueId = 0; listenerUniqueId < 0xFFFF; ++listenerUniqueId) { pDescriptor = openavbAemGetDescriptor(openavbAemGetConfigIdx(), OPENAVB_AEM_DESCRIPTOR_STREAM_INPUT, listenerUniqueId); if (pDescriptor && pDescriptor->stream && strcmp(pDescriptor->stream->friendly_name, pCfg->friendly_name) == 0) { // We found a match. AVB_LOGF_INFO("Listener %s waiting to fast connect to flags=0x%04x, talker_unique_id=0x%04x, talker_entity_id=" ENTITYID_FORMAT ", controller_entity_id=" ENTITYID_FORMAT, pCfg->friendly_name, flags, talker_unique_id, ENTITYID_ARGS(talker_entity_id), ENTITYID_ARGS(controller_entity_id)); pDescriptor->fast_connect_status = OPENAVB_FAST_CONNECT_STATUS_TIMED_OUT; memcpy(pDescriptor->fast_connect_talker_entity_id, talker_entity_id, sizeof(pDescriptor->fast_connect_talker_entity_id)); CLOCK_GETTIME(OPENAVB_CLOCK_REALTIME, &pDescriptor->fast_connect_start_time); pDescriptor->fast_connect_start_time.tv_sec += 5; // Give the Talker some time to shutdown or stabilize break; } } } } } // Now that we have handled this, treat this state as a normal stop. pState->lastReportedState = OPENAVB_AVDECC_MSG_STOPPED; } } AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); return true; }
bool openavbAvdeccMsgSrvrHndlInitIdentifyFromClient(int avdeccMsgHandle, char * friendly_name, U8 talker) { AVB_TRACE_ENTRY(AVB_TRACE_AVDECC_MSG); openavb_tl_data_cfg_t * currentStream; avdecc_msg_state_t *pState = AvdeccMsgStateListGet(avdeccMsgHandle); if (pState) { // The handle was already specified. Something has gone terribly wrong! AVB_LOGF_ERROR("avdeccMsgHandle %d already used", avdeccMsgHandle); AvdeccMsgStateListRemove(pState); free(pState); AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); return false; } // Make sure the supplied string is nil-terminated. friendly_name[FRIENDLY_NAME_SIZE - 1] = '\0'; // Create a structure to hold the client information. pState = (avdecc_msg_state_t *) calloc(1, sizeof(avdecc_msg_state_t)); if (!pState) { AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); return false; } pState->avdeccMsgHandle = avdeccMsgHandle; pState->bTalker = (talker != 0); pState->lastRequestedState = pState->lastReportedState = OPENAVB_AVDECC_MSG_UNKNOWN; // Find the state information matching this item. for (currentStream = streamList; currentStream != NULL; currentStream = currentStream->next) { if (strncmp(currentStream->friendly_name, friendly_name, FRIENDLY_NAME_SIZE) == 0) { break; } } if (!currentStream) { AVB_LOGF_WARNING("Ignoring unexpected client %d, friendly_name: %s", avdeccMsgHandle, friendly_name); free(pState); AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); return false; } // Keep track of this new state item. if (!AvdeccMsgStateListAdd(pState)) { AVB_LOGF_ERROR("Error saving client identity information %d", avdeccMsgHandle); free(pState); AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); return false; } // Associate this Listener instance with the stream information. pState->stream = currentStream; currentStream->client = pState; AVB_LOGF_INFO("Client %d Detected, friendly_name: %s", avdeccMsgHandle, friendly_name); // Enable ADP support, now that we have at least one Talker/Listener. openavbAdpHaveTL(true); AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); return true; }
// 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; memset(&streamID, 0, sizeof(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; // Notify AVDECC Msg of the state change. openavbAvdeccMsgClntNotifyCurrentState(pTLState); // Do until we are stopped or lose 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); // Notify AVDECC Msg of the state change. openavbAvdeccMsgClntNotifyCurrentState(pTLState); } 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); }
/* 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; }
// Returns TRUE, to say we're connected and registers tspec with FQTSS tspec should be initialized bool openavbTLRunTalkerInit(tl_state_t *pTLState) { openavb_tl_cfg_t *pCfg = &pTLState->cfg; talker_data_t *pTalkerData = pTLState->pPvtTalkerData; //avtp_stream_t *pStream = (avtp_stream_t *)(pTalkerData->avtpHandle); strncpy(pTalkerData->ifname, pCfg->ifname, IFNAMSIZ); // CORE_TODO: It would be good to have some parts of endpoint moved into non-endpoint general code to handle some the stream // configuration values. // strncpy(pTalkerData->ifname, pCfg->ifname, IFNAMSIZ); if (pCfg->stream_addr.mac) { memcpy(pTalkerData->streamID.addr, pCfg->stream_addr.mac, ETH_ALEN); }else { AVB_LOG_WARNING("Stream Address Not Set"); } pTalkerData->streamID.uniqueID = pCfg->stream_uid; if (pCfg->sr_class == SR_CLASS_A) { pTalkerData->classRate = 8000; pTalkerData->vlanID = pCfg->vlan_id == VLAN_NULL ? SR_CLASS_A_DEFAULT_VID : pCfg->vlan_id; pTalkerData->vlanPCP = SR_CLASS_A_DEFAULT_PRIORITY; } else if (pCfg->sr_class == SR_CLASS_B) { pTalkerData->classRate = 4000; pTalkerData->vlanID = pCfg->vlan_id == VLAN_NULL ? SR_CLASS_B_DEFAULT_VID : pCfg->vlan_id; pTalkerData->vlanPCP = SR_CLASS_B_DEFAULT_PRIORITY; } memcpy(&pTalkerData->destAddr, &pCfg->dest_addr.mac->ether_addr_octet, ETH_ALEN); unsigned int maxBitrate = 0; if (pCfg->intf_cb.intf_get_src_bitrate_cb != NULL) { maxBitrate = pCfg->intf_cb.intf_get_src_bitrate_cb(pTLState->pMediaQ); } if (maxBitrate > 0) { if (pCfg->map_cb.map_set_src_bitrate_cb != NULL) { pCfg->map_cb.map_set_src_bitrate_cb(pTLState->pMediaQ, maxBitrate); } if (pCfg->map_cb.map_get_max_interval_frames_cb != NULL) { unsigned int map_intv_frames = pCfg->map_cb.map_get_max_interval_frames_cb(pTLState->pMediaQ, pTLState->cfg.sr_class); pCfg->max_interval_frames = map_intv_frames > 0 ? map_intv_frames : pCfg->max_interval_frames; } } pTalkerData->tSpec.maxIntervalFrames = pCfg->max_interval_frames; pTalkerData->tSpec.maxFrameSize = pCfg->map_cb.map_max_data_size_cb(pTLState->pMediaQ); // TODO_COREAVB : This wakeRate should also be set in the endpoint case and removed from the tasker.c start stream if (!pCfg->map_cb.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 = pCfg->map_cb.map_transmit_interval_cb(pTLState->pMediaQ) / pCfg->batch_factor; } if_info_t ifinfo; openavbCheckInterface(pTalkerData->ifname, &ifinfo); pTalkerData->fwmark = openavbQmgrAddStream((SRClassIdx_t)pCfg->sr_class, pTalkerData->wakeRate, pTalkerData->tSpec.maxIntervalFrames, pTalkerData->tSpec.maxFrameSize); if (pTalkerData->fwmark == INVALID_FWMARK) return FALSE; AVB_LOGF_INFO("Dest Addr: "ETH_FORMAT, ETH_OCTETS(pTalkerData->destAddr)); AVB_LOGF_INFO("Starting stream: "STREAMID_FORMAT, STREAMID_ARGS(&pTalkerData->streamID)); talkerStartStream(pTLState); return TRUE; }
// Get a buffer from the ring to use for TX U8* ringRawsockGetTxFrame(void *pvRawsock, bool blocking, unsigned int *len) { AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL); ring_rawsock_t *rawsock = (ring_rawsock_t*)pvRawsock; // Displays only warning when buffer busy after second try int bBufferBusyReported = 0; if (!VALID_TX_RAWSOCK(rawsock) || len == NULL) { AVB_LOG_ERROR("Getting TX frame; bad arguments"); AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); return NULL; } if (rawsock->buffersOut >= rawsock->frameCount) { AVB_LOG_ERROR("Getting TX frame; too many TX buffers in use"); AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); return NULL; } // Get pointer to next framebuf. volatile struct tpacket2_hdr *pHdr = (struct tpacket2_hdr*)(rawsock->pMem + (rawsock->blockIndex * rawsock->blockSize) + (rawsock->bufferIndex * rawsock->bufferSize)); // And pointer to portion of buffer to be filled with frame volatile U8 *pBuffer = (U8*)pHdr + rawsock->bufHdrSize; AVB_LOGF_VERBOSE("block=%d, buffer=%d, out=%d, pBuffer=%p, pHdr=%p", rawsock->blockIndex, rawsock->bufferIndex, rawsock->buffersOut, pBuffer, pHdr); // Check if buffer ready for user // In send mode, we want to see TP_STATUS_AVAILABLE while (pHdr->tp_status != TP_STATUS_AVAILABLE) { switch (pHdr->tp_status) { case TP_STATUS_SEND_REQUEST: case TP_STATUS_SENDING: if (blocking) { #if 0 // We should be able to poll on the socket to wait for the buffer to // be ready, but it doesn't work (at least on 2.6.37). // Keep this code, because it may work on newer kernels // poll until tx buffer is ready struct pollfd pfd; pfd.fd = rawsock->sock; pfd.events = POLLWRNORM; pfd.revents = 0; int ret = poll(&pfd, 1, -1); if (ret < 0 && errno != EINTR) { AVB_LOGF_DEBUG("getting TX frame; poll failed: %s", strerror(errno)); } #else // Can't poll, so sleep instead to avoid tight loop if(0 == bBufferBusyReported) { if(!rawsock->txOutOfBuffer) { // Display this info only once just to let know that something like this happened AVB_LOGF_INFO("Getting TX frame (sock=%d): TX buffer busy", rawsock->sock); } ++rawsock->txOutOfBuffer; ++rawsock->txOutOfBufferCyclic; } else if(1 == bBufferBusyReported) { //Display this warning if buffer was busy more than once because it might influence late/lost AVB_LOGF_WARNING("Getting TX frame (sock=%d): TX buffer busy after usleep(50) verify if there are any lost/late frames", rawsock->sock); } ++bBufferBusyReported; usleep(50); #endif } else { AVB_LOG_DEBUG("Non-blocking, return NULL"); AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); return NULL; } break; case TP_STATUS_WRONG_FORMAT: default: pHdr->tp_status = TP_STATUS_AVAILABLE; break; } } // Remind client how big the frame buffer is if (len) *len = rawsock->base.frameSize; // increment indexes to point to next buffer if (++(rawsock->bufferIndex) >= (rawsock->frameCount/rawsock->blockCount)) { rawsock->bufferIndex = 0; if (++(rawsock->blockIndex) >= rawsock->blockCount) { rawsock->blockIndex = 0; } } // increment the count of buffers held by client rawsock->buffersOut += 1; // warn if too many are out if (rawsock->buffersOut >= (rawsock->frameCount * 4)/5) { AVB_LOGF_WARNING("Getting TX frame; consider increasing buffers: count=%d, out=%d", rawsock->frameCount, rawsock->buffersOut); } AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); return (U8*)pBuffer; }
/* Talker callback comes from endpoint, to indicate when listeners * come and go. We may need to start or stop the talker thread. */ void openavbEptClntNotifyTlkrOfSrpCb(int endpointHandle, AVBStreamID_t *streamID, char *ifname, U8 destAddr[], openavbSrpLsnrDeclSubtype_t lsnrDecl, U8 srClass, U32 classRate, U16 vlanID, U8 priority, U16 fwmark) { AVB_TRACE_ENTRY(AVB_TRACE_TL); tl_state_t *pTLState = TLHandleListGet(endpointHandle); talker_data_t *pTalkerData = pTLState->pPvtTalkerData; if (!pTLState) { AVB_LOG_WARNING("Unable to get talker from endpoint handle."); return; } // If not a talker, ignore this callback. if (pTLState->cfg.role != AVB_ROLE_TALKER) { AVB_LOG_DEBUG("Ignoring Talker callback"); return; } AVB_LOGF_DEBUG("%s streaming=%d, lsnrDecl=%d", __FUNCTION__, pTLState->bStreaming, lsnrDecl); openavb_tl_cfg_t *pCfg = &pTLState->cfg; if (!pTLState->bStreaming) { if (lsnrDecl == openavbSrp_LDSt_Ready || lsnrDecl == openavbSrp_LDSt_Ready_Failed) { // Save the data provided by endpoint/SRP if (!pCfg->ifname[0]) { strncpy(pTalkerData->ifname, ifname, IFNAMSIZ); } else { strncpy(pTalkerData->ifname, pCfg->ifname, IFNAMSIZ); } memcpy(&pTalkerData->streamID, streamID, sizeof(AVBStreamID_t)); memcpy(&pTalkerData->destAddr, destAddr, ETH_ALEN); pTalkerData->srClass = srClass; pTalkerData->classRate = classRate; pTalkerData->vlanID = vlanID; pTalkerData->vlanPCP = priority; pTalkerData->fwmark = fwmark; // We should start streaming AVB_LOGF_INFO("Starting stream: "STREAMID_FORMAT, STREAMID_ARGS(streamID)); talkerStartStream(pTLState); } else if (lsnrDecl == openavbSrp_LDSt_Stream_Info) { // Stream information is available does NOT mean listener is ready. Stream not started yet. if (!pCfg->ifname[0]) { strncpy(pTalkerData->ifname, ifname, IFNAMSIZ); } else { strncpy(pTalkerData->ifname, pCfg->ifname, IFNAMSIZ); } memcpy(&pTalkerData->streamID, streamID, sizeof(AVBStreamID_t)); memcpy(&pTalkerData->destAddr, destAddr, ETH_ALEN); pTalkerData->srClass = srClass; pTalkerData->classRate = classRate; pTalkerData->vlanID = vlanID; pTalkerData->vlanPCP = priority; pTalkerData->fwmark = fwmark; } } else { if (lsnrDecl != openavbSrp_LDSt_Ready && lsnrDecl != openavbSrp_LDSt_Ready_Failed) { // Nobody is listening any more AVB_LOGF_INFO("Stopping stream: "STREAMID_FORMAT, STREAMID_ARGS(streamID)); talkerStopStream(pTLState); } } // Let the AVDECC Msg server know our current stream ID, in case it was updated by MAAP. if (pTLState->avdeccMsgHandle != AVB_AVDECC_MSG_HANDLE_INVALID) { if (!openavbAvdeccMsgClntTalkerStreamID(pTLState->avdeccMsgHandle, pTalkerData->srClass, pTalkerData->streamID.addr, pTalkerData->streamID.uniqueID, pTalkerData->destAddr, pTalkerData->vlanID)) { AVB_LOG_ERROR("openavbAvdeccMsgClntTalkerStreamID() failed"); } } AVB_TRACE_EXIT(AVB_TRACE_TL); }
// A call to this callback indicates that this interface module will be // a talker. Any talker initialization can be done in this function. void openavbIntfMpeg2tsGstTxInitCB(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; } pPvtData->pipe = (GstElement*)NULL; pPvtData->appsink = (GstAppSink*)NULL; pPvtData->appsrc = (GstAppSrc*)NULL; pPvtData->bus = (GstBus*)NULL; pPvtData->nWaiting = 0; GError *error = NULL; pPvtData->pipe = gst_parse_launch(pPvtData->pPipelineStr, &error); if (error) { AVB_LOGF_ERROR("Error creating pipeline: %s", error->message); return; } AVB_LOGF_INFO("Pipeline: %s", pPvtData->pPipelineStr); pPvtData->appsink = GST_APP_SINK(gst_bin_get_by_name(GST_BIN(pPvtData->pipe), APPSINK_NAME)); if (!pPvtData->appsink) { AVB_LOG_ERROR("Failed to find appsink element"); return; } // create bus pPvtData->bus = gst_pipeline_get_bus(GST_PIPELINE(pPvtData->pipe)); if (!pPvtData->bus) { AVB_LOG_ERROR("Failed to create bus"); return; } /* add callback for bus messages */ gst_bus_add_watch(pPvtData->bus, (GstBusFunc)bus_message, pMediaQ); // Setup callback function to handle new buffers delivered to sink GstAppSinkCallbacks cbfns; memset(&cbfns, 0, sizeof(GstAppSinkCallbacks)); gst_al_set_callback(&cbfns, sinkNewBufferSample); gst_app_sink_set_callbacks(pPvtData->appsink, &cbfns, (gpointer)(pMediaQ), NULL); // Set most capabilities in pipeline (config), not code // Don't drop buffers g_object_set(pPvtData->appsink, "drop", 0, NULL); // Start playing gst_element_set_state(pPvtData->pipe, GST_STATE_PLAYING); } AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); }
// A call to this callback indicates that this interface module will be // a listener. Any listener initialization can be done in this function. void openavbIntfMpeg2tsGstRxInitCB(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; } pPvtData->pipe = (GstElement*)NULL; pPvtData->appsink = (GstAppSink*)NULL; pPvtData->appsrc = (GstAppSrc*)NULL; pPvtData->bus = (GstBus*)NULL; pPvtData->srcPaused = FALSE; GError *error = NULL; pPvtData->pipe = gst_parse_launch(pPvtData->pPipelineStr, &error); if (error) { AVB_LOGF_ERROR("Error creating pipeline: %s", error->message); return; } AVB_LOGF_INFO("Pipeline: %s", pPvtData->pPipelineStr); pPvtData->appsrc = GST_APP_SRC(gst_bin_get_by_name(GST_BIN(pPvtData->pipe), APPSRC_NAME)); if (!pPvtData->appsrc) { AVB_LOG_ERROR("Failed to find appsrc element"); return; } // Make appsrc non-blocking g_object_set(G_OBJECT(pPvtData->appsrc), "block", FALSE, NULL); // create bus pPvtData->bus = gst_pipeline_get_bus(GST_PIPELINE(pPvtData->pipe)); if (!pPvtData->bus) { AVB_LOG_ERROR("Failed to create bus"); return; } /* add callback for bus messages */ gst_bus_add_watch(pPvtData->bus, (GstBusFunc)bus_message, pMediaQ); // Setup callback function to handle request from src to pause/start data flow GstAppSrcCallbacks cbfns; memset(&cbfns, 0, sizeof(GstAppSrcCallbacks)); cbfns.enough_data = srcStopFeed; cbfns.need_data = srcStartFeed; gst_app_src_set_callbacks(pPvtData->appsrc, &cbfns, (gpointer)(pMediaQ), NULL); // Set most capabilities in pipeline (config), not code // Don't block g_object_set(pPvtData->appsrc, "block", 0, NULL); // PLAY gst_element_set_state(pPvtData->pipe, GST_STATE_PLAYING); } AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); }
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=%" PRIu32 ", data-rate=%lu, frames=%" PRIu16 ", size=%" PRIu16 ", batch=%" PRIu32 ", sleep=%" PRIu64 "us, sr-Kbps=%d, data-Kbps=%d", STREAMID_ARGS(&pTalkerData->streamID), pTalkerData->classRate, 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; }