EXTERN_DLL_EXPORT bool openavbTLReadIniFileOsal(tl_handle_t TLhandle, const char *fileName, openavb_tl_cfg_t *pCfg, openavb_tl_cfg_name_value_t *pNVCfg) { AVB_TRACE_ENTRY(AVB_TRACE_TL); parse_ini_data_t parseIniData; parseIniData.pTLState = (tl_state_t *)TLhandle; parseIniData.pCfg = pCfg; parseIniData.pNVCfg = pNVCfg; int result = ini_parse(fileName, openavbTLCfgCallback, &parseIniData); if (result == 0) { if_info_t ifinfo; if (!openavbCheckInterface(&parseIniData.pCfg->ifname, &ifinfo)) { AVB_LOGF_ERROR("Invalid value: name=%s, value=%s", "ifname", parseIniData.pCfg->ifname); return FALSE; } } if (result < 0) { AVB_LOGF_ERROR("Couldn't parse INI file: %s", fileName); return FALSE; } if (result > 0) { AVB_LOGF_ERROR("Error in INI file: %s, line %d", fileName, result); return FALSE; } AVB_TRACE_EXIT(AVB_TRACE_TL); return TRUE; }
// Open a rawsock for TX or RX void *pcapRawsockOpen(pcap_rawsock_t* rawsock, const char *ifname, bool rx_mode, bool tx_mode, U16 ethertype, U32 frame_size, U32 num_frames) { AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK); AVB_LOGF_DEBUG("Open, rx=%d, tx=%d, ethertype=%x size=%d, num=%d", rx_mode, tx_mode, ethertype, frame_size, num_frames); baseRawsockOpen(&rawsock->base, ifname, rx_mode, tx_mode, ethertype, frame_size, num_frames); if (tx_mode) { AVB_LOG_DEBUG("pcap rawsock transmit mode will bypass FQTSS"); } rawsock->handle = 0; // Get info about the network device if (!simpleAvbCheckInterface(ifname, &(rawsock->base.ifInfo))) { AVB_LOGF_ERROR("Creating rawsock; bad interface name: %s", ifname); free(rawsock); AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); return NULL; } // Deal with frame size. if (rawsock->base.frameSize == 0) { // use interface MTU as max frames size, if none specified rawsock->base.frameSize = rawsock->base.ifInfo.mtu + ETH_HLEN + VLAN_HLEN; } else if (rawsock->base.frameSize > rawsock->base.ifInfo.mtu + ETH_HLEN + VLAN_HLEN) { AVB_LOG_ERROR("Creating rawsock; requested frame size exceeds MTU"); free(rawsock); AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); return NULL; } char errbuf[PCAP_ERRBUF_SIZE]; rawsock->handle = open_pcap_dev(ifname, rawsock->base.frameSize, errbuf); if (!rawsock->handle) { AVB_LOGF_ERROR("Cannot open device %s: %s", ifname, errbuf); free(rawsock); AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); return NULL; } // fill virtual functions table rawsock_cb_t *cb = &rawsock->base.cb; cb->close = pcapRawsockClose; cb->getTxFrame = pcapRawsockGetTxFrame; cb->txFrameReady = pcapRawsockTxFrameReady; cb->send = pcapRawsockSend; cb->getRxFrame = pcapRawsockGetRxFrame; cb->rxMulticast = pcapRawsockRxMulticast; cb->rxParseHdr = pcapRawsockRxParseHdr; AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); return rawsock; }
/* Listener client attaches to a stream */ bool openavbEptSrvrAttachStream(int h, AVBStreamID_t *streamID, openavbSrpLsnrDeclSubtype_t ld) { openavbRC rc = OPENAVB_SUCCESS; static U8 emptyMAC[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; static AVBTSpec_t emptytSpec = {0, 0}; AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); clientStream_t *ps = findStream(streamID); if (ps && ps->clientHandle != h) { AVB_LOGF_ERROR("Error attaching listener: multiple clients for stream %d", streamID->uniqueID); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return FALSE; } if (!ps) { ps = addStream(h, streamID); if (!ps) { AVB_LOGF_ERROR("Error attaching listener: unable to add client stream %d", streamID->uniqueID); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return FALSE; } ps->role = clientListener; } if(x_cfg.noSrp) { // we are operating in a mode supporting preconfigured streams; SRP is not in use, if(ld == openavbSrp_LDSt_Interest) { // As a proxy for SRP, which would normally make this call after confirming // availability of the stream, call the callback from here strmRegCb((void*)ps, openavbSrp_AtTyp_TalkerAdvertise, emptyMAC, // a flag to listener to read info from configuration file &emptytSpec, MAX_AVB_SR_CLASSES, // srClass - value doesn't matter because openavbEptSrvrNotifyLstnrOfSrpCb() throws it away 1, // accumLatency NULL); // *failInfo } } else { // Normal SRP Operation so pass to SRP rc = openavbSrpAttachStream((void*)ps, streamID, ld); if (!IS_OPENAVB_SUCCESS(rc)) delStream(ps); } openavbEndPtLogAllStaticStreams(); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return IS_OPENAVB_SUCCESS(rc); }
// Send all packets that are ready (i.e. tell kernel to send them) int ringRawsockSend(void *pvRawsock) { AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL); ring_rawsock_t *rawsock = (ring_rawsock_t*)pvRawsock; if (!VALID_TX_RAWSOCK(rawsock)) { AVB_LOG_ERROR("Send; invalid argument"); AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); return -1; } // Linux does something dumb to wait for frames to be sent. // Without MSG_DONTWAIT, CPU usage is bad. int flags = MSG_DONTWAIT; int sent = send(rawsock->sock, NULL, 0, flags); if (errno == EINTR) { // ignore } else if (sent < 0) { AVB_LOGF_ERROR("Send failed: %s", strerror(errno)); assert(0); } else { AVB_LOGF_VERBOSE("Sent %d bytes, %d frames", sent, rawsock->buffersReady); rawsock->buffersOut -= rawsock->buffersReady; rawsock->buffersReady = 0; } AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); return sent; }
U8 *pcapRawsockGetRxFrame(void *pvRawsock, U32 timeout, unsigned int *offset, unsigned int *len) { pcap_rawsock_t *rawsock = (pcap_rawsock_t*)pvRawsock; rawsock->rxHeader = 0; const u_char *packet = 0; int ret; if (rawsock) { ret = pcap_next_ex(rawsock->handle, &rawsock->rxHeader, &packet); switch(ret) { case 1: *offset = 0; *len = rawsock->rxHeader->caplen; return (U8*)packet; case -1: AVB_LOGF_ERROR("pcap_next_ex failed: %s", pcap_geterr(rawsock->handle)); break; case 0: // timeout; break; case -2: // no packets to be read from savefile // this should not happened break; default: break; } } return NULL; }
bool openavbAvdeccMsgSrvrTalkerStreamID(int avdeccMsgHandle, U8 sr_class, U8 stream_id_valid, const U8 stream_src_mac[6], U16 stream_uid, U8 stream_dest_valid, const U8 stream_dest_mac[6], U8 stream_vlan_id_valid, U16 stream_vlan_id) { AVB_TRACE_ENTRY(AVB_TRACE_AVDECC_MSG); openavbAvdeccMessage_t msgBuf; 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; } // Send the stream information to the client. memset(&msgBuf, 0, OPENAVB_AVDECC_MSG_LEN); msgBuf.type = OPENAVB_AVDECC_MSG_S2C_TALKER_STREAM_ID; openavbAvdeccMsgParams_S2C_TalkerStreamID_t * pParams = &(msgBuf.params.s2cTalkerStreamID); pParams->sr_class = sr_class; pParams->stream_id_valid = stream_id_valid; memcpy(pParams->stream_src_mac, stream_src_mac, 6); pParams->stream_uid = htons(stream_uid); pParams->stream_dest_valid = stream_dest_valid; memcpy(pParams->stream_dest_mac, stream_dest_mac, 6); pParams->stream_vlan_id_valid = stream_vlan_id_valid; pParams->stream_vlan_id = htons(stream_vlan_id); bool ret = openavbAvdeccMsgSrvrSendToClient(avdeccMsgHandle, &msgBuf); AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); return ret; }
bool openavbAvdeccMsgSrvrChangeRequest(int avdeccMsgHandle, openavbAvdeccMsgStateType_t desiredState) { AVB_TRACE_ENTRY(AVB_TRACE_AVDECC_MSG); openavbAvdeccMessage_t msgBuf; 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; } memset(&msgBuf, 0, OPENAVB_AVDECC_MSG_LEN); msgBuf.type = OPENAVB_AVDECC_MSG_CLIENT_CHANGE_REQUEST; openavbAvdeccMsgParams_ClientChangeRequest_t * pParams = &(msgBuf.params.clientChangeRequest); pParams->desired_state = (U8) desiredState; bool ret = openavbAvdeccMsgSrvrSendToClient(avdeccMsgHandle, &msgBuf); if (ret) { // Save the requested state for future reference. pState->lastRequestedState = desiredState; } AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); return ret; }
static bool openavbEptClntSendToServer(int h, openavbEndpointMessage_t *msg) { AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); if (!msg || h == AVB_ENDPOINT_HANDLE_INVALID) { AVB_LOG_ERROR("Client send: invalid argument passed"); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return FALSE; } ssize_t nWrite = write(h, msg, OPENAVB_ENDPOINT_MSG_LEN); AVB_LOGF_VERBOSE("Sent message, len=%zu, nWrite=%zu", OPENAVB_ENDPOINT_MSG_LEN, nWrite); if (nWrite < OPENAVB_ENDPOINT_MSG_LEN) { if (nWrite < 0) { AVB_LOGF_ERROR("Client failed to write socket: %s", strerror(errno)); } else if (nWrite == 0) { AVB_LOG_ERROR("Client send: socket closed unexpectedly"); } else { AVB_LOG_ERROR("Client send: short write"); } socketClose(h); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return FALSE; } AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return TRUE; }
bool openavbEndpointServerOpen(void) { AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); int i; for (i=0; i < POLL_FD_COUNT; i++) { fds[i].fd = SOCK_INVALID; fds[i].events = 0; } lsock = socket(AF_UNIX, SOCK_STREAM, 0); if (lsock < 0) { AVB_LOGF_ERROR("Failed to open socket: %s", strerror(errno)); goto error; } // serverAddr is file static serverAddr.sun_family = AF_UNIX; snprintf(serverAddr.sun_path, UNIX_PATH_MAX, AVB_ENDPOINT_UNIX_PATH); int rslt = bind(lsock, (struct sockaddr*)&serverAddr, sizeof(struct sockaddr_un)); if (rslt != 0) { AVB_LOGF_ERROR("Failed to create %s: %s", serverAddr.sun_path, strerror(errno)); AVB_LOG_WARNING("** If endpoint process crashed, run the cleanup script **"); goto error; } rslt = listen(lsock, 5); if (rslt != 0) { AVB_LOGF_ERROR("Failed to listen on socket: %s", strerror(errno)); goto error; } AVB_LOGF_DEBUG("Listening on socket: %s", serverAddr.sun_path); fds[AVB_ENDPOINT_LISTEN_FDS].fd = lsock; fds[AVB_ENDPOINT_LISTEN_FDS].events = POLLIN; AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return TRUE; error: if (lsock >= 0) { close(lsock); lsock = -1; } AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return FALSE; }
bool parse_mac(const char *str, cfg_mac_t *mac) { memset(&mac->buffer, 0, sizeof(struct ether_addr)); mac->mac = ether_aton_r(str, &mac->buffer); if (mac->mac) return TRUE; AVB_LOGF_ERROR("Failed to parse addr: %s", str); return FALSE; }
bool openavbAvdeccMsgSrvrHndlTalkerStreamIDFromClient(int avdeccMsgHandle, U8 sr_class, const U8 stream_src_mac[6], U16 stream_uid, const U8 stream_dest_mac[6], U16 stream_vlan_id) { 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; } openavb_tl_data_cfg_t *pCfg = pState->stream; if (!pCfg) { AVB_LOGF_ERROR("avdeccMsgHandle %d stream not valid", avdeccMsgHandle); AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); return false; } // Update the stream information supplied by the client. pCfg->sr_class = sr_class; memcpy(pCfg->stream_addr.buffer.ether_addr_octet, stream_src_mac, 6); pCfg->stream_addr.mac = &(pCfg->stream_addr.buffer); // Indicate that the MAC Address is valid. pCfg->stream_uid = stream_uid; memcpy(pCfg->dest_addr.buffer.ether_addr_octet, stream_dest_mac, 6); pCfg->dest_addr.mac = &(pCfg->dest_addr.buffer); // Indicate that the MAC Address is valid. pCfg->vlan_id = stream_vlan_id; AVB_LOGF_DEBUG("Talker-supplied sr_class: %u", pCfg->sr_class); AVB_LOGF_DEBUG("Talker-supplied stream_id: " ETH_FORMAT "/%u", ETH_OCTETS(pCfg->stream_addr.buffer.ether_addr_octet), pCfg->stream_uid); AVB_LOGF_DEBUG("Talker-supplied dest_addr: " ETH_FORMAT, ETH_OCTETS(pCfg->dest_addr.buffer.ether_addr_octet)); AVB_LOGF_DEBUG("Talker-supplied vlan_id: %u", pCfg->vlan_id); // Notify the state machine that we received this information. openavbAcmpSMTalker_updateStreamInfo(pCfg); AVB_TRACE_EXIT(AVB_TRACE_AVDECC_MSG); return true; }
// Setup the rawsock to receive multicast packets bool pcapRawsockRxMulticast(void *pvRawsock, bool add_membership, const U8 addr[ETH_ALEN]) { pcap_rawsock_t *rawsock = (pcap_rawsock_t*)pvRawsock; struct bpf_program comp_filter_exp; char filter_exp[30]; sprintf(filter_exp, "ether dst %02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); AVB_LOGF_DEBUG("%s %d %s", __func__, (int)add_membership, filter_exp); if (pcap_compile(rawsock->handle, &comp_filter_exp, filter_exp, 0, PCAP_NETMASK_UNKNOWN) < 0) { AVB_LOGF_ERROR("Could not parse filter %s: %s", filter_exp, pcap_geterr(rawsock->handle)); return false; } if (pcap_setfilter(rawsock->handle, &comp_filter_exp) < 0) { AVB_LOGF_ERROR("Could not install filter %s: %s", filter_exp, pcap_geterr(rawsock->handle)); return false; } return true; }
// Parse ini file, and create config data // int openavbReadConfig(const char *ini_file, openavb_endpoint_cfg_t *pCfg) { AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); // defaults - most are handled by setting everything to 0 memset(pCfg, 0, sizeof(openavb_endpoint_cfg_t)); pCfg->fqtss_mode = -1; int result = ini_parse(ini_file, cfgCallback, pCfg); if (result < 0) { AVB_LOGF_ERROR("Couldn't parse INI file: %s", ini_file); return -1; } if (result > 0) { AVB_LOGF_ERROR("Error in INI file: %s, line %d", ini_file, result); return -1; } AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); // Yay, we did it. return 0; }
bool pcapRawsockTxFrameReady(void *pvRawsock, U8 *pBuffer, unsigned int len, U64 timeNsec) { pcap_rawsock_t *rawsock = (pcap_rawsock_t*)pvRawsock; int ret = -1; if (timeNsec) { IF_LOG_INTERVAL(1000) AVB_LOG_WARNING("launch time is unsupported in pcap_rawsock"); } if (rawsock) { ret = pcap_sendpacket(rawsock->handle, pBuffer, len); if (ret == -1) { AVB_LOGF_ERROR("pcap_sendpacket failed: %s", pcap_geterr(rawsock->handle)); } } return ret == 0; }
void openavbEndpointServerClose(void) { AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); int i; for (i = 0; i < POLL_FD_COUNT; i++) { if (fds[i].fd != SOCK_INVALID) { close(fds[i].fd); } } if (lsock != SOCK_INVALID) { close(lsock); } if (unlink(serverAddr.sun_path) != 0) { AVB_LOGF_ERROR("Failed to unlink %s: %s", serverAddr.sun_path, strerror(errno)); } AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); }
/* Client (talker or listener) going away */ bool openavbEptSrvrStopStream(int h, AVBStreamID_t *streamID) { AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); clientStream_t *ps = findStream(streamID); if (!ps || ps->clientHandle != h) { AVB_LOGF_ERROR("Error stopping client: missing record for stream %d", streamID->uniqueID); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return FALSE; } bool rc = FALSE; if (ps->role == clientTalker) rc = x_talkerDeregister(ps); else if (ps->role == clientListener) rc = x_listenerDetach(ps); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return rc; }
static bool openavbEptSrvrSendToClient(int h, openavbEndpointMessage_t *msg) { AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); if (h < 0 || h >= POLL_FD_COUNT) { AVB_LOG_ERROR("Sending message; invalid handle"); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return FALSE; } if (!msg) { AVB_LOG_ERROR("Sending message; invalid argument passed"); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return FALSE; } int csock = fds[h].fd; if (csock == SOCK_INVALID) { AVB_LOG_ERROR("Socket closed unexpectedly"); return FALSE; } ssize_t nWrite = write(csock, msg, OPENAVB_ENDPOINT_MSG_LEN); AVB_LOGF_VERBOSE("Sent message, len=%zu, nWrite=%zu", OPENAVB_ENDPOINT_MSG_LEN, nWrite); if (nWrite < OPENAVB_ENDPOINT_MSG_LEN) { if (nWrite < 0) { AVB_LOGF_ERROR("Failed to write socket: %s", strerror(errno)); } else if (nWrite == 0) { AVB_LOG_ERROR("Socket closed unexpectedly"); } else { AVB_LOG_ERROR("Socket write too short"); } socketClose(h); AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); return FALSE; } AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return TRUE; }
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; }
// 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 openavbIntfMpeg2tsGstTxCB(media_q_t *pMediaQ) { AVB_TRACE_ENTRY(AVB_TRACE_INTF_DETAIL); if (!pMediaQ) { AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); return FALSE; } pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; if (!pPvtData) { AVB_LOG_ERROR("Private interface module data not allocated."); return FALSE; } if (!pPvtData->appsink) { AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); return FALSE; } media_q_item_t *pMediaQItem; GstAlBuf *txBuf; while (g_atomic_int_get(&pPvtData->nWaiting) > 0) { // Get a mediaQItem to hold the buffered data pMediaQItem = openavbMediaQHeadLock(pMediaQ); if (!pMediaQItem) { IF_LOG_INTERVAL(1000) AVB_LOG_ERROR("Media queue full"); break; } /* Retrieve the buffer */ txBuf = gst_al_pull_buffer(pPvtData->appsink); if (txBuf) { g_atomic_int_add(&pPvtData->nWaiting, -1); if ( GST_AL_BUF_SIZE(txBuf) > pMediaQItem->itemSize ) { AVB_LOGF_ERROR("GStreamer buffer too large (size=%d) for mediaQ item (dataLen=%d)", GST_AL_BUF_SIZE(txBuf), pMediaQItem->itemSize); pMediaQItem->dataLen = 0; openavbMediaQHeadUnlock(pMediaQ); } else { memcpy(pMediaQItem->pPubData, GST_AL_BUF_DATA(txBuf), GST_AL_BUF_SIZE(txBuf)); pMediaQItem->dataLen = GST_AL_BUF_SIZE(txBuf); openavbAvtpTimeSetToWallTime(pMediaQItem->pAvtpTime); openavbMediaQHeadPush(pMediaQ); } gst_al_buffer_unref(txBuf); } else { AVB_LOG_ERROR("GStreamer buffer pull failed"); // assume the pipeline is empty g_atomic_int_set(&pPvtData->nWaiting, 0); // abandon the mediaq item pMediaQItem->dataLen = 0; openavbMediaQHeadUnlock(pMediaQ); // and get out break; } } AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); return TRUE; }
// 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); }
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 openavbEptClntService(int h, int timeout) { AVB_TRACE_ENTRY(AVB_TRACE_ENDPOINT); bool rc = FALSE; if (h == AVB_ENDPOINT_HANDLE_INVALID) { AVB_LOG_ERROR("Client service: invalid socket"); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return FALSE; } struct pollfd fds[1]; memset(fds, 0, sizeof(struct pollfd)); fds[0].fd = h; fds[0].events = POLLIN; AVB_LOG_VERBOSE("Waiting for event..."); int pRet = poll(fds, 1, timeout); if (pRet == 0) { AVB_LOG_VERBOSE("Poll timeout"); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return TRUE; } else if (pRet < 0) { if (errno == EINTR) { AVB_LOG_VERBOSE("Poll interrupted"); AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return TRUE; } else { AVB_LOGF_ERROR("Poll error: %s", strerror(errno)); } } else { AVB_LOGF_DEBUG("Poll returned %d events", pRet); // only one fd, so it's readable. openavbEndpointMessage_t msgBuf; memset(&msgBuf, 0, OPENAVB_ENDPOINT_MSG_LEN); ssize_t nRead = read(h, &msgBuf, OPENAVB_ENDPOINT_MSG_LEN); if (nRead < OPENAVB_ENDPOINT_MSG_LEN) { // sock closed if (nRead == 0) { AVB_LOG_ERROR("Socket closed unexpectedly"); } else if (nRead < 0) { AVB_LOGF_ERROR("Socket read error: %s", strerror(errno)); } else { AVB_LOG_ERROR("Socket read to short"); } socketClose(h); } else { // got a message if (openavbEptClntReceiveFromServer(h, &msgBuf)) { rc = TRUE; } else { AVB_LOG_ERROR("Invalid message received"); socketClose(h); } } } AVB_TRACE_EXIT(AVB_TRACE_ENDPOINT); return rc; }
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; }
// callback function - called for each name/value pair by ini parsing library static int openavbTLCfgCallback(void *user, const char *tlSection, const char *name, const char *value) { AVB_TRACE_ENTRY(AVB_TRACE_TL); parse_ini_data_t *pParseIniData = (parse_ini_data_t *)user; openavb_tl_cfg_t *pCfg = pParseIniData->pCfg; openavb_tl_cfg_name_value_t *pNVCfg = pParseIniData->pNVCfg; tl_state_t *pTLState = pParseIniData->pTLState; AVB_LOGF_DEBUG("name=[%s] value=[%s]", name, value); bool valOK = FALSE; char *pEnd; int i; 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, "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, "ifname")) { if_info_t ifinfo; if (openavbCheckInterface(value, &ifinfo)) { strncpy(pCfg->ifname, value, IFNAMSIZ - 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, "map_lib")) { if (pTLState->mapLib.libName) free(pTLState->mapLib.libName); pTLState->mapLib.libName = strdup(value); valOK = TRUE; } else if (MATCH(name, "map_fn")) { if (pTLState->mapLib.funcName) free(pTLState->mapLib.funcName); pTLState->mapLib.funcName = strdup(value); valOK = TRUE; } else if (MATCH(name, "intf_lib")) { if (pTLState->intfLib.libName) free(pTLState->intfLib.libName); pTLState->intfLib.libName = strdup(value); valOK = TRUE; } else if (MATCH(name, "intf_fn")) { if (pTLState->intfLib.funcName) free(pTLState->intfLib.funcName); pTLState->intfLib.funcName = strdup(value); valOK = TRUE; } else if (MATCH_LEFT(name, "intf_nv_", 8) || MATCH_LEFT(name, "map_nv_", 7)) { // Need to save the interface and mapping module configuration // until later (after those libraries are loaded.) // check if this setting replaces an earlier one for (i = 0; i < pNVCfg->nLibCfgItems; i++) { if (MATCH(name, pNVCfg->libCfgNames[i])) { if (pNVCfg->libCfgValues[i]) free(pNVCfg->libCfgValues[i]); pNVCfg->libCfgValues[i] = strdup(value); valOK = TRUE; } } if (i >= pNVCfg->nLibCfgItems) { // is a new name/value if (i >= MAX_LIB_CFG_ITEMS) { AVB_LOG_ERROR("Too many INI settings for interface/mapping modules"); } else { pNVCfg->libCfgNames[i] = strdup(name); pNVCfg->libCfgValues[i] = strdup(value); pNVCfg->nLibCfgItems++; valOK = TRUE; } } } else { // unmatched item, fail AVB_LOGF_ERROR("Unrecognized configuration item: name=%s", name); return 0; } if (!valOK) { // bad value AVB_LOGF_ERROR("Invalid value: name=%s, value=%s", name, value); return 0; } AVB_TRACE_EXIT(AVB_TRACE_TL); return 1; // OK }
// Open a rawsock for TX or RX void* ringRawsockOpen(ring_rawsock_t *rawsock, const char *ifname, bool rx_mode, bool tx_mode, U16 ethertype, U32 frame_size, U32 num_frames) { AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK); if (!simpleRawsockOpen((simple_rawsock_t*)rawsock, ifname, rx_mode, tx_mode, ethertype, frame_size, num_frames)) { AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); return NULL; } rawsock->pMem = (void*)(-1); // Use version 2 headers for the MMAP packet stuff - avoids 32/64 // bit problems, gives nanosecond timestamps, and allows rx of vlan id int val = TPACKET_V2; if (setsockopt(rawsock->sock, SOL_PACKET, PACKET_VERSION, &val, sizeof(val)) < 0) { AVB_LOGF_ERROR("Creating rawsock; get PACKET_VERSION: %s", strerror(errno)); ringRawsockClose(rawsock); AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); return NULL; } // Get the size of the headers in the ring unsigned len = sizeof(val); if (getsockopt(rawsock->sock, SOL_PACKET, PACKET_HDRLEN, &val, &len) < 0) { AVB_LOGF_ERROR("Creating rawsock; get PACKET_HDRLEN: %s", strerror(errno)); ringRawsockClose(rawsock); AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); return NULL; } rawsock->bufHdrSize = TPACKET_ALIGN(val); if (rawsock->base.rxMode) { rawsock->bufHdrSize = rawsock->bufHdrSize + TPACKET_ALIGN(sizeof(struct sockaddr_ll)); } rawsock->bufferSize = rawsock->base.frameSize + rawsock->bufHdrSize; rawsock->frameCount = num_frames; AVB_LOGF_DEBUG("frameSize=%d, bufHdrSize=%d(%d+%zu) bufferSize=%d, frameCount=%d", rawsock->base.frameSize, rawsock->bufHdrSize, val, sizeof(struct sockaddr_ll), rawsock->bufferSize, rawsock->frameCount); // Get number of bytes in a memory page. The blocks we ask for // must be a multiple of pagesize. (Actually, it should be // (pagesize * 2^N) to avoid wasting memory.) int pagesize = getpagesize(); rawsock->blockSize = pagesize * 4; AVB_LOGF_DEBUG("pagesize=%d blockSize=%d", pagesize, rawsock->blockSize); // Calculate number of buffers and frames based on blocks int buffersPerBlock = rawsock->blockSize / rawsock->bufferSize; rawsock->blockCount = rawsock->frameCount / buffersPerBlock + 1; rawsock->frameCount = buffersPerBlock * rawsock->blockCount; AVB_LOGF_DEBUG("frameCount=%d, buffersPerBlock=%d, blockCount=%d", rawsock->frameCount, buffersPerBlock, rawsock->blockCount); // Fill in the kernel structure with our calculated values struct tpacket_req s_packet_req; memset(&s_packet_req, 0, sizeof(s_packet_req)); s_packet_req.tp_block_size = rawsock->blockSize; s_packet_req.tp_frame_size = rawsock->bufferSize; s_packet_req.tp_block_nr = rawsock->blockCount; s_packet_req.tp_frame_nr = rawsock->frameCount; // Ask the kernel to create the TX_RING or RX_RING if (rawsock->base.txMode) { if (setsockopt(rawsock->sock, SOL_PACKET, PACKET_TX_RING, (char*)&s_packet_req, sizeof(s_packet_req)) < 0) { AVB_LOGF_ERROR("Creating rawsock; TX_RING: %s", strerror(errno)); ringRawsockClose(rawsock); AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); return NULL; } AVB_LOGF_DEBUG("PACKET_%s_RING OK", "TX"); } else { if (setsockopt(rawsock->sock, SOL_PACKET, PACKET_RX_RING, (char*)&s_packet_req, sizeof(s_packet_req)) < 0) { AVB_LOGF_ERROR("Creating rawsock, RX_RING: %s", strerror(errno)); ringRawsockClose(rawsock); AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); return NULL; } AVB_LOGF_DEBUG("PACKET_%s_RING OK", "TX"); } // Call MMAP to get access to the memory used for the ring rawsock->memSize = rawsock->blockCount * rawsock->blockSize; AVB_LOGF_DEBUG("memSize=%zu (%d, %d), sock=%d", rawsock->memSize, rawsock->blockCount, rawsock->blockSize, rawsock->sock); rawsock->pMem = mmap((void*)0, rawsock->memSize, PROT_READ|PROT_WRITE, MAP_SHARED, rawsock->sock, (off_t)0); if (rawsock->pMem == (void*)(-1)) { AVB_LOGF_ERROR("Creating rawsock; MMAP: %s", strerror(errno)); ringRawsockClose(rawsock); AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); return NULL; } AVB_LOGF_DEBUG("mmap: %p", rawsock->pMem); // Initialize the memory memset(rawsock->pMem, 0, rawsock->memSize); // Initialize the state of the ring rawsock->blockIndex = 0; rawsock->bufferIndex = 0; rawsock->buffersOut = 0; rawsock->buffersReady = 0; // fill virtual functions table rawsock_cb_t *cb = &rawsock->base.cb; cb->close = ringRawsockClose; cb->getTxFrame = ringRawsockGetTxFrame; cb->relTxFrame = ringRawsockRelTxFrame; cb->txFrameReady = ringRawsockTxFrameReady; cb->send = ringRawsockSend; cb->txBufLevel = ringRawsockTxBufLevel; cb->rxBufLevel = ringRawsockRxBufLevel; cb->getRxFrame = ringRawsockGetRxFrame; cb->rxParseHdr = ringRawsockRxParseHdr; cb->relRxFrame = ringRawsockRelRxFrame; cb->getTXOutOfBuffers = ringRawsockGetTXOutOfBuffers; cb->getTXOutOfBuffersCyclic = ringRawsockGetTXOutOfBuffersCyclic; AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK); return rawsock; }
// This callback is called when acting as a listener. bool openavbIntfMpeg2tsGstRxCB(media_q_t *pMediaQ) { AVB_TRACE_ENTRY(AVB_TRACE_INTF_DETAIL); if (!pMediaQ) { AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); return FALSE; } pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; if (!pPvtData) { AVB_LOG_ERROR("Private interface module data not allocated."); return FALSE; } if (!pPvtData->appsrc) { AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); return FALSE; } bool moreData = TRUE; bool retval = TRUE; while (moreData) { media_q_item_t *pMediaQItem = openavbMediaQTailLock(pMediaQ, pPvtData->ignoreTimestamp); if (pMediaQItem) { unsigned long len = pMediaQItem->dataLen; if (len > 0) { GstAlBuf *rxBuf = gst_al_alloc_buffer(len); if (rxBuf) { GST_AL_BUFFER_TIMESTAMP(rxBuf) = GST_CLOCK_TIME_NONE; GST_AL_BUFFER_DURATION(rxBuf) = GST_CLOCK_TIME_NONE; memcpy(GST_AL_BUF_DATA(rxBuf), pMediaQItem->pPubData, GST_AL_BUF_SIZE(rxBuf)); GstFlowReturn gstret = gst_al_push_buffer(GST_APP_SRC(pPvtData->appsrc), rxBuf); if (gstret != GST_FLOW_OK) { AVB_LOGF_ERROR("Pushing buffer to gstreamer failed: %d", gstret); retval = moreData = FALSE; } } else { AVB_LOG_ERROR("Failed to get gstreamer buffer"); retval = moreData = FALSE; } } openavbMediaQTailPull(pMediaQ); } else { moreData = FALSE; } } AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); return retval; }
/********************************************** * 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); }
// Get a RX frame U8* ringRawsockGetRxFrame(void *pvRawsock, U32 timeout, unsigned int *offset, unsigned int *len) { AVB_TRACE_ENTRY(AVB_TRACE_RAWSOCK_DETAIL); ring_rawsock_t *rawsock = (ring_rawsock_t*)pvRawsock; if (!VALID_RX_RAWSOCK(rawsock)) { AVB_LOG_ERROR("Getting RX frame; invalid arguments"); AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); return NULL; } if (rawsock->buffersOut >= rawsock->frameCount) { AVB_LOG_ERROR("Too many RX buffers in use"); AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); return NULL; } // Get pointer to active buffer in ring volatile struct tpacket2_hdr *pHdr = (struct tpacket2_hdr*)(rawsock->pMem + (rawsock->blockIndex * rawsock->blockSize) + (rawsock->bufferIndex * rawsock->bufferSize)); 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 receive mode, we want TP_STATUS_USER flag set if ((pHdr->tp_status & TP_STATUS_USER) == 0) { struct timespec ts, *pts = NULL; struct pollfd pfd; // Use poll to wait for "ready to read" condition // Poll even if our timeout is 0 - to catch the case where // kernel is writing to the wrong slot (see below.) if (timeout != OPENAVB_RAWSOCK_BLOCK) { ts.tv_sec = timeout / MICROSECONDS_PER_SECOND; ts.tv_nsec = (timeout % MICROSECONDS_PER_SECOND) * NANOSECONDS_PER_USEC; pts = &ts; } pfd.fd = rawsock->sock; pfd.events = POLLIN; pfd.revents = 0; int ret = ppoll(&pfd, 1, pts, NULL); if (ret < 0) { if (errno != EINTR) { AVB_LOGF_ERROR("Getting RX frame; poll failed: %s", strerror(errno)); } AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); return NULL; } if ((pfd.revents & POLLIN) == 0) { // timeout AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); return NULL; } if ((pHdr->tp_status & TP_STATUS_USER) == 0) { // Hmmm, this is unexpected. poll indicated that the // socket was ready to read, but the slot in the TX ring // that we're looking for the kernel to fill isn't filled. // If there aren't any RX buffers held by the application, // we can try to fix this sticky situation... if (rawsock->buffersOut == 0) { // Scan forward through the RX ring, and look for a // buffer that's ready for us to read. The kernel has // a bad habit of not starting at the beginning of the // ring when the listener process is restarted. int nSkipped = 0; while((pHdr->tp_status & TP_STATUS_USER) == 0) { // Move to next slot in ring. // (Increment buffer/block indexes) if (++(rawsock->bufferIndex) >= (rawsock->frameCount/rawsock->blockCount)) { rawsock->bufferIndex = 0; if (++(rawsock->blockIndex) >= rawsock->blockCount) { rawsock->blockIndex = 0; } } // Adjust pHdr, pBuffer to point to the new slot pHdr = (struct tpacket2_hdr*)(rawsock->pMem + (rawsock->blockIndex * rawsock->blockSize) + (rawsock->bufferIndex * rawsock->bufferSize)); pBuffer = (U8*)pHdr + rawsock->bufHdrSize; // If we've scanned all the way around the ring, bail out. if (++nSkipped > rawsock->frameCount) { AVB_LOG_WARNING("Getting RX frame; no frame after poll"); AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); return NULL; } } // We found a slot that's ready. Hopefully, we're good now. AVB_LOGF_WARNING("Getting RX frame; skipped %d empty slots (rawsock=%p)", nSkipped, rawsock); } else { AVB_LOG_WARNING("Getting RX frame; no frame after poll"); AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); return NULL; } } } AVB_LOGF_VERBOSE("Buffer status=0x%4.4lx", (unsigned long)pHdr->tp_status); if (pHdr->tp_status & TP_STATUS_COPY) { AVB_LOG_WARNING("Frame too big for receive buffer"); } // Check the "losing" flag. That indicates that the ring is full, // and the kernel had to toss some frames. There is no "winning" flag. if ((pHdr->tp_status & TP_STATUS_LOSING)) { if (!rawsock->bLosing) { AVB_LOG_WARNING("Getting RX frame; mmap buffers full"); rawsock->bLosing = TRUE; } } else { rawsock->bLosing = FALSE; } // increment indexes for next time if (++(rawsock->bufferIndex) >= (rawsock->frameCount/rawsock->blockCount)) { rawsock->bufferIndex = 0; if (++(rawsock->blockIndex) >= rawsock->blockCount) { rawsock->blockIndex = 0; } } // Remember that the client has another buffer rawsock->buffersOut += 1; if (pHdr->tp_snaplen < pHdr->tp_len) { #if (AVB_LOG_LEVEL >= AVB_LOG_LEVEL_VERBOSE) AVB_LOGF_WARNING("Getting RX frame; partial frame ignored (len %d, snaplen %d)", pHdr->tp_len, pHdr->tp_snaplen); AVB_LOG_BUFFER(AVB_LOG_LEVEL_VERBOSE, (const U8 *) pBuffer + (pHdr->tp_mac - rawsock->bufHdrSize), pHdr->tp_len, 16); #else IF_LOG_INTERVAL(1000) AVB_LOGF_WARNING("Getting RX frame; partial frame ignored (len %d, snaplen %d)", pHdr->tp_len, pHdr->tp_snaplen); #endif ringRawsockRelRxFrame(rawsock, (U8*)pBuffer); AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); return NULL; } // Return pointer to the buffer and length *offset = pHdr->tp_mac - rawsock->bufHdrSize; *len = pHdr->tp_snaplen; AVB_LOGF_VERBOSE("Good RX frame (len %d, snaplen %d)", pHdr->tp_len, pHdr->tp_snaplen); AVB_TRACE_EXIT(AVB_TRACE_RAWSOCK_DETAIL); return (U8*)pBuffer; }