// Delete a connection with saved state // bool openavbAvdeccClearSavedState(const openavb_tl_data_cfg_t *pListener) { AVB_TRACE_ENTRY(AVB_TRACE_AVDECC); int i; // Delete the saved state matching the supplied one. // If the supplied saved state does not match any of the ones already saved, do nothing and return. for (i = 0; i < 1000; ++i) { const openavb_saved_state_t * pTest = openavbAvdeccGetSavedState(i); if (!pTest) { break; } if (strcmp(pTest->listener_friendly_name, pListener->friendly_name) == 0) { // We found the index for the item to delete. // Delete the item and save the updated file. openavbAvdeccDeleteSavedState(i); AVB_LOGF_DEBUG("Cleared saved state: listener_id=\"%s\"", pListener->friendly_name); AVB_TRACE_EXIT(AVB_TRACE_AVDECC); return true; } } AVB_LOGF_WARNING("Unable to find saved state to clear: listener_id=\"%s\"", pListener->friendly_name); AVB_TRACE_EXIT(AVB_TRACE_AVDECC); return false; }
// Each configuration name value pair for this mapping will result in this callback being called. void openavbIntfMpeg2tsGstCfgCB(media_q_t *pMediaQ, const char *name, const char *value) { 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; } char *pEnd; unsigned long tmp; bool nameOK = TRUE, valueOK = FALSE; if (strcmp(name, "intf_nv_gst_pipeline") == 0) { if (pPvtData->pPipelineStr) free(pPvtData->pPipelineStr); pPvtData->pPipelineStr = strdup(value); valueOK = (value != NULL && strlen(value) > 0); } else if (strcmp(name, "intf_nv_ignore_timestamp") == 0) { tmp = strtol(value, &pEnd, 10); if (*pEnd == '\0' && pEnd != value && (tmp == 0 || tmp == 1)) { pPvtData->ignoreTimestamp = (tmp == 1); valueOK = TRUE; } } else { AVB_LOGF_WARNING("Unknown configuration item: %s", name); nameOK = FALSE; } if (nameOK && !valueOK) { AVB_LOGF_WARNING("Bad value for configuration item: %s = %s", name, value); } } AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); }
static pcap_t* open_pcap_dev(const char* ifname, int frameSize, char* errbuf) { pcap_t* handle = pcap_create(ifname, errbuf); if (handle) { int err; err = pcap_set_snaplen(handle, frameSize); if (err) AVB_LOGF_WARNING("Cannot set snap len %d", err); err = pcap_set_promisc(handle, 1); if (err) AVB_LOGF_WARNING("Cannot set promisc %d", err); err = pcap_set_immediate_mode(handle, 1); if (err) AVB_LOGF_WARNING("Cannot set immediate mode %d", err); // we need timeout (here 100ms) otherwise we could block for ever err = pcap_set_timeout(handle, 100); if (err) AVB_LOGF_WARNING("Cannot set timeout %d", err); err = pcap_set_tstamp_precision(handle, PCAP_TSTAMP_PRECISION_NANO); if (err) AVB_LOGF_WARNING("Cannot set tstamp nano precision %d", err); err = pcap_set_tstamp_type(handle, PCAP_TSTAMP_ADAPTER_UNSYNCED); if (err) AVB_LOGF_WARNING("Cannot set tstamp adapter unsynced %d", err); err = pcap_activate(handle); if (err) AVB_LOGF_WARNING("Cannot activate pcap %d", err); } return handle; }
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 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); }
// 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); }
// 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; }
// 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; }
bool openavbReadTlDataIniFile(const char *fileName, openavb_tl_data_cfg_t *pCfg) { AVB_TRACE_ENTRY(AVB_TRACE_AVDECC); openavbIniCfgInit(pCfg); // Use the .INI file name as the default friendly name. strncpy(pCfg->friendly_name, fileName, FRIENDLY_NAME_SIZE - 1); char * pszComma = strchr(pCfg->friendly_name, ','); if (pszComma) { // Get rid of anything following the file name. *pszComma = '\0'; } char * pszExtension = strrchr(pCfg->friendly_name, '.'); if (pszExtension && strcasecmp(pszExtension, ".ini") == 0) { // Get rid of the .INI file extension. *pszExtension = '\0'; } int result = ini_parse(fileName, openavbIniCfgCallback, pCfg); if (result < 0) { AVB_LOGF_ERROR("Couldn't parse INI file: %s", fileName); AVB_TRACE_EXIT(AVB_TRACE_AVDECC); return FALSE; } if (result > 0) { AVB_LOGF_ERROR("Error in INI file: %s, line %d", fileName, result); AVB_TRACE_EXIT(AVB_TRACE_AVDECC); return FALSE; } if (pCfg->current_sampling_rate != 0 && pCfg->audioRate != 0 && pCfg->current_sampling_rate != pCfg->audioRate) { AVB_LOGF_ERROR("current_sampling_rate(%u) and intf_nv_audio_rate(%u) do not match.", pCfg->current_sampling_rate, pCfg->audioRate); AVB_TRACE_EXIT(AVB_TRACE_AVDECC); return FALSE; } if (pCfg->current_sampling_rate == 0) { /* Make sure we have a default sampling rate. */ if (pCfg->audioRate != 0) { pCfg->current_sampling_rate = pCfg->audioRate; } else if (pCfg->sampling_rates[0] != 0) { pCfg->current_sampling_rate = pCfg->sampling_rates[0]; } else { pCfg->current_sampling_rate = AVB_AUDIO_RATE_48KHZ; } if (pCfg->audioRate == 0) { AVB_LOGF_WARNING("current_sampling_rate not specified in %s. Defaulting to %u", fileName, pCfg->current_sampling_rate); } } if (pCfg->sampling_rates_count == 0) { /* Set the list of sampling rates to the current sampling rate. */ pCfg->sampling_rates[0] = pCfg->current_sampling_rate; pCfg->sampling_rates_count = 1; if (pCfg->audioRate == 0) { AVB_LOGF_WARNING("sampling_rates not specified in %s. Defaulting to %u", fileName, pCfg->current_sampling_rate); } } else { /* Make sure the current sampling rate is in the list of sampling rates. */ U16 i; for (i = 0; i < pCfg->sampling_rates_count; ++i) { if (pCfg->sampling_rates[i] == pCfg->current_sampling_rate) break; } if (i >= pCfg->sampling_rates_count) { AVB_LOGF_ERROR("current_sampling_rate(%u) not in list of sampling_rates.", pCfg->current_sampling_rate); AVB_TRACE_EXIT(AVB_TRACE_AVDECC); return FALSE; } } AVB_TRACE_EXIT(AVB_TRACE_AVDECC); return TRUE; }