diff options
Diffstat (limited to 'Src')
43 files changed, 9448 insertions, 0 deletions
diff --git a/Src/AutoLock.h b/Src/AutoLock.h new file mode 100644 index 0000000..35404cf --- /dev/null +++ b/Src/AutoLock.h @@ -0,0 +1,57 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CThread class. + */ +/*----------------------------------------------------------*/ +#ifndef AUTOLOCK_H +#define AUTOLOCK_H + +#include <pthread.h> +#include <assert.h> + +/*----------------------------------------------------------*/ +/*! \brief Helper class to deal with automatic Mutexes. + */ +/*----------------------------------------------------------*/ +class CAutoLock +{ + pthread_mutex_t *m_Mutex; + +public: + CAutoLock(pthread_mutex_t *pMutex) : m_Mutex(NULL) + { + assert(NULL != pMutex); + m_Mutex = pMutex; + pthread_mutex_lock( m_Mutex ); + } + virtual ~CAutoLock() + { + pthread_mutex_unlock( m_Mutex ); + } +}; + +#endif + diff --git a/Src/ConnectionInfo.cpp b/Src/ConnectionInfo.cpp new file mode 100644 index 0000000..927dcb3 --- /dev/null +++ b/Src/ConnectionInfo.cpp @@ -0,0 +1,712 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include <stdint.h> +#include <stdbool.h> +#include <stddef.h> +#include <string.h> +#include "SafeVector.h" +#include "MacAddr.h" +#include "Types.h" +#include "Console.h" +#include "ConnectionInfo.h" + +#define MY_IPC_HEADER "most mac dev-type instance channel-id type bandwidth direction con-label buffer state offset offset-max p-xact dev-name" +#define MY_IPC_HEADER_LINE_END MY_IPC_HEADER"\n" + +CConnectionInfo::CConnectionInfo() : deviceType( 0 ), deviceInstance( 0 ), channelId( 0 ), mostInstance( 0 ), + dataType( EP_Unknown ), reservedBandwidth( 0 ), bufferSize( 0 ), + isTX( false), inSocketCreated( false ), outSocketCreated( false ), socketsConnected( false ), + mostConnectionLabel( 0xFFFFFFFF ), splittedOffset( 0 ), splittedMaxBandwidth( 0 ), packetsPerXact( 0 ) +{ + macAddr = new CMacAddr(); + deviceName[0] = '\0'; +} + +CConnectionInfo::CConnectionInfo( CMacAddr *macAddr, TChannelId cId, uint8_t mInst, + bool isTx ) : deviceType( 0 ), deviceInstance( 0 ), channelId( cId ), mostInstance(mInst), + dataType( EP_Unknown ), reservedBandwidth( 0 ), bufferSize( 0 ), isTX( isTx ), + inSocketCreated( false ), outSocketCreated( false ), socketsConnected( false ), + mostConnectionLabel( 0xFFFFFFFF ), splittedOffset( -1 ), splittedMaxBandwidth( -1 ), + packetsPerXact( 0 ) +{ + this->macAddr = new CMacAddr( macAddr ); //Copy the object + deviceName[0] = '\0'; +} + +CConnectionInfo::~CConnectionInfo() +{ + if( NULL != macAddr ) + { + delete macAddr; + macAddr = NULL; + } +} + +CConnectionInfoContainer::CConnectionInfoContainer() +{ +} + +CConnectionInfoContainer::~CConnectionInfoContainer() +{ + DestroyAllInfos(); +} + +uint32_t CConnectionInfoContainer::GetAllInfoAmount() +{ + return allInfos.Size(); +} + +CConnectionInfo *CConnectionInfoContainer::GetInfo( uint32_t index ) +{ + if( index >= allInfos.Size() ) + { + ConsolePrintf( PRIO_ERROR, + RED"CConnectionInfoContainer::GetInfo was called with index out of range"RESETCOLOR"\n" ); + return NULL; + } + return allInfos[index]; +} + +bool CConnectionInfoContainer::Compare( CConnectionInfo *c, CConnectionInfo *t ) +{ + if( NULL == c || NULL == t ) + { + ConsolePrintf( PRIO_ERROR, RED"Compare parameter error"RESETCOLOR"\n" ); + return false; + } + return ( ( c->bufferSize == t->bufferSize ) + && ( c->channelId == t->channelId ) + && ( c->dataType == t->dataType ) + && ( c->deviceType == t->deviceType ) + && ( c->mostConnectionLabel == t->mostConnectionLabel ) + && ( c->mostInstance == t->mostInstance ) + && ( c->reservedBandwidth == t->reservedBandwidth ) + && ( 0 == strcmp( c->deviceName, t->deviceName ) ) + && ( c->isTX == t->isTX ) + && ( *c->macAddr == *t->macAddr ) + && ( c->packetsPerXact == t->packetsPerXact ) + ); +} + +bool CConnectionInfoContainer::Compare( CConnectionInfoContainer *remote ) +{ + if( NULL == remote || + ( remote->GetAllInfoAmount() != GetAllInfoAmount() ) ) + return false; + bool equal = true; + for( uint32_t i = 0; equal && i < allInfos.Size(); i++ ) + { + equal = false; + CConnectionInfo *t = allInfos[i]; + if( NULL == t ) + break; + for( uint32_t j = 0; !equal && j < remote->GetAllInfoAmount(); j++ ) + { + CConnectionInfo *c = remote->GetInfo( j ); + if( NULL == c ) + break; + if( Compare( c, t ) ) + { + equal = true; + break; + } + } + } + return equal; +} + +bool CConnectionInfoContainer::ExistsEntry( CConnectionInfo *c ) +{ + if( NULL == c ) + return false; + bool exists = false; + for( uint32_t i = 0; !false && i < allInfos.Size(); i++ ) + { + CConnectionInfo *t = allInfos[i]; + if( NULL == t ) + break; + if( Compare( c, t ) ) + { + exists = true; + break; + } + } + return exists; +} + +CConnectionInfo *CConnectionInfoContainer::GetInfo( CMacAddr *macAddr, TChannelId channelId, uint8_t mostInstance ) +{ + CConnectionInfo *instance = NULL; + if( NULL == macAddr ) + { + return NULL; + } + for( uint32_t i = 0; i < allInfos.Size(); i++ ) + { + CConnectionInfo *temp = allInfos[i]; + if( NULL != temp && *temp->macAddr == *macAddr + && temp->channelId == channelId + && temp->mostInstance == mostInstance ) + { + instance = temp; + break; + } + } + return instance; +} + +uint8_t CConnectionInfoContainer::GetAmountOfDevices( TMostInstace mostInstance, TDeviceId deviceId ) +{ + uint8_t amount = 0; + CSafeVector<CMacAddr *> allMacs; + for( uint32_t i = 0; i < allInfos.Size(); i++ ) + { + CConnectionInfo *temp = allInfos[i]; + if( NULL != temp && temp->deviceType == deviceId + && temp->mostInstance == mostInstance + && NULL != temp->macAddr ) + { + bool update = true; + for( uint32_t j = 0; j < allMacs.Size(); j++ ) + { + if( *allMacs[j] == *temp->macAddr ) + { + update = false; + break; + } + } + if( update ) + { + allMacs.PushBack( temp->macAddr ); + ++amount; + } + } + } + return amount; +} + +CConnectionInfo *CConnectionInfoContainer::GetInfo( CMacAddr *macAddr, TChannelId channelId, uint8_t mostInstance, + bool isTx ) +{ + CConnectionInfo *instance = GetInfo( macAddr, channelId, mostInstance ); + if( NULL == instance ) + { + instance = new CConnectionInfo( macAddr, channelId, mostInstance, isTx ); + allInfos.PushBack( instance ); + } + return instance; +} + +void CConnectionInfoContainer::AddInfo( CConnectionInfo *info ) +{ + if( NULL == info ) + { + ConsolePrintf( PRIO_ERROR, RED"AddInfo parameter error"RESETCOLOR"\n" ); + return; + } + allInfos.PushBack( info ); +} + +void CConnectionInfoContainer::DestroyAllInfos() +{ + allInfos.RemoveAll(true); +} + +void CConnectionInfoContainer::DestroyInfo( CMacAddr *macAddr, TChannelId channelId, uint8_t mostInstance ) +{ + if( NULL == macAddr ) + { + ConsolePrintf( PRIO_ERROR, RED"DestroyInstance was called with invalid parameters."RESETCOLOR"\n" ); + return; + } + for( uint32_t i = 0; i < allInfos.Size(); i++ ) + { + CConnectionInfo *temp = allInfos[i]; + if( NULL != temp && *temp->macAddr == *macAddr + && temp->channelId == channelId + && temp->mostInstance == mostInstance ) + { + allInfos.Remove(temp); + delete temp; + break; + } + } +} + +void CConnectionInfoContainer::PrintTable( bool hideTx, bool hideRx ) +{ + char macString[32]; + char sizeString[10]; + char stateString[32]; + char offsetString[32]; + char lastDeviceName[64]; + const char *typeString; + int16_t lastMaxOffset = -1; + + lastDeviceName[0] = '\0'; + ConsolePrintfStart( PRIO_HIGH, + "Index | MOST | MAC-ADDRESS | DEV | INST | ID | Type | BW | Dir | Con-Label | Buffer | State |DEVICE-NAME \n" ); + ConsolePrintfContinue( + "------|------|-------------------|------|------|-----|-------|-----|-----|-----------|--------|--------|-------------\n" ); + for( uint32_t i = 0; i < allInfos.Size(); i++ ) + { + CConnectionInfo *temp = allInfos[i]; + if ( NULL == temp ) + continue; + + if( hideTx && temp->isTX ) + continue; + + if( hideRx && !temp->isTX ) + continue; + + switch( temp->dataType ) + { + case EP_Synchron: + typeString = "SYNC "; + break; + case EP_Asynchron: + typeString = "ASYNC"; + break; + case EP_Control: + typeString = "CTRL "; + break; + case EP_Isochron: + typeString = "ISOC "; + break; + case EP_Unused: + typeString = "EMPTY"; + break; + case EP_Unknown: + default: + typeString = "ERROR"; + break; + } + + if( NULL == temp->macAddr || NULL == temp->macAddr->ToString() ) + strncpy( macString, "Unknown MAC Addr.", sizeof( macString ) ); + else + strncpy( macString, temp->macAddr->ToString(), sizeof( macString ) ); + + if( 0 > temp->bufferSize ) + strncpy( sizeString, "None", sizeof( sizeString ) ); + else + snprintf( sizeString, sizeof ( sizeString ), "%04d", temp->bufferSize ); + + + const char *goodStr = GREEN"X"; + const char *badStr = RED"o"; + snprintf( stateString, sizeof ( stateString ), "%s%s%s"RESETCOLOR, ( temp->inSocketCreated ? goodStr : + badStr ), ( temp->outSocketCreated ? goodStr : badStr ), ( temp->socketsConnected ? goodStr : + badStr ) ); + + offsetString[0] = '\0'; + if( -1 != temp->splittedOffset && -1 != temp->splittedMaxBandwidth ) + { + if( 0 == temp->splittedOffset ) + { + lastMaxOffset = temp->splittedMaxBandwidth; + if( 0 != strlen( temp->deviceName ) ) + strncpy( lastDeviceName, temp->deviceName, sizeof( lastDeviceName ) ); + } + snprintf( offsetString, sizeof( offsetString ), " (offset %d/%d)", + temp->splittedOffset, ( 0 == temp->splittedOffset ? temp->splittedMaxBandwidth : lastMaxOffset ) ); + } + + ConsolePrintfContinue( "%02d ", i ); + ConsolePrintfContinue( "| %02d ", temp->mostInstance ); + ConsolePrintfContinue( "| %s ", macString ); + ConsolePrintfContinue( "| %03X ", temp->deviceType ); + ConsolePrintfContinue( "| %02d ", temp->deviceInstance ); + ConsolePrintfContinue( "| %03d ", temp->channelId ); + ConsolePrintfContinue( "| %s ", typeString ); + ConsolePrintfContinue( "| %03d ", temp->reservedBandwidth ); + ConsolePrintfContinue( "| %s ", ( temp->isTX ? "TX" : "RX" ) ); + ConsolePrintfContinue( "| %03X ", ( temp->mostConnectionLabel != 0xFFFFFFFF ) ? temp-> + mostConnectionLabel : 0xFFF ); + ConsolePrintfContinue( "| %s ", sizeString ); + ConsolePrintfContinue( "| %s ", stateString ); + ConsolePrintfContinue( "| %s", ( '\0' == offsetString[0] ) ? temp->deviceName : lastDeviceName ); + ConsolePrintfContinue( "%s\n", offsetString ); + } + ConsolePrintfExit( + "------|------|-------------------|------|------|-----|-------|-----|-----|-----------|--------|--------|-------------\n" ); +} + +uint32_t CConnectionInfoContainer::SerializeToString( char *pBuffer, uint32_t bufferLen ) +{ + if( NULL == pBuffer || 0 == bufferLen ) + { + ConsolePrintf( PRIO_ERROR, RED"SerializeToString was called with wrong parameters"RESETCOLOR"\n" ); + return 0; + } + uint32_t bytesUsed = strlen( pBuffer ); + if (bytesUsed > bufferLen) + { + ConsolePrintf(PRIO_ERROR, RED"CConnectionInfoContainer::SerializeToString given buffer"\ + " is too small!"RESETCOLOR); + return 0; + } + const char *typeString = NULL; + char stringBuffer[200]; + strncpy( pBuffer, MY_IPC_HEADER_LINE_END, bufferLen ); + + for( uint32_t i = 0; i < allInfos.Size(); i++ ) + { + CConnectionInfo *sourceInfo = allInfos[i]; + if ( NULL == sourceInfo ) + continue; + switch( sourceInfo->dataType ) + { + case EP_Synchron: + typeString = "SYNC"; + break; + case EP_Asynchron: + typeString = "ASYNC"; + break; + case EP_Control: + typeString = "CTRL"; + break; + case EP_Isochron: + typeString = "ISOC"; + break; + case EP_Unused: + typeString = "EMPTY"; + break; + case EP_Unknown: + default: + typeString = "ERROR"; + break; + } + + snprintf( stringBuffer, sizeof ( stringBuffer ), "%d %s %d %d %d %s %d %s %d %d %c%c%c %d %d %d %s\n", + sourceInfo->mostInstance, sourceInfo->macAddr->ToString(), sourceInfo->deviceType, + sourceInfo->deviceInstance, sourceInfo->channelId, typeString, + sourceInfo->reservedBandwidth, ( sourceInfo->isTX ? "TX" : "RX" ), sourceInfo->mostConnectionLabel, + sourceInfo->bufferSize, ( sourceInfo->inSocketCreated ? 'X' : '0' ), ( sourceInfo->outSocketCreated ? 'X' : + '0' ), ( sourceInfo->socketsConnected ? 'X' : '0' ), sourceInfo->splittedOffset, + sourceInfo->splittedMaxBandwidth, sourceInfo->packetsPerXact, sourceInfo->deviceName ); + strncat( pBuffer, stringBuffer, ( bufferLen - bytesUsed ) ); + bytesUsed = strlen( pBuffer ); + } + + return strlen( pBuffer ) + 1; +} + +bool CConnectionInfoContainer::DeserializeFromString( char *pBuffer ) +{ + if( NULL == pBuffer || 0 == strlen( pBuffer ) ) + { + ConsolePrintf( PRIO_ERROR, RED"DeserializeFromString was called with wrong parameters"RESETCOLOR"\n" ); + return false; + } + DestroyAllInfos(); + char *helpPtr; + char *token = strtok_r( pBuffer, "\n", &helpPtr ); + bool first = true; + while( NULL != token ) + { + if( first ) + { + first = false; + if( 0 != strcmp( MY_IPC_HEADER, token ) ) + { + ConsolePrintf( PRIO_ERROR, + RED"DeserializeFromString: Incompatible header found, aborting\n"\ + "Expected:'%s'\n"\ + "Got: '%s'"\ + RESETCOLOR"\n", MY_IPC_HEADER, token ); + return false; + } + } + else + { + CConnectionInfo *info = new CConnectionInfo(); + uint32_t mostInstance; + char macString[20]; + uint32_t deviceType; + uint32_t deviceInstance; + uint32_t channelId; + char typeString[10]; + uint32_t reservedBandwidth; + char deviceDirection[10]; + uint32_t mostConnectionLabel; + uint32_t bufferSize; + int32_t offset; + int32_t maxOffset; + uint32_t xact; + char connectString[10]; + sscanf( token, "%d %s %d %d %d %s %d %s %d %d %s %d %d %d %s\n", &mostInstance, + macString, &deviceType, &deviceInstance, &channelId, typeString, &reservedBandwidth, + deviceDirection, &mostConnectionLabel, &bufferSize, connectString, &offset, &maxOffset, + &xact, info->deviceName ); + + info->mostInstance = mostInstance; + info->deviceType = deviceType; + info->deviceInstance = deviceInstance; + info->channelId = channelId; + info->reservedBandwidth = reservedBandwidth; + info->mostConnectionLabel = mostConnectionLabel; + info->bufferSize = bufferSize; + info->splittedOffset = offset; + info->splittedMaxBandwidth = maxOffset; + info->packetsPerXact = xact; + + info->isTX = strstr( deviceDirection, "TX" ); + + if( NULL != connectString && 3 == strlen( connectString ) ) + { + info->inSocketCreated = connectString[0] == 'X'; + info->outSocketCreated = connectString[1] == 'X'; + info->socketsConnected = connectString[2] == 'X'; + } + + if( NULL == info->macAddr ) + info->macAddr = new CMacAddr(); + info->macAddr->CopyValuesFromString( macString ); + + if( strstr( typeString, "ASYNC" ) ) + { + info->dataType = EP_Asynchron; + } + else if( strstr( typeString, "SYNC" ) ) + { + info->dataType = EP_Synchron; + } + else if( strstr( typeString, "CTRL" ) ) + { + info->dataType = EP_Control; + } + else if( strstr( typeString, "ISOC" ) ) + { + info->dataType = EP_Isochron; + } + else if( strstr( typeString, "EMPTY" ) ) + { + info->dataType = EP_Unused; + } + else + { + info->dataType = EP_Unknown; + } + allInfos.PushBack( info ); + } + token = strtok_r( NULL, "\n", &helpPtr ); + } + return true; +} + +uint32_t CConnectionInfoContainer::GetAmountOfCDevs() +{ + uint32_t amount = 0; + for( uint32_t i = 0; i < allInfos.Size(); i++ ) + { + CConnectionInfo *temp = allInfos[i]; + if( NULL != temp && NULL != temp->deviceName && 0 != strlen( temp->deviceName ) ) + ++amount; + } + return amount; +} + +CConnectionInfo *CConnectionInfoContainer::GetConnectionInfoOfCdev( uint32_t cdevInst ) +{ + uint32_t amount = 0; + CConnectionInfo *info = NULL; + for( uint32_t i = 0; i < allInfos.Size(); i++ ) + { + CConnectionInfo *temp = allInfos[i]; + if( NULL != temp && NULL != temp->deviceName && 0 != strlen( temp->deviceName ) ) + { + if( cdevInst == amount ) + { + info = temp; + break; + } + ++amount; + } + } + return info; +} + +CConnectionInfo *CConnectionInfoContainer::GetConnectionInfoOfCdev( uint32_t cdevInst, uint32_t splittedIndex ) +{ + uint32_t amountCdev = 0; + uint32_t amountSplit = 0; + CConnectionInfo *infoCdev = NULL; + CConnectionInfo *infoSplitted = NULL; + for( uint32_t i = 0; i < allInfos.Size(); i++ ) + { + CConnectionInfo *temp = allInfos[i]; + if( NULL == infoCdev ) + { + if( NULL != temp && NULL != temp->deviceName && 0 != strlen( temp->deviceName ) ) + { + if( cdevInst == amountCdev ) + { + infoCdev = temp; + if( 0 != infoCdev->splittedOffset ) + { + ConsolePrintf( PRIO_ERROR, + RED"CConnectionInfoContainer::GetConnectionInfoOfCdev"\ + " was called for a non splitted CDEV"RESETCOLOR"\n" ); + break; + } + } + ++amountCdev; + } + } + else + { + if( ( 0 == temp->splittedOffset ) || ( -1 == temp->splittedOffset ) ) + break; + if( amountSplit == splittedIndex ) + { + infoSplitted = temp; + break; + } + ++amountSplit; + } + } + return infoSplitted; +} + +uint32_t CConnectionInfoContainer::GetAmountOfSinks() +{ + uint32_t amount = 0; + for( uint32_t i = 0; i < allInfos.Size(); i++ ) + { + CConnectionInfo *sourceInfo = allInfos[i]; + if( NULL == sourceInfo || NULL == sourceInfo->macAddr || NULL == sourceInfo->macAddr->ToString() ) + continue; + if( NULL == sourceInfo->deviceName || 0 == strlen( sourceInfo->deviceName ) ) + continue; + if( !sourceInfo->socketsConnected ) + continue; + for( uint32_t j = 0; j < allInfos.Size(); j++ ) + { + CConnectionInfo *sinkInfo = allInfos[j]; + if( i == j ) + continue; + if( NULL == sinkInfo->macAddr || NULL == sinkInfo->macAddr->ToString() ) + continue; + if( !sinkInfo->socketsConnected ) + continue; + if( sourceInfo->mostConnectionLabel != sinkInfo->mostConnectionLabel ) + continue; + if( sourceInfo->mostInstance != sinkInfo->mostInstance ) + continue; + ++amount; + } + } + return amount; +} + +CConnectionInfo *CConnectionInfoContainer::GetConnectionInfoOfSink( uint32_t sinkInst ) +{ + uint32_t amount = 0; + CConnectionInfo *info = NULL; + for( uint32_t i = 0; NULL == info && i < allInfos.Size(); i++ ) + { + CConnectionInfo *sourceInfo = allInfos[i]; + if( NULL == sourceInfo || NULL == sourceInfo->macAddr || NULL == sourceInfo->macAddr->ToString() ) + continue; + if( NULL == sourceInfo->deviceName || 0 == strlen( sourceInfo->deviceName ) ) + continue; + if( !sourceInfo->socketsConnected ) + continue; + for( uint32_t j = 0; NULL == info && j < allInfos.Size(); j++ ) + { + CConnectionInfo *sinkInfo = allInfos[j]; + if( i == j ) + continue; + if( NULL == sinkInfo->macAddr || NULL == sinkInfo->macAddr->ToString() ) + continue; + if( !sinkInfo->socketsConnected ) + continue; + if( sourceInfo->mostConnectionLabel != sinkInfo->mostConnectionLabel ) + continue; + if( sourceInfo->mostInstance != sinkInfo->mostInstance ) + continue; + if( sinkInst == amount ) + { + info = sinkInfo; + } + ++amount; + } + } + return info; +} + +CConnectionInfo *CConnectionInfoContainer::GetConnectionInfoByMacAddress( const uint8_t *mac ) +{ + if( NULL == mac ) + return NULL; + char macAddr[20]; + snprintf( ( char * )&macAddr, sizeof( macAddr ), "%02X-%02X-%02X-%02X-%02X-%02X", mac[0], mac[1], mac[2], mac[3], + mac[4], mac[5] ); + CConnectionInfo *info = NULL; + for( uint32_t i = 0; i < allInfos.Size(); i++ ) + { + CConnectionInfo *temp = allInfos[i]; + if( NULL != temp && NULL != temp->macAddr ) + { + if( 0 == strcmp( temp->macAddr->ToString(), macAddr ) ) + { + info = temp; + break; + } + } + } + return info; +} + +CConnectionInfo *CConnectionInfoContainer::GetRemoteConnectionInfoByMacAddress( const uint8_t *mac ) +{ + if( NULL == mac ) + return NULL; + char macAddr[20]; + snprintf( ( char * )&macAddr, sizeof( macAddr ), "%02X-%02X-%02X-%02X-%02X-%02X", mac[0], mac[1], mac[2], mac[3], + mac[4], mac[5] ); + CConnectionInfo *remoteInfo = NULL; + CConnectionInfo *info = GetConnectionInfoByMacAddress( mac ); + if( NULL == info ) + return NULL; + for( uint32_t i = 0; i < allInfos.Size(); i++ ) + { + CConnectionInfo *temp = allInfos[i]; + if( NULL != temp && NULL != temp->macAddr ) + { + if( 0 == strcmp( temp->macAddr->ToString(), macAddr ) ) + continue; + if( ( temp->mostInstance == info->mostInstance ) && + ( temp->mostConnectionLabel == info->mostConnectionLabel ) && + temp->socketsConnected ) + { + remoteInfo = temp; + break; + } + } + } + return remoteInfo; +} diff --git a/Src/ConnectionInfo.h b/Src/ConnectionInfo.h new file mode 100644 index 0000000..13016f5 --- /dev/null +++ b/Src/ConnectionInfo.h @@ -0,0 +1,316 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains a helper class to store MOST connections + */ +/*----------------------------------------------------------*/ +#ifndef CONNECTIONINFO_H +#define CONNECTIONINFO_H + +#include <stdint.h> +#include <stdbool.h> +#include <stddef.h> +#include "SafeVector.h" +#include "Types.h" +#include "MacAddr.h" + +/*! + * \brief This is a helper class for the CNetworkManagerHandler. It keeps track of the reported connections + * and the instanced CMultiplexer classes. + */ +class CConnectionInfo +{ +public: + CConnectionInfo(); + CConnectionInfo( CMacAddr *macAddr, TChannelId channelId, uint8_t mostInstance, bool isTx ); + ~CConnectionInfo(); + + ///The Ethernet MAC address of this connection. + CMacAddr *macAddr; + + ///The device type as specified in XML file and configured in the config string (Group-Address) + TDeviceId deviceType; + + ///The instance count of this device class. Starting at 0 for the first instance. Incremented with every occurrence. + uint8_t deviceInstance; + + ///The channel ID as declared in the configuration XML file. + TChannelId channelId; + + ///The MOST ring instance. Starting at 0 for the first MOST ring, on the first OS81110. + TMostInstace mostInstance; + + ///The MOST data used by this channel. + EPDataType_t dataType; + + ///The reserved MOST bandwidth in bytes for this channel. + TBlockwidth reservedBandwidth; + + ///The amount of bytes reserved for this channel in Driver. If no buffers were allocated, this value is -1. + int32_t bufferSize; + + ///In case this is a local connection, the path of the character device is stored in this variable. (e.g. /dev/mdev0). + char deviceName[64]; + + ///TRUE if this connections is a source. FALSE if this connection is a sink, + bool isTX; + + //TRUE, if the input socket of this connection was successfully created. + bool inSocketCreated; + + //TRUE, if the output socket of this connection was successfully created. + bool outSocketCreated; + + //TRUE, if the input socket and the output socket of this connection were successfully connected. + bool socketsConnected; + + ///The MOST connection label. + uint32_t mostConnectionLabel; + + ///If this socket is a splitted / combined socket, this value holds the offset in byte. -1 if this is a normal socket. + int16_t splittedOffset; + + ///If this socket is a splitted / combined socket, this value holds the maximum bandwidth of all splitted sockets in byte. -1 if this is a normal socket. + int16_t splittedMaxBandwidth; + + ///This is USB specific. It describes how many sub-frames are concatenated into a single USB microframe. + uint16_t packetsPerXact; +}; + +/*! + * \brief This is a storage class holding multiple CConnectionInfo in a container. + * It supports adding, removing, serializing and deserializing + */ +class CConnectionInfoContainer +{ +private: + CSafeVector<CConnectionInfo *> allInfos; + bool Compare( CConnectionInfo *c, CConnectionInfo *t ); +public: + + /*----------------------------------------------------------*/ + /*! \brief Default Constructor of CConnectionInfoContainer + */ + /*----------------------------------------------------------*/ + CConnectionInfoContainer(); + + /*----------------------------------------------------------*/ + /*! \brief Default Destructor of CConnectionInfoContainer + */ + /*----------------------------------------------------------*/ + ~CConnectionInfoContainer(); + + + + /*----------------------------------------------------------*/ + /*! \brief Gets the amount of entries stored in the whole database. + * \return The amount of entries. + */ + /*----------------------------------------------------------*/ + uint32_t GetAllInfoAmount(); + + + + /*----------------------------------------------------------*/ + /*! \brief Gets the information at the corresponding position. + * \param index - the position inside the database + * \note Use GetAllInfoAmount() to get the max index (n-1). + * \return The information if found, NULL if out of range + */ + /*----------------------------------------------------------*/ + CConnectionInfo *GetInfo( uint32_t index ); + + + /*----------------------------------------------------------*/ + /*! \brief Compares to container. + * \param containerToCompare - The container to compare. + * \return true, if both containers stores the same information. false, otherwise. + */ + /*----------------------------------------------------------*/ + bool Compare( CConnectionInfoContainer *containerToCompare ); + + + + /*----------------------------------------------------------*/ + /*! \brief Checks if the given information is already stored inside the database. + * \param checkInfo - the information object to be checked + * \return true, if the information is already stored inside the database. false, otherwise. + */ + /*----------------------------------------------------------*/ + bool ExistsEntry( CConnectionInfo *checkInfo ); + + + /*----------------------------------------------------------*/ + /*! \brief Gets an info object with the given parameters + * \param macAddr - The MAC address of the device + * \param channelId - The channel identifier as used in the XML file. + * \param mostInstance - The MOST ring instance, starting 0 for the first MOST instance. + * \return The info object if found. NULL, otherwise. + */ + /*----------------------------------------------------------*/ + CConnectionInfo *GetInfo( CMacAddr *macAddr, TChannelId channelId, uint8_t mostInstance ); + + + /*----------------------------------------------------------*/ + /*! \brief Gets the amount of devices with the same + * \param mostInstance - The MOST ring instance. Starting at 0 for the first MOST ring. + * \param deviceId - The device ID as declared in the configuration XML file. + * \return The Amount of devices found, with the given deviceId + */ + /*----------------------------------------------------------*/ + uint8_t GetAmountOfDevices( TMostInstace mostInstance, TDeviceId deviceId ); + + + /*----------------------------------------------------------*/ + /*! \brief Helper function, which delivers a CConnectionInfo object to the given search parameters. + * \param macAddr - Searches for the given MAC address. + * \param channelId - Search for the given Channel address. + * \param mostInstance - Search on the given MOST ring instance. + * \param isTx - true, search for a transmission connection. false, search for a reception connection. + * \return An pointer to the matching CConnectionInfo object if found. NULL otherwise. + */ + /*----------------------------------------------------------*/ + CConnectionInfo *GetInfo( CMacAddr *macAddr, TChannelId channelId, uint8_t mostInstance, bool isTx ); + + + /*----------------------------------------------------------*/ + /*! \brief Stores the given information inside the database. + * \param info - The information to be stored. + */ + /*----------------------------------------------------------*/ + void AddInfo( CConnectionInfo *info ); + + + /*----------------------------------------------------------*/ + /*! \brief Helper function, which deletes all stored informations retrieved so far. + */ + /*----------------------------------------------------------*/ + void DestroyAllInfos(); + + + /*----------------------------------------------------------*/ + /*! \brief Helper function, which delete the information matching to the given parameters. + * \param macAddr - Searches for the given MAC address. + * \param channelId - Search for the given Channel address. + * \param mostInstance - Search on the given MOST ring instance. + */ + /*----------------------------------------------------------*/ + void DestroyInfo( CMacAddr *macAddr, TChannelId channelId, uint8_t mostInstance ); + + /*----------------------------------------------------------*/ + /*! \brief Helper function, which prints all available information to the console. + * \param hideTx - true, when transmission connections shall not be shown in the table. + * \param hideRx - true, when reception connections shall not be shown in the table. + */ + /*----------------------------------------------------------*/ + void PrintTable( bool hideTx, bool hideRx ); + + /*----------------------------------------------------------*/ + /*! \brief Serializes all connection informations to multi line string. + * \param pBuffer, buffer which then will hold the serialized string. + * \param bufferLen, the size of the given buffer. + * \return The amount of bytes used from the given buffer. + */ + /*----------------------------------------------------------*/ + uint32_t SerializeToString( char *pBuffer, uint32_t bufferLen ); + + /*----------------------------------------------------------*/ + /*! \brief Deserializes all connection informations to multi line string. + * \param pBuffer, buffer which will read of the serialized string. + * \return True, if the string could be used to deserialize all data. False, error occurred. + * \note The given buffer will be manipulated by strtok operation! + */ + /*----------------------------------------------------------*/ + bool DeserializeFromString( char *pBuffer ); + + + /*----------------------------------------------------------*/ + /*! \brief Returns the amount of entries, which contains a CDEV device name + * \return The amount of devices. + */ + /*----------------------------------------------------------*/ + uint32_t GetAmountOfCDevs(); + + + /*----------------------------------------------------------*/ + /*! \brief Retrieves the information of the specific CDEV. + * \param cdevInst - The index of the CDEV device, starting with 0 for the first device. + * \note Use GetAmountOfCDevs() the get the maximum possible index (n-1). + * \return The information object, if found. NULL, otherwise. + */ + /*----------------------------------------------------------*/ + CConnectionInfo *GetConnectionInfoOfCdev( uint32_t cdevInst ); + + + + /*----------------------------------------------------------*/ + /*! \brief Retrieves the information of the specific CDEV. + * \param cdevInst - The index of the CDEV device, starting with 0 for the first device. + * \param splittedIndex - Index of splitted socket entry, starting with 0 for the first splitted entry which has an offset greater than zero. + * \note Use GetAmountOfCDevs() the get the maximum possible index (n-1). + * \return The information object, if found. NULL, otherwise. + */ + /*----------------------------------------------------------*/ + CConnectionInfo *GetConnectionInfoOfCdev( uint32_t cdevInst, uint32_t splittedIndex ); + + + /*----------------------------------------------------------*/ + /*! \brief Returns the amount of entries, which acts as a sink channel. + * \return The amount of devices. + */ + /*----------------------------------------------------------*/ + uint32_t GetAmountOfSinks(); + + + /*----------------------------------------------------------*/ + /*! \brief Retrieves the information of the specific sink. + * \param cdevInst - The index of the sink, starting with 0 for the sink. + * \note Use GetAmountOfSinks() the get the maximum possible index (n-1). + * \return The information object, if found. NULL, otherwise. + */ + /*----------------------------------------------------------*/ + CConnectionInfo *GetConnectionInfoOfSink( uint32_t sinkInst ); + + + + /*----------------------------------------------------------*/ + /*! \brief Gets the information object by the given MAC address. + * \param mac - The MAC address of the searched device. + * \return The information object, if found. NULL, otherwise. + */ + /*----------------------------------------------------------*/ + CConnectionInfo *GetConnectionInfoByMacAddress( const uint8_t *mac ); + + + /*----------------------------------------------------------*/ + /*! \brief Gets the opposite device information connected with given MAC address. + * \param mac - The MAC address of the device, which then the information of connected opposite device is returned. + * \return The information object, if found. NULL, otherwise. + */ + /*----------------------------------------------------------*/ + CConnectionInfo *GetRemoteConnectionInfoByMacAddress( const uint8_t *mac ); +}; + +#endif //CONNECTIONINFO_H diff --git a/Src/Console.c b/Src/Console.c new file mode 100644 index 0000000..4ae52a3 --- /dev/null +++ b/Src/Console.c @@ -0,0 +1,209 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include <stdio.h> +#include <stdbool.h> +#include <stdlib.h> +#include <pthread.h> +#include <stdarg.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ipc.h> +#include <sys/shm.h> +#include "Console.h" + +///Our shared memory key +static int shmkey = 072162537; +//The minimum priority +static ConsolePrio_t minPrio = PRIO_LOW; + +/*! \cond PRIVATE */ +typedef struct +{ + ///The first process will setup the critical section and set this variable to true. + bool initialized; + ///If set to true, multiple processeses are synced via shared memory. + bool processSynced; + ///If set to true, there is an segmented print ongoing (Start, Continue, Exit). + bool criticalSection; + //If is in a critical segmented print, this variable will hold the prio for Start, Continue, Exit. + ConsolePrio_t criticalSectionPrio; + ///Handle of the shared mutex. + pthread_mutex_t mutex; +} sharedData_t; +/*! \endcond */ + +/*----------------------------------------------------------*/ +/*! \brief Pointer to the shared memory instance. + */ +/*----------------------------------------------------------*/ +static sharedData_t *data = NULL; + +void ConsoleInit( bool synchronizeProcesses ) +{ + pthread_mutexattr_t attr; + + if( synchronizeProcesses ) + { + int shmid = shmget( shmkey, sizeof( sharedData_t ), IPC_CREAT | 0666 ); + if( ( sharedData_t * )-1 == ( data = ( sharedData_t* )shmat( shmid, NULL, 0 ) ) ) + { + data = NULL; + fprintf( stderr, RED"ConsoleInit failed, because shared memory could not be accessed."RESETCOLOR"\n" ); + return; + } + } + else + { + data = ( sharedData_t * )calloc( 1, sizeof( sharedData_t ) ); + } + if( ( NULL != data ) && !data->initialized ) + { + data->processSynced = synchronizeProcesses; + data->initialized = true; + data->criticalSection = false; + + pthread_mutexattr_init( &attr ); + if( synchronizeProcesses ) + pthread_mutexattr_setpshared( &attr, PTHREAD_PROCESS_SHARED ); + else + pthread_mutexattr_setpshared( &attr, PTHREAD_PROCESS_PRIVATE ); + pthread_mutex_init( &data->mutex, &attr ); + } +} + +void ConsoleDeinit( void ) +{ + if( NULL != data && !data->processSynced ) + { + free( data ); + data = NULL; + } +} + +void ConsoleSetPrio( ConsolePrio_t prio ) +{ + minPrio = prio; +} + +void ConsolePrintf( ConsolePrio_t prio, const char *statement, ... ) +{ + int err; + if( prio < minPrio || NULL == statement ) + return; + if( NULL == data ) + { + fprintf( stderr, RED"ConsolePrintf data was null"RESETCOLOR"\n" ); + return; + } + if( 0 != ( err = pthread_mutex_lock( &data->mutex ) ) ) + { + fprintf( stderr, RED"ConsolePrintf, pthread_mutex_lock error: %d"RESETCOLOR"\n", err ); + return; + } + + va_list args; + va_start( args, statement ); + vfprintf( stderr, statement, args ); + va_end( args ); + + if( 0 != ( err = pthread_mutex_unlock( &data->mutex ) ) ) + { + fprintf( stderr, RED"ConsolePrintf, pthread_mutex_unlock error: %d"RESETCOLOR"\n", err ); + return; + } +} + +void ConsolePrintfStart( ConsolePrio_t prio, const char *statement, ... ) +{ + int err; + if( NULL == data ) + { + fprintf( stderr, RED"ConsolePrintfStart data was null"RESETCOLOR"\n" ); + return; + } + if( 0 != ( err = pthread_mutex_lock( &data->mutex ) ) ) + { + fprintf( stderr, RED"ConsolePrintfStart, pthread_mutex_lock error: %d"RESETCOLOR"\n", err ); + return; + } + data->criticalSection = true; + data->criticalSectionPrio = prio; + + if( data->criticalSectionPrio >= minPrio && NULL != statement ) + { + va_list args; + va_start( args, statement ); + vfprintf( stderr, statement, args ); + va_end( args ); + } +} + +void ConsolePrintfContinue( const char *statement, ... ) +{ + if( NULL == data ) + { + fprintf( stderr, RED"ConsolePrintfContinue data was null"RESETCOLOR"\n" ); + return; + } + if( !data->criticalSection ) + { + fprintf( stderr, RED"ConsolePrintfContinue not in critical section"RESETCOLOR"\n" ); + return; + } + + if( data->criticalSectionPrio >= minPrio && NULL != statement ) + { + va_list args; + va_start( args, statement ); + vfprintf( stderr, statement, args ); + va_end( args ); + } +} + +void ConsolePrintfExit( const char *statement, ... ) +{ + int err; + if( NULL == data ) + { + fprintf( stderr, RED"ConsolePrintfExit data was null"RESETCOLOR"\n" ); + return; + } + if( !data->criticalSection ) + { + fprintf( stderr, RED"ConsolePrintfExit not in critical section"RESETCOLOR"\n" ); + return; + } + if( data->criticalSectionPrio >= minPrio && NULL != statement ) + { + va_list args; + va_start( args, statement ); + vfprintf( stderr, statement, args ); + va_end( args ); + } + data->criticalSection = false; + if( 0 != ( err = pthread_mutex_unlock( &data->mutex ) ) ) + { + fprintf( stderr, RED"ConsolePrintfExit, pthread_mutex_unlock error: %d"RESETCOLOR"\n", err ); + } +} diff --git a/Src/Console.h b/Src/Console.h new file mode 100644 index 0000000..aab7e71 --- /dev/null +++ b/Src/Console.h @@ -0,0 +1,113 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains C-functions starting with "Console" to provide + * process and thread safe access to the console output. + */ +/*----------------------------------------------------------*/ +#ifndef _CONSOLE_H_ +#define _CONSOLE_H_ + +#define RESETCOLOR "\033[0m" +#define GREEN "\033[0;32m" +#define RED "\033[0;31m" +#define YELLOW "\033[1;33m" +#define BLUE "\033[0;34m" + +#include <stdbool.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef enum + { + PRIO_LOW = 0, + PRIO_MEDIUM = 1, + PRIO_HIGH = 2, + PRIO_ERROR = 0xFF + } ConsolePrio_t; + + /*----------------------------------------------------------*/ + /*! \brief Initializes the resources needed to synchronize between processes and threads. + * \note This function must be called before any other function of this component. + * \param synchronizeProcesses - If set to true, multiple processes using this component will synchronize their output. + * + */ + /*----------------------------------------------------------*/ + void ConsoleInit( bool synchronizeProcesses ); + + /*----------------------------------------------------------*/ + /*! \brief Destroys the resources needed to synchronize between processes and threads. + * \note After this function, any other function (except ConsoleInit) must not be called. + * + */ + /*----------------------------------------------------------*/ + void ConsoleDeinit( void ); + + + /*----------------------------------------------------------*/ + /*! \brief Sets the minimum priority to be displayed. Lower priority messages are discarded + * \param prio - The minimum priority to display + */ + /*----------------------------------------------------------*/ + void ConsoleSetPrio( ConsolePrio_t prio ); + + /*----------------------------------------------------------*/ + /*! \brief Uses the board specific PRINT mechanism and provides thread and process safety. + * + */ + /*----------------------------------------------------------*/ + void ConsolePrintf( ConsolePrio_t prio, const char *statement, ... ); + + + /*----------------------------------------------------------*/ + /*! \brief Starts to print and stay blocked after exit of this function + * + */ + /*----------------------------------------------------------*/ + void ConsolePrintfStart( ConsolePrio_t prio, const char *statement, ... ); + + /*----------------------------------------------------------*/ + /*! \brief Continue to print and stay blocked after exit of this function + * \note ConsolePrintfStart must be called before and when finished ConsolePrintfExit must be called. + * \note This function may be called multiple times. + */ + /*----------------------------------------------------------*/ + void ConsolePrintfContinue( const char *statement, ... ); + + /*----------------------------------------------------------*/ + /*! \brief Continue to print and unblock after finishing. + * \note ConsolePrintfStart must be called before. ConsolePrintfContinue may have been called before multiple times. + */ + /*----------------------------------------------------------*/ + void ConsolePrintfExit( const char *statement, ... ); + +#ifdef __cplusplus +} +#endif + +#endif //_CONSOLE_H_ diff --git a/Src/DoxyGenStartPage.h b/Src/DoxyGenStartPage.h new file mode 100644 index 0000000..47dc411 --- /dev/null +++ b/Src/DoxyGenStartPage.h @@ -0,0 +1,109 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file generates the start page of the document you are currently reading. + */ +/*----------------------------------------------------------*/ +#ifndef DOXYGEN_START_PAGE_H +#define DOXYGEN_START_PAGE_H + + +/*----------------------------------------------------------*/ +/*! \mainpage + * + * \section intro_sec Introduction + * + * The client device may have MLB attached INIC OS81110 or USB attached INIC OS81118. \n + * All components where written for and tested for the FreeScale i.MX6q platform. \n + * + * \subsection step1 Step 1: Execute Server Application + * + * Login to the server target device. \n + * Switch to the directory, where you have copied all outputs of the previous steps. \n + * Make sure, that all devices are up and running and the MOST bus is well connected. \n + * Start the application. \n + * Enter: + * \code sudo ./VideoOnDemand config.xml \endcode + * + * After a while, you should see the main menu, with available connections: \n + * Display Output: \n + * \code + * Index | MOST | MAC-ADDRESS | DEV | INST | ID | Type | BW | Dir | Multiplex | Con-Label | Buffer | State |DEVICE-NAME + * ------|------|-------------------|------|------|----|-------|-----|-----|-----------|-----------|--------|--------|------------- + * 00 | 00 | 02-00-00-00-00-01 | 310 | 00 | 01 | ISOC | 088 | TX | Yes | 077 | 8192 | XXX | /dev/mdev0-ep0a + * 01 | 00 | 02-00-00-00-00-01 | 310 | 00 | 02 | ISOC | 088 | TX | Yes | 0CF | 8192 | XXX | /dev/mdev0-ep0b + * 02 | 00 | 02-00-00-00-00-01 | 310 | 00 | 03 | ISOC | 088 | TX | Yes | 127 | 8192 | XXX | /dev/mdev0-ep0c + * 03 | 00 | 02-00-00-00-00-01 | 310 | 00 | 04 | ISOC | 088 | TX | Yes | 17F | 8192 | XXX | /dev/mdev0-ep0d + * 04 | 00 | 02-00-00-01-01-01 | 330 | 00 | 01 | ISOC | 088 | RX | No | 077 | None | XXX | + * 05 | 00 | 02-00-00-02-01-02 | 330 | 01 | 01 | ISOC | 088 | RX | No | 0CF | None | XXX | + * ------|------|-------------------|------|------|----|-------|-----|-----|-----------|-----------|--------|--------|------------- + * \endcode + * + * At this point you should be able to connect with either the native C++ application or the Android HMI app.<br/> + * Make sure, you set the IP address correct on all devices. + * + * - In order to check if the streaming is correctly working, enable the the statistics printing. + * - Enter "p" and press Enter key. + * + * Display Output: + * \code + * ==============Statistic Start============== + * Device:/dev/mdev0-ep0a, Stream 02-00-00-00-00-01, sent:0 (kBit/s), stuffed:28700 (kBit/s), read:0 (kBit/s), PCR-error:0, Buf Ovfl-error:0, Buf Unfl-error:0, TS-error:0 + * Device:/dev/mdev0-ep0b, Stream 02-00-00-00-00-01, sent:0 (kBit/s), stuffed:28730 (kBit/s), read:0 (kBit/s), PCR-error:0, Buf Ovfl-error:0, Buf Unfl-error:0, TS-error:0 + * Device:/dev/mdev0-ep0c, Stream 02-00-00-00-00-01, sent:0 (kBit/s), stuffed:33627 (kBit/s), read:0 (kBit/s), PCR-error:0, Buf Ovfl-error:0, Buf Unfl-error:0, TS-error:0 + * Device:/dev/mdev0-ep0d, Stream 02-00-00-00-00-01, sent:0 (kBit/s), stuffed:33627 (kBit/s), read:0 (kBit/s), PCR-error:0, Buf Ovfl-error:0, Buf Unfl-error:0, TS-error:0 + * Device:/dev/mdev0-ep0a, Stream 02-00-00-01-01-01, sent:4927 (kBit/s), stuffed:0 (kBit/s), read:5197 (kBit/s), PCR-error:0, Buf Ovfl-error:0, Buf Unfl-error:0, TS-error:0 + * Device:/dev/mdev0-ep0b, Stream 02-00-00-02-01-02, sent:4897 (kBit/s), stuffed:0 (kBit/s), read:5005 (kBit/s), PCR-error:0, Buf Ovfl-error:0, Buf Unfl-error:0, TS-error:0 + * Overall sent performance of all streams: 134511 (kBit/s) + * ===============Statistic End=============== + * \endcode + * - If everything is okay, the sent and read values are not zero. They may be different, as the Transport Stream file usually contains + * stuffing and control informations, which will not be sent over the MOST network. + * - The error values must be zero. + * + * + * \subsection step2 Step 2: Retrieve the isochronous stream on non Android clients + * + * If you are not using the Android App, login in to the client devices.\n + * \note Make sure you performed step + * - Step 1: Load Linux driver on target + * - Step 2: Enable Ethernet communication on MOST + * - Step 3: Execute the native C++ VideoOnDemand Client sample application + * + * + * + * In order to dump the received data to disk, enter: + * \code + * dd if=/dev/mdev0-ep0a of=myCapturedVideo.ts bs=7520 + * \endcode + * \note Adapt the name of the character device, as listed by the VideoOnDemand sample application. + * + * You can also use the dev2udp helper tool, which is stored in the SlimCamera example folder. + */ +/*----------------------------------------------------------*/ + + + +#endif //DOXYGEN_START_PAGE_H diff --git a/Src/IP/MostIpc.cpp b/Src/IP/MostIpc.cpp new file mode 100644 index 0000000..86e7341 --- /dev/null +++ b/Src/IP/MostIpc.cpp @@ -0,0 +1,510 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <arpa/inet.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <assert.h> +#include "Console.h" +#include "MostIpc.h" + + +#define TCP_BUFFER_SIZE (1024*1024) + +CMostIpc::CMostIpc( int port, bool server ) : isServer( server ), allowThreadRun( true ), + udpRxThreadIsRunning( false ), txThreadIsRunning (false), acceptThreadIsRunning( false ) +{ + if( isServer && 0 >= port ) + { + ConsolePrintf( PRIO_ERROR, RED"CMostIpc: Server port must be greater than 0!"RESETCOLOR"\n" ); + return; + } + pthread_mutex_init( &critical_mutex_tcp, NULL ); + sem_init( &semTx, 0, 0 ); //Mutex, initialized to 0 => sem_wait will block instantly + + struct sockaddr_in listenAddress; + /* Receive address configuration */ + memset( &listenAddress, 0, sizeof( listenAddress ) ); + listenAddress.sin_family = AF_INET; + listenAddress.sin_addr.s_addr = htonl( INADDR_ANY ); + listenAddress.sin_port = htons( port ); + + if( 0 > ( udpSock = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP ) ) ) + { + ConsolePrintf( PRIO_ERROR, RED"CMostIpc: Failed to create UDP socket"RESETCOLOR"\n" ); + return; + } + if( 0 > ( tcpSock = socket( PF_INET, SOCK_STREAM, 0 ) ) ) + { + ConsolePrintf( PRIO_ERROR, RED"CMostIpc: Failed to create TCP socket"RESETCOLOR"\n" ); + return; + } + + //Avoid hanging socket after killing application, only works with Linux kernel > 3.9 +#if defined (SO_REUSEADDR) && defined (SO_REUSEPORT) + int one = 1; + setsockopt(udpSock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &one, sizeof(one)); + setsockopt(tcpSock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &one, sizeof(one)); +#endif + + // this call is what allows broadcast UDP packets to be sent: + int enableSocketFlag = 1; + if( 0 > setsockopt( udpSock, SOL_SOCKET, SO_BROADCAST, &enableSocketFlag, sizeof( enableSocketFlag ) ) ) + { + ConsolePrintf( + PRIO_ERROR, RED"CMostIpc: setsockopt (SO_BROADCAST) failed, tried to set to: %d"RESETCOLOR"\n", enableSocketFlag ); + } + if( 0 < port ) + { + if( 0 > bind( udpSock, ( struct sockaddr * )&listenAddress, sizeof listenAddress ) ) + { + ConsolePrintf( PRIO_ERROR, RED"Failed to bind UDP socket"RESETCOLOR"\n" ); + } + if( 0 > bind( tcpSock, ( struct sockaddr * )&listenAddress, sizeof listenAddress ) ) + { + ConsolePrintf( PRIO_ERROR, RED"CMostIpc: Failed to bind TCP socket"RESETCOLOR"\n" ); + } + } + if( isServer ) + { + pthread_t acceptThread; + pthread_create( &acceptThread, NULL, CMostIpc::TcpAcceptThread, ( void * )this ); + } + pthread_t udpRxThread, txThread; + pthread_create( &udpRxThread, NULL, CMostIpc::UdpWorkerRXThread, ( void * )this ); + pthread_create( &txThread, NULL, CMostIpc::TXThread, ( void * )this ); +} + +CMostIpc::~CMostIpc() +{ + allowThreadRun = false; + close( udpSock ); + close( tcpSock ); + sem_post(&semTx); + int32_t timeout = 100; + while( ( udpRxThreadIsRunning || txThreadIsRunning || acceptThreadIsRunning ) && ( --timeout > 0 ) ) + usleep( 10000 ); + pthread_mutex_lock( &critical_mutex_tcp ); + for( uint32_t i = 0; i < m_TcpClientConnections.Size(); i++ ) + { + TcpClientInfo_t *var = m_TcpClientConnections[i]; + if( NULL != var ) + { + if( var->clientSock >= 0 ) + close( var->clientSock ); + var->clientSock = -1; + } + } + m_TcpClientConnections.RemoveAll(false); + pthread_mutex_unlock( &critical_mutex_tcp ); +} + +void CMostIpc::SendMsg( CMostMsgTx *Msg ) +{ + if( NULL != Msg ) + { + Msg->AddReference(); + m_MsgTxQueue.PushBack( Msg ); + sem_post(&semTx); + } +} + +void CMostIpc::RegisterMessageHandler( CMsgFilter *Filter ) +{ + m_MsgFilterQueue.PushBack( Filter ); +} + +void CMostIpc::UnregisterMessageHandler( CMsgFilter *Filter ) +{ + m_MsgFilterQueue.Remove(Filter); +} + +void *CMostIpc::TcpWorkerThread( void *pInst ) +{ + bool allowWorker = true; + TcpClientInfo_t *clientInfo = ( TcpClientInfo_t * )pInst; + if( NULL == clientInfo || NULL == clientInfo->ipc || 0 > clientInfo->clientSock ) + { + ConsolePrintf( PRIO_ERROR, RED"TcpWorkerThread: Invalid parameters"RESETCOLOR"\n" ); + return NULL; + } + uint8_t *buff = ( uint8_t * )malloc( TCP_BUFFER_SIZE ); + if( NULL == buff ) + { + ConsolePrintf( PRIO_ERROR, RED"TcpWorkerThread: Failed to allocate buffer"RESETCOLOR"\n" ); + return NULL; + } + + ConsolePrintf( PRIO_MEDIUM, "TcpWorkerThread: Start for IP:%s, Port:%d\n", clientInfo->clientIP, clientInfo->clientPort ); + + CMsgAddr *adr = new CMsgAddr( clientInfo->clientIP, clientInfo->clientPort, IpcTcp_V2_0 ); + CMostMsg *msg = NULL; + + while( clientInfo->ipc->allowThreadRun && allowWorker ) + { + int nRead; + if( 0 >= ( nRead = read( clientInfo->clientSock, buff, TCP_BUFFER_SIZE ) ) ) + { + if( clientInfo->ipc->allowThreadRun ) + { + if( 0 == nRead ) + ConsolePrintf( PRIO_MEDIUM, GREEN"TcpWorkerThread: TCP Client disconnected (IP:%s)"RESETCOLOR"\n", + clientInfo->clientIP ); + else + ConsolePrintf( PRIO_ERROR, YELLOW"TcpWorkerThread: Read error (return=%d), aborting .."RESETCOLOR"\n", nRead ); + } + allowWorker = false; + break; + } + for( int i = 0; i < nRead; i++ ) + { + if( NULL == msg ) + msg = new CMostMsg(); + if( !msg->Parse( buff[i] ) ) + { + ConsolePrintf( PRIO_ERROR, RED"TcpWorkerThread: Parse error, aborting.."RESETCOLOR"\n" ); + allowWorker = false; + break; + } + if( msg->IsValid() ) + { + clientInfo->ipc->OnMsgReceived( adr, msg ); + msg->RemoveReference(); + msg = NULL; + } + } + } + ConsolePrintf( PRIO_LOW, "TcpWorkerThread: End\n" ); + if( clientInfo->clientSock >= 0 ) + close( clientInfo->clientSock ); + + pthread_mutex_t *pMutex = &clientInfo->ipc->critical_mutex_tcp; + pthread_mutex_lock( pMutex ); + clientInfo->ipc->m_TcpClientConnections.Remove(clientInfo); + free( clientInfo ); + pthread_mutex_unlock( pMutex ); + + adr->RemoveReference(); + free( buff ); + return NULL; +} + +void *CMostIpc::TcpAcceptThread( void *pInst ) +{ + CMostIpc *ipc = ( CMostIpc * )pInst; + if( NULL == ipc || 0 > ipc->tcpSock ) + { + ConsolePrintf( PRIO_ERROR, RED"TcpAcceptThread was called with invalid parameters"RESETCOLOR"\n" ); + return NULL; + } + + ConsolePrintf( PRIO_LOW, "TcpAcceptThread starts\n" ); + ipc->acceptThreadIsRunning = true; + while( ipc->allowThreadRun ) + { + listen( ipc->tcpSock, 5 ); + socklen_t size = sizeof( struct sockaddr_in ); + TcpClientInfo_t *var = ( TcpClientInfo_t * )calloc( 1, sizeof( TcpClientInfo_t ) ); + if( NULL == var ) + { + ConsolePrintf( PRIO_ERROR, RED"Failed to allocate memory in TCP accept Thread, aborting.."RESETCOLOR"\n" ); + break; + } + struct sockaddr_in clientAddr; + var->clientSock = accept( ipc->tcpSock, ( struct sockaddr * )&clientAddr, &size ); + if( !ipc->allowThreadRun ) + break; + if( -1 == var->clientSock ) + { + ConsolePrintf( PRIO_ERROR, RED"Failed to accept connection"RESETCOLOR"\n" ); + free( var ); + continue; + } + var->ipc = ipc; + inet_ntop( AF_INET, ( struct sockaddr_in * )&clientAddr.sin_addr, var->clientIP, sizeof ( var->clientIP ) ); + var->clientPort = ntohs( clientAddr.sin_port ); + ConsolePrintf( PRIO_MEDIUM, GREEN"TCP Client connected: IP=%s, Port=%d"RESETCOLOR"\n", + var->clientIP, var->clientPort ); + pthread_mutex_lock( &var->ipc->critical_mutex_tcp ); + var->ipc->m_TcpClientConnections.PushBack( var ); + pthread_mutex_unlock( &var->ipc->critical_mutex_tcp ); + pthread_create( &var->workerThread, NULL, CMostIpc::TcpWorkerThread, ( void * )var ); + } + ConsolePrintf( PRIO_LOW, "TcpAcceptThread ends\n" ); + close( ipc->tcpSock ); + ipc->acceptThreadIsRunning = false; + return NULL; +} + +void CMostIpc::OnMsgReceived( CMsgAddr *Addr, CMostMsg *msg ) +{ + if( NULL == Addr || NULL == msg ) + { + ConsolePrintf( + PRIO_ERROR, RED"CMostIpc::OnMsgReceived was called with invalid parameters"RESETCOLOR"\n" ); + return; + } + ConsolePrintf( PRIO_LOW, "Received MOST-IPC message IP=%s, Protocol:%s, "\ + "FBlock:0x%X, Func:0x%X, OP:0x%X, PL:%d Bytes\n", Addr->GetInetAddress(), ( Addr->GetProtocol() == + IpcUdp_V2_0 ? "UDP" : "TCP" ), msg->GetFBlock(), msg->GetFunc(), msg->GetOpType(), msg->GetPayloadLen() ); + + for( uint32_t i = 0; i < m_MsgFilterQueue.Size(); i++ ) + { + m_MsgFilterQueue[i]->Filter( Addr, msg ); + } +} + +bool CMostIpc::SendUdp( const char *ipAddress, uint32_t port, const uint8_t *buffer, uint32_t bufferLen ) +{ + struct sockaddr_in destination; + memset( &destination, 0, sizeof ( destination ) ); + destination.sin_family = AF_INET; + destination.sin_addr.s_addr = inet_addr( ipAddress ); + destination.sin_port = htons( port ); + ssize_t bytesSent = sendto( udpSock, buffer, bufferLen, + 0, ( struct sockaddr * )&destination, sizeof ( destination ) ); + return ( ( uint32_t )bytesSent == bufferLen ); +} + +bool CMostIpc::SendTcp( const char *ipAddress, uint32_t port, const uint8_t *buffer, uint32_t bufferLen ) +{ + bool success = false; + TcpClientInfo_t *tcp = NULL; + + //Find the correct client connection + pthread_mutex_lock( &critical_mutex_tcp ); + for( uint32_t i = 0; i < m_TcpClientConnections.Size(); i++ ) + { + TcpClientInfo_t *t = m_TcpClientConnections[i]; + if( NULL != t && t->clientPort == port && 0 == strcmp( t->clientIP, ipAddress ) ) + { + tcp = t; + break; + } + } + //There is no connection matching to the given address and port, try to connect if we are not master + if( !isServer && NULL == tcp ) + { + TcpClientInfo_t *var = ( TcpClientInfo_t * )calloc( 1, sizeof( TcpClientInfo_t ) ); + if( NULL != var ) + { + var->ipc = this; + var->clientPort = port; + var->clientSock = tcpSock; + strncpy( var->clientIP, ipAddress, sizeof( var->clientIP ) ); + + struct hostent *hostp; + hostp = gethostbyname( var->clientIP ); + if( NULL != hostp ) + { + struct sockaddr_in addr; + memset( &addr, 0x00, sizeof( struct sockaddr_in ) ); + addr.sin_family = AF_INET; + addr.sin_port = htons( var->clientPort ); + memcpy( &addr.sin_addr, hostp->h_addr, sizeof( addr.sin_addr ) ); + if( 0 == connect( tcpSock, ( struct sockaddr * )&addr, sizeof( addr ) ) ) + { + tcp = var; + m_TcpClientConnections.PushBack( tcp ); + pthread_create( &tcp->workerThread, NULL, CMostIpc::TcpWorkerThread, ( void * )tcp ); + ConsolePrintf( PRIO_MEDIUM, GREEN"Connect to TCP host succeeded: IP=%s Port=%d"RESETCOLOR"\n", tcp->clientIP, + tcp->clientPort ); + + } + else + { + ConsolePrintf( PRIO_ERROR, RED"Could not connect to host: '%s'"RESETCOLOR"\n", var->clientIP ); + free( var ); + } + } + else + { + ConsolePrintf( PRIO_ERROR, RED"Could not find host: '%s'"RESETCOLOR"\n", var->clientIP ); + free( var ); + } + } + else + { + ConsolePrintf( PRIO_ERROR, RED"Failed to allocate memory in SendTcp method"RESETCOLOR"\n" ); + } + } + + if( NULL != tcp ) + { + int result; + uint32_t written = 0; + success = true; + while( written < bufferLen ) + { + result = write( tcp->clientSock, &buffer[written], bufferLen - written ); + if( result < 0 ) + { + ConsolePrintf( PRIO_ERROR, RED"SendTcp: Write error"RESETCOLOR"\n" ); + success = false; + break; + } + else if( result == 0 ) + { + //Socket is non blocking, sleep to avoid 100% CPU load + usleep( 1000 ); + } + else + { + written += result; + } + } + pthread_mutex_unlock( &critical_mutex_tcp ); + } + return success; +} + +void *CMostIpc::UdpWorkerRXThread( void *pInst ) +{ + CMostIpc *ipc = ( CMostIpc * )pInst; + if( NULL == ipc || 0 > ipc->udpSock ) + { + ConsolePrintf( PRIO_ERROR, RED"UdpWorkerRXThread was called with invalid parameters"RESETCOLOR"\n" ); + return NULL; + } + ipc->udpRxThreadIsRunning = true; + while( ipc->allowThreadRun ) + { + //Receive + if( ipc->allowThreadRun ) + { + uint8_t tempBuffer[8*1024]; + char remoteIP[INET6_ADDRSTRLEN]; + uint32_t port; + struct sockaddr_in fromAddr; + socklen_t fromAddrLen = sizeof( fromAddr ); + ssize_t receivedLength; + receivedLength = recvfrom + ( ipc->udpSock, tempBuffer, sizeof( tempBuffer ), + 0, ( struct sockaddr * )&fromAddr, &fromAddrLen ); + if( receivedLength > 0 ) + { + inet_ntop( AF_INET, ( struct sockaddr_in * )&fromAddr.sin_addr, remoteIP, sizeof( remoteIP ) ); + port = ntohs( fromAddr.sin_port ); + CMsgAddr *adr = new CMsgAddr( remoteIP, port ); + CMostMsg *msg = new CMostMsg(); + for( int32_t i = 0; i < receivedLength; i++ ) + { + if( !msg->Parse( tempBuffer[i] ) ) + { + ConsolePrintf( PRIO_ERROR, RED"CMostIpc UDP received unparsable message"RESETCOLOR"\n" ); + break; + } + } + if( msg->IsValid() ) + ipc->OnMsgReceived( adr, msg ); + adr->RemoveReference(); + msg->RemoveReference(); + } + } + } + ipc->udpRxThreadIsRunning = false; + return NULL; +} + +void *CMostIpc::TXThread( void *pInst ) +{ + CMostIpc *ipc = ( CMostIpc * )pInst; + if( NULL == ipc || 0 > ipc->udpSock ) + { + ConsolePrintf( PRIO_ERROR, RED"TXThread was called with invalid parameters"RESETCOLOR"\n" ); + return NULL; + } + ipc->txThreadIsRunning = true; + + while( ipc->allowThreadRun ) + { + sem_wait(&ipc->semTx); + if (!ipc->allowThreadRun) + break; + CMostMsgTx *MsgTx = ipc->m_MsgTxQueue.PopFront(); + assert (NULL != MsgTx); + if( NULL != MsgTx ) + { + uint8_t *tempBuffer = NULL; + uint32_t bytesUsed = MsgTx->ToByteArray( &tempBuffer ); + if( NULL == tempBuffer || 0 == bytesUsed ) + { + ConsolePrintf( PRIO_ERROR, RED"MOST-IP: Failed to serialize message"RESETCOLOR"\n" ); + if (NULL != tempBuffer) + free(tempBuffer); + MsgTx->MsgSent( false ); + continue; + } + CMsgAddr *addr = MsgTx->GetAddr(); + if( 0 != bytesUsed && NULL != addr ) + { + char *ipAddress = addr->GetInetAddress(); + uint32_t port = addr->GetPort(); + if( NULL != ipAddress ) + { + bool success = false; + switch( addr->GetProtocol() ) + { + case IpcUdp_V2_0: + success = ipc->SendUdp( ipAddress, port, tempBuffer, bytesUsed ); + break; + case IpcTcp_V2_0: + success = ipc->SendTcp( ipAddress, port, tempBuffer, bytesUsed ); + break; + default: + ConsolePrintf( PRIO_ERROR, RED"MOST-IP: Unknown Protocol"RESETCOLOR"\n" ); + break; + } + MsgTx->MsgSent( success ); + } + else + { + ConsolePrintf( PRIO_ERROR, RED"MOST-IP: Can not resolve IP address"RESETCOLOR"\n" ); + MsgTx->MsgSent( false ); + } + } + else + { + ConsolePrintf( PRIO_ERROR, RED"Error while sending in MOST IPC."RESETCOLOR"\n" ); + MsgTx->MsgSent( false ); + } + if (NULL != tempBuffer) + free( tempBuffer ); + MsgTx->RemoveReference(); + } + } + ipc->txThreadIsRunning = false; + return NULL; +} + diff --git a/Src/IP/MostIpc.h b/Src/IP/MostIpc.h new file mode 100644 index 0000000..0f8596d --- /dev/null +++ b/Src/IP/MostIpc.h @@ -0,0 +1,164 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CMostIpc class. + */ +/*----------------------------------------------------------*/ +#ifndef IPC_H +#define IPC_H + +#include <stdbool.h> +#include <pthread.h> +#include <semaphore.h> +#include "SafeVector.h" +#include "MsgAddr.h" +#include "MostMsgTx.h" +#include "MsgAddr.h" +#include "MsgFilter.h" + +class CMostIpc; + +/*! \cond PRIVATE */ +typedef struct +{ + pthread_t workerThread; + CMostIpc *ipc; + int clientSock; + char clientIP[46]; + uint32_t clientPort; +} TcpClientInfo_t; +/*! \endcond */ + +/*----------------------------------------------------------*/ +/*! + * \brief Inter Process Communication + * 1. sends messages using MOST protocol over UDP + * 2. receives messages and forwards them to their + * handlers + */ +/*----------------------------------------------------------*/ +class CMostIpc +{ +private: + bool isServer; + bool allowThreadRun; + bool udpRxThreadIsRunning; + bool txThreadIsRunning; + bool acceptThreadIsRunning; + CSafeVector<CMostMsgTx *> m_MsgTxQueue; + CSafeVector<CMsgFilter *> m_MsgFilterQueue; + CSafeVector<TcpClientInfo_t *> m_TcpClientConnections; + pthread_mutex_t critical_mutex_rx; + pthread_mutex_t critical_mutex_tcp; + sem_t semTx; + int udpSock; + int tcpSock; + + /*----------------------------------------------------------*/ + /*! \brief TCP accept thread + * \note Never call this method directly! + */ + /*----------------------------------------------------------*/ + static void *TcpAcceptThread( void *pInst ); + + /*----------------------------------------------------------*/ + /*! \brief TCP read / write thread + * \note Never call this method directly! + */ + /*----------------------------------------------------------*/ + static void *TcpWorkerThread( void *pInst ); + + + /*----------------------------------------------------------*/ + /*! \brief UDP receiving thread + * \note Never call this method directly! + */ + /*----------------------------------------------------------*/ + static void *UdpWorkerRXThread( void *pInst ); + + /*----------------------------------------------------------*/ + /*! \brief Sending thread + * \note Never call this method directly! + */ + /*----------------------------------------------------------*/ + static void *TXThread( void *pInst ); + + /*----------------------------------------------------------*/ + /*! \brief handle a received message + */ + /*----------------------------------------------------------*/ + void OnMsgReceived( CMsgAddr *Addr, CMostMsg *msg ); + + bool SendUdp( const char *ipAddress, uint32_t port, const uint8_t *buffer, uint32_t bufferLen ); + bool SendTcp( const char *ipAddress, uint32_t port, const uint8_t *buffer, uint32_t bufferLen ); + +public: + + + /*----------------------------------------------------------*/ + /*! \brief constructs Inter Process Communication, which acts as Server + * \param port - If set to server by isServer=true, this value is the server port which the UDP and TCP sockets are listening. + * If set to client by isServer=false, this is the client socket port. If set to 0 or below, a random port will be chosen. + * Otherwise the given port will be used as source port of the connection. The server always can handle random and fixed client port connections. + * \param isServer - true, if this component shall act as "the" server. false, if this component shall act as one of many clients. + */ + /*----------------------------------------------------------*/ + CMostIpc( int port, bool isServer ); + + /*----------------------------------------------------------*/ + /*! \brief Deletes every resources used by Inter Process Communication + */ + /*----------------------------------------------------------*/ + ~CMostIpc(); + + + /*----------------------------------------------------------*/ + /*! \brief send a MOST message + */ + /*----------------------------------------------------------*/ + void SendMsg( CMostMsgTx *Msg ); + + + + + /*----------------------------------------------------------*/ + /*! \brief adds a message handler + */ + /*----------------------------------------------------------*/ + void RegisterMessageHandler( CMsgFilter *Filter ); + + + + + /*----------------------------------------------------------*/ + /*! \brief removes a message handler + */ + /*----------------------------------------------------------*/ + void UnregisterMessageHandler( CMsgFilter *Filter ); + +}; + +#endif //IPC_H + diff --git a/Src/IP/MostMsg.cpp b/Src/IP/MostMsg.cpp new file mode 100644 index 0000000..3ca043f --- /dev/null +++ b/Src/IP/MostMsg.cpp @@ -0,0 +1,405 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include "Console.h" +#include "MostMsg.h" +#include <stdlib.h> + +static uint32_t crc32_tab[] = +{ + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +static uint32_t CalcCRC32( const uint8_t *p, uint32_t size ) +{ + uint32_t crc = ~0U; + + while( size-- ) + crc = crc32_tab[( crc ^ *p++ ) & 0xFF] ^ ( crc >> 8 ); + + return crc ^ ~0U; +} + + +CMostMsg::CMostMsg() : m_nIsValid( false ), m_nRefCount( 1 ), m_nFBlock( 0 ), m_nFunc( 0 ), m_nInst( 0 ), + m_nOpType( 0 ), m_nPayloadLen( 0 ), m_Payload( NULL ), m_nParsePos( 0 ), + m_nHeaderCrc( 0 ), m_nPayloadCrc( 0 ) +{ +} + +CMostMsg::CMostMsg( uint32_t nFBlock, uint32_t nInst, uint32_t nFunc, uint8_t nOpType, uint32_t nPayloadLen, + const uint8_t *Payload ) : m_nIsValid( true ), m_nRefCount( 1 ), m_Payload( NULL ), + m_nParsePos( 0xFFFFFFFF ), m_nHeaderCrc( 0 ), m_nPayloadCrc( 0 ) +{ + m_nFBlock = nFBlock; + m_nInst = nInst; + m_nFunc = nFunc; + m_nOpType = nOpType; + m_nPayloadLen = nPayloadLen; + if( nPayloadLen > 0 ) + { + m_Payload = ( uint8_t * )malloc( m_nPayloadLen ); + if( NULL == m_Payload ) + { + ConsolePrintf( + PRIO_ERROR, RED"CMostMsg Constructor failed to allocated %d bytes of memory"RESETCOLOR"\n", m_nPayloadLen ); + m_nIsValid = false; + m_nPayloadLen = 0; + return; + } + memcpy( m_Payload, Payload, nPayloadLen ); + } +} + +CMostMsg::~CMostMsg() +{ + m_nIsValid = false; + m_nPayloadLen = 0; + if( NULL != m_Payload ) + { + free( m_Payload ); + m_Payload = NULL; + } +} + +void CMostMsg::AddReference() +{ + ++m_nRefCount; +} + +void CMostMsg::RemoveReference() +{ + if( 0 == --m_nRefCount ) + delete this; +} + +uint32_t CMostMsg::ToByteArray( uint8_t **ppBuffer ) +{ + if( NULL == ppBuffer ) + { + ConsolePrintf( + PRIO_ERROR, RED"CMostMsg::ToByteArray ppBuffer is NULL < %d"RESETCOLOR"\n" ); + return 0; + } + uint32_t serializedLen = 20; + if( 0 != m_nPayloadLen ) + serializedLen += m_nPayloadLen + 4; //If there is payload, it will be CRC checked + uint8_t *b = ( uint8_t * )malloc( serializedLen ); + if( NULL == b ) + { + ConsolePrintf( + PRIO_ERROR, RED"CMostMsg::ToByteArray failed to allocate %d bytes"RESETCOLOR"\n", ( m_nPayloadLen + 24 ) ); + *ppBuffer = NULL; + return 0; + } + *ppBuffer = b; + b[0] = ( uint8_t )0x49; + b[1] = ( uint8_t )0x72; + b[2] = ( uint8_t )0x16; + b[3] = ( uint8_t )0x25; + b[4] = ( uint8_t )( m_nFBlock & 0xFF ); + b[5] = ( uint8_t )( m_nInst & 0xFF ); + b[6] = ( uint8_t )( ( m_nFunc >> 8 ) & 0xFF ); + b[7] = ( uint8_t )( ( m_nFunc )&0xFF ); + b[8] = ( uint8_t )( m_nOpType ); + b[9] = ( uint8_t )0x0; + b[10] = ( uint8_t )0x0; + b[11] = ( uint8_t )0x0; + b[12] = ( uint8_t )( ( m_nPayloadLen >> 24 ) & 0xFF ); + b[13] = ( uint8_t )( ( m_nPayloadLen >> 16 ) & 0xFF ); + b[14] = ( uint8_t )( ( m_nPayloadLen >> 8 ) & 0xFF ); + b[15] = ( uint8_t )( m_nPayloadLen & 0xFF ); + + uint32_t headerCrc = CalcCRC32( b, 16 ); + + b[16] = ( uint8_t )( ( headerCrc >> 24 ) & 0xFF ); + b[17] = ( uint8_t )( ( headerCrc >> 16 ) & 0xFF ); + b[18] = ( uint8_t )( ( headerCrc >> 8 ) & 0xFF ); + b[19] = ( uint8_t )( headerCrc & 0xFF ); + + if( 0 != m_nPayloadLen ) + { + for( uint32_t i = 0; i < m_nPayloadLen; i++ ) + b[20 + i] = m_Payload[i]; + + uint32_t payloadCrc = CalcCRC32( &b[20], m_nPayloadLen ); + + b[20 + m_nPayloadLen] = ( uint8_t )( ( payloadCrc >> 24 ) & 0xFF ); + b[21 + m_nPayloadLen] = ( uint8_t )( ( payloadCrc >> 16 ) & 0xFF ); + b[22 + m_nPayloadLen] = ( uint8_t )( ( payloadCrc >> 8 ) & 0xFF ); + b[23 + m_nPayloadLen] = ( uint8_t )( payloadCrc & 0xFF ); + } + return serializedLen; +} + +bool CMostMsg::Parse( uint8_t receivedByte ) +{ + if( 0xFFFFFFFF == m_nParsePos ) + { + ConsolePrintf( + PRIO_ERROR, RED"CMostMsg::Parse was called, but the parse state is invalid"RESETCOLOR"\n" ); + return false; + } + bool valid = true; + if( m_nParsePos <= 19 ) + { + if( m_nParsePos <= 15 ) + m_zHeader[m_nParsePos] = receivedByte; + + //Check Header + switch( m_nParsePos ) + { + case 0: + if( 0x49 != receivedByte ) + valid = false; + break; + case 1: + if( 0x72 != receivedByte ) + valid = false; + break; + case 2: + if( 0x16 != receivedByte ) + valid = false; + break; + case 3: + if( 0x25 != receivedByte ) + valid = false; + break; + case 4: + m_nFBlock = receivedByte; + break; + case 5: + m_nInst = receivedByte; + break; + case 6: + m_nFunc = receivedByte << 8; + break; + case 7: + m_nFunc += receivedByte; + break; + case 8: + m_nOpType = receivedByte; + break; + case 9: + case 10: + case 11: + //Reserved for future use cases + break; + case 12: + m_nPayloadLen = receivedByte << 24; + break; + case 13: + m_nPayloadLen += receivedByte << 16; + break; + case 14: + m_nPayloadLen += receivedByte << 8; + break; + case 15: + m_nPayloadLen += receivedByte; + break; + case 16: + m_nHeaderCrc = receivedByte << 24; + break; + case 17: + m_nHeaderCrc += receivedByte << 16; + break; + case 18: + m_nHeaderCrc += receivedByte << 8; + break; + case 19: + m_nHeaderCrc += receivedByte; + if( m_nHeaderCrc == CalcCRC32( m_zHeader, 16 ) ) + { + if( 0 != m_nPayloadLen ) + { + //Continue to parse the payload + m_Payload = ( uint8_t * )malloc( m_nPayloadLen ); + if( NULL == m_Payload ) + { + ConsolePrintf( + PRIO_ERROR, RED"MostMsg::Protocol deserializing error, could not allocate %d bytes of memory"RESETCOLOR + "\n", m_nPayloadLen ); + valid = false; + m_nPayloadLen = 0; + } + } + else + { + //No payload, we are finished + m_nIsValid = true; + m_nParsePos = 0xFFFFFFFF - 1; + } + } + else + { + ConsolePrintf( + PRIO_ERROR, RED"MostMsg::Protocol error, header CRC does not match"RESETCOLOR"\n" ); + valid = false; + } + break; + } + } + else + { + if( m_nParsePos < ( m_nPayloadLen + 20 ) ) + { + //Check Payload and store it + if( NULL != m_Payload ) + { + m_Payload[m_nParsePos - 20] = receivedByte; + } + else + { + ConsolePrintf( + PRIO_ERROR, RED"MostMsg::Protocol deserializing error, payload buffer is NULL"RESETCOLOR"\n" ); + valid = false; + m_nPayloadLen = 0; + } + } + else + { + //Finally check CRC of payload + uint32_t crcState = m_nParsePos - m_nPayloadLen - 20; + switch( crcState ) + { + case 0: + m_nPayloadCrc = receivedByte << 24; + break; + case 1: + m_nPayloadCrc += receivedByte << 16; + break; + case 2: + m_nPayloadCrc += receivedByte << 8; + break; + case 3: + m_nPayloadCrc += receivedByte; + if( m_nPayloadCrc == CalcCRC32( m_Payload, m_nPayloadLen ) ) + { + //Payload is successfully received + m_nIsValid = true; + m_nParsePos = 0xFFFFFFFF - 1; + } + else + { + ConsolePrintf( + PRIO_ERROR, RED"MostMsg::Protocol deserializing error, Payload CRC mismatch"RESETCOLOR"\n" ); + valid = false; + } + break; + default: + ConsolePrintf( + PRIO_ERROR, RED"MostMsg::Protocol deserializing error, Payload CRC state is out of range"RESETCOLOR"\n" ); + valid = false; + } + } + } + + if( valid ) + { + ++m_nParsePos; + } + else + { + ConsolePrintf( + PRIO_ERROR, RED"CMostMsg::Parse failed at parse position %d"RESETCOLOR"\n", m_nParsePos ); + m_nParsePos = 0xFFFFFFFF; + } + return valid; +} + +bool CMostMsg::IsValid() +{ + return m_nIsValid; +} + +uint32_t CMostMsg::GetFBlock() +{ + return m_nFBlock; +} + +uint32_t CMostMsg::GetFunc() +{ + return m_nFunc; +} + +uint32_t CMostMsg::GetInst() +{ + return m_nInst; +} + +uint8_t CMostMsg::GetOpType() +{ + return m_nOpType; +} + +uint8_t *CMostMsg::GetPayload() +{ + return m_Payload; +} + +uint32_t CMostMsg::GetPayloadLen() +{ + return m_nPayloadLen; +} diff --git a/Src/IP/MostMsg.h b/Src/IP/MostMsg.h new file mode 100644 index 0000000..fb4bdd8 --- /dev/null +++ b/Src/IP/MostMsg.h @@ -0,0 +1,184 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CMostMsg class. + */ +/*----------------------------------------------------------*/ +#ifndef MOSTMSG_H +#define MOSTMSG_H + +#include <stdint.h> +#include <stdbool.h> +#include <string.h> + + +/*----------------------------------------------------------*/ +/*! + * \brief class holding a MOST message, it support serializing and deserializing + * \note Serialize-Protocol is: </br> + * p[0] = 0x49 (Start) </br> + * p[1] = 0x72 (Start) </br> + * p[2] = 0x16 (Start) </br> + * p[3] = 0x25 (Start) </br> + * p[4] = FBlock Identifier </br> + * p[5] = Instance Identifier </br> + * p[6] = Function Identifier << 8 </br> + * p[7] = Function Identifier << 0 </br> + * p[8] = OP Type </br> + * p[9] = 0x0 (Reserved) </br> + * p[10] = 0x0 (Reserved) </br> + * p[11] = 0x0 (Reserved) </br> + * p[12] = Payload Length << 24 </br> + * p[13] = Payload Length << 16 </br> + * p[14] = Payload Length << 8 </br> + * p[15] = Payload Length << 0 </br> + * p[16] = Header CRC32(p[0] - p[15]) << 24 </br> + * p[17] = Header CRC32(p[0] - p[15]) << 16 </br> + * p[18] = Header CRC32(p[0] - p[15]) << 8 </br> + * p[19] = Header CRC32(p[0] - p[15]) << 0 </br> + * p[20] - p[n] = Payload </br> + * p[n+1] = Payload CRC32(p[20] - p[n]) << 24 </br> + * p[n+2] = Payload CRC32(p[20] - p[n]) << 16 </br> + * p[n+3] = Payload CRC32(p[20] - p[n]) << 8 </br> + * p[n+4] = Payload CRC32(p[20] - p[n]) << 0 </br> + */ + +/*----------------------------------------------------------*/ +class CMostMsg +{ +private: + bool m_nIsValid; + int32_t m_nRefCount; + uint32_t m_nFBlock; + uint32_t m_nFunc; + uint32_t m_nInst; + uint8_t m_nOpType; + uint32_t m_nPayloadLen; + uint8_t *m_Payload; + uint32_t m_nParsePos; + uint32_t m_nHeaderCrc; + uint32_t m_nPayloadCrc; + uint8_t m_zHeader[16]; + +protected: + /*----------------------------------------------------------*/ + /*! \brief Default Destructor + */ + /*----------------------------------------------------------*/ + virtual ~CMostMsg(); + +public: + + static const uint8_t OP_SET = 0x0; + static const uint8_t OP_GET = 0x1; + static const uint8_t OP_SETGET = 0x2; + static const uint8_t OP_INC = 0x3; + static const uint8_t OP_DEC = 0x4; + static const uint8_t OP_GETINTERFACE = 0x5; + static const uint8_t OP_STATUS = 0xC; + static const uint8_t OP_INTERFACE = 0xE; + static const uint8_t OP_ERROR = 0xF; + static const uint8_t OP_START = 0x0; + static const uint8_t OP_ABORT = 0x1; + static const uint8_t OP_STARTRESULT = 0x2; + static const uint8_t OP_STARTRESULTACK = 0x6; + static const uint8_t OP_ABORTACK = 0x7; + static const uint8_t OP_STARTACK = 0x8; + static const uint8_t OP_ERRORACK = 0x9; + static const uint8_t OP_PROCESSINGACK = 0xA; + static const uint8_t OP_PROCESSING = 0xB; + static const uint8_t OP_RESULT = 0xC; + static const uint8_t OP_RESULTACK = 0xD; + static const uint8_t OP_REPORTS = 0x9; + + + /*----------------------------------------------------------*/ + /*! \brief constructs a new message, which is initial not valid. + * \note It has to fed by the Parse method until IsValid method reports true. + */ + /*----------------------------------------------------------*/ + CMostMsg(); + + + /*----------------------------------------------------------*/ + /*! \brief constructs a new message which is fully set up and valid + * \param nFBlock - The MOST Function Block Identifier + * \param nInst - The MOST Function Block Instance Identifier + * \param nFunc - The MOST Function Block Function Identifier + * \param nPayloadLen - The amount of bytes stored in Payload + * \param Payload - The pointer to the payload byte array. + */ + /*----------------------------------------------------------*/ + CMostMsg( uint32_t nFBlock, uint32_t nInst, uint32_t nFunc, uint8_t nOpType, uint32_t nPayloadLen, + const uint8_t *Payload ); + + /*----------------------------------------------------------*/ + /*! \brief Increments the reference counter + */ + /*----------------------------------------------------------*/ + void AddReference(); + + /*----------------------------------------------------------*/ + /*! \brief Decrements the reference counter, if the value reaches 0, this object destroys it self. + */ + /*----------------------------------------------------------*/ + void RemoveReference(); + + /*----------------------------------------------------------*/ + /*! \brief Fills out an empty CMostMsg (created with default constructor) + * \return true, if parsing was valid. false, parser error, no way to continue. + * \note Check with IsValid method if parsing is finished. + */ + /*----------------------------------------------------------*/ + bool Parse( uint8_t receivedByte ); + + + + /*----------------------------------------------------------*/ + /*! \brief serializes a message to an byte array + * \note The byte array will be created inside this method and has to be freed after usage! + * \return The bytes used inside the given buffer. + */ + /*----------------------------------------------------------*/ + uint32_t ToByteArray( uint8_t **ppBuffer ); + + + bool IsValid(); + + uint32_t GetFBlock(); + + uint32_t GetFunc(); + + uint32_t GetInst(); + + uint8_t GetOpType(); + + uint8_t *GetPayload(); + + uint32_t GetPayloadLen(); +}; + + +#endif //MOSTMSG_H diff --git a/Src/IP/MostMsgTx.cpp b/Src/IP/MostMsgTx.cpp new file mode 100644 index 0000000..d3c5864 --- /dev/null +++ b/Src/IP/MostMsgTx.cpp @@ -0,0 +1,86 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include "MostMsgTx.h" + +CMostMsgTx::CMostMsgTx( CMsgAddr *Addr, uint32_t nFBlock, uint32_t nInst, uint32_t nFunc, uint8_t nOpType, + uint32_t nPayloadLen, const uint8_t *Payload, uint32_t nTimeoutMs, OnMessageSent_t MessageSent, + void *UserContext ) : CMostMsg( nFBlock, nInst, nFunc, nOpType, nPayloadLen, Payload ) +{ + if( NULL != Addr ) + { + Addr->AddReference(); + m_Addr = Addr; + } + m_nTimeoutMs = nTimeoutMs; + m_MessageSent = MessageSent; + m_UserContext = UserContext; +} + +CMostMsgTx::~CMostMsgTx() +{ + if( NULL != m_Addr ) + { + m_Addr->RemoveReference(); + m_Addr = NULL; + } +} + + + +/*----------------------------------------------------------*/ +/*! \brief calls message handler with the send result +*/ + +/*----------------------------------------------------------*/ +void CMostMsgTx::MsgSent( bool bSuccess ) +{ + if( NULL != m_MessageSent ) + m_MessageSent( bSuccess, this ); +} + + + + +/*----------------------------------------------------------*/ +/*! \brief get target address +*/ + +/*----------------------------------------------------------*/ +CMsgAddr *CMostMsgTx::GetAddr() +{ + return m_Addr; +} + + + + +/*----------------------------------------------------------*/ +/*! \brief get user context +*/ + +/*----------------------------------------------------------*/ +void *CMostMsgTx::GetUserContext() +{ + return m_UserContext; +} diff --git a/Src/IP/MostMsgTx.h b/Src/IP/MostMsgTx.h new file mode 100644 index 0000000..00736ae --- /dev/null +++ b/Src/IP/MostMsgTx.h @@ -0,0 +1,96 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CMostMsgTx class. + */ +/*----------------------------------------------------------*/ +#ifndef MOSTMSGTX_H +#define MOSTMSGTX_H + +#include <stdint.h> +#include <stdbool.h> +#include "MostMsg.h" +#include "MsgAddr.h" + + + + +/*----------------------------------------------------------*/ +/*! +* \brief class holding a MOST message which needs to be +* sent +*/ + +/*----------------------------------------------------------*/ +class CMostMsgTx : public CMostMsg +{ +private: + typedef void ( *OnMessageSent_t )( bool bSucceeded, CMostMsgTx *Msg ); + CMsgAddr *m_Addr; + uint32_t m_nTimeoutMs; + OnMessageSent_t m_MessageSent; + void *m_UserContext; + +protected: + virtual ~CMostMsgTx(); + +public: + + + /*----------------------------------------------------------*/ + /*! \brief constructs a new message + * + * \param Addr - Target address + * \param nFBlock - Function Block ID + * \param nInst - Instance ID + * \param nFunc - Function ID + * \param nOpType - Operation + * \param nPayloadLen - length of payload in bytes + * \param Payload - Payload + * \param nTimeoutMs - Timeout for sending in Milliseconds + * \param MessageSentCB - Handler called, after message was sent + * \param UserContext - a class passed to the handler + */ + /*----------------------------------------------------------*/ + CMostMsgTx( CMsgAddr *Addr, uint32_t nFBlock, uint32_t nInst, uint32_t nFunc, uint8_t nOpType, + uint32_t nPayloadLen, const uint8_t *Payload, uint32_t nTimeoutMs, OnMessageSent_t MessageSentCB, + void *UserContext ); + + + void MsgSent( bool bSuccess ); + + + + + CMsgAddr *GetAddr(); + + + + + void *GetUserContext(); +}; + + +#endif //MOSTMSGTX_H diff --git a/Src/IP/MsgAddr.cpp b/Src/IP/MsgAddr.cpp new file mode 100644 index 0000000..3da23f0 --- /dev/null +++ b/Src/IP/MsgAddr.cpp @@ -0,0 +1,71 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include <string.h> +#include <stdio.h> +#include "MsgAddr.h" +#include "Console.h" + +CMsgAddr::CMsgAddr( const char *Addr, uint32_t nPort ) : m_nRefCount( 1 ), m_nPort( nPort ), + m_protocol( IpcUdp_V2_0 ) +{ + if( NULL != Addr ) + strncpy( m_IpAddr, Addr, sizeof( m_IpAddr ) ); +} + +CMsgAddr::CMsgAddr( const char *Addr, uint32_t nPort, IpcProtocol_t protocol ) : m_nRefCount( 1 ), m_nPort( nPort ), + m_protocol( protocol ) +{ + if( NULL != Addr ) + strncpy( m_IpAddr, Addr, sizeof( m_IpAddr ) ); +} + +CMsgAddr::~CMsgAddr() +{ +} + +void CMsgAddr::AddReference() +{ + ++m_nRefCount; +} + +void CMsgAddr::RemoveReference() +{ + if( 0 >= --m_nRefCount ) + delete this; +} + +char *CMsgAddr::GetInetAddress() +{ + return ( char * )m_IpAddr; +} + +uint32_t CMsgAddr::GetPort() +{ + return m_nPort; +} + +IpcProtocol_t CMsgAddr::GetProtocol() +{ + return m_protocol; +} diff --git a/Src/IP/MsgAddr.h b/Src/IP/MsgAddr.h new file mode 100644 index 0000000..9a3fa80 --- /dev/null +++ b/Src/IP/MsgAddr.h @@ -0,0 +1,73 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CMsgAddr class. + */ +/*----------------------------------------------------------*/ +#ifndef MSGADDR_H +#define MSGADDR_H +#include <stdint.h> + +typedef enum +{ + IpcUdp_V2_0 = 1, + IpcTcp_V2_0 = 2 +} IpcProtocol_t; + +/*----------------------------------------------------------*/ +/*! \brief Storage class, which holds IP address, IP port and MAC address. +*/ +/*----------------------------------------------------------*/ +class CMsgAddr +{ +private: + int32_t m_nRefCount; + char m_IpAddr[32]; + uint32_t m_nPort; + IpcProtocol_t m_protocol; + +public: + CMsgAddr( const char *Addr, uint32_t nPort ); + CMsgAddr( const char *Addr, uint32_t nPort, IpcProtocol_t protocol ); + ~CMsgAddr(); + + /*----------------------------------------------------------*/ + /*! \brief Increments the reference counter + */ + /*----------------------------------------------------------*/ + void AddReference(); + + /*----------------------------------------------------------*/ + /*! \brief Decrements the reference counter, if the value reaches 0, this object destroys it self. + */ + /*----------------------------------------------------------*/ + void RemoveReference(); + + char *GetInetAddress(); + uint32_t GetPort(); + IpcProtocol_t GetProtocol(); +}; + +#endif //MSGADDR_H diff --git a/Src/IP/MsgFilter.cpp b/Src/IP/MsgFilter.cpp new file mode 100644 index 0000000..f9684f4 --- /dev/null +++ b/Src/IP/MsgFilter.cpp @@ -0,0 +1,47 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include "Console.h" +#include "MsgFilter.h" + +CMsgFilter::CMsgFilter( uint32_t nFBlock, uint32_t nFunc, uint8_t nOpType, OnMsg_t MsgHandler ) +{ + m_nFBlock = nFBlock; + m_nFunc = nFunc; + m_nOpType = nOpType; + m_MsgHandler = MsgHandler; +} + +void CMsgFilter::Filter( CMsgAddr *Addr, CMostMsg *Msg ) +{ + if( NULL == Addr || NULL == Msg ) + { + ConsolePrintf( + PRIO_ERROR, RED"CMsgFilter::Filter was called with invalid parameters"RESETCOLOR"\n" ); + return; + } + if( NULL != m_MsgHandler ) + if( Msg->GetFBlock() == m_nFBlock && Msg->GetFunc() == m_nFunc + && Msg->GetOpType() == m_nOpType ) + m_MsgHandler( Addr, Msg ); +} diff --git a/Src/IP/MsgFilter.h b/Src/IP/MsgFilter.h new file mode 100644 index 0000000..72ef45f --- /dev/null +++ b/Src/IP/MsgFilter.h @@ -0,0 +1,72 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CMsgFilter class. + */ +/*----------------------------------------------------------*/ +#ifndef MSGFILTER_H +#define MSGFILTER_H + +#include <stdint.h> +#include "MsgAddr.h" +#include "MostMsg.h" + +typedef void ( *OnMsg_t )( CMsgAddr *Addr, CMostMsg *Msg ); + +/*----------------------------------------------------------*/ +/*! + * \brief filters messages and dispatches result to a + * handler + */ + +/*----------------------------------------------------------*/ +class CMsgFilter +{ + uint32_t m_nFBlock; + uint32_t m_nFunc; + uint8_t m_nOpType; + OnMsg_t m_MsgHandler; + +public: + + + /*----------------------------------------------------------*/ + /*! \brief construct a message filter + */ + /*----------------------------------------------------------*/ + CMsgFilter( uint32_t nFBlock, uint32_t nFunc, uint8_t nOpType, OnMsg_t MsgHandler ); + + + + + /*----------------------------------------------------------*/ + /*! \brief filter an incoming message + */ + /*----------------------------------------------------------*/ + void Filter( CMsgAddr *Addr, CMostMsg *Msg ); +}; + + +#endif //MSGFILTER_H diff --git a/Src/MacAddr.cpp b/Src/MacAddr.cpp new file mode 100644 index 0000000..8b1f699 --- /dev/null +++ b/Src/MacAddr.cpp @@ -0,0 +1,165 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include "MacAddr.h" + + + + +CMacAddr::CMacAddr() +{ + memset( m_Mac, 0, sizeof( m_Mac ) ); +} + + + +CMacAddr::CMacAddr( const uint8_t *pAddress ) +{ + if( NULL == pAddress ) + { + memset( m_Mac, 0, sizeof( m_Mac ) ); + return; + } + m_Mac[0] = pAddress[0]; + m_Mac[1] = pAddress[1]; + m_Mac[2] = pAddress[2]; + m_Mac[3] = pAddress[3]; + m_Mac[4] = pAddress[4]; + m_Mac[5] = pAddress[5]; +} + + + +CMacAddr::CMacAddr( uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5 ) +{ + m_Mac[0] = b0; + m_Mac[1] = b1; + m_Mac[2] = b2; + m_Mac[3] = b3; + m_Mac[4] = b4; + m_Mac[5] = b5; +} + + + +CMacAddr::CMacAddr( CMacAddr *copySouce ) +{ + if( NULL != copySouce ) + { + memcpy( m_Mac, copySouce->m_Mac, sizeof( m_Mac ) ); + } +} + + +const char *CMacAddr::ToString() +{ + snprintf( stringRep, sizeof( stringRep ), "%02X-%02X-%02X-%02X-%02X-%02X", m_Mac[0], m_Mac[1], m_Mac[2], m_Mac[3], + m_Mac[4], m_Mac[5] ); + return stringRep; +} + + +const uint8_t *CMacAddr::GetBytes() +{ + return m_Mac; +} + + + +void CMacAddr::CopyValuesFromByteArray( const uint8_t *pAddress ) +{ + if( NULL == pAddress ) + return; + m_Mac[0] = pAddress[0]; + m_Mac[1] = pAddress[1]; + m_Mac[2] = pAddress[2]; + m_Mac[3] = pAddress[3]; + m_Mac[4] = pAddress[4]; + m_Mac[5] = pAddress[5]; +} + + + +void CMacAddr::CopyValuesFromString( const char *pAddress ) +{ + if( NULL == pAddress ) + return; + uint8_t pos = 0; + char buf[64]; + char *tok; + char *tkPtr; + strncpy( buf, pAddress, sizeof( buf ) ); + tok = strtok_r( buf, "-: ,.", &tkPtr ); + while( NULL != tok && pos < sizeof( m_Mac ) ) + { + uint32_t val; + sscanf( tok, "%X", &val ); + m_Mac[pos++] = val; + tok = strtok_r( NULL, "-: ,.", &tkPtr ); + } +} + + + +CMacAddr::CMacAddr( uint8_t deviceInstance, uint16_t nodeAddress ) +{ + if( 0x01 == nodeAddress ) + nodeAddress = 0x100; + + m_Mac[0] = + 0x02; //Local defined MAC address flag. Has to be unique in the local network. + m_Mac[1] = 0; + m_Mac[2] = deviceInstance; + m_Mac[3] = ( uint8_t )( nodeAddress & 0xFF ); + m_Mac[4] = ( uint8_t )( ( nodeAddress >> 8 ) & 0xFF ); + m_Mac[5] = ( uint8_t )( nodeAddress & 0xFF ); +} + + + + +CMacAddr::~CMacAddr() +{ +} + + + + + + + +uint8_t CMacAddr::operator[]( int nByte ) +{ + return ( 5 < nByte ) ? 0 : m_Mac[nByte]; +} + + + + +bool CMacAddr::operator==( CMacAddr const &rhs ) +{ + return ( 0 == memcmp( m_Mac, rhs.m_Mac, sizeof( m_Mac ) ) ); +} diff --git a/Src/MacAddr.h b/Src/MacAddr.h new file mode 100644 index 0000000..0ff9858 --- /dev/null +++ b/Src/MacAddr.h @@ -0,0 +1,153 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CMacAddr class. + */ +/*----------------------------------------------------------*/ +#ifndef _MACADDR_H_ +#define _MACADDR_H_ + +#include <stdint.h> + +/*----------------------------------------------------------*/ +/*! \brief Class to deal with MAC addresses. + */ +/*----------------------------------------------------------*/ +class CMacAddr +{ +private: + uint8_t m_Mac[6]; + char stringRep[32]; + +public: + /*----------------------------------------------------------*/ + /*! \brief Default constructor of CMacAddr. + */ + /*----------------------------------------------------------*/ + CMacAddr(); + + + + /*----------------------------------------------------------*/ + /*! \brief Default destructor of CMacAddr. + */ + /*----------------------------------------------------------*/ + ~CMacAddr(); + + + /*----------------------------------------------------------*/ + /*! \brief Constructor of CMacAddr, which uses the given bytes to initialize this class. + * \param pAddress - 6 Byte array containing the MAC address. + */ + /*----------------------------------------------------------*/ + CMacAddr( const uint8_t *pAddress ); + + /*----------------------------------------------------------*/ + /*! \brief Constructor of CMacAddr, which uses the given bytes to initialize this class. + * \param b0 - The 1st byte of the MAC address. + * \param b1 - The 2nd byte of the MAC address. + * \param b2 - The 3rd byte of the MAC address. + * \param b3 - The 4th byte of the MAC address. + * \param b4 - The 5th byte of the MAC address. + * \param b5 - The 6th byte of the MAC address. + */ + /*----------------------------------------------------------*/ + CMacAddr( uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5 ); + + + + /*----------------------------------------------------------*/ + /*! \brief Constructor of CMacAddr, which uses the given parameters to create a system wide unique MAC address. + * \param deviceIndex - The index of the physical device instance of the server device. + * \param nodeAddress - The MOST node address (e.g. 0x101). + */ + /*----------------------------------------------------------*/ + CMacAddr( uint8_t deviceInstance, uint16_t nodeAddress ); + + + + /*----------------------------------------------------------*/ + /*! \brief Copy-Constructor of CMacAddr, which creates a new instance out of the given object (deep copy). + * \param copySouce - The object, to copy. + */ + /*----------------------------------------------------------*/ + CMacAddr( CMacAddr *copySouce ); + + + + /*----------------------------------------------------------*/ + /*! \brief Gets a zero terminated string representation of this MAC address. The delimiter is '-'. + * \return A string representation of this MAC address. + */ + /*----------------------------------------------------------*/ + const char *ToString(); + + + /*----------------------------------------------------------*/ + /*! \brief Gets a Byte array with the length of 6 containing the MAC address + * \return MAC address byte array. + */ + /*----------------------------------------------------------*/ + const uint8_t *GetBytes(); + + + /*----------------------------------------------------------*/ + /*! \brief Copy the MAC address value from a given byte array. + * \param pAddress - 6 Byte array containing the MAC address. + */ + /*----------------------------------------------------------*/ + void CopyValuesFromByteArray( const uint8_t *pAddress ); + + + + + /*----------------------------------------------------------*/ + /*! \brief Copy the MAC address value from a given string. + * \param pAddress - String containing the MAC address. + */ + /*----------------------------------------------------------*/ + void CopyValuesFromString( const char *pAddress ); + + + /*----------------------------------------------------------*/ + /*! \brief Access the MAC address byte wise. + * \param nByte - offset of the byte array of the MAC address. Must be between 0 - 5. + * \return The byte value of the MAC address on the given byte offset. + */ + /*----------------------------------------------------------*/ + uint8_t operator[]( int nByte ); + + + + /*----------------------------------------------------------*/ + /*! \brief Compares the given CMacAddr instance with this one. + * \param rhs - The source CMacAddr instance, which then will be compared. + * \return true, if the value of the MAC address is equal. false, the value of the MAC address is different. + */ + /*----------------------------------------------------------*/ + bool operator==( CMacAddr const &rhs ); +}; + +#endif diff --git a/Src/Main.cpp b/Src/Main.cpp new file mode 100644 index 0000000..489f454 --- /dev/null +++ b/Src/Main.cpp @@ -0,0 +1,160 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include <DoxyGenStartPage.h> +#include "Console.h" +#include "VodHandler.h" + +static void PrintMenu() +{ + ConsolePrintfStart( PRIO_HIGH, "Main Menu, press key and enter:\n" ); + ConsolePrintfContinue( "-------------------------------\n" ); + ConsolePrintfContinue( " m - Print this menu\n" ); + ConsolePrintfContinue( " r - Toggle periodically statistics printing\n" ); + ConsolePrintfContinue( " s - Stream multiple streams to fake sinks\n" ); + ConsolePrintfContinue( " x - Exit this application\n" ); + ConsolePrintfExit( "-------------------------------\n" ); +} + +int main( int argc, char *argv[] ) +{ + ConsoleInit( false ); + + char *searchPath = NULL; + bool disableMenu = true; + ConsolePrio_t prio = PRIO_HIGH; + + ConsolePrintf( PRIO_HIGH, BLUE"========== Video On Demand Server Start =========="RESETCOLOR"\n" ); + CVodHandler *mainClass = CVodHandler::GetInstance(); + + for( int32_t i = 1; i < argc; i++ ) + { + if( strstr( argv[i], "-p" ) ) + { + if( argc <= ( i + 1 ) ) + { + ConsolePrintf( PRIO_ERROR, RED"-p parameter requires search path as next parameter"RESETCOLOR"\n" ); + return -1; + } + searchPath = argv[i + 1]; + i++; + } + else if( strstr( argv[i], "-i" ) ) + { + if( argc <= ( i + 1 ) ) + { + ConsolePrintf( PRIO_ERROR, RED"-i parameter requires string fragment which shall be ignored"RESETCOLOR"\n" ); + return -1; + } + mainClass->SetCdevIgnorePattern(argv[i + 1]); + i++; + } + else if( strstr( argv[i], "-m" ) ) + { + disableMenu = false; + } + else if( strstr( argv[i], "-vv" ) ) + { + prio = PRIO_LOW; + } + else if( strstr( argv[i], "-v" ) ) + { + prio = PRIO_MEDIUM; + } + else if( strstr( argv[i], "--help" ) ) + { + ConsolePrintfStart( PRIO_HIGH, "Usage: %s [OPTION] ... [FILE] ... \n", argv[0] ); + ConsolePrintfContinue( "Example MOST VideoOnDemand streaming service.\n" ); + + ConsolePrintfContinue( "\t-p\t\t path to search the media files."\ + " If not set, the callee path will be searched\n" ); + ConsolePrintfContinue( "\t-m\t\t enable user menu\n" ); + ConsolePrintfContinue( "\t-i\t\t ignore all cdev with the given string fragments\n" ); + ConsolePrintfContinue( "\t-v\t\t verbose mode - print debug info\n" ); + ConsolePrintfContinue( "\t-vv\t\t super verbose mode - print even more debug info\n" ); + + ConsolePrintfContinue( "Example: %s -p /root\n", argv[0] ); + return 0; + } + else + { + ConsolePrintf( PRIO_ERROR, RED + "Ignoring command line parameter at pos %d: %s, type '%s --help'"\ + " to see the correct parameter syntax"RESETCOLOR\ + "\n", i, argv[i], argv[0] ); + } + } + + ConsoleSetPrio( prio ); + + if( NULL != searchPath ) + mainClass->SetSearchPath( searchPath ); + else + mainClass->SetSearchPath( "." ); + + mainClass->ConnectToNetworkManager(); + mainClass->GetConnectionListFromNetworkManager(); + + if( disableMenu || !isatty( fileno( stdin ) ) ) + { + while( true ) + sleep( 1 ); + } + else + { + PrintMenu(); + + while( true ) + { + int c = getchar(); + fflush( stdin ); + switch( c ) + { + case 'M': + case 'm': + PrintMenu(); + break; + + case 'R': + case 'r': + mainClass->ToggleStatisticsPrint(); + break; + + case 'S': + case 's': + mainClass->CreateSampleStreams( 4 ); + break; + + case 'X': + case 'x': + CVodHandler::DestroyInstance(); + ConsolePrintf( PRIO_HIGH, BLUE"========== Video On Demand Server End =========="RESETCOLOR"\n" ); + ConsoleDeinit(); + return 0; + default: + break; + } + usleep( 10000 ); //Avoid high CPU load, if terminal is disconnected (running as daemon) + } + } +} diff --git a/Src/Multiplexer/Multiplexer.h b/Src/Multiplexer/Multiplexer.h new file mode 100644 index 0000000..d90e4b0 --- /dev/null +++ b/Src/Multiplexer/Multiplexer.h @@ -0,0 +1,476 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CMultiplexer class. + */ +/*----------------------------------------------------------*/ +#ifndef _MULTIPLEXER_H_ +#define _MULTIPLEXER_H_ + +//#define TS_STUFFING +//#define UDP_STREAM_TARGET_IP "127.0.0.1" + +#include <stdint.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include "Console.h" +#include "ThreadReadHdd.h" +#include "ThreadWriteNetwork.h" + +#ifdef UDP_STREAM_TARGET_IP +#include "udp-stream.h" +static bool udpStreamCreated = false; +#endif + + + +typedef enum WriteResult_tag +{ + WriteResult_Success, + WriteResult_Failed, + WriteResult_WouldBlock +} WriteResult_t; + + +/*----------------------------------------------------------*/ +/*! \brief Class multiplexing multiple transport streams into + * a single driver output. + */ +/*----------------------------------------------------------*/ +class CMultiplexer : public CStreamList +{ + int m_hDriver; + char m_szDriver[64]; + uint64_t m_nBytesStuffed; + uint32_t m_nStuffingCount; + uint32_t m_nWriteLen; + uint8_t *m_pBuf; + uint32_t m_nBufPos; + uint8_t m_scratchBuffer[CTsPacket::TS_PACKET_LEN]; + uint8_t m_scratchBufPos; + bool m_patSent; + CThreadReadHdd *readThread; + CThreadWriteNetwork *writeThread; +#ifdef UDP_STREAM_TARGET_IP + bool useUdp; +#endif + + static const char *GetErrnoString(int err) + { + switch( err ) + { + case 0: + return "Nothing stored in errno"; + case 1: + return "Operation not permitted"; + case 2: + return "No such file or directory"; + case 3: + return "No such process"; + case 4: + return "Interrupted system call"; + case 5: + return "I/O error"; + case 6: + return "No such device or address"; + case 7: + return "Argument list too long"; + case 8: + return "Exec format error"; + case 9: + return "Bad file number"; + case 10: + return "No child processes"; + case 11: + return "Try again"; + case 12: + return "Out of memory"; + case 13: + return "Permission denied"; + case 14: + return "Bad address"; + case 15: + return "Block device required"; + case 16: + return "Device or resource busy"; + case 17: + return "File exists"; + case 18: + return "Cross-device link"; + case 19: + return "No such device"; + case 20: + return "Not a directory"; + case 21: + return "Is a directory"; + case 22: + return "Invalid argument"; + case 23: + return "File table overflow"; + case 24: + return "Too many open files"; + case 25: + return "Not a typewriter"; + case 26: + return "Text file busy"; + case 27: + return "File too large"; + case 28: + return "No space left on device"; + case 29: + return "Illegal seek"; + case 30: + return "Read-only file system"; + case 31: + return "Too many links"; + case 32: + return "Broken pipe"; + case 33: + return "Math argument out of domain of func"; + case 34: + return "Math result not representable"; + default: + break; + } + return "Unknown"; + } + +public: + /*----------------------------------------------------------*/ + /*! \brief Constructs CMultiplexer instance + * + * \param szDriver - The character device name of isochronous + * MOST device channel. (e.g. "/root/myFifo") + * minimal bandwidth. + */ + /*----------------------------------------------------------*/ + CMultiplexer( const char *szDriver, uint32_t nWriteLen ) : + m_nStuffingCount( 0 ), m_nWriteLen( nWriteLen ), m_nBufPos( 0 ), + m_scratchBufPos( 0 ), m_patSent( false ) + { +#ifdef UDP_STREAM_TARGET_IP + useUdp = !udpStreamCreated; + if (!udpStreamCreated) + { + udpStreamCreated = true; + UdpStream_Init( UDP_STREAM_TARGET_IP, 1234 ); + } +#endif + m_pBuf = (uint8_t *)malloc( nWriteLen ); + if ( NULL == m_pBuf ) + { + ConsolePrintf( PRIO_ERROR, RED"! memory error in CMultiplexer"\ + " constructor"RESETCOLOR"\n"); + throw( -40 ); + } + strncpy(m_szDriver, szDriver, sizeof(m_szDriver)); + m_hDriver = open( szDriver, O_WRONLY ); + + if( -1 == m_hDriver ) + { + int err = errno; + ConsolePrintf( PRIO_ERROR, RED"! failed to open driver: '%s', "\ + "ERRNO: %d ('%s')"RESETCOLOR"\n", + szDriver, err, GetErrnoString( err )); + throw( -30 ); + return; + } + + readThread = new CThreadReadHdd(); + readThread->AddStreamList( this ); + + writeThread = new CThreadWriteNetwork(); + writeThread->AddMultiplexer( this ); + } + + + + + /*----------------------------------------------------------*/ + /*! \brief Destructs CMultiplexer instance + */ + /*----------------------------------------------------------*/ + ~CMultiplexer() + { + readThread->RemoveStreamList( this ); + delete readThread; + + writeThread->RemoveMultiplexer( this ); + if( -1 != m_hDriver ) + close( m_hDriver ); + } + + + + + /*----------------------------------------------------------*/ + /*! \brief Returns the name of the character device. + * \return The name of the device (e.g. "/dev/mdev0"). + */ + /*----------------------------------------------------------*/ + const char *GetDriverName( void ) + { + return m_szDriver; + } + + + + + /*----------------------------------------------------------*/ + /*! \brief Starts to add/change a sub-stream in the + * multiplex. + * \param MacAddr - The MAC address of the destination device. + * \param MacAddr - The full path to transport stream file. + */ + /*----------------------------------------------------------*/ + void PlayStream( CMacAddr *MacAddr, CSource *pSource ) + { + CStream *pStream = GetStream( MacAddr ); // is there already a stream assigned to this MAC? + + if( NULL == pStream ) + { + pStream = GetStream( NULL ); // find a stream object having an empty MAC + + if( NULL == pStream ) + { // all streams in use? + ConsolePrintf( PRIO_ERROR, RED"too many streams open"RESETCOLOR ); + return; + } + pStream->SetMacAddr( MacAddr ); // set MAC address of the stream + } + + pStream->SetSource( pSource ); // select file to be streamed + pStream->SetPause( false ); // switch off pause + } + + + + + /*----------------------------------------------------------*/ + /*! \brief Changes play position of a stream + * \param MacAddr - The MAC address of the destination device. + * \param nPos - The new time position of the stream. + */ + /*----------------------------------------------------------*/ + void SetPos( CMacAddr *MacAddr, uint16_t nPos ) + { + CStream *pStream = GetStream( MacAddr ); + if( pStream ) + pStream->SetPos( nPos ); + } + + + + + /*----------------------------------------------------------*/ + /*! \brief Pauses a stream + * \param MacAddr - The MAC address of the destination device. + * \param bPause - true, if the stream shall be paused. false, + * if the stream shall be playing. + */ + /*----------------------------------------------------------*/ + void SetPause( CMacAddr *MacAddr, bool bPause ) + { + CStream *pStream = GetStream( MacAddr ); + if( pStream ) + pStream->SetPause( bPause ); + } + + + + + /*----------------------------------------------------------*/ + /*! \brief Pauses a stream + * \param MacAddr - The MAC address of the destination device. + * \param bRepetition - true, if the stream shall be + * repeated infinitely, false, if the stream shall be + * stopped after reaching the end. + */ + /*----------------------------------------------------------*/ + void SetRepetition( CMacAddr *MacAddr, bool bRepetition ) + { + CStream *pStream = GetStream( MacAddr ); + if( pStream ) + pStream->SetRepetition( bRepetition ); + } + + + + + /*----------------------------------------------------------*/ + /*! \brief Stops a stream and releases resources + * \param MacAddr - The MAC address of the destination device. + */ + /*----------------------------------------------------------*/ + void StopStream( CMacAddr *MacAddr ) + { + CStream *pStream = GetStream( MacAddr ); + if( pStream ) + pStream->SetSource( NULL ); + } + + + + + /*----------------------------------------------------------*/ + /*! \brief Structure holding statistics values. + * \note Pass this structure to the GetStatistics method. + */ + /*----------------------------------------------------------*/ + typedef struct + { + uint64_t bytesSent; /*! The amount of bytes sent with video and audio + * data are written into the given buffer. */ + uint64_t bytesStuffed; /*! The amount of bytes sent with stuffing payload. */ + uint64_t bytesRead; /*! The amount of bytes read with video and audio + * data are written into the given buffer. */ + uint64_t errPcr; /*! The amount of PCR errors are written into + * the given buffer. */ + uint64_t errBufUnderflow; /*! Number of buffer underflows + * occurred since last call. Values higher than zero + * are indicating that the thread reading stream data + * from files, has not enough performance */ + uint64_t errBufOverflow; /*! Number of buffer overflows + * occurred since last call. Values higher than zero + * are indicating that the sending thread does not + * get away data fast enough. Reason could be that + * the MOST channel is too small, that the CPU is + * too small, ... */ + uint64_t errTs; /*! The amount of TS errors are written into + * the given buffer. */ + } MultiplexStats_t; + + + + + /*----------------------------------------------------------*/ + /*! \brief Retrieves the statistics for the stream + * \param MacAddr - The MAC address of the destination device. + * \param pStats - Pointer to the MultiplexStats_t structure. + * This will be filled with data by this method. + */ + /*----------------------------------------------------------*/ + bool GetStatistics( CMacAddr *MacAddr, MultiplexStats_t *pStats ) + { + CStream *pStream = GetStream( MacAddr ); + if( NULL == pStats ) + return false; + + memset( ( void * )pStats, 0, sizeof( MultiplexStats_t ) ); + if( NULL != pStream ) + { + pStats->bytesSent = pStream->GetBytesSent(); + pStats->bytesRead = pStream->GetBytesRead(); + pStats->errPcr = pStream->GetErrPcr(); + pStats->errBufUnderflow = pStream->GetErrBufUnderflow(); + pStats->errBufOverflow = pStream->GetErrBufOverflow(); + pStats->errTs = pStream->GetErrTs(); + } + + pStats->bytesStuffed = m_nBytesStuffed; + m_nBytesStuffed = 0; + + return true; + } + + + + + /*----------------------------------------------------------*/ + /*! \brief sends some data if possible + * \return enumeration, holding the result of this operation + */ + /*----------------------------------------------------------*/ + WriteResult_t WriteToDriver() + { + uint8_t *pBuf; + if (0 != m_scratchBufPos) + { + uint32_t tail = CTsPacket::TS_PACKET_LEN - m_scratchBufPos; + memcpy(&m_pBuf[m_nBufPos], &m_scratchBuffer[m_scratchBufPos], tail); + m_nBufPos += tail; + } + m_scratchBufPos = 0; + for( ; m_nBufPos < m_nWriteLen; m_nBufPos += CTsPacket::TS_PACKET_LEN ) + { + if ( m_nBufPos + CTsPacket::TS_PACKET_LEN <= m_nWriteLen ) + { + pBuf = m_pBuf + m_nBufPos; //The buffer has enough space to hold a complete transport stream packet + } + else + { + pBuf = m_scratchBuffer; //The buffer is not full, but there is a little space left, so use scratch buffer + m_scratchBufPos = m_nWriteLen - m_nBufPos; + } + + if ( !m_patSent && GetPatPacket( pBuf ) ) + { + m_patSent = true; + } + else if( !GetTsPacket( pBuf ) ) + { +#ifdef TS_STUFFING + CTsPacket::CreateStuffing( pBuf, m_nStuffingCount++ ); + m_nBytesStuffed += CTsPacket::TS_PACKET_LEN; +#else + return WriteResult_WouldBlock; +#endif + } + if (0 != m_scratchBufPos) + { + memcpy(m_pBuf + m_nBufPos, m_scratchBuffer, m_scratchBufPos); + break; + } + } +#ifdef UDP_STREAM_TARGET_IP + if (useUdp) + { + uint32_t udpSent = 0; + const uint32_t maxUdpSend = 7 * CTsPacket::TS_PACKET_LEN; + do + { + uint32_t bytesToSend = m_nWriteLen - udpSent; + if (bytesToSend > maxUdpSend) + bytesToSend = maxUdpSend; + UdpStream_Send( &m_pBuf[udpSent], bytesToSend ); + udpSent += bytesToSend; + } + while (udpSent < m_nWriteLen); + } +#endif + m_nBufPos = 0; + m_patSent = false; + return ( m_nWriteLen == ( uint32_t )write( m_hDriver, m_pBuf, m_nWriteLen ) ) ? WriteResult_Success : WriteResult_Failed; + } +}; + + + + +#endif diff --git a/Src/Multiplexer/RingBuffer.h b/Src/Multiplexer/RingBuffer.h new file mode 100644 index 0000000..29cafcd --- /dev/null +++ b/Src/Multiplexer/RingBuffer.h @@ -0,0 +1,187 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CRingBuffer class. + */ +/*----------------------------------------------------------*/ + +#ifndef CRINGBUFFER_H +#define CRINGBUFFER_H + +#include "Console.h" + + +/*----------------------------------------------------------*/ +/*! \brief Class providing a ring buffer. One thread can +* write to the buffer while another thread is reading +* from it. This class has highest performance for +* synchronization, since it is not using mutexes. +* +* LIMITATIONS: +* - only one thread can read +* - only one thread can write +* - the ring buffer size needs to be aligned to +* read, write block size +* +* Violating any limitation will lead to unpredictable +* results. +*/ +/*----------------------------------------------------------*/ +class CRingBuffer +{ + uint8_t *m_pBuf; + uint32_t m_nWriteTot; // total bytes written to ring buffer + uint32_t m_nReadTot; // total bytes read from ring buffer + uint32_t m_nWritePos; // memory write position + uint32_t m_nReadPos; // memory read position + uint32_t m_nReadLen; // bytes per read + uint32_t m_nWriteLen; // bytes per write + uint32_t m_nBufLen; // bytes of buffer + +public: + /*----------------------------------------------------------*/ + /*! \brief constructs a ring buffer instance + * \param nReadLen - read block size in bytes + * \param nWriteLen - write block size in bytes + * \param nWriteBlocks - total buffer size counted in write + * blocks + */ + /*----------------------------------------------------------*/ + CRingBuffer(uint32_t nReadLen, uint32_t nWriteLen, uint32_t nWriteBlocks) + : m_nWriteTot(0) + , m_nReadTot(0) + , m_nWritePos(0) + , m_nReadPos(0) + , m_nReadLen(nReadLen) + , m_nWriteLen(nWriteLen) + , m_nBufLen(nWriteBlocks*nWriteLen) + { + if ( 0 != (m_nBufLen % m_nReadLen) ) { + ConsolePrintf( PRIO_ERROR, RED"! ring size needs to be dividable by read block size"RESETCOLOR); + throw (-10); + } + m_pBuf = new uint8_t[m_nBufLen]; + + if (NULL == m_pBuf) { + ConsolePrintf( PRIO_ERROR, RED"! out of memory"RESETCOLOR); + throw (-10); + } + } + + + + /*----------------------------------------------------------*/ + /*! \brief destructs ring buffer instance + */ + /*----------------------------------------------------------*/ + ~CRingBuffer() + { + delete m_pBuf; + } + + + + /*----------------------------------------------------------*/ + /*! \brief checks if there is enough memory available to + * write a chunk of data to the ring buffer + * \return true, if can be written + */ + /*----------------------------------------------------------*/ + bool GetCanWrite() + { + uint32_t nFilled = m_nWriteTot - m_nReadTot; + return nFilled < m_nBufLen - m_nWriteLen; + } + + + + /*----------------------------------------------------------*/ + /*! \brief checks if there is enough memory available to + * read a chunk of data from the ring buffer + * \return true, if can be read + */ + /*----------------------------------------------------------*/ + bool GetCanRead() + { + uint32_t nFilled = m_nWriteTot - m_nReadTot; + return nFilled >= m_nReadLen; + } + + + + /*----------------------------------------------------------*/ + /*! \brief gets pointer to current read position + * \return pointer to current read position + */ + /*----------------------------------------------------------*/ + uint8_t *GetReadPos() + { + return m_pBuf + m_nReadPos; + } + + + + /*----------------------------------------------------------*/ + /*! \brief gets pointer to current write position + * \return pointer to current write position + */ + /*----------------------------------------------------------*/ + uint8_t *GetWritePos() + { + return m_pBuf + m_nWritePos; + } + + + + /*----------------------------------------------------------*/ + /*! \brief called after a chunk of data has been written + * to the ring buffer + */ + /*----------------------------------------------------------*/ + void WriteDone() + { + m_nWriteTot += m_nWriteLen; + m_nWritePos += m_nWriteLen; + if (m_nBufLen == m_nWritePos) + m_nWritePos = 0; + } + + + + /*----------------------------------------------------------*/ + /*! \brief called after a chunk of data has been read + * from the ring buffer + */ + /*----------------------------------------------------------*/ + void ReadDone() + { + m_nReadTot += m_nReadLen; + m_nReadPos += m_nReadLen; + if (m_nBufLen == m_nReadPos) + m_nReadPos = 0; + } +}; + +#endif diff --git a/Src/Multiplexer/Source.h b/Src/Multiplexer/Source.h new file mode 100644 index 0000000..9747e39 --- /dev/null +++ b/Src/Multiplexer/Source.h @@ -0,0 +1,408 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CSource class. + */ +/*----------------------------------------------------------*/ +#ifndef _SOURCE_H_ +#define _SOURCE_H_ + +#include <stdint.h> +#include <stddef.h> +#include <sys/time.h> +#include <time.h> +#include <pthread.h> +#include "TsPacket.h" +#include "Stream.h" +#include "SafeVector.h" +#include "AutoLock.h" + + + +/*----------------------------------------------------------*/ +/*! \brief Base class sourcing a transport stream + */ +/*----------------------------------------------------------*/ +class CSource : public CTsPacket, public CRingBuffer +{ +protected: + static uint32_t m_nInstCnt; + uint32_t m_nInst; + bool m_bAutoDestroy; + bool m_bEmptyRing; + uint32_t m_nAPid; + uint32_t m_nVPid; + uint32_t m_nPPid; + uint32_t m_nLastPcr; + uint16_t m_fReqPos; + bool m_bPause; + bool m_bRepetition; + uint64_t m_nBytesRead; + uint64_t m_nErrBuf; + uint64_t m_nErrPcr; + uint64_t m_nErrTs; + uint32_t m_tTsStart; + uint32_t m_tTsLast; + uint32_t m_tTimerStart; + uint64_t m_tLastPmt; + + + CSafeVector<CStream *>m_Stream; + pthread_mutex_t m_StreamMutex; + +public: + static const uint16_t INVALID_POS = 0xFFFF; + static const uint32_t READ_LEN = (47*512); // this number MUST be dividable by 188 and 512 + static const uint32_t NUM_READ_AHEAD = 8; // number of blocks reading ahead + + + + /*----------------------------------------------------------*/ + /*! \brief Constructs Source instance + * \param bAutoDestroy - true, destroys it self, when the + * last listener gone + */ + /*----------------------------------------------------------*/ + CSource(bool bAutoDestroy = true) + : CRingBuffer(TS_PACKET_LEN, READ_LEN, NUM_READ_AHEAD) + , m_nInst(m_nInstCnt++) + , m_bAutoDestroy(bAutoDestroy) + , m_bEmptyRing(false) + , m_nAPid(PID_INVALID) + , m_nVPid(PID_INVALID) + , m_nPPid(PID_INVALID) + , m_nLastPcr(PCR_INVALID) + , m_fReqPos(INVALID_POS) + , m_bPause(false) + , m_bRepetition(false) + , m_nBytesRead(0) + , m_nErrBuf(0) + , m_nErrPcr(0) + , m_nErrTs(0) + , m_tTsStart(0) + , m_tTsLast(0) + , m_tTimerStart(0) + , m_tLastPmt(0) + { + pthread_mutex_init( &m_StreamMutex, NULL ); + } + + + virtual ~CSource() + { + pthread_mutex_destroy( &m_StreamMutex ); + } + + + /*----------------------------------------------------------*/ + /*! \brief Add a listener to a source + * + * \param pRingBuffer - RingBuffer to source to + */ + /*----------------------------------------------------------*/ + void AddStream(CStream *pStream) + { + volatile CAutoLock autoLock( &m_StreamMutex ); + if ( pStream ) m_Stream.PushBack(pStream); + } + + + + + /*----------------------------------------------------------*/ + /*! \brief Remove a stream receiving source data + */ + /*----------------------------------------------------------*/ + void RemoveStream(CStream *pStream) + { + pthread_mutex_lock( &m_StreamMutex ); + m_Stream.Remove(pStream); + if (m_bAutoDestroy && (0 == m_Stream.Size())) + { + pthread_mutex_unlock( &m_StreamMutex ); + delete this; + return; + } + pthread_mutex_unlock( &m_StreamMutex ); + } + + + + /*----------------------------------------------------------*/ + /*! \brief called periodically to fill ring buffers of all + * m_Stream + */ + /*----------------------------------------------------------*/ + virtual bool FillBuffer() = 0; + + + + /*----------------------------------------------------------*/ + /*! \brief sends a TS packet from the ring buffer to all + * streams + */ + /*----------------------------------------------------------*/ + bool SendTsPacket(CStream *pStream) + { + volatile CAutoLock autoLock( &m_StreamMutex ); + if ( (0 == m_Stream.Size()) || // no sources available? + (pStream != m_Stream[0]) ) // don't call a source multiple times if it is sourcing multiple streams + { + return false; + } + + if ( m_bEmptyRing ) // after a seek, the stored data can be flushed + { + while ( GetCanRead() ) // as long as there is data + ReadDone(); // skip TS packet + + m_bEmptyRing = false; // ring is now empty, don't empry again + return false; + } + + if ( !GetCanRead() ) + { + m_nErrBuf++; // read thread too slow + return false; + } + + uint8_t *pTs = GetReadPos(); // get pointer to current TS packet + + if ( !GetHasSyncByte(pTs) ) // check packet + { + m_nErrTs++; + ReadDone(); + return false; + } + + struct timeval tv; + gettimeofday(&tv, 0); + uint64_t tTimerCur = tv.tv_sec * 45000 + (tv.tv_usec * 45) / 1000; + + if ( (tTimerCur - m_tLastPmt) >= (30 * 45) ) // time to send PMT packet (approx 30ms)? + { + for (uint32_t i = 0; i < m_Stream.Size(); i++) + m_Stream[i]->SendPmt(); + + m_tLastPmt = tTimerCur; + return true; + } + + // ------------------------------------------------ // + // check PCR // + // ------------------------------------------------ // + if ((pTs[3] & 0x20) && // check existence of an adaption field + (pTs[4]) && // adaption field has length > 0 + (pTs[5] & 0x10) ) // adaption field contains PCR? + { + unsigned long tTsCur = // PCR runs from 0 to 45000 in a second + (pTs[6] << 24) + + (pTs[7] << 16) + + (pTs[8] << 8) + + (pTs[9]); + + if ((m_tTsStart > tTsCur) || // not the first call, or jumped to start of file + (m_tTsLast > tTsCur ) || + (tTsCur - m_tTsLast > 45 * 1000)) { // discontinuity? + if (0 != m_tTimerStart) // stream not just started? + m_nErrPcr++; // only for statistics + + m_tTimerStart = tTimerCur; // reset time measurement + m_tTsStart = tTsCur; + } + m_tTsLast = tTsCur; // remember last PCR + + int dT = (int)(tTsCur - m_tTsStart) - (int)(tTimerCur - m_tTimerStart); + + if (0 < dT) { + if ((45 * 1000 / 2) > dT) { // if the recreation of the TS timing is less 500ms + return false; // wait before outputting this packet + } + + m_tTimerStart = tTimerCur; // reset time measurement + m_tTsStart = tTsCur; + m_nErrPcr++; // only for statistics + } else if ((-45 * 1000 / 2) > dT) { // if the recreation of the TS timing is off for 500ms + m_tTimerStart = tTimerCur; // reset time measurement + m_tTsStart = tTsCur; + m_tTsStart = tTsCur; + m_nErrPcr++; // only for statistics + } + } + + uint32_t nCurPid = GetPid(pTs); + + if ( nCurPid == m_nAPid ) + { + for (uint32_t i = 0; i < m_Stream.Size(); i++) + m_Stream[i]->SendAudio(pTs); + + ReadDone(); // skip and continue with next TS packet + return true; + } + else if ( nCurPid == m_nVPid ) + { + for (uint32_t i = 0; i < m_Stream.Size(); i++) + m_Stream[i]->SendVideo(pTs); + + ReadDone(); // skip and continue with next TS packet + return true; + } + + ReadDone(); // skip and continue with next TS packet + return false; + } + + + + /*----------------------------------------------------------*/ + /*! \brief returns source's audio PID + */ + /*----------------------------------------------------------*/ + uint32_t GetAPid() { return m_nAPid; } + + + + /*----------------------------------------------------------*/ + /*! \brief returns source's audio PID + */ + /*----------------------------------------------------------*/ + uint32_t GetVPid() { return m_nVPid; } + + + + /*----------------------------------------------------------*/ + /*! \brief number of buffer underflows since last call + */ + /*----------------------------------------------------------*/ + uint64_t GetErrBuf() + { + uint64_t nTmp = m_nErrBuf; + m_nErrBuf = 0; + return nTmp; + }; + + + + /*----------------------------------------------------------*/ + /*! \brief number of PCR errors since last call + */ + /*----------------------------------------------------------*/ + uint64_t GetErrPcr() + { + uint64_t nTmp = m_nErrPcr; + m_nErrPcr = 0; + return nTmp; + }; + + + + /*----------------------------------------------------------*/ + /*! \brief get number of TS syntax violations since last call + */ + /*----------------------------------------------------------*/ + uint64_t GetErrTs() + { + uint64_t nTmp = m_nErrTs; + m_nErrTs = 0; + return nTmp; + }; + + + + /*----------------------------------------------------------*/ + /*! \brief returns source's last PCR. By dividing this value + * with CTsPacket::PCR_TICKS_PER_SEC the length of + * time can be detected. If no length is available, + * CTsPacket::PCR_INVALID is returned + * + */ + /*----------------------------------------------------------*/ + uint32_t GetLastPcr() { return m_nLastPcr; } + + + + /*----------------------------------------------------------*/ + /*! \brief returns number of bytes filled into listener's + * ring buffers + */ + /*----------------------------------------------------------*/ + uint64_t GetBytesRead() + { + uint64_t oldVal = m_nBytesRead; + m_nBytesRead = 0; + return oldVal; + } + + + + /*----------------------------------------------------------*/ + /*! \brief returns if stream is paused + */ + /*----------------------------------------------------------*/ + bool GetPause() { return m_bPause; } + + + + + /*----------------------------------------------------------*/ + /*! \brief unique ID for each stream + */ + /*----------------------------------------------------------*/ + uint32_t GetInstId() { return m_nInst; }; + + + + + /*----------------------------------------------------------*/ + /*! \brief sets a new position in the stream + */ + /*----------------------------------------------------------*/ + void SetPos(uint16_t fReqPos) { + m_fReqPos = fReqPos; + m_bEmptyRing = true; + } + + + + /*----------------------------------------------------------*/ + /*! \brief set repeat mode + * \param bRepetition - if true stream will repeated for ever + */ + /*----------------------------------------------------------*/ + void SetRepetition(bool bRepetition) { m_bRepetition = bRepetition; } + + + + /*----------------------------------------------------------*/ + /*! \brief sets a pause mode on/off + */ + /*----------------------------------------------------------*/ + void SetPause(bool bPause) { m_bPause = bPause; } +}; + + + +#endif //_SOURCE_H_ diff --git a/Src/Multiplexer/SourceFile.cpp b/Src/Multiplexer/SourceFile.cpp new file mode 100644 index 0000000..3105145 --- /dev/null +++ b/Src/Multiplexer/SourceFile.cpp @@ -0,0 +1,176 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include <stdint.h> +#include "Console.h" +#include "SourceFile.h" + +uint32_t CSource::m_nInstCnt = 0; + +CSourceFile::CSourceFile(const char *szFile, bool autoDestroy) +{ + m_bAutoDestroy = autoDestroy; + m_hFile = fopen64(szFile, "rb"); // try to open file + if (NULL == m_hFile) { + ConsolePrintf( PRIO_ERROR, RED"CSourceFile: unable to open file %s"RESETCOLOR"\n", szFile); + return; + } + strncpy(m_szFileName, szFile, sizeof(m_szFileName)); + + fseek(m_hFile, 0, SEEK_END); // get file's length + fpos64_t pos; + int result = fgetpos64(m_hFile, &pos); + if (result == 0) + m_nLength = pos.__pos; + else + m_nLength = -1; + fseek(m_hFile, 0, SEEK_SET); // start reading from start of file + + uint8_t Ts[READ_LEN]; // get PIDs for video and audio + uint32_t bytesRead = fread(// read a chunk from file and write it into ring buffer + Ts, + 1, + READ_LEN, + m_hFile); + + if (READ_LEN != bytesRead) { // no complete block available? + Close(); + return; + } + + for (uint8_t *pTs = Ts; pTs < Ts + READ_LEN; pTs += 188) { + if (PID_INVALID == m_nPPid) { + if (GetIsPat(pTs)) + m_nPPid = GetPmtPidFromPat(pTs, 0); + } else if (GetHasPid(pTs, m_nPPid)) { + m_nAPid = GetAudioPidFromPmt(pTs); + m_nVPid = GetVideoPidFromPmt(pTs); + break; + } + } + + if (PID_INVALID == m_nPPid) { // PMT found ? + ConsolePrintf( PRIO_ERROR, RED"CSourceFile: no PMT found in file %s"RESETCOLOR"\n", szFile); + Close(); + return; + } + + uint64_t nBlocks = (m_nLength / READ_LEN) - + 1; // number of read blocks in file + + for (; (nBlocks) && (PCR_INVALID == m_nLastPcr); nBlocks--) { + fseek(m_hFile, nBlocks * READ_LEN, + SEEK_SET); // read backwards from end of file + if (READ_LEN != + fread( // read a chunk from file and write it into ring buffer + Ts, + 1, + READ_LEN, + m_hFile)) { // no complete block available? + Close(); + return; + } + for (uint8_t *pTs = Ts; (pTs < Ts + READ_LEN) + && (PCR_INVALID == m_nLastPcr); pTs += 188) { + m_nLastPcr = GetPcr(pTs); + } + } + + fseek(m_hFile, 0, SEEK_SET); // start reading from start of file + ConsolePrintf( PRIO_MEDIUM, "Stream started file: %s\n", m_szFileName); +} + + + + +CSourceFile::~CSourceFile() +{ + Close(); +} + + + + +void CSourceFile::Close() +{ + if (NULL != m_hFile) { + fclose(m_hFile); + m_hFile = NULL; + ConsolePrintf( PRIO_MEDIUM, "Stream closed file: %s\n", m_szFileName); + } +} + + + + +bool CSourceFile::FillBuffer() +{ + if (true == m_bPause || // source paused? + 0 == m_Stream.Size() || // no receiving streams? + NULL == m_hFile || // file already closed? + !GetCanWrite() ) // ring buffer full? + { + return false; + } + + + if ( INVALID_POS != m_fReqPos) { // user requested to play from other position? + int64_t nPos = ((m_nLength * (int64_t)m_fReqPos / (int64_t)1000) / + (int64_t)TS_PACKET_LEN) * (int64_t)TS_PACKET_LEN; // stay TS packet aligned + + ConsolePrintf( PRIO_MEDIUM, "Stream seek to %lld (%lld per mil) for file: %s\r\n", nPos,((int64_t)1000 * nPos / m_nLength), m_szFileName); + + m_fReqPos = INVALID_POS; + + if (PID_INVALID != GetVPid()) { // if there is a video ES, seek to previous I frame + uint8_t Ts[TS_PACKET_LEN]; + + for (; nPos; nPos -= TS_PACKET_LEN) { + fseeko64(m_hFile, nPos, SEEK_SET); + if ( TS_PACKET_LEN != fread(Ts, 1, TS_PACKET_LEN, m_hFile) ) + break; + + if (GetIsStartOfIFrame(Ts)) // start of I frame? + break; + } + } + fseeko64(m_hFile, nPos, SEEK_SET); // seek to requested position + } + + + if ( READ_LEN != fread(GetWritePos(),1,READ_LEN,m_hFile) ) { // no complete chunk available? + ConsolePrintf( PRIO_MEDIUM, "CSourceFile: EOF for file: %s\r\n", m_szFileName); + + //TODO: remove quick hack: + if (true || m_bRepetition) + m_fReqPos = 0; + else + Close(); + + return false; + } + + m_nBytesRead += READ_LEN; + WriteDone(); + return true; +} diff --git a/Src/Multiplexer/SourceFile.h b/Src/Multiplexer/SourceFile.h new file mode 100644 index 0000000..02afea2 --- /dev/null +++ b/Src/Multiplexer/SourceFile.h @@ -0,0 +1,77 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CSourceFile class. + */ +/*----------------------------------------------------------*/ +#ifndef _SOURCEFILE_H_ +#define _SOURCEFILE_H_ +#include <stdio.h> +#include "Source.h" + + +/*----------------------------------------------------------*/ +/*! \brief source streaming a file from HDD + */ +/*----------------------------------------------------------*/ +class CSourceFile : public CSource +{ + FILE *m_hFile; + int64_t m_nLength; + char m_szFileName[100]; + + void Close(); + +public: + /*----------------------------------------------------------*/ + /*! \brief Constructs Source instance + * + * \param szFile - name of file to stream from + * \param autoDestroy - true, destroys it self, when the last listener gone + */ + /*----------------------------------------------------------*/ + CSourceFile(const char *szFile, bool autoDestroy); + + + /*----------------------------------------------------------*/ + /*! \brief Destructs Source instance + */ + /*----------------------------------------------------------*/ + ~CSourceFile(); + + + + + /*----------------------------------------------------------*/ + /*! \brief called periodically to fill ring buffers of all + * listeners + */ + /*----------------------------------------------------------*/ + bool FillBuffer(); +}; + + + +#endif
\ No newline at end of file diff --git a/Src/Multiplexer/SourceFileConverted.cpp b/Src/Multiplexer/SourceFileConverted.cpp new file mode 100644 index 0000000..2816663 --- /dev/null +++ b/Src/Multiplexer/SourceFileConverted.cpp @@ -0,0 +1,188 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2016 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include <stdint.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <signal.h> +#include <sys/wait.h> +#include <stdlib.h> +#include <errno.h> +#include "Console.h" +#include "SourceFileConverted.h" + + +CSourceFileConverted::CSourceFileConverted(const char *szFile, bool autoDestroy) + : m_hFile(-1) + , m_nLength(0) + , m_nChildPid(0) +{ + m_bAutoDestroy = autoDestroy; + strncpy(m_szFileName, szFile, sizeof(m_szFileName)); + m_nLength = 6*60; // TODO: determine actual stream length, assume 6 minutes for now... + + Start(0); + + m_nPPid = 0x1000; // avconv default / -mpegts_pmt_start_pid X + m_nAPid = 0x101; // avconv default / -streamid 1:X + m_nVPid = 0x100; // avconv default / -streamid 0:X + + m_nLastPcr = PCR_INVALID; +} + + + + +CSourceFileConverted::~CSourceFileConverted() +{ + Close(); +} + + + + +void CSourceFileConverted::Start(int64_t nPos) +{ + // fork() and start avconv in child: + int fd[2]; + if (pipe(fd)) { + ConsolePrintf(PRIO_ERROR, RED"CSourceFileConverted: failed to open pipe for %s"RESETCOLOR"\n", m_szFileName); + return; + } + pid_t forkret = fork(); + char *ext; + char chstr[20] = ""; + switch (forkret) { + case 0: + /* the child process: */ + close(fd[0]); + dup2(fd[1], 1); // close stdout, duplicate input side of the pipe to stdout +// execl("/bin/cat", "/bin/cat", m_szFileName, NULL); + sprintf(chstr, "%lli", nPos); + ext = rindex(m_szFileName, '.'); + if (ext && !strcasecmp(ext, ".mp3")) + execl("/usr/bin/avconv", "/usr/bin/avconv", + "-ss", chstr, "-i", m_szFileName, + "-f", "mpegts", "-codec", "copy", + "-map", "0:a:0", + "-streamid", "0:0x101", "-mpegts_pmt_start_pid", "0x1000", + "pipe:1", NULL); + else + execl("/usr/bin/avconv", "/usr/bin/avconv", + "-ss", chstr, "-i", m_szFileName, + "-f", "mpegts", "-codec", "copy", "-bsf:v", "h264_mp4toannexb", + "-map", "0:v:0", "-map", "0:a:0", + "-streamid", "0:0x100", "-streamid", "1:0x101", "-mpegts_pmt_start_pid", "0x1000", + "pipe:1", NULL); + ConsolePrintf(PRIO_ERROR, RED"CSourceFileConverted: execl() call failed for %s"RESETCOLOR"\n", m_szFileName); + exit(0); + case -1: + /* fork() failed */ + close(fd[0]); + close(fd[1]); + ConsolePrintf(PRIO_ERROR, RED"CSourceFileConverted: failed to fork for %s"RESETCOLOR"\n", m_szFileName); + return; + default: + /* parent process after fork: */ + m_nChildPid = forkret; + close(fd[1]); + m_hFile = fd[0]; /* read from child via pipe */ + } + + m_nLastPcr = PCR_INVALID; + + ConsolePrintf( PRIO_MEDIUM, "Stream started file: %s\n", m_szFileName); +} + + + + +void CSourceFileConverted::Close() +{ + if (m_nChildPid > 0) { + ConsolePrintf(PRIO_LOW, "sending SIGKILL to pid %u\n", m_nChildPid); + kill(m_nChildPid, SIGKILL); + ConsolePrintf(PRIO_LOW, "waiting for child to exit...\n"); + int childStatus = 0; + waitpid(m_nChildPid, &childStatus, 0); + m_nChildPid = 0; + if (WIFEXITED(childStatus)) + ConsolePrintf(PRIO_LOW, "child terminated normally (exit status %u)\n", WEXITSTATUS(childStatus)); + else if (WIFSIGNALED(childStatus)) + ConsolePrintf(PRIO_LOW, "child terminated by signal number %u\n", WTERMSIG(childStatus)); + } + if (m_hFile > 0) { + close(m_hFile); + m_hFile = -1; + ConsolePrintf( PRIO_MEDIUM, "Stream closed file: %s\n", m_szFileName); + } +} + + + + +bool CSourceFileConverted::FillBuffer() +{ + if (true == m_bPause || // source paused? + 0 == m_Stream.Size() || // no receiving streams? + m_hFile < 0 || // file already closed? + !GetCanWrite() ) // ring buffer full? + { + return false; + } + + + if ( INVALID_POS != m_fReqPos) { // user requested to play from other position? + int64_t nPos = (m_nLength * (int64_t)m_fReqPos / 1000); + + ConsolePrintf( PRIO_MEDIUM, "Stream seek to %lld (%lld per mil) for file: %s\r\n", nPos,(1000 * nPos / m_nLength), m_szFileName); + + m_fReqPos = INVALID_POS; + + Close(); + Start(nPos); + } + + + unsigned int readcnt = 0; + while (readcnt < READ_LEN) { + int readres = read(m_hFile, GetWritePos()+readcnt, READ_LEN-readcnt); + if (readres <= 0) { // remote pipe end closed, or other error? + ConsolePrintf( PRIO_MEDIUM, "CSourceFileConverted: EOF for file: %s (result=%i, errno=%i)\r\n", m_szFileName, readres, errno); + + //TODO: remove quick hack: + if (true || m_bRepetition) + m_fReqPos = 0; + else + Close(); + + return false; + } + readcnt += readres; + } + + m_nBytesRead += READ_LEN; + WriteDone(); + return true; +} diff --git a/Src/Multiplexer/SourceFileConverted.h b/Src/Multiplexer/SourceFileConverted.h new file mode 100644 index 0000000..297b582 --- /dev/null +++ b/Src/Multiplexer/SourceFileConverted.h @@ -0,0 +1,79 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2016 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CSourceFileConverted class. + */ +/*----------------------------------------------------------*/ +#ifndef _SOURCEFILECONVERTED_H_ +#define _SOURCEFILECONVERTED_H_ +#include <stdio.h> +#include "Source.h" + + +/*----------------------------------------------------------*/ +/*! \brief source streaming a file from HDD, using avconv + */ +/*----------------------------------------------------------*/ +class CSourceFileConverted : public CSource +{ + int m_hFile; + int64_t m_nLength; /* file length in seconds */ + char m_szFileName[100]; + pid_t m_nChildPid; /* PID of external TS muxer */ + + void Start(int64_t nPos); + void Close(); + +public: + /*----------------------------------------------------------*/ + /*! \brief Constructs Source instance + * + * \param szFile - name of file to stream from + * \param autoDestroy - true, destroys it self, when the last listener gone + */ + /*----------------------------------------------------------*/ + CSourceFileConverted(const char *szFile, bool autoDestroy); + + + /*----------------------------------------------------------*/ + /*! \brief Destructs Source instance + */ + /*----------------------------------------------------------*/ + ~CSourceFileConverted(); + + + + + /*----------------------------------------------------------*/ + /*! \brief called periodically to fill ring buffers of all + * listeners + */ + /*----------------------------------------------------------*/ + bool FillBuffer(); +}; + + + +#endif
\ No newline at end of file diff --git a/Src/Multiplexer/Stream.cpp b/Src/Multiplexer/Stream.cpp new file mode 100644 index 0000000..37fa93b --- /dev/null +++ b/Src/Multiplexer/Stream.cpp @@ -0,0 +1,236 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include <stdint.h> +#include "Source.h" +#include "Stream.h" +#include "TsPacket.h" + + + +uint32_t CStream::m_nInstCnt = 0; + + + + +CStream::CStream() + : CRingBuffer(CTsPacket::TS_PACKET_LEN, CTsPacket::TS_PACKET_LEN, NUM_PACKETS_BUFFERED) + , m_nInst(m_nInstCnt++) + , m_MacAddr(NULL) + , m_pCurrSource(NULL) + , m_pNextSource(NULL) + , m_nBytesSent(0) + , m_nErrBufOvfl(0) +{ +} + + + + +CStream::~CStream() +{ + if (NULL != m_MacAddr) { + delete m_MacAddr; + m_MacAddr = NULL; + } + + if (NULL != m_pCurrSource) { + m_pCurrSource->RemoveStream(this); + } +} + + + + +bool CStream::ReadHdd() +{ + if (NULL != m_pNextSource) { //Switch sources, if there is a new one stored. + if (NULL != m_pCurrSource) // release old source + m_pCurrSource->RemoveStream(this); + + m_pCurrSource = m_pNextSource; // store new source + m_pNextSource = NULL; + m_pCurrSource->AddStream(this); // start listening to this source + } + + return (NULL == m_pCurrSource) ? false : m_pCurrSource->FillBuffer(); +} + + + + +void CStream::SendPmt() +{ + if ( !GetCanWrite() ) + { + m_nErrBufOvfl++; + return; + } + + m_TsPmt[3] = (m_TsPmt[3] & 0xF0) | // update continuity counter + (((m_TsPmt[3] & 0x0F) + 1) & 0x0F); + + memcpy(GetWritePos(), m_TsPmt, CTsPacket::TS_PACKET_LEN); + WriteDone(); +} + + + + +void CStream::SendAudio(uint8_t *pTs) +{ + if ( !GetCanWrite() ) + { + m_nErrBufOvfl++; + return; + } + + pTs[1] = (pTs[1] & 0xE0) | (mPID_AUDIO >> 8); // replace Audio PID + pTs[2] = mPID_AUDIO & 0xFF; + + memcpy(GetWritePos(), pTs, CTsPacket::TS_PACKET_LEN); + WriteDone(); +} + + + + +void CStream::SendVideo(uint8_t *pTs) +{ + if ( !GetCanWrite() ) + { + m_nErrBufOvfl++; + return; + } + + pTs[1] = (pTs[1] & 0xE0) | (mPID_VIDEO >> 8); // replace video PID + pTs[2] = mPID_VIDEO & 0xFF; + + memcpy(GetWritePos(), pTs, CTsPacket::TS_PACKET_LEN); + WriteDone(); +} + + + +bool CStream::GetTsPacket(uint8_t *pTs) +{ + if ( NULL == pTs || // pointer check + NULL == m_MacAddr ) // we have a target address? + return false; + + if ( !GetCanRead() ) // ring buffer empty? + if ( m_pCurrSource ) + m_pCurrSource->SendTsPacket(this); // get data from source + + if ( !GetCanRead() ) // ring buffer still empty? + return false; + + memcpy(pTs, GetReadPos(), CTsPacket::TS_PACKET_LEN); + ReadDone(); + m_nBytesSent += CTsPacket::TS_PACKET_LEN; + return true; +} + + + + +void CStream::SetMacAddr(CMacAddr *pMacAddr) +{ + if (NULL != m_MacAddr) { + delete m_MacAddr; + m_MacAddr = NULL; + } + m_MacAddr = new CMacAddr(pMacAddr); + + if (NULL != m_MacAddr) + CTsPacket::CreatePmt(m_TsPmt, mPID_AUDIO, mPID_VIDEO, mPID_PMT); +} + + + +uint64_t CStream::GetBytesRead() +{ + return (NULL == m_pCurrSource) ? 0 : m_pCurrSource->GetBytesRead(); +} + + + +uint64_t CStream::GetErrBufUnderflow() +{ + return m_pCurrSource ? m_pCurrSource->GetErrBuf() : 0; +} + + + +uint32_t CStream::GetErrBufOverflow() +{ + uint32_t nRet = m_nErrBufOvfl; + m_nErrBufOvfl = 0; + return nRet; +} + + + +uint64_t CStream::GetErrPcr() +{ + return m_pCurrSource ? m_pCurrSource->GetErrPcr() : 0; +}; + + + +uint64_t CStream::GetErrTs() +{ + return m_pCurrSource ? m_pCurrSource->GetErrTs() : 0; +}; + + + +void CStream::SetPause(bool bPause) +{ + if (NULL != m_pCurrSource) + m_pCurrSource->SetPause(bPause); +} + + + +void CStream::SetRepetition(bool bRepetition) { + if (m_pCurrSource) + m_pCurrSource->SetRepetition(bRepetition); +} + + + +void CStream::SetPos(uint16_t nPos) { + if ( m_pCurrSource ) + m_pCurrSource->SetPos(nPos); +} + + +void CStream::PrintStream() { + ConsolePrintf( PRIO_HIGH, " %2u %3d %3u %s\r\n" + , GetInstId() + , NULL == m_pCurrSource ? (-1) : (m_pCurrSource->GetInstId()) + , m_nErrBufOvfl + , NULL == m_MacAddr ? "NULL " : m_MacAddr->ToString() + ); +} diff --git a/Src/Multiplexer/Stream.h b/Src/Multiplexer/Stream.h new file mode 100644 index 0000000..9206f27 --- /dev/null +++ b/Src/Multiplexer/Stream.h @@ -0,0 +1,250 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CStream class. + */ +/*----------------------------------------------------------*/ +#ifndef STREAM_H +#define STREAM_H + +#include "MacAddr.h" +#include "RingBuffer.h" +#include "Source.h" + +#define PID_VIDEO(x) ( 0x100 + (x) ) +#define PID_AUDIO(x) ( 0x200 + (x) ) +#define PID_PMT(x) ( 0x300 + (x) ) + +#define mPID_VIDEO ( PID_VIDEO((*m_MacAddr)[5]) ) +#define mPID_AUDIO ( PID_AUDIO((*m_MacAddr)[5]) ) +#define mPID_PMT ( PID_PMT((*m_MacAddr)[5]) ) + +class CSource; + +/*----------------------------------------------------------*/ +/*! \brief Class representing a single transport stream for multiplexing. + */ +/*----------------------------------------------------------*/ +class CStream : public CRingBuffer +{ + static const uint32_t NUM_PACKETS_BUFFERED = 64; // the number has to be set that the total buffer is bigger than the buffer used by the driver. Otherwise streams might run dry or get overflows + static uint32_t m_nInstCnt; + uint32_t m_nInst; + CMacAddr *m_MacAddr; + CSource *m_pCurrSource; + CSource *m_pNextSource; + uint64_t m_nBytesSent; + uint32_t m_nErrBufOvfl; + uint8_t m_TsPmt[188]; + + /*----------------------------------------------------------*/ + /*! \brief switch to a new source file + */ + /*----------------------------------------------------------*/ + void Switch(); + + + + /*----------------------------------------------------------*/ + /*! \brief calculates a PMT packet + */ + /*----------------------------------------------------------*/ + void CalcPmt(); + + + +public: + /*----------------------------------------------------------*/ + /*! \brief constructs CStream instance + */ + /*----------------------------------------------------------*/ + CStream(); + + + + /*----------------------------------------------------------*/ + /*! \brief de-constructs CStream instance + */ + /*----------------------------------------------------------*/ + ~CStream(); + + + + /*----------------------------------------------------------*/ + /*! \brief gets the current mac address for the stream + * \return Pointer to the assigned MAC address + */ + /*----------------------------------------------------------*/ + CMacAddr *GetMacAddr() + { + return m_MacAddr; + }; + + + + /*----------------------------------------------------------*/ + /*! \brief Statistic function: gets the amount of Bytes sent. + * \note Calling this method will clear this value to 0. + */ + /*----------------------------------------------------------*/ + uint64_t GetBytesSent() + { + uint64_t nTmp = m_nBytesSent; + m_nBytesSent = 0; + return nTmp; + }; + + /*----------------------------------------------------------*/ + /*! \brief Statistic function: gets the amount of Bytes read. + * \note Calling this method will clear this value to 0. + */ + /*----------------------------------------------------------*/ + uint64_t GetBytesRead(); + + + + /*----------------------------------------------------------*/ + /*! \brief number of PCR errors since last call + */ + /*----------------------------------------------------------*/ + uint64_t GetErrPcr(); + + + + /*----------------------------------------------------------*/ + /*! \brief number of source buffer underflows since last call + */ + /*----------------------------------------------------------*/ + uint64_t GetErrBufUnderflow(); + + + + /*----------------------------------------------------------*/ + /*! \brief number of send buffer overflows since last call + */ + /*----------------------------------------------------------*/ + uint32_t GetErrBufOverflow(); + + + + /*----------------------------------------------------------*/ + /*! \brief Statistic function: gets the amount of erroneous transport streams packets. + * \note Calling this method will clear this value to 0. + */ + /*----------------------------------------------------------*/ + uint64_t GetErrTs(); + + + /*----------------------------------------------------------*/ + /*! \brief unique ID for each stream + */ + /*----------------------------------------------------------*/ + uint32_t GetInstId() { return m_nInst; }; + + + + + void SendPmt(); + void SendAudio(uint8_t *pTs); + void SendVideo(uint8_t *pTs); + + + + /*----------------------------------------------------------*/ + /*! \brief set a new source + * \param pSource - pointer to source object + */ + /*----------------------------------------------------------*/ + void SetSource(CSource *pSource) { m_pNextSource = pSource; } + + + + + /*----------------------------------------------------------*/ + /*! \brief sets a new mac address for the stream + * \param pMacAddr - The new MAC address assigned to this stream. + * \note This method performs a deep copy of the pMacAddr, so you are free to delete the variable at any time. + */ + /*----------------------------------------------------------*/ + void SetMacAddr(CMacAddr *pMacAddr); + + + + /*----------------------------------------------------------*/ + /*! \brief set pause on/off + * \param bPause - true, to pause the stream. false, to play the stream. + */ + /*----------------------------------------------------------*/ + void SetPause(bool bPause); + + + + /*----------------------------------------------------------*/ + /*! \brief set repeat mode + * \param bRepetition - if true stream will repeated for ever + */ + /*----------------------------------------------------------*/ + void SetRepetition(bool bRepetition); + + + + + /*----------------------------------------------------------*/ + /*! \brief Sets the new time position in the stream. + * \param nPos - The new time positon to set. + */ + /*----------------------------------------------------------*/ + void SetPos(uint16_t nPos); + + + + /*----------------------------------------------------------*/ + /*! \brief Read from HDD to fill ring buffer + * + * \return true, if something was read from file. + */ + /*----------------------------------------------------------*/ + bool ReadHdd(); + + + + /*----------------------------------------------------------*/ + /*! \brief gets next TS packet to be sent + * + * \return true, if a packet was available + */ + /*----------------------------------------------------------*/ + bool GetTsPacket(uint8_t *pTrg); + + + + /*----------------------------------------------------------*/ + /*! \brief Trace stream properties to console + */ + /*----------------------------------------------------------*/ + void PrintStream(); +}; + +#endif diff --git a/Src/Multiplexer/StreamList.cpp b/Src/Multiplexer/StreamList.cpp new file mode 100644 index 0000000..a2766e0 --- /dev/null +++ b/Src/Multiplexer/StreamList.cpp @@ -0,0 +1,246 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include <time.h> +#include "StreamList.h" +#include "TsPacket.h" + + +uint32_t CStreamList::m_nInstCnt = 0; + +CStreamList::CStreamList() + : m_nInst(m_nInstCnt++) + , m_nPatVersion(1) + , m_nPatCont(0) + , m_nNextStreamSending(0) +{ +} + + + + +bool CStreamList::ReadHdd() +{ + bool bRead = false; // no read done yet + + for (uint32_t n = 0; n < MAX_STREAM_LIST_LEN; n++) // run over all streams + bRead |= m_Stream[n].ReadHdd(); // read from HDD + + return bRead; // return if there was a read +} + + + + +void CStreamList::SetSource(CSource *pSource) +{ + for (uint32_t n = 0; n < MAX_STREAM_LIST_LEN; n++) // run over all streams + if (NULL != + m_Stream[n].GetMacAddr()) // only set in active streams + m_Stream[n].SetSource(pSource); +} + + + + +void CStreamList::GetPat(uint8_t *pTs) +{ + // ---------------------------------------------------- // + // create PAT packet // + // ---------------------------------------------------- // + pTs[0] = 0x47; // packet start + + pTs[1] = (1 << 6); // Payload Unit start indicator, PID = 0; + pTs[2] = 0; + + pTs[3] = (1 << 4) | // no adaption field + m_nPatCont; // continuity counter + + m_nPatCont = (m_nPatCont + 1) & 0xF; // update 4 bit continuity counter + + pTs[4] = 0; // PointerField: new section + + uint8_t *pPat = pTs + 5; // pointer to PAT + + pPat[0] = 0; // Table ID: Program Association Section + + uint16_t wSectionLength = 4 + 5 + 0; // 4 byte CRC, 5 bytes offset to entries, zero 4 byte entry, + + pPat[3] = 0; // stream ID (HB) + pPat[4] = 0; // stream ID (LB) + + pPat[5] = (3 << 6) | // reserved + (m_nPatVersion << 1) | // version + 1; // current/next + + pPat[6] = 0; // section number + pPat[7] = 0; // last section number + + for (uint32_t n = 0; n < MAX_STREAM_LIST_LEN; n++) { // run over all streams + CMacAddr *pMac = m_Stream[n].GetMacAddr(); + if (pMac) { + pPat[wSectionLength - 4 + 3 + 0] = (PID_PMT((*pMac)[5]) >> 8); // program number + pPat[wSectionLength - 4 + 3 + 1] = PID_PMT((*pMac)[5]) & 0xFF; + + pPat[wSectionLength - 4 + 3 + 2] = (7 << 5) | // reserved + (PID_PMT((*pMac)[5]) >> 8); // Pmt PID (HB) + pPat[wSectionLength - 4 + 3 + 3] = PID_PMT((*pMac)[5]) & 0xFF; // Pmt PID (LB) + + wSectionLength += 4; + } + } + + pPat[1] = (1 << 7) | // section syntax indicator + (3 << 4) | // reserved + (wSectionLength >> 8); // section syntax 1, section length + + pPat[2] = wSectionLength & 0xFF; + + uint32_t nCrc = CTsPacket::GetCrc32(pPat,wSectionLength + 3 - 4); // CRC over entire PAT section without 4 CRC bytes + pPat[wSectionLength + 3 - 4 + 0] = (nCrc >> 24) & 0xFF; + pPat[wSectionLength + 3 - 4 + 1] = (nCrc >> 16) & 0xFF; + pPat[wSectionLength + 3 - 4 + 2] = (nCrc >> 8) & 0xFF; + pPat[wSectionLength + 3 - 4 + 3] = (nCrc) & 0xFF; + + // fill remaining packet with 0xFF + for (uint8_t *p = 1 + &pPat[wSectionLength + 3 - 4 + 3]; p < pTs + 188; p++) + *p = 0xFF; +} + +bool CStreamList::GetPatPacket(uint8_t *pTs) +{ + GetPat(pTs); + return true; +} + +bool CStreamList::GetTsPacket(uint8_t *pTs) +{ + for (uint32_t n = 0; n < MAX_STREAM_LIST_LEN; n++) // run over all streams + { + m_nNextStreamSending++; + if ( m_nNextStreamSending >= MAX_STREAM_LIST_LEN ) + m_nNextStreamSending = 0; + + if ( m_Stream[m_nNextStreamSending].GetTsPacket(pTs) ) + return true; + } + + return false; +} + + + + +uint32_t CStreamList::GetBytesRead() +{ + uint32_t nSum = 0; + + for (uint32_t n = 0; n < MAX_STREAM_LIST_LEN; n++) // run over all streams + nSum += m_Stream[n].GetBytesRead(); + + return nSum; +} + + + + +uint32_t CStreamList::GetBytesSent() +{ + uint32_t nSum = 0; + + for (uint32_t n = 0; n < MAX_STREAM_LIST_LEN; n++) // run over all streams + nSum += m_Stream[n].GetBytesSent(); + + return nSum; +} + + + + +uint32_t CStreamList::GetErrPcr() +{ + uint32_t nSum = 0; + + for (uint32_t n = 0; n < MAX_STREAM_LIST_LEN; n++) // run over all streams + nSum += m_Stream[n].GetErrPcr(); + + return nSum; +} + + + + +uint32_t CStreamList::GetErrBufUnderflow() +{ + uint32_t nSum = 0; + + for (uint32_t n = 0; n < MAX_STREAM_LIST_LEN; n++) // run over all streams + nSum += m_Stream[n].GetErrBufUnderflow(); + + return nSum; +} + + + + +uint32_t CStreamList::GetErrTs() +{ + uint32_t nSum = 0; + + for (uint32_t n = 0; n < MAX_STREAM_LIST_LEN; n++) // run over all streams + nSum += m_Stream[n].GetErrTs(); + + return nSum; +} + + + + +CStream *CStreamList::GetStream(CMacAddr *pMacAddr) +{ + for (uint32_t n = 0; n < MAX_STREAM_LIST_LEN; n++) { + CMacAddr *pSource = m_Stream[n].GetMacAddr(); + if (NULL == pMacAddr) { + //In case of a given NULL pointer, return the first unassigned stream + if (NULL == pSource) + return &m_Stream[n]; + } else if (NULL != pSource) { + //Else search for the correct instance + if (*pMacAddr == *pSource) + return &m_Stream[n]; + } + } + return NULL; +} + + + + +void CStreamList::PrintStreamList() +{ + ConsolePrintfStart( PRIO_HIGH, "StreamListID: %u\n\n", m_nInst); + ConsolePrintfContinue("StreamID SourceID Ovfl MAC \n"); + ConsolePrintfExit("-----------------------------------------------------------\n"); + for (uint32_t n = 0; n < MAX_STREAM_LIST_LEN; n++) + m_Stream[n].PrintStream(); +} diff --git a/Src/Multiplexer/StreamList.h b/Src/Multiplexer/StreamList.h new file mode 100644 index 0000000..8770a86 --- /dev/null +++ b/Src/Multiplexer/StreamList.h @@ -0,0 +1,179 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CStreamList class. + */ +/*----------------------------------------------------------*/ +#ifndef CSTREAMLIST_H +#define CSTREAMLIST_H + +#include "Source.h" + + + +/*----------------------------------------------------------*/ +/*! \brief Aggregator class to access all available stream with a single call. + */ +/*----------------------------------------------------------*/ +class CStreamList +{ + static const uint32_t MAX_STREAM_LIST_LEN = 30; + static uint32_t m_nInstCnt; + uint32_t m_nInst; + CStream m_Stream[MAX_STREAM_LIST_LEN]; + uint8_t m_nPatVersion; + uint8_t m_nPatCont; + uint32_t m_nNextStreamSending; + + + void GetPat(uint8_t *pTs); + +public: + /*----------------------------------------------------------*/ + /*! \brief construct a list of streams + */ + /*----------------------------------------------------------*/ + CStreamList(); + + + + /*----------------------------------------------------------*/ + /*! \brief Read from all streams' HDD to fill ring buffer + * + * \return true, if something was read from any file + */ + /*----------------------------------------------------------*/ + bool ReadHdd(); + + + + + /*----------------------------------------------------------*/ + /*! \brief sets the same source for all active streams + * + * \param pSource - source object to stream + */ + /*----------------------------------------------------------*/ + void SetSource(CSource *pSource); + + + /*----------------------------------------------------------*/ + /*! \brief fills a packet with the PAT information. + * \note this method, writes only PAT every 30ms. Otherwise nothing is done. + * + * \return true, if the PAT was written to the buffer. false, because of the 30ms rule, there was no data stored. + */ + /*----------------------------------------------------------*/ + bool GetPatPacket(uint8_t *pTs); + + + + /*----------------------------------------------------------*/ + /*! \brief fills a packet with packets coming from the + * streams. + * + * \return true, if packet was available + */ + /*----------------------------------------------------------*/ + bool GetTsPacket(uint8_t *pTs); + + + + + /*----------------------------------------------------------*/ + /*! \brief get bytes read from all streams + * + * \return total bytes read + */ + /*----------------------------------------------------------*/ + uint32_t GetBytesRead(); + + + + + /*----------------------------------------------------------*/ + /*! \brief get amount of bytes sent by video streams from all streams + * \note this function does not count the stuffed dummy packets. + * + * \return total bytes sent + */ + /*----------------------------------------------------------*/ + uint32_t GetBytesSent(); + + + + + /*----------------------------------------------------------*/ + /*! \brief get PCR errors from all streams + * + * \return total bytes read + */ + /*----------------------------------------------------------*/ + uint32_t GetErrPcr(); + + + + + /*----------------------------------------------------------*/ + /*! \brief get PCR errors from all streams + * + * \return total bytes read + */ + /*----------------------------------------------------------*/ + uint32_t GetErrBufUnderflow(); + + + + + /*----------------------------------------------------------*/ + /*! \brief get PCR errors from all streams + * + * \return total bytes read + */ + /*----------------------------------------------------------*/ + uint32_t GetErrTs(); + + + + + /*----------------------------------------------------------*/ + /*! \brief finds a stream with the given MAC address + * \param pMacAddr - Pointer to the MAC address instance to search for. + * \return pointer to found stream or NULL if not found + */ + /*----------------------------------------------------------*/ + CStream *GetStream(CMacAddr *pMacAddr); + + + + /*----------------------------------------------------------*/ + /*! \brief prints list of stream to console + */ + /*----------------------------------------------------------*/ + void PrintStreamList(); +}; + +#endif + diff --git a/Src/Multiplexer/ThreadReadHdd.cpp b/Src/Multiplexer/ThreadReadHdd.cpp new file mode 100644 index 0000000..7009642 --- /dev/null +++ b/Src/Multiplexer/ThreadReadHdd.cpp @@ -0,0 +1,23 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + diff --git a/Src/Multiplexer/ThreadReadHdd.h b/Src/Multiplexer/ThreadReadHdd.h new file mode 100644 index 0000000..74996fd --- /dev/null +++ b/Src/Multiplexer/ThreadReadHdd.h @@ -0,0 +1,171 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CThreadReadHdd class. + */ +/*----------------------------------------------------------*/ +#ifndef THREADREADHDD_H +#define THREADREADHDD_H + +#include <unistd.h> +#include "SafeVector.h" +#include <pthread.h> +#include <assert.h> +#include "Console.h" +#include "StreamList.h" + + + +/*----------------------------------------------------------*/ +/*! \brief Class creating a thread to call stream's reading function. + */ +/*----------------------------------------------------------*/ +class CThreadReadHdd +{ +private: + pthread_t m_hThread; + bool m_bRunning; + CStreamList *m_StreamList; + +public: + + CThreadReadHdd() : m_hThread(0), m_bRunning(false), m_StreamList(NULL) + { + } + + + /*----------------------------------------------------------*/ + /*! \brief thread reading from all stream lists + */ + /*----------------------------------------------------------*/ + static void *Run(void *pInst) + { + assert(NULL != pInst); + + CThreadReadHdd *that = (CThreadReadHdd *)pInst; + /* set thread to max priority: */ + int ret; + pthread_t this_thread = pthread_self(); + struct sched_param params; + params.sched_priority = sched_get_priority_max(SCHED_FIFO); + ret = pthread_setschedparam(this_thread, SCHED_FIFO, ¶ms); + if (ret != 0) { + ConsolePrintf( PRIO_ERROR, "ThreadReadHdd_setschedparam(SCHED_FIFO) failed\n"); + } + // Now verify the change in thread priority + int policy = 0; + ret = pthread_getschedparam(this_thread, &policy, ¶ms); + if (ret != 0) { + ConsolePrintf( PRIO_ERROR, "ThreadReadHdd_getschedparam() failed\n"); + } + + while (that->m_bRunning) + { + if ( !that->m_StreamList->ReadHdd() ) // buffer full? + usleep(50000); // we have 300ms buffer size, so it's save to sleep 50ms + } + pthread_exit(0); + } + + + + + /*----------------------------------------------------------*/ + /*! \brief start reading thread + */ + /*----------------------------------------------------------*/ + void Start() + { + if ( m_bRunning ) + return; + + if ( NULL == m_StreamList ) + return; + + struct sched_param param; + pthread_attr_t tattr; + pthread_attr_init(&tattr); + pthread_attr_getschedparam(&tattr, ¶m); + pthread_attr_setschedpolicy(&tattr, SCHED_RR); + param.sched_priority = sched_get_priority_max(SCHED_RR); + pthread_attr_setschedparam(&tattr, ¶m); + m_bRunning = true; + if ( 0 != pthread_create(&m_hThread, &tattr, Run, this) ) + ConsolePrintf( PRIO_ERROR, RED"! failed to create thread"RESETCOLOR"\n"); + } + + + + + /*----------------------------------------------------------*/ + /*! \brief stop reading thread + */ + /*----------------------------------------------------------*/ + void Stop() + { + if ( m_bRunning ) + { + m_bRunning = false; + void *pVal; + pthread_join(m_hThread, &pVal); + m_hThread = 0; + } + } + +public: + /*----------------------------------------------------------*/ + /*! \brief add a stream list to read thread + * \param StreamList - stream list to add for reading + * \return true, if stream list was added successfully + */ + /*----------------------------------------------------------*/ + void AddStreamList(CStreamList *streamList) + { + Stop(); + m_StreamList = streamList; + Start(); + } + + + + + /*----------------------------------------------------------*/ + /*! \brief remove a stream list from read thread + */ + /*----------------------------------------------------------*/ + void RemoveStreamList(CStreamList *streamList) + { + Stop(); + m_StreamList = NULL; + } +}; + + + + + + +#endif + diff --git a/Src/Multiplexer/ThreadWriteNetwork.cpp b/Src/Multiplexer/ThreadWriteNetwork.cpp new file mode 100644 index 0000000..5723a6c --- /dev/null +++ b/Src/Multiplexer/ThreadWriteNetwork.cpp @@ -0,0 +1,134 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include <stdint.h> +#include "Console.h" +#include "ThreadWriteNetwork.h" +#include "Multiplexer.h" + + + + + +CThreadWriteNetwork::CThreadWriteNetwork() : + m_hThread(0), m_bRunning(false), m_Multiplexer(NULL) +{ +} + + +void *CThreadWriteNetwork::Run(void *pInst) { + assert(NULL != pInst); + CThreadWriteNetwork *that = (CThreadWriteNetwork *)pInst; + + /* set thread to max priority: */ + int ret; + pthread_t this_thread = pthread_self(); + struct sched_param params; + params.sched_priority = sched_get_priority_max(SCHED_FIFO); + ret = pthread_setschedparam(this_thread, SCHED_FIFO, ¶ms); + if (ret != 0) { + ConsolePrintf( PRIO_ERROR, RED"CThreadWriteNetwork setschedparam(SCHED_FIFO) failed"RESETCOLOR"\n"); + } + // Now verify the change in thread priority + int policy = 0; + ret = pthread_getschedparam(this_thread, &policy, ¶ms); + if (ret != 0) { + ConsolePrintf( PRIO_ERROR, RED"CThreadWriteNetwork getschedparam() failed"RESETCOLOR"\n"); + } + bool errorPrinted = false; + WriteResult_t result; + bool shallWait; + while (that->m_bRunning) + { + shallWait = true; + result = that->m_Multiplexer->WriteToDriver(); + switch (result) + { + case WriteResult_Failed: + if (!errorPrinted) + { + errorPrinted = true; + ConsolePrintf( PRIO_ERROR, RED"failed to write driver"RESETCOLOR"\n"); + } + break; + case WriteResult_WouldBlock: + break; + case WriteResult_Success: + default: + shallWait = false; + break; + } + if ( that->m_bRunning && shallWait ) + usleep(1000); + } + pthread_exit(0); +} + + + + +void CThreadWriteNetwork::Start() +{ + if ( m_bRunning ) + return; + + if ( NULL == m_Multiplexer ) + return; + + m_bRunning = true; + if ( 0 != pthread_create(&m_hThread, NULL, Run, this) ) + ConsolePrintf( PRIO_ERROR, RED"! failed to create thread"RESETCOLOR"\n"); +} + + + + +void CThreadWriteNetwork::Stop() +{ + if ( m_bRunning ) + { + m_bRunning = false; + void *pVal; + pthread_join(m_hThread, &pVal); + m_hThread = 0; + } +} + + + + +void CThreadWriteNetwork::AddMultiplexer(CMultiplexer *multiplexer) +{ + Stop(); + m_Multiplexer = multiplexer; + Start(); +} + + + + +void CThreadWriteNetwork::RemoveMultiplexer(CMultiplexer *multiplexer) +{ + Stop(); + m_Multiplexer = NULL; +} diff --git a/Src/Multiplexer/ThreadWriteNetwork.h b/Src/Multiplexer/ThreadWriteNetwork.h new file mode 100644 index 0000000..c0fda28 --- /dev/null +++ b/Src/Multiplexer/ThreadWriteNetwork.h @@ -0,0 +1,97 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CThreadWriteNetwork class. + */ +/*----------------------------------------------------------*/ +#ifndef THREADWRITENETWORK_H +#define THREADWRITENETWORK_H + +#include "SafeVector.h" +#include <pthread.h> + +class CMultiplexer; + + + + +/*----------------------------------------------------------*/ +/*! \brief class creating a thread to call stream's sending + * function. + */ +/*----------------------------------------------------------*/ +class CThreadWriteNetwork +{ + pthread_t m_hThread; + bool m_bRunning; + CMultiplexer *m_Multiplexer; + + /*----------------------------------------------------------*/ + /*! \brief thread reading from all stream lists + */ + /*----------------------------------------------------------*/ + static void *Run(void *pInst); + +public: + CThreadWriteNetwork(); + + + /*----------------------------------------------------------*/ + /*! \brief start reading thread + */ + /*----------------------------------------------------------*/ + void Start(); + + + + + /*----------------------------------------------------------*/ + /*! \brief stop reading thread + */ + /*----------------------------------------------------------*/ + void Stop(); + + + + +public: + /*----------------------------------------------------------*/ + /*! \brief add a multiplexer to send thread + * \param multiplexer - multiplexer to add to sending thread + */ + /*----------------------------------------------------------*/ + void AddMultiplexer(CMultiplexer *multiplexer); + + + + + /*----------------------------------------------------------*/ + /*! \brief remove a multiplexer from send thread + */ + /*----------------------------------------------------------*/ + void RemoveMultiplexer(CMultiplexer *multiplexer); +}; + +#endif diff --git a/Src/Multiplexer/TsPacket.h b/Src/Multiplexer/TsPacket.h new file mode 100644 index 0000000..fe68289 --- /dev/null +++ b/Src/Multiplexer/TsPacket.h @@ -0,0 +1,661 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains static helper C-functions to parse TS packets. + */ +/*----------------------------------------------------------*/ +#ifndef TSPACKET_H +#define TSPACKET_H + +//#define TS_PATTERN_CHECK + +#include <stdint.h> +#include <stdbool.h> +#include <string.h> + +/*----------------------------------------------------------*/ +/*! + * \brief Handles a transport stream packet of 188 bytes + */ +/*----------------------------------------------------------*/ +class CTsPacket +{ +private: + static const uint32_t ADAPTION_FIELD_CONTROL__RESERVED = (0x00 << 4); + static const uint32_t ADAPTION_FIELD_CONTROL__PAYLOAD_ONLY = (0x01 << 4); + static const uint32_t ADAPTION_FIELD_CONTROL__NO_PAYLOAD = (0x02 << 4); + static const uint32_t ADAPTION_FIELD_CONTROL__BOTH = (0x03 << 4); + static const uint32_t program_stream_map = 0xBC; + static const uint32_t padding_stream = 0xBE; + static const uint32_t private_stream_2 = 0xBF; + static const uint32_t ECM = 0xF0; + static const uint32_t EMM = 0xF1; + static const uint32_t DSMCC_stream = 0xF2; + static const uint32_t H222_stream = 0xF8; + static const uint32_t program_stream_directory = 0xFF; + static const uint32_t PROGRAM_ASSOSICIATION_SECTION = 0x00; + static const uint32_t PROGRAM_MAP_SECTION = 0x02; + static const uint32_t ISO_IEC_11172_AUDIO = 0x03; + static const uint32_t ISO_IEC_13818_2_Video = 0x02; + static const uint32_t ISO_IEC_13818_3_AUDIO = 0x04; + static const uint32_t ISO_IEC_13818_1_RESERVED = 0x1B; + static const uint8_t PCR_FLAG = 0x10; + +public: + static const uint32_t PID_INVALID = 0xFFFF; + static const uint32_t PCR_INVALID = 0xFFFFFFFF; + static const uint32_t PCR_TICKS_PER_SEC = 45000; + static const uint8_t TS_PACKET_LEN = 188; + + + + /*----------------------------------------------------------*/ + /*! \brief extract continuity counter + * + * \param pTs - uint8_t array containing TS packet + */ + /*----------------------------------------------------------*/ + static uint32_t GetContinuityCounter(uint8_t *pTs) + { + return (pTs[3] & 0xF); + } + + + + + /*----------------------------------------------------------*/ + /*! \brief get array index where ES payload + * + * \param pTs - uint8_t array containing TS packet + */ + /*----------------------------------------------------------*/ + static uint32_t GetElementaryPayloadStart(uint8_t *pTs) + { + uint32_t nRet = GetPayloadStart(pTs); // index to TS payload + + if (GetHasPesHeader(pTs)) { + nRet += 3 + 1 + 2 + 1 + 1; // packet start(3) + stream_id(1) + PES_packet_length(2) + PES_scampling...(1) + PTS_DTS_flags...(1) + nRet += (pTs[nRet] & 0xFF) + 1; // PES_header_data_length + } + + return nRet; + } + + + + + /*----------------------------------------------------------*/ + /*! \brief check if there is a discontinuity in the stream + * + * \param pTs - uint8_t array containing TS packet + * \param nLastDiscontinuityCounter - preceding counter + */ + /*----------------------------------------------------------*/ + static bool GetHasDiscontinuity(uint8_t *pTs, + uint32_t nLastDiscontinuityCounter) + { + nLastDiscontinuityCounter = (nLastDiscontinuityCounter + 1) & 0xF; + return (pTs[3] & 0xF) != nLastDiscontinuityCounter; + } + + + + + /*----------------------------------------------------------*/ + /*! \brief check if a TS packet has the given PID + * + * \param pTs - uint8_t array containing TS packet + * \param nPid - PID to check for + * + * \return true if PID is equal + */ + /*----------------------------------------------------------*/ + static bool GetHasPid(uint8_t *pTs, uint32_t nPid) + { + return nPid == GetPid(pTs); + } + + + + + /*----------------------------------------------------------*/ + /*! \brief check if a TS packet contains a PAT + * + * \param pTs - uint8_t array containing TS packet + * + * \return true if PAT is contained + */ + /*----------------------------------------------------------*/ + static bool GetIsPat(uint8_t *pTs) + { + if (!GetHasPid(pTs, 0)) + return false; + + uint32_t nPat = GetPayloadStart(pTs); // start of PSI + + nPat += 1 + (0xFF & pTs[nPat]); // pouint32_ter_field + + return (pTs[nPat] == PROGRAM_ASSOSICIATION_SECTION); // table_id + } + + + + + /*----------------------------------------------------------*/ + /*! \brief gets the PMT PID from the n-th program of a PAT + * packet + * + * \param pTs - uint8_t array containing TS packet + * \param nPrg - number of program + * + * \return true if PAT is contained + */ + /*----------------------------------------------------------*/ + static uint32_t GetPmtPidFromPat(uint8_t *pTs, uint32_t nPrg) + { + uint32_t nPat = GetPayloadStart(pTs); // PSI + nPat += 1 + (0xFF & pTs[nPat]); // pouint32_ter_field + + return ((0x1F & pTs[nPat + 10 + nPrg * 4]) << 8) + // program_map_PID (high uint8_t *) + (0xFF & pTs[nPat + 10 + nPrg * 4 + 1]); // program_map_PID (low uint8_t *) + } + + + + + /*----------------------------------------------------------*/ + /*! \brief gets the audio PID from a PMT packet + * + * \param pTs - uint8_t array containing TS packet + * + * \return PID of 1st audio channel + */ + /*----------------------------------------------------------*/ + static uint32_t GetAudioPidFromPmt(uint8_t *pTs) + { + uint32_t nPmt = GetPayloadStart(pTs); // PSI + nPmt += 1 + (0xFF & pTs[nPmt]); // pouint32_ter_field + + if (PROGRAM_MAP_SECTION != pTs[nPmt]) // check table_id + return PID_INVALID; + + uint32_t section_length = ((0x0F & pTs[nPmt + 1]) << 8) + + (0xFF & pTs[nPmt + 2]); + uint32_t program_info_length = ((0x0F & pTs[nPmt + 10]) << 8) + + (0xFF & pTs[nPmt + 11]); + + uint32_t n = nPmt + 12 + program_info_length; + + while (n < section_length + nPmt + 2) { // run through all streams + uint32_t stream_type = pTs[n]; + uint32_t elementary_pid = ((0x1F & pTs[n + 1]) << 8) + (0xFF & pTs[n + 2]); + uint32_t ES_info_length = ((0x0F & pTs[n + 3]) << 8) + (0xFF & pTs[n + 4]); + + if (ISO_IEC_11172_AUDIO == stream_type + || ISO_IEC_13818_3_AUDIO == stream_type) { + return elementary_pid; + } + + n += 5 + ES_info_length; // switch to next stream + } + + return PID_INVALID; // no audio stream found + } + + + + + /*----------------------------------------------------------*/ + /*! \brief gets the audio PID from a PMT packet + * + * \param pTs - uint8_t array containing TS packet + * + * \return PID of 1st audio channel + */ + /*----------------------------------------------------------*/ + static uint32_t GetVideoPidFromPmt(uint8_t *pTs) + { + uint32_t nPmt = GetPayloadStart(pTs); // PSI + nPmt += 1 + (0xFF & pTs[nPmt]); // pouint32_ter_field + + if (PROGRAM_MAP_SECTION != pTs[nPmt]) // check table_id + return PID_INVALID; + + uint32_t section_length = ((0x0F & pTs[nPmt + 1]) << 8) + + (0xFF & pTs[nPmt + 2]); + uint32_t program_info_length = ((0x0F & pTs[nPmt + 10]) << 8) + + (0xFF & pTs[nPmt + 11]); + + uint32_t n = nPmt + 12 + program_info_length; + + while (n < section_length + nPmt + 2) { // run through all streams + uint32_t stream_type = pTs[n]; + uint32_t elementary_pid = ((0x1F & pTs[n + 1]) << 8) + (0xFF & pTs[n + 2]); + uint32_t ES_info_length = ((0x0F & pTs[n + 3]) << 8) + (0xFF & pTs[n + 4]); + + if (ISO_IEC_13818_2_Video == stream_type + || ISO_IEC_13818_1_RESERVED == stream_type) { + return elementary_pid; + } + + n += 5 + ES_info_length; // switch to next stream + } + + return PID_INVALID; // no audio stream found + } + + + + + /*----------------------------------------------------------*/ + /*! \brief checks if TS packet is the start of an I frame + * + * \param pTs - uint8_t array containing TS packet + */ + /*----------------------------------------------------------*/ + static bool GetIsStartOfIFrame(uint8_t *pTs) + { + uint32_t n = GetPayloadStart(pTs); + + for (; n < 188 - 4; n++) // search for IDR (instantaneous decoder refresh) + if (0 == pTs[n] && 0 == pTs[n + 1] && 1 == pTs[n + 2] + && (5 == (pTs[n + 3] & 0x1F))) + return true; + + return false; + } + + + + + /*----------------------------------------------------------*/ + /*! \brief check if TS packet contains a PES header + * + * \param pTs - uint8_t array containing TS packet + */ + /*----------------------------------------------------------*/ + static bool GetHasPesHeader(uint8_t *pTs) + { + if (!GetIsPayloadUnitStart(pTs)) + return false; + + uint32_t nPes = GetPayloadStart(pTs); + + return !(0 != pTs[nPes ] || + 0 != pTs[nPes + 1] || + 1 != pTs[nPes + 2] || // is it a PES Header? + program_stream_map == pTs[nPes + 3] || + padding_stream == pTs[nPes + 3] || + private_stream_2 == pTs[nPes + 3] || + ECM == pTs[nPes + 3] || + EMM == pTs[nPes + 3] || + program_stream_directory == pTs[nPes + 3] || + DSMCC_stream == pTs[nPes + 3] || + H222_stream == pTs[nPes + 3] || + 0 == (0x80 & pTs[nPes + 7])); + } + + + + + /*----------------------------------------------------------*/ + /*! \brief check if TS packet contains a PTS + * + * \param pTs - uint8_t array containing TS packet + */ + /*----------------------------------------------------------*/ + static bool GetHasPts(uint8_t *pTs) + { + if (!GetHasPesHeader(pTs)) + return false; + + uint32_t nPes = GetPayloadStart(pTs); + + return (0 != ((pTs[nPes + 7] & 0x80))); // PTS_DTS_flags is 0x2 or 0x3 + } + + + + + /*----------------------------------------------------------*/ + /*! \brief get PCR from a packet if available + * + * \param pTs - uint8_t array containing TS packet + * + * \return PCR or PCR_INVALID if packet contains no PCR + */ + /*----------------------------------------------------------*/ + static uint32_t GetPcr(uint8_t *pTs) + { + switch ((pTs[3] & 0x30)) { // check if packet contains adaption field + case ADAPTION_FIELD_CONTROL__PAYLOAD_ONLY: + case ADAPTION_FIELD_CONTROL__RESERVED: + return PCR_INVALID; + default: + break; + } + + if (0 == pTs[4]) // 0 == adaption_field_length ? + return PCR_INVALID; + + if (PCR_FLAG != (pTs[5] & PCR_FLAG)) // 1 == PCR_flag ? + return PCR_INVALID; + + return (pTs[6] << 24) || (pTs[7] << 16) || (pTs[8] << 8) || (pTs[9]); + } + + + + + /*----------------------------------------------------------*/ + /*! \brief check if a TS packet has sync uint8_t * + * + * \param pTs - uint8_t array containing TS packet + */ + /*----------------------------------------------------------*/ + static bool GetHasSyncByte(uint8_t *pTs) + { + return 0x47 == pTs[0]; + } + + + + + /*----------------------------------------------------------*/ + /*! \brief check if TS packet starts a new payload unit + * + * \param pTs - uint8_t array containing TS packet + */ + /*----------------------------------------------------------*/ + static bool GetIsPayloadUnitStart(uint8_t *pTs) + { + return (0 != (pTs[1] & 0x40)); + } + + + + + /*----------------------------------------------------------*/ + /*! \brief get arry index of payload start in packet + * + * \param pTs - uint8_t array containing TS packet + */ + /*----------------------------------------------------------*/ + static uint32_t GetPayloadStart(uint8_t *pTs) + { + switch ((pTs[3] & 0x30)) { + case ADAPTION_FIELD_CONTROL__PAYLOAD_ONLY: + return 4; + + case ADAPTION_FIELD_CONTROL__BOTH: + return 4 + 1 + (pTs[4] & 0xFF); + + case ADAPTION_FIELD_CONTROL__RESERVED: + case ADAPTION_FIELD_CONTROL__NO_PAYLOAD: + return 188; + } + + return TS_PACKET_LEN; + } + + + + + /*----------------------------------------------------------*/ + /*! \brief get PES packet length from packet + * + * \param pTs - uint8_t array containing TS packet + * \return length of PES packet in uint8_t *s + */ + /*----------------------------------------------------------*/ + static uint32_t GetPesPacketLength(uint8_t *pTs) + { + uint32_t nPes = GetPayloadStart(pTs); + return ((pTs[nPes + 4] & 0xFF) << 8) + (pTs[nPes + 5] & 0xFF); + } + + + + + /*----------------------------------------------------------*/ + /*! \brief extract PID from a TS packet + * + * \param pTs - uint8_t array containing TS packet + */ + /*----------------------------------------------------------*/ + static uint32_t GetPid(uint8_t *pTs) + { + return (((uint32_t)pTs[1] & 0x1F) << 8) + ((uint32_t)pTs[2] & 0xFF); + } + + + + + /*----------------------------------------------------------*/ + /*! \brief extract PTS from a TS packet in 45 kHz units + * + * \param pTs - uint8_t array containing TS packet + */ + /*----------------------------------------------------------*/ + static uint32_t GetPts(uint8_t *pTs) + { + uint32_t nPes = GetPayloadStart(pTs); + + return ((pTs[nPes + 9] & 0x0E) << 28) + + ((pTs[nPes + 10] & 0xFF) << 21) + + ((pTs[nPes + 11] & 0xFE) << 13) + + ((pTs[nPes + 12] & 0xFF) << 6) + + ((pTs[nPes + 13] & 0xFF) >> 2); + } + + + + + /*----------------------------------------------------------*/ + /*! \brief calculate CRC from a memory area + * + * \param pSrc - start of memory area + * \param nLen - length of memory area in bytes + */ + /*----------------------------------------------------------*/ + static uint32_t GetCrc32(uint8_t *pSrc, int nLen) + { + static const uint32_t CrcTable[256] = { + 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, + 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, + 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, + 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, + 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, + 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, + 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, + 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, + 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, + 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, + 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, + 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, + 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, + 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, + 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, + 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, + 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, + 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, + 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, + 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, + 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, + 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, + 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, + 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9, + 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, + 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, + 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, + 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, + 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, + 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, + 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, + 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, + 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, + 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, + 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, + 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, + 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, + 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, + 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 + }; + + uint32_t nCrc = 0xFFFFFFFF; + + while (nLen--) + nCrc = (nCrc << 8) ^ CrcTable[((nCrc >> 24) ^ *pSrc++) & 0xFF ]; + + return nCrc; + } + + + + + /*----------------------------------------------------------*/ + /*! \brief calculate a PMT packet for a program containing + * one video and one audio stream + * + * \param pTs - pointer to TS packet to store result + * \param nAPid - PID of audio + * \param nAPid - PID of video + * \param nAPid - PID of PMT + */ + /*----------------------------------------------------------*/ + static void CreatePmt(uint8_t *pTs, uint32_t nAPid, uint32_t nVPid, + uint32_t nPPid) + { + if (NULL == pTs) + return; + + pTs[0] = 0x47; // packet start + + pTs[1] = (1 << 6) | // Payload Unit start indicator, PID = 0; + (nPPid >> 8); // PMT PID (HB) + + pTs[2] = nPPid & 0xFF; // PMT PID (LB) + + pTs[3] = (1 << 4); // no adaption field + + pTs[4] = 0; // PointerField: new section + + uint8_t *pPmt = pTs + 5; // pointer to PMT section + + pPmt[0] = 0x02; // Table ID: Program Map Section + + uint16_t wSectionLength = + 12 - 3 + // section until program info length + 5 + // video entry + 5 + // audio entry + 4; // CRC + + pPmt[1] = (1 << 7) | // section syntax indicator + (3 << 4) | // reserved + (wSectionLength >> 8); // section syntax 1, section length + pPmt[2] = wSectionLength & 0xFF; + + pPmt[3] = (nPPid >> 8); // program number (HB) + pPmt[4] = nPPid & 0xFF; // program number (LB) + + pPmt[5] = (3 << 6) | 1; // version, current/next + + pPmt[6] = 0; // section number + pPmt[7] = 0; // last section number + + pPmt[8] = (7 << 5) | // reserved + (nVPid >> 8); // PCR PID, assuming video has PCR + + pPmt[9] = nVPid & 0xFF; // PCR PID (LB) + + pPmt[10] = (0xF << 4) | // reserved + (0); // program info length + + pPmt[11] = 0; + + // video + pPmt[12] = 27; // stream type = video + + pPmt[13] = (7 << 5) | // reserved + (nVPid >> 8); // ES PID (HB) + pPmt[14] = nVPid & 0xFF; // ES PID (LB) + + pPmt[15] = (0xF << 4) | // reserved + (0); // ES Info length + pPmt[16] = 0; // ES Info length + + // audio + pPmt[17] = 3; // stream type = audio + + pPmt[18] = (7 << 5) | // reserved + (nAPid >> 8); // ES PID + + pPmt[19] = nAPid & 0xFF; + + pPmt[20] = (0xF << 4) | // reserved + (0); // ES Info + + pPmt[21] = 0; + + // CRC + uint32_t nCrc = GetCrc32(pPmt, + wSectionLength + 3 - 4);// Crc for entire pmt section + pPmt[wSectionLength + 3 - 4 + 0] = (nCrc >> 24) & 0xFF; + pPmt[wSectionLength + 3 - 4 + 1] = (nCrc >> 16) & 0xFF; + pPmt[wSectionLength + 3 - 4 + 2] = (nCrc >> 8) & 0xFF; + pPmt[wSectionLength + 3 - 4 + 3] = (nCrc) & 0xFF; + } + + + + /*----------------------------------------------------------*/ + /*! \brief calculate a stuffing packet + * + * \param pTs - pointer to TS packet to store result + * \param nPatternCount - Actual pattern packet count, shall be incremented each time. + */ + /*----------------------------------------------------------*/ + static void CreateStuffing(uint8_t *pTs, uint32_t nPatternCount) + { + pTs[0] = 0x47; + pTs[1] = 0x1F; + pTs[2] = 0xFF; + pTs[3] = 0x00; +#ifdef TS_PATTERN_CHECK + memcpy(&pTs[4], &nPatternCount, sizeof(nPatternCount)); + uint8_t startByte = (uint8_t)(nPatternCount & 0xFF); + for (uint8_t i = 8; i < 188; i++) { + pTs[i] = startByte + i; + } +#endif + } + +}; + +#endif //TSPACKET_H diff --git a/Src/Multiplexer/udp-stream.c b/Src/Multiplexer/udp-stream.c new file mode 100644 index 0000000..c830789 --- /dev/null +++ b/Src/Multiplexer/udp-stream.c @@ -0,0 +1,127 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include <stdio.h> +#include <stdint.h> +#include <stdbool.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <errno.h> +#include "Console.h" + +/*----------------------------------------------------------*/ +/*! \brief Private structure used by UdpStream component. + */ +/*----------------------------------------------------------*/ +typedef struct localVar_tag +{ + ///Socket handle. + int sock; + ///Target port. + int port; + ///Target IP address as zero terminated string. + char targetIpAddress[32]; + ///SocketAddress structure to address remote node. + struct sockaddr_in destination; + ///Helper variable to suppress a lot of error messages. + bool errorMode; +} localVar_t; + +static localVar_t udpLocal; + +bool UdpStream_Init( const char *targetIp, int targetPort ) +{ + if( ( NULL == targetIp ) || ( 0 == targetPort ) ) + { + return false; + } + ConsolePrintf( PRIO_HIGH, "Sending UDP stream to IP:'%s', port:%d\n", targetIp, targetPort ); + strcpy( udpLocal.targetIpAddress, targetIp ); + udpLocal.port = targetPort; + udpLocal.errorMode = false; + + memset( &udpLocal.destination, 0, sizeof( udpLocal.destination ) ); + + /* Default send address */ + udpLocal.destination.sin_family = AF_INET; + udpLocal.destination.sin_addr.s_addr = inet_addr( udpLocal.targetIpAddress ); + udpLocal.destination.sin_port = htons( udpLocal.port ); + + if( ( udpLocal.sock = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP ) ) < 0 ) + { + ConsolePrintf( PRIO_ERROR, RED"Failed to create socket"RESETCOLOR"\n" ); + return false; + } +#if defined (SO_REUSEADDR) && defined (SO_REUSEPORT) + int one = 1; + setsockopt(udpLocal.sock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &one, sizeof(one)); +#endif + + /* set default destination */ + setsockopt( udpLocal.sock, IPPROTO_IP, IP_MULTICAST_IF, &udpLocal.destination, sizeof( udpLocal.destination ) ); + + // this call is what allows broadcast packets to be sent: + int enableSocketFlag = 1; + if( setsockopt( udpLocal.sock, SOL_SOCKET, SO_BROADCAST, &enableSocketFlag, sizeof( enableSocketFlag ) ) < 0 ) + { + ConsolePrintf( PRIO_ERROR, RED"setsockopt (SO_BROADCAST) failed"RESETCOLOR"\n" ); + return false; + } + int ttl = 5; + if (!(setsockopt( udpLocal.sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)))) { + ConsolePrintf( PRIO_HIGH, "TTL set successfully to %d\n", ttl ); + } else { + ConsolePrintf( PRIO_ERROR, "Error setting TTL: %s\n", strerror(errno)); + } + + return true; +} + +void UdpStream_Close() +{ + close( udpLocal.sock ); +} + +bool UdpStream_Send( const uint8_t *data, uint32_t length ) +{ + int is_length = sendto( udpLocal.sock, data, length, + 0, ( struct sockaddr * )&udpLocal.destination, sizeof( udpLocal.destination ) ); + if( is_length != length ) + { + if( !udpLocal.errorMode ) + { + udpLocal.errorMode = true; + ConsolePrintf( PRIO_ERROR, RED"Send failed; shall len = %d; is length = %d"RESETCOLOR"\n", length, is_length ); + } + return false; + } + else + udpLocal.errorMode = false; + + return true; +} diff --git a/Src/Multiplexer/udp-stream.h b/Src/Multiplexer/udp-stream.h new file mode 100644 index 0000000..df6c5db --- /dev/null +++ b/Src/Multiplexer/udp-stream.h @@ -0,0 +1,71 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + + /*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the a simple C-API to send a UDP stream. + */ +/*----------------------------------------------------------*/ +#ifndef UDP_STREAM_H +#define UDP_STREAM_H + +#include <stdint.h> +#include <stdbool.h> + + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*----------------------------------------------------------*/ +/*! \brief Initializies the UDP socket. + * \note Do not call any other function of this component, before calling this function. + * \param targetIp - IP address of remote node as zero terminated string. + * \param targetPort - Port number of remote node. + * \return True, if successful, false otherwise. + */ +/*----------------------------------------------------------*/ +bool UdpStream_Init( const char *targetIp, int targetPort ); + +/*----------------------------------------------------------*/ +/*! \brief Deinitializies the UDP socket. + * \note Do not call any other function of this component, after calling this function. + */ +/*----------------------------------------------------------*/ +void UdpStream_Close(); + +/*----------------------------------------------------------*/ +/*! \brief Send the given Data via the UDP socket. + * \param data - Byte array to be sent. + * \param length - The length of the Byte array. + * \return True, if successful, false otherwise. + */ +/*----------------------------------------------------------*/ +bool UdpStream_Send( const uint8_t *data, uint32_t length ); + +#ifdef __cplusplus +} +#endif + +#endif //UDP_STREAM_H diff --git a/Src/SafeVector.h b/Src/SafeVector.h new file mode 100644 index 0000000..97a4882 --- /dev/null +++ b/Src/SafeVector.h @@ -0,0 +1,192 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CSafeVector class. + */ +/*----------------------------------------------------------*/ + +#ifndef SAFE_VECTOR_H +#define SAFE_VECTOR_H + +#include <stdbool.h> +#include <stdint.h> +#include <stddef.h> +#include <semaphore.h> +#include <vector> +#include "SafeVector.h" + +using namespace std; + +/*----------------------------------------------------------*/ +/*! \brief Thread safe vector class. + */ +/*----------------------------------------------------------*/ +template<typename T> +class CSafeVector +{ +private: + bool initialized; + std::vector<T> vec; + sem_t sem; +public: + /*----------------------------------------------------------*/ + /*! \brief Default constructor of CSafeVector. + */ + /*----------------------------------------------------------*/ + CSafeVector() : initialized(false) + { + sem_init( &sem, 0, 1 ); //Mutex, initialized to 1 => sem_wait will block at 2nd call + } + + /*----------------------------------------------------------*/ + /*! \brief Default destructor of CSafeVector. + */ + /*----------------------------------------------------------*/ + ~CSafeVector() + { + vec.empty(); + sem_destroy( &sem); + } + + /*----------------------------------------------------------*/ + /*! \brief Stores the given object into the last postion of the vector. + */ + /*----------------------------------------------------------*/ + void PushBack(T obj) + { + if( NULL == obj ) + return; + sem_wait( &sem ); + vec.push_back( obj ); + initialized = true; + sem_post( &sem ); + } + + /*----------------------------------------------------------*/ + /*! \brief Gets the first element from the vector and dequeue it. + * \return The first object if exists, otherwise NULL, + */ + /*----------------------------------------------------------*/ + T PopFront() + { + T obj = NULL; + sem_wait( &sem ); + if (vec.size() >= 1) + { + obj = vec[0]; + vec.erase( vec.begin() ); + } + sem_post( &sem ); + return obj; + } + + /*----------------------------------------------------------*/ + /*! \brief Gets the first element from the vector and dequeue it. + * \return The first object if exists, otherwise NULL, + */ + /*----------------------------------------------------------*/ + T operator[](uint32_t p) + { + if( !initialized ) + return NULL; + T obj = NULL; + sem_wait( &sem ); + uint32_t s = vec.size(); + if (0 != s && p < s) + { + obj = vec[p]; + } + sem_post( &sem ); + return obj; + } + + /*----------------------------------------------------------*/ + /*! \brief Gets the amount of objects stored in the vector. + * \return The amount of objects available. + */ + /*----------------------------------------------------------*/ + uint32_t Size() + { + uint32_t s; + if( !initialized ) + return 0; + + sem_wait( &sem ); + s = vec.size(); + sem_post( &sem ); + return s; + } + + /*----------------------------------------------------------*/ + /*! \brief Safley removes the given object from the vector. + * \note If not found, it will be silently ignored. + * \note The memory of the object will NOT be freed! + */ + /*----------------------------------------------------------*/ + void Remove(T obj) + { + int32_t deleteIndex = -1; + if( !initialized || NULL == obj ) + return; + + sem_wait( &sem ); + for( uint32_t i = 0; i < vec.size(); i++ ) + { + if( obj == vec[i] ) + { + deleteIndex = i; + break; + } + } + if( -1 != deleteIndex ) + { + vec.erase( vec.begin() + deleteIndex ); + } + sem_post( &sem ); + } + + + /*----------------------------------------------------------*/ + /*! \brief Safley removes the given object from the vector. + * \note If not found, it will be silently ignored. + * \param destroy - if set to true, the memory will be freed for each deleted object. + */ + /*----------------------------------------------------------*/ + void RemoveAll(bool destroy) + { + if( !initialized ) + return; + sem_wait( &sem ); + for( uint32_t i = 0; destroy && i < vec.size(); i++ ) + { + T obj = vec[i]; + delete obj; + } + vec.clear(); + sem_post( &sem ); + } +}; + +#endif //SAFE_VECTOR_H
\ No newline at end of file diff --git a/Src/Thread.cpp b/Src/Thread.cpp new file mode 100644 index 0000000..b6bb01e --- /dev/null +++ b/Src/Thread.cpp @@ -0,0 +1,129 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include "Thread.h" +#include <string.h> +#include "Console.h" + +CThread::CThread( const char *threadName, bool maxPrio ) : m_hThread( THREAD_STATE_DONE ) +{ + strncpy( m_threadName, threadName, sizeof( m_threadName ) ); + m_maxPrio = maxPrio; +#ifdef THREAD_DEBUG + ConsolePrintf(PRIO_LOW, "===== Thread '%s' has been created =====\n", m_threadName); +#endif +} + +CThread::~CThread() +{ + Stop(); + usleep( 10000 ); + while( IsThreadRunning() ) + { + ConsolePrintf( PRIO_LOW, "===== Thread '%s' is still running =====\n", m_threadName ); + usleep( 1000000 ); + } +} + +void *CThread::RunInst( void *pInst ) +{ + CThread *thread = ( CThread * )pInst; + if( NULL == thread ) + return 0; + + /* set thread priority: */ + if( thread->m_maxPrio ) + { + int ret; + pthread_t this_thread = pthread_self(); + struct sched_param params; + params.sched_priority = sched_get_priority_max( SCHED_FIFO ); + ret = pthread_setschedparam( this_thread, SCHED_FIFO, ¶ms ); + if( ret != 0 ) + { + ConsolePrintf( PRIO_ERROR, "Thread %s: pthread_setschedparam(SCHED_FIFO) failed\n", thread->m_threadName ); + } + // Now verify the change in thread priority + int policy = 0; + ret = pthread_getschedparam( this_thread, &policy, ¶ms ); + if( ret != 0 ) + { + ConsolePrintf( PRIO_ERROR, "Thread %s: pthread_getschedparam() failed\n", thread->m_threadName ); + } + if( policy != SCHED_FIFO ) + { + ConsolePrintf( PRIO_ERROR, "Thread %s: Scheduling is NOT SCHED_FIFO!\n", thread->m_threadName ); + } + else + { + ConsolePrintf( PRIO_LOW, "Thread %s: SCHED_FIFO OK\n", thread->m_threadName ); + } + ConsolePrintf( PRIO_LOW, "Thread %s: Thread priority is %d\n", thread->m_threadName, params.sched_priority ); + } + + while( THREAD_STATE_STOPPING != + thread->m_hThread ) + { // until thread is marked for termination +#ifdef THREAD_DEBUG + ConsolePrintf(PRIO_LOW, "+++++ Thread '%s' run before Run +++++\n", thread->m_threadName); +#endif + ( ( CThread * )pInst )->Run(); +#ifdef THREAD_DEBUG + ConsolePrintf(PRIO_LOW, "----- Thread '%s' run after Run -----\n", thread->m_threadName); +#endif + } + + ( ( CThread * )pInst )->m_hThread = THREAD_STATE_DONE; // mark thread as done + + return 0; +} + +void CThread::Start() +{ + if( THREAD_STATE_DONE != m_hThread ) + return; + + struct sched_param param; + pthread_attr_t tattr; + pthread_attr_init( &tattr ); + pthread_attr_getschedparam( &tattr, ¶m ); + pthread_attr_setschedpolicy( &tattr, SCHED_RR ); + if( m_maxPrio ) + { + param.sched_priority = sched_get_priority_max( SCHED_RR ); + pthread_attr_setschedparam( &tattr, ¶m ); + // note: this doesn't seem to work, so we're setting prio in RunInst() now + } + pthread_create( &m_hThread, &tattr, CThread::RunInst, ( void * )this ); +} + +void CThread::Stop() +{ + if( m_hThread ) + m_hThread = THREAD_STATE_STOPPING; +} + +bool CThread::IsThreadRunning() +{ + return ( THREAD_STATE_DONE != m_hThread ); +} diff --git a/Src/Thread.h b/Src/Thread.h new file mode 100644 index 0000000..01f6500 --- /dev/null +++ b/Src/Thread.h @@ -0,0 +1,88 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CThread class. + */ +/*----------------------------------------------------------*/ +#ifndef THREAD_H +#define THREAD_H + +#include <pthread.h> +#include <unistd.h> +#include <stdint.h> +#include <stdio.h> + +//#define THREAD_DEBUG +#define THREAD_STATE_DONE 0 +#define THREAD_STATE_STOPPING (pthread_t)-1 + +/*----------------------------------------------------------*/ +/*! \brief Helper class to deal with Threads. + */ +/*----------------------------------------------------------*/ +class CThread +{ + pthread_t m_hThread; + char m_threadName[32]; + bool m_maxPrio; + +public: + CThread( const char *threadName, bool maxPrio ); + virtual ~CThread(); + + /*----------------------------------------------------------*/ + /*! \brief Starts the thread execution. + */ + /*----------------------------------------------------------*/ + void Start(); + + /*----------------------------------------------------------*/ + /*! \brief Stops the thread execution. + */ + /*----------------------------------------------------------*/ + void Stop(); + + /*----------------------------------------------------------*/ + /*! \brief Determines if the thread is running. + * \return true, if the thread is running. false, if the thread is stopped. + */ + /*----------------------------------------------------------*/ + bool IsThreadRunning(); + + /*----------------------------------------------------------*/ + /*! \brief This method must be overridden the deriving class. + * This method will then run in an own thread. + * This method will be infinitely called, as long as the CThread object is marked to run. + */ + /*----------------------------------------------------------*/ + virtual void Run() = 0; + +private: + static void *RunInst( void *pInst ); + +}; + +#endif + diff --git a/Src/Types.h b/Src/Types.h new file mode 100644 index 0000000..13ab01b --- /dev/null +++ b/Src/Types.h @@ -0,0 +1,230 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the EPDataType_t, EPDirection_t, MlbPortSpeed_t, V1I2SPortClkDriveMode_t, V1I2SStreamingDataFormat_t, + * V1I2SPin_t, V3I2SPortOption_t, V3I2SClockMode_t, V3I2SDelayMode_t, V3I2SPortSpeed_t, V3I2SAlignment_t, V3I2SPin_t type definition. + */ +/*----------------------------------------------------------*/ +#ifndef _TYPES_H_ +#define _TYPES_H_ + +#include <inttypes.h> +#include <pthread.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <fcntl.h> +#include "Console.h" + +#define LOG(x) ConsolePrintf(PRIO_ERROR, RED"! %s, %u, %s"RESETCOLOR"\n",x,__LINE__,__FILE__) + +#define SCM_IN 0x00 +#define SCM_OUT 0x01 + +typedef uint32_t TChannelId; +typedef uint16_t TDeviceId; +typedef uint8_t TNodePos; +typedef uint16_t TNodeAddr; +typedef uint32_t TBlockwidth; +typedef uint8_t TMostInstace; +typedef uint16_t TPos; ///Value is in per mile. (0..1000) + +typedef enum AimType_tag +{ + AIM_UNKNOWN, + AIM_CDEV, + AIM_AUDIO, + AIM_V4L +} AimType_t; + +///Data type enum values are matched to INIC API V1 & V2 +typedef enum +{ + EP_Synchron = 0x00, + EP_Asynchron = 0x01, + EP_Control = 0x02, + EP_Isochron = 0x03, + EP_Unused = 0xFE, + EP_Unknown = 0xFF +} EPDataType_t; + +///Direction type enum values are matched to INIC API V1 & V2 +typedef enum +{ + EPDIR_IN = 0x00, + EPDIR_OUT = 0x01, + EPDIR_Unknown = 0xFF +} EPDirection_t; + +///INIC API V1 and V2: MLB Port speed in mulitple of MOST base clock (FS) +typedef enum +{ + MlbSpeed256FS = 0, + MlbSpeed512FS = 1, + MlbSpeed1024FS = 2, + MlbSpeed2048FS = 3, + MlbSpeed3072FS = 4, + MlbSpeed4096FS = 5, + MlbSpeed6144FS = 6, + MlbSpeed8192FS = 7, + MlbSpeedNotSet = 0xFF +} MlbPortSpeed_t; + +///TSI Port ID +typedef enum +{ + V1TsiPortInstance0 = 0x5, + V1TsiPortInstance1 = 0x25, + V1TsiPortInstanceNotSet = 0xFF +} V1TsiPortInstance_t; + +///TSI Port Mode +typedef enum +{ + V1TsiPortModeSlave = 0x0, + V1TsiPortModeMaster = 0x1, + V1TsiPortModeNotSet = 0xFF +} V1TsiPortMode; + +///I2S Port drive mode +typedef enum +{ + V1I2sClockModeOutput = 0, + V1I2sClockModeInput = 1, + V1I2sClockModeIsocOutput = 2, + V1I2sClockModeIsocInput = 3, + V1I2sClockModeUnknown = 0xFE, +} V1I2SPortClkDriveMode_t; + +///I2S Streaming Port mode +typedef enum +{ + V1I2sStreamingPortInOut = 0, + V1I2sStreamingPortDualIn = 1, + V1I2sStreamingPortDualOut = 2, + V1I2sStreamingPortFullStream = 3, + V1I2sStreamingPortUnknown = 0xFE, + V1I2sStreamingPortWildcard = 0xFF ///Only valid for OS81110 +} V1I2SStreamingPortMode_t; + +///I2S Port streaming data format +typedef enum +{ + V1I2sDelay64Fs16Bit = 0x00, + V1I2sDelay64Fs24Bit = 0x01, + V1I2sDelay64FsSeq = 0x04, + V1I2sDelay128FsSeq = 0x05, + V1I2sDelay256FsSeq = 0x06, + V1I2sRight64Fs16Bit = 0x07, + V1I2sRight64Fs24Bit = 0x08, + V1I2sSeq64Fs = 0x0B, + V1I2sSeq128Fs = 0x0C, + V1I2sSeq256Fs = 0x0D, + V1I2sLeft64Fs16Bit = 0x0E, + V1I2sLeft64Fs24Bit = 0x0F, + V1I2sDelay512FsSeq = 0x17, + V1I2sSeq512Fs = 0x18, + V1I2sDataFormatUnknown = 0xFE, +} V1I2SStreamingDataFormat_t; + +///I2S Stream Pin ID +typedef enum +{ + V1I2sSR0Pin = 0x00, + V1I2sSX0Pin = 0x01, + V1I2sSR1Pin = 0x02, + V1I2sSX1Pin = 0x03, + V1I2sSR2Pin = 0x04, + V1I2sSX2Pin = 0x05, + V1I2sSR3Pin = 0x06, + V1I2sSX3Pin = 0x07, + V1I2sFSYSCKPins = 0x10, + V1I2sInvalidPin = 0xFE, +} V1I2SPin_t; + +///I2S Port option +typedef enum +{ + V3I2SOptionInOut = 0, + V3I2SOptionDualIn = 1, + V3I2SOptionDualOut = 2, + V3I2SOptionUnknown = 0xFE +} V3I2SPortOption_t; + +///I2S Clock mode +typedef enum +{ + V3I2SClockModeOutput = 0, + V3I2SClockModeInput = 1, + V3I2SClockModeWildcard = 0xFF, + V3I2SClockModeUnknown = 0xFE +} V3I2SClockMode_t; + +///I2S Delay option +typedef enum +{ + V3I2SDelayOff = 0, + V3I2SDelayEnabled = 1, + V3I2SDelayWildcard = 0xFF, + V3I2SDelayUnknown = 0xFE +} V3I2SDelayMode_t; + +///I2S Port speed in mulitple of MOST base clock (FS) +typedef enum +{ + V3I2SSpeed64FS = 3, + V3I2SSpeed128FS = 4, + V3I2SSpeed256FS = 5, + V3I2SSpeed512FS = 6, + V3I2SSpeedWildcard = 0xFF, + V3I2SSpeedUnknown = 0xFE +} V3I2SPortSpeed_t; + +///I2S Data Alignment +typedef enum +{ + V3I2SAlignLeft16 = 0, + V3I2SAlignLeft24 = 1, + V3I2SAlignRight16 = 2, + V3I2SAlignRight24 = 3, + V3I2SAlignSeq = 4, + V3I2SAlignUnknown = 0xFE +} V3I2SAlignment_t; + +///I2S Stream Pin ID +typedef enum +{ + V3I2SSRXA0 = 0x0, + V3I2SSRXA1 = 0x1, + V3I2SSRXB0 = 0x10, + V3I2SSRXB1 = 0x11, + V3I2SInvalidPin = 0xFE +} V3I2SPin_t; + +#endif diff --git a/Src/VodHandler.cpp b/Src/VodHandler.cpp new file mode 100644 index 0000000..98ead21 --- /dev/null +++ b/Src/VodHandler.cpp @@ -0,0 +1,1128 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include <stdint.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <dirent.h> +#include "Console.h" +#include "VodHandler.h" +#include "SourceFile.h" +#include "SourceFileConverted.h" +#include "AutoLock.h" + +using namespace std; + +#define MAX_IPC_PAYLOAD_LEN (65000) + +#define VOD_VERSION_MAJOR ((uint8_t)3) +#define VOD_VERSION_MINOR ((uint8_t)0) +#define VOD_VERSION_BUGFIX ((uint8_t)4) +#define VOD_VERSION_BUILD ((uint8_t)0) + +#define IPC_PORT_NUMBER_VOD (5544) +#define FBLOCK_VOD (17) +#define FUNC_VOD_SELECT_FILE (1) +#define FUNC_VOD_PLAYMODE (2) +#define FUNC_VOD_TIMEPOSITION (3) +#define FUNC_VOD_REPETITION (4) +#define FUNC_VOD_SERVERVERSION (5) +#define FUNC_VOD_MEDIAPID (6) +#define FUNC_VOD_FILE_LIST (7) +#define FUNC_VOD_NEW_INDEPEND_STREAM (8) +#define FUNC_VOD_FAKE_MODE (99) + +#define IPC_PORT_NUMBER_NM (5533) +#define FBLOCK_NETWORK_MANAGER (10) +#define FUNC_NM_SERVERVERSION (1) +#define FUNC_NM_SUBSCRIBE_MCM (2) +#define FUNC_NM_UNSUBSCRIBE_MCM (3) +#define FUNC_NM_RECEIVED_MCM (4) +#define FUNC_NM_SEND_MCM_ADR (5) +#define FUNC_NM_SEND_MCM_DEV (6) +#define FUNC_NM_COUNTER_ROUTE (10) +#define FUNC_NM_CONNECTION_LIST (100) + + +static CVodHandler *s_mainClass = NULL; + +CVodHandler::CVodHandler() : CThread( "CVodHandler", false ), valuesCleared( false ), statisticIsEnabled( false ), + infoContainer( NULL ) +{ + searchPath[0] = '.'; + searchPath[0] = '\0'; + + pthread_mutex_init( &infoContainerMutex, NULL ); + + mostIpcVod = new CMostIpc( IPC_PORT_NUMBER_VOD, true ); + vodSelectFile_Set = new CMsgFilter( FBLOCK_VOD, FUNC_VOD_SELECT_FILE, + CMostMsg::OP_SET, OnVodSelectFile_Set ); + mostIpcVod->RegisterMessageHandler( vodSelectFile_Set ); + + vodPlayMode_Set = new CMsgFilter( FBLOCK_VOD, FUNC_VOD_PLAYMODE, CMostMsg::OP_SET, + OnVodPlayMode_Set ); + mostIpcVod->RegisterMessageHandler( vodPlayMode_Set ); + + vodTimePostion_Set = new CMsgFilter( FBLOCK_VOD, FUNC_VOD_TIMEPOSITION, + CMostMsg::OP_SET, OnVodTimePosition_Set ); + mostIpcVod->RegisterMessageHandler( vodTimePostion_Set ); + + vodRepetition_Set = new CMsgFilter( FBLOCK_VOD, FUNC_VOD_REPETITION, + CMostMsg::OP_SET, OnVodRepetition_Set ); + mostIpcVod->RegisterMessageHandler( vodRepetition_Set ); + + vodServerVersion_Get = new CMsgFilter( FBLOCK_VOD, FUNC_VOD_SERVERVERSION, + CMostMsg::OP_GET, OnVodServerVersion_Get ); + mostIpcVod->RegisterMessageHandler( vodServerVersion_Get ); + + vodMediaPid_Get = new CMsgFilter( FBLOCK_VOD, FUNC_VOD_MEDIAPID, + CMostMsg::OP_GET, OnVodMediaPid_Get ); + mostIpcVod->RegisterMessageHandler( vodMediaPid_Get ); + + vodFileList_Get = new CMsgFilter( FBLOCK_VOD, FUNC_VOD_FILE_LIST, + CMostMsg::OP_GET, OnVodFileList_Get ); + mostIpcVod->RegisterMessageHandler( vodFileList_Get ); + + vodIndependStream_SetGet = new CMsgFilter( FBLOCK_VOD, FUNC_VOD_NEW_INDEPEND_STREAM, + CMostMsg::OP_SETGET, OnVodIndependentStream_SetGet ); + mostIpcVod->RegisterMessageHandler( vodIndependStream_SetGet ); + + vodFakeMode_Set = new CMsgFilter( FBLOCK_VOD, FUNC_VOD_FAKE_MODE, + CMostMsg::OP_SET, OnFakeMode_Set ); + mostIpcVod->RegisterMessageHandler( vodFakeMode_Set ); + + mostIpcNm = new CMostIpc( 0, false ); + nmServerVersion_Status = new CMsgFilter( FBLOCK_NETWORK_MANAGER, FUNC_NM_SERVERVERSION, + CMostMsg::OP_STATUS, OnNmServerVersion_Status ); + mostIpcNm->RegisterMessageHandler( nmServerVersion_Status ); + + nmConnectionList_Status = new CMsgFilter( FBLOCK_NETWORK_MANAGER, FUNC_NM_CONNECTION_LIST, + CMostMsg::OP_STATUS, OnNmConnectionList_Status ); + mostIpcNm->RegisterMessageHandler( nmConnectionList_Status ); + + RegisterBroadcastFiles( SelectFileType_Radio ); + RegisterBroadcastFiles( SelectFileType_Navigation ); + RegisterBroadcastFiles( SelectFileType_Cluster ); +} + +CVodHandler::~CVodHandler() +{ + Stop(); + + if( NULL != mostIpcVod ) + { + delete mostIpcVod; + mostIpcVod = NULL; + } + + if( NULL != vodSelectFile_Set ) + { + delete vodSelectFile_Set; + vodSelectFile_Set = NULL; + } + + if( NULL != vodPlayMode_Set ) + { + delete vodPlayMode_Set; + vodPlayMode_Set = NULL; + } + + if( NULL != vodTimePostion_Set ) + { + delete vodTimePostion_Set; + vodTimePostion_Set = NULL; + } + + if( NULL != vodRepetition_Set ) + { + delete vodRepetition_Set; + vodRepetition_Set = NULL; + } + + if( NULL != vodServerVersion_Get ) + { + delete vodServerVersion_Get; + vodServerVersion_Get = NULL; + } + + if( NULL != vodMediaPid_Get ) + { + delete vodMediaPid_Get; + vodMediaPid_Get = NULL; + } + + if( NULL != vodFileList_Get ) + { + delete vodFileList_Get; + vodFileList_Get = NULL; + } + + if( NULL != vodFakeMode_Set ) + { + delete vodFakeMode_Set; + vodFakeMode_Set = NULL; + } + + if( NULL != mostIpcNm ) + { + delete mostIpcNm; + mostIpcNm = NULL; + } + + if( NULL != nmServerVersion_Status ) + { + delete nmServerVersion_Status; + nmServerVersion_Status = NULL; + } + + if( NULL != nmConnectionList_Status ) + { + delete nmConnectionList_Status; + nmConnectionList_Status = NULL; + } + + pthread_mutex_lock( &infoContainerMutex ); + if ( NULL != infoContainer ) + { + delete infoContainer; + infoContainer = NULL; + } + pthread_mutex_unlock( &infoContainerMutex ); + + broadcastFiles.RemoveAll( true ); + allMultiplexer.RemoveAll(true); + ignoreCdevPatterns.RemoveAll( true ); +} + +bool CVodHandler::ExistsFile( const char *pFileName ) +{ + struct stat64 buffer; + return ( stat64( pFileName, &buffer ) == 0 ); +} + +CMultiplexer *CVodHandler::GetMultiplexer( const char* deviceName ) +{ + if ( NULL == deviceName ) + return NULL; + for( uint32_t i = 0; i < allMultiplexer.Size(); i++ ) + { + CMultiplexer *t = allMultiplexer[i]; + if (NULL == t) + continue; + if ( 0 == strcmp( deviceName, t->GetDriverName() ) ) + { + return t; + } + } + return NULL; +} + +const char *CVodHandler::GetSubPath( SelectFileType_t fileType ) +{ + switch( fileType ) + { + case SelectFileType_Video: + return "video"; + case SelectFileType_Audio: + return "audio"; + case SelectFileType_Radio: + return "radio"; + case SelectFileType_Navigation: + return "navigation"; + case SelectFileType_Cluster: + return "cluster"; + default: + return ""; + } +} + +void CVodHandler::RegisterBroadcastFiles( SelectFileType_t fileType ) +{ + DIR *d; + struct dirent *dir; + char fullPath[200]; + snprintf( fullPath, sizeof ( fullPath ), "%s/%s", searchPath, GetSubPath( fileType ) ); + + d = opendir( fullPath ); + if( d ) + { + while( ( dir = readdir( d ) ) != NULL ) + { + if( DT_REG == dir->d_type || DT_LNK == dir->d_type ) + { + BroadcastEntry_t *entry = ( BroadcastEntry_t * )malloc( sizeof ( BroadcastEntry_t ) ); + if( NULL != entry ) + { + snprintf( entry->fileName, sizeof ( entry->fileName ), "%s/%s", fullPath, dir->d_name ); + char *ext = rindex(dir->d_name, '.'); + if (ext && !strcasecmp(ext, ".ts")) + entry->sourceFile = new CSourceFile( entry->fileName, false ); + else + entry->sourceFile = new CSourceFileConverted( entry->fileName, false ); + broadcastFiles.PushBack( entry ); + } + } + } + } +} + +CMultiplexer *CVodHandler::GetMultiplexer( const uint8_t *mac ) +{ + volatile CAutoLock autoLock( &infoContainerMutex ); + if( NULL == mac ) + { + ConsolePrintf( PRIO_ERROR, + RED"GetMultiplexer was called with invalid parameters."RESETCOLOR"\n" ); + return NULL; + } + if( NULL == infoContainer ) + return NULL; + CMultiplexer *pMultiplexer = NULL; + char macAddr[20]; + snprintf( ( char * )&macAddr, sizeof( macAddr ), "%02X-%02X-%02X-%02X-%02X-%02X", mac[0], mac[1], mac[2], mac[3], + mac[4], mac[5] ); + uint32_t sinkCount = infoContainer->GetAllInfoAmount(); + uint32_t mostInstance = 0xFFFFFFFF; + uint32_t mostConnectionLabel = 0xFFFFFFFF; + for (uint32_t i = 0; i < sinkCount; i++) + { + CConnectionInfo *con = infoContainer->GetInfo(i); + if ( ( NULL != con ) && ( NULL != con->macAddr ) && ( 0 == strcmp( con->macAddr->ToString(), macAddr ) ) ) + { + pMultiplexer = GetMultiplexer( con->deviceName ); + if ( NULL != pMultiplexer ) + { + break; + } + else if ( ( EP_Isochron == con->dataType ) || + ( EP_Synchron == con->dataType && con->reservedBandwidth > 4 ) ) + { + mostInstance = con->mostInstance; + mostConnectionLabel = con->mostConnectionLabel; + break; + } + } + } + if( NULL == pMultiplexer && ( 0xFFFFFFFF != mostInstance ) && ( 0xFFFFFFFF != mostConnectionLabel ) ) + { + for (uint32_t i = 0; i < sinkCount; i++) + { + CConnectionInfo *con = infoContainer->GetInfo(i); + if ( NULL != con && con->mostInstance == mostInstance && con->mostConnectionLabel == mostConnectionLabel ) + { + pMultiplexer = GetMultiplexer( con->deviceName ); + if ( NULL != pMultiplexer ) + break; + } + } + } + if( NULL == pMultiplexer ) + { + ConsolePrintf( PRIO_ERROR, RED"CVodHandler::GetMultiplexer: Could not get Multiplexer for MAC: %s"RESETCOLOR"\n", macAddr ); + } + return pMultiplexer; +} + +void CVodHandler::OnVodSelectFile_Set( CMsgAddr *pAddr, CMostMsg *pMsg ) +{ + if( NULL == s_mainClass || NULL == pAddr || NULL == pMsg || pMsg->GetPayloadLen() < 8 ) + { + ConsolePrintf( PRIO_ERROR, RED"OnVodSelectFile_Set parameters are invalid.\n"\ + "This could happen, if the client uses an outdated version of the IPC,"\ + " try to update your client."RESETCOLOR"\n" ); + return; + } + + uint8_t *mac = pMsg->GetPayload(); + + SelectFileType_t fileType = ( SelectFileType_t )pMsg->GetPayload()[6]; + char *movieName = ( char * )&pMsg->GetPayload()[7]; + ConsolePrintf( PRIO_HIGH, "OnVodSelectFile_Set IP=%s MAC=%02X-%02X-%02X-%02X-%02X-%02X type=%d video:'%s'\n", + pAddr->GetInetAddress(), mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], fileType, movieName ); + + s_mainClass->StreamFile( mac, fileType, movieName ); +} + +void CVodHandler::OnVodPlayMode_Set( CMsgAddr *pAddr, CMostMsg *pMsg ) +{ + if( NULL == s_mainClass || NULL == pAddr || NULL == pMsg || pMsg->GetPayloadLen() < 7 ) + { + ConsolePrintf( PRIO_ERROR, RED"OnVodPlayMode_Set parameters are invalid.\n"\ + "This could happen, if the client uses an outdated version of the IPC,"\ + " try to update your client."RESETCOLOR"\n" ); + return; + } + uint8_t *mac = pMsg->GetPayload(); + bool isPaused = 0 != pMsg->GetPayload()[6]; + ConsolePrintf( PRIO_HIGH, "OnVodPlayMode_IP=%s MAC=%02X-%02X-%02X-%02X-%02X-%02X paused:'%d'\n", + pAddr->GetInetAddress(), mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], isPaused ); + s_mainClass->SetPause( mac, isPaused ); +} + +void CVodHandler::OnVodTimePosition_Set( CMsgAddr *pAddr, CMostMsg *pMsg ) +{ + if( NULL == s_mainClass || NULL == pAddr || NULL == pMsg || pMsg->GetPayloadLen() < 8 ) + { + ConsolePrintf( PRIO_ERROR, RED"OnVodTimePosition_Set parameters are invalid.\n"\ + "This could happen, if the client uses an outdated version of the IPC,"\ + " try to update your client."RESETCOLOR"\n" ); + return; + } + uint8_t *mac = pMsg->GetPayload(); + uint16_t perMil = *( ( uint16_t * )&pMsg->GetPayload()[6] ); + + ConsolePrintf( PRIO_HIGH, "OnVodTimePosition_IP=%s MAC=%02X-%02X-%02X-%02X-%02X-%02X TimePos=%d/1000\n", + pAddr->GetInetAddress(), mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], perMil ); + s_mainClass->SetTimePos( mac, perMil ); +} + +void CVodHandler::OnVodRepetition_Set( CMsgAddr *pAddr, CMostMsg *pMsg ) +{ + if( NULL == s_mainClass || NULL == pAddr || NULL == pMsg || pMsg->GetPayloadLen() < 7 ) + { + ConsolePrintf( PRIO_ERROR, RED"OnVodRepetition_Set parameters are invalid\n"\ + "This could happen, if the client uses an outdated version of the IPC,"\ + " try to update your client."RESETCOLOR"\n" ); + return; + } + uint8_t *mac = pMsg->GetPayload(); + bool isRepeated = 0 != pMsg->GetPayload()[6]; + ConsolePrintf( PRIO_HIGH, "OnVodRepetition_IP=%s MAC=%02X-%02X-%02X-%02X-%02X-%02X repeated=%d\n", + pAddr->GetInetAddress(), mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], isRepeated ); + s_mainClass->SetRepetition( mac, isRepeated ); +} + +void CVodHandler::OnVodServerVersion_Get( CMsgAddr *pAddr, CMostMsg *pMsg ) +{ + if( NULL == s_mainClass || NULL == pAddr || NULL == pMsg || pMsg->GetPayloadLen() < 6 ) + { + ConsolePrintf( PRIO_ERROR, RED"OnVodServerVersion_Get parameters are invalid.\n"\ + "This could happen, if the client uses an outdated version of the IPC,"\ + " try to update your client."RESETCOLOR"\n" ); + return; + } + uint8_t *mac = pMsg->GetPayload(); + ConsolePrintf( PRIO_HIGH, + GREEN"MOST-IPC Client connected (asked for server version). IP=%s MAC=%02X-%02X-%02X-%02X-%02X-%02X "RESETCOLOR + "\n", pAddr->GetInetAddress(), mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] ); + const uint8_t version[] = + { + VOD_VERSION_MAJOR, VOD_VERSION_MINOR, + VOD_VERSION_BUGFIX, VOD_VERSION_BUILD + }; + CMostMsgTx *mostMsg = new CMostMsgTx( pAddr, //Addr + FBLOCK_VOD, // nFBlock + 0, // nInst + FUNC_VOD_SERVERVERSION, // nFunc + CMostMsg::OP_STATUS, // nOpType + sizeof ( version ), //nPayloadLen + version, //Payload + 500, //nTimeoutMs + NULL, //MessageSentCB + NULL ); //UserContext + s_mainClass->mostIpcVod->SendMsg( mostMsg ); + mostMsg->RemoveReference(); +} + +void CVodHandler::OnVodMediaPid_Get( CMsgAddr *pAddr, CMostMsg *pMsg ) +{ + if( NULL == s_mainClass || NULL == pAddr || NULL == pMsg || pMsg->GetPayloadLen() < 6 ) + { + ConsolePrintf( PRIO_ERROR, RED"OnVodMediaPid_Get parameters are invalid.\n"\ + "This could happen, if the client uses an outdated version of the IPC,"\ + " try to update your client."RESETCOLOR"\n" ); + return; + } + uint8_t *mac = pMsg->GetPayload(); + + uint16_t videoPid = 0x100 + mac[5]; + uint16_t audioPid = 0x200 + mac[5]; + uint16_t pmtPid = 0x300 + mac[5]; + uint8_t payload[6]; + payload[0] = videoPid / 256; + payload[1] = videoPid % 256; + payload[2] = audioPid / 256; + payload[3] = audioPid % 256; + payload[4] = pmtPid / 256; + payload[5] = pmtPid % 256; + + ConsolePrintf( PRIO_MEDIUM, + GREEN"MOST-IPC Client asked for PIDs. IP=%s MAC=%02X-%02X-%02X-%02X-%02X-%02X"\ + " video-pid=%X audio-pid=%X pmt-pid=%X."RESETCOLOR"\n", pAddr->GetInetAddress(), mac[0], mac[1], mac[2], + mac[3], mac[4], mac[5], videoPid, audioPid, pmtPid ); + + CMostMsgTx *mostMsg = new CMostMsgTx( pAddr, //Addr + FBLOCK_VOD, // nFBlock + 0, // nInst + FUNC_VOD_MEDIAPID, // nFunc + CMostMsg::OP_STATUS, // nOpType + sizeof ( payload ), //nPayloadLen + payload, //Payload + 500, //nTimeoutMs + NULL, //MessageSentCB + NULL ); //UserContext + s_mainClass->mostIpcVod->SendMsg( mostMsg ); + mostMsg->RemoveReference(); +} + +void CVodHandler::OnVodFileList_Get( CMsgAddr *pAddr, CMostMsg *pMsg ) +{ + if( NULL == s_mainClass || NULL == pAddr || NULL == pMsg || pMsg->GetPayloadLen() < 6 ) + { + ConsolePrintf( PRIO_ERROR, RED"OnVodFileList_Get parameters are invalid.\n"\ + "This could happen, if the client uses an outdated version of the IPC, "\ + " try to update your client."RESETCOLOR"\n" ); + return; + } + uint8_t *mac = pMsg->GetPayload(); + SelectFileType_t fileType = ( SelectFileType_t )pMsg->GetPayload()[6]; + + ConsolePrintf( PRIO_MEDIUM, + GREEN"MOST-IPC Client asked for File List IP=%s MAC=%02X-%02X-%02X-%02X-%02X-%02X."RESETCOLOR"\n", + pAddr->GetInetAddress(), mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] ); + + DIR *d; + struct dirent *dir; + char fullPath[200]; + char *payload = ( char * )calloc( 1, MAX_IPC_PAYLOAD_LEN ); + if( NULL == payload ) + return; + + snprintf( fullPath, sizeof( fullPath ), "%s/%s", s_mainClass->searchPath, GetSubPath( fileType ) ); + + d = opendir( fullPath ); + ConsolePrintfStart( PRIO_LOW, "Available Files:\n" ); + ConsolePrintfContinue( "File Name\n" ); + ConsolePrintfContinue( "----------------------------------------\n" ); + bool isFirst = true; + if( d ) + { + while( ( dir = readdir( d ) ) != NULL ) + { + char *ext = rindex(dir->d_name, '.'); + if (ext && !strcasecmp(ext, ".ts")) + { + ConsolePrintfContinue( "%s\n", dir->d_name ); + if( ( strlen( payload ) + strlen( dir->d_name ) + 1 ) < MAX_IPC_PAYLOAD_LEN ) + { + if ( !isFirst ) + strcat( payload, ":" ); + isFirst = false; + strcat( payload, dir->d_name ); + } + } + } + } + ConsolePrintfExit( "----------------------------------------\n" ); + + CMostMsgTx *mostMsg = new CMostMsgTx( pAddr, //Addr + FBLOCK_VOD, // nFBlock + 0, // nInst + FUNC_VOD_FILE_LIST, // nFunc + CMostMsg::OP_STATUS, // nOpType + ( strlen( payload ) + 1 ), //nPayloadLen + ( uint8_t * )payload, //Payload + 500, //nTimeoutMs + NULL, //MessageSentCB + NULL ); //UserContext + s_mainClass->mostIpcVod->SendMsg( mostMsg ); + + free( payload ); + mostMsg->RemoveReference(); +} + +void CVodHandler::OnVodIndependentStream_SetGet( CMsgAddr *pAddr, CMostMsg *pMsg ) +{ + if( NULL == s_mainClass || NULL == pAddr || NULL == pMsg || pMsg->GetPayloadLen() < 2 ) + { + ConsolePrintf( PRIO_ERROR, RED"OnVodIndependentStream_SetGet parameters are invalid.\n"RESETCOLOR"\n" ); + return; + } + uint8_t *payload = pMsg->GetPayload(); + uint8_t cdevIndex = payload[0]; + uint8_t streamIndex = payload[1]; + uint8_t responseOpType = CMostMsg::OP_ERROR; + uint8_t responsePayloadLength = 2; + uint8_t responsePayload[8]; + responsePayload[0] = cdevIndex; + responsePayload[1] = streamIndex; + if (s_mainClass->CreateMacIndependentStream(cdevIndex, streamIndex, &responsePayload[2])) + { + responseOpType = CMostMsg::OP_STATUS; + responsePayloadLength = sizeof(responsePayload); + } + CMostMsgTx *mostMsg = new CMostMsgTx( pAddr, //Addr + FBLOCK_VOD, // nFBlock + 0, // nInst + FUNC_VOD_NEW_INDEPEND_STREAM, // nFunc + responseOpType, // nOpType + responsePayloadLength, //nPayloadLen + responsePayload, //Payload + 500, //nTimeoutMs + NULL, //MessageSentCB + NULL ); //UserContext + s_mainClass->mostIpcVod->SendMsg( mostMsg ); + mostMsg->RemoveReference(); +} + +void CVodHandler::OnFakeMode_Set( CMsgAddr *pAddr, CMostMsg *pMsg ) +{ + if( NULL == s_mainClass || NULL == pAddr || NULL == pMsg || pMsg->GetPayloadLen() < 7 ) + { + ConsolePrintf( PRIO_ERROR, RED"OnFakeMode_Set parameters are invalid.\n"\ + "This could happen, if the client uses an outdated version of the IPC, "\ + " try to update your client."RESETCOLOR"\n" ); + return; + } + uint8_t *mac = pMsg->GetPayload(); + uint8_t fakeSinks = pMsg->GetPayload()[6]; + + ConsolePrintf( PRIO_MEDIUM, + GREEN"MOST-IPC Client IP=%s MAC=%02X-%02X-%02X-%02X-%02X-%02X hast started"\ + " %d fake sinks"RESETCOLOR"\n", pAddr->GetInetAddress(), mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], + fakeSinks ); + + s_mainClass->CreateSampleStreams( fakeSinks ); +} + +void CVodHandler::OnNmServerVersion_Status( CMsgAddr *pAddr, CMostMsg *pMsg ) +{ + if( NULL == s_mainClass || NULL == pAddr || NULL == pMsg ) + { + ConsolePrintf( PRIO_ERROR, RED"OnNmServerVersion_Status parameters are invalid."RESETCOLOR"\n" ); + return; + } + if( !s_mainClass->networkManagerFound ) + { + s_mainClass->networkManagerFound = true; + strncpy( s_mainClass->networkManagerIP, pAddr->GetInetAddress(), sizeof ( s_mainClass->networkManagerIP ) ); + } + if( pMsg->GetPayloadLen() >= 4 ) + { + uint8_t *p = pMsg->GetPayload(); + ConsolePrintf( PRIO_HIGH, "Got Network Manager version V%02d.%02d.%02d.%02d\n", p[0], p[1], p[2], p[3] ); + } +} + +void CVodHandler::OnNmConnectionList_Status( CMsgAddr *pAddr, CMostMsg *pMsg ) +{ + if( NULL == s_mainClass || NULL == pAddr || NULL == pMsg ) + { + ConsolePrintf( PRIO_ERROR, RED"OnNmConnectionList_Status parameters are invalid."RESETCOLOR"\n" ); + return; + } + volatile CAutoLock autoLock( &s_mainClass->infoContainerMutex ); + CConnectionInfoContainer *container = new CConnectionInfoContainer(); + if( !container->DeserializeFromString( ( char * )pMsg->GetPayload() ) ) + { + ConsolePrintf( PRIO_ERROR, RED"Failed to parse connection list"RESETCOLOR"\n" ); + delete container; + return; + } + container->PrintTable( false, false ); + if( 0 == container->GetAllInfoAmount() ) + { + ConsolePrintf( PRIO_MEDIUM, "No data in connection list, ignoring..\n" ); + delete container; + return; + } + CMultiplexer *multiplexer; + for( uint32_t i = 0; i < container->GetAmountOfCDevs(); i++ ) + { + CConnectionInfo *info = container->GetConnectionInfoOfCdev( i ); + if( NULL == info ) + continue; + if( NULL != s_mainClass->GetMultiplexer( info->deviceName ) ) + { + ConsolePrintf( PRIO_LOW, "Multiplexer exists for device: '%s', ignoring\n", info->deviceName ); + continue; + } + ConsolePrintfStart( PRIO_MEDIUM, "CDev: %s, Type: %d, Direction: %s, MAC: %s, Buffer:%d", info->deviceName, + info->dataType, ( info->isTX ? "TX" : "RX" ), info->macAddr->ToString(), info->bufferSize ); + + bool ignored = false; + for (uint32_t i = 0; i < s_mainClass->ignoreCdevPatterns.Size(); i++) + { + char *pattern = s_mainClass->ignoreCdevPatterns[i]; + if ( NULL == pattern ) + continue; + if ( NULL != strstr(info->deviceName, pattern) ) + { + ignored = true; + break; + } + } + + if( ( !ignored && info->isTX ) && ( ( EP_Isochron == info->dataType ) || + ( EP_Synchron == info->dataType && info->reservedBandwidth > 4 ) ) ) + { + ConsolePrintfExit( ", creating Multiplexer.\n" ); + try + { + multiplexer = new CMultiplexer( info->deviceName, info->bufferSize ); + s_mainClass->allMultiplexer.PushBack( multiplexer ); + } + catch( int e ) + { + ConsolePrintf( PRIO_ERROR, RED"Failed to instance Multiplexer!"\ + " Err-Code:%d"RESETCOLOR"\n", e ); + } + } + else + { + ConsolePrintfExit( ", will be ignored.\n" ); + } + } + if( NULL != s_mainClass->infoContainer ) + delete s_mainClass->infoContainer; + s_mainClass->infoContainer = container; +} + +CVodHandler *CVodHandler::GetInstance() +{ + if( NULL == s_mainClass ) + s_mainClass = new CVodHandler(); + return s_mainClass; +} + +void CVodHandler::DestroyInstance() +{ + if( NULL != s_mainClass ) + { + delete s_mainClass; + s_mainClass = NULL; + } +} + +void CVodHandler::SetSearchPath( const char *pSearchPath ) +{ + if( NULL == pSearchPath ) + return; + strncpy( searchPath, pSearchPath, sizeof ( searchPath ) ); + + RegisterBroadcastFiles( SelectFileType_Radio ); + RegisterBroadcastFiles( SelectFileType_Navigation ); + RegisterBroadcastFiles( SelectFileType_Cluster ); +} + + + void CVodHandler::SetCdevIgnorePattern( const char *pIgnorePattern ) + { + if (NULL == pIgnorePattern) + return; + char *str = (char *)malloc(strlen(pIgnorePattern) + 1); + strcpy(str, pIgnorePattern); + ignoreCdevPatterns.PushBack(str); + } + +bool CVodHandler::StreamFile( const uint8_t *mac, SelectFileType_t fileType, const char *fileName ) +{ + if( NULL == mac || NULL == fileName || 0 == strlen( fileName ) ) + { + ConsolePrintf( PRIO_ERROR, + RED"StreamFile was called with invalid parameters."RESETCOLOR"\n" ); + return false; + } + + char fullPath[200]; + snprintf( fullPath, sizeof ( fullPath ), "%s/%s/%s", searchPath, GetSubPath( fileType ), fileName ); + if( !ExistsFile( fullPath ) ) + { + ConsolePrintf( PRIO_ERROR, RED"File was not found: '%s'"RESETCOLOR"\n", fullPath ); + return false; + } + CMultiplexer *pMultiplexer = GetMultiplexer( mac ); + if( NULL == pMultiplexer ) + { + ConsolePrintf( PRIO_ERROR, RED"Could not get Multiplexer for MAC=%02X-%02X-%02X-%02X-%02X-%02X"RESETCOLOR"\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] ); + return false; + } + CSource *pSource = NULL; + for( uint32_t i = 0; i < broadcastFiles.Size(); i++ ) + { + BroadcastEntry_t *entry = broadcastFiles[i]; + if( NULL != entry && ( 0 == strcmp( fullPath, entry->fileName ) ) ) + pSource = entry->sourceFile; + } + if( NULL == pSource ) + { + char *ext = rindex(fullPath, '.'); + if (ext && !strcasecmp(ext, ".ts")) + pSource = new CSourceFile( fullPath, true ); + else + pSource = new CSourceFileConverted( fullPath, true ); + } + + + CMacAddr macAdress; + macAdress.CopyValuesFromByteArray( mac ); + pMultiplexer->PlayStream( &macAdress, pSource ); + return true; +} + +bool CVodHandler::SetPause( const uint8_t *mac, bool isPaused ) +{ + CMultiplexer *pMultiplexer = GetMultiplexer( mac ); + if( NULL == pMultiplexer ) + return false; + CMacAddr macAdress; + macAdress.CopyValuesFromByteArray( mac ); + pMultiplexer->SetPause( &macAdress, isPaused ); + return true; +} + +bool CVodHandler::SetRepetition( const uint8_t *mac, bool isRepeated ) +{ + CMultiplexer *pMultiplexer = GetMultiplexer( mac ); + if( NULL == pMultiplexer ) + return false; + CMacAddr macAdress; + macAdress.CopyValuesFromByteArray( mac ); + pMultiplexer->SetRepetition( &macAdress, isRepeated ); + return true; +} + +bool CVodHandler::SetTimePos( const uint8_t *mac, uint16_t timePosPerMile ) +{ + CMultiplexer *pMultiplexer = GetMultiplexer( mac ); + if( NULL == pMultiplexer ) + return false; + CMacAddr macAddress; + macAddress.CopyValuesFromByteArray( mac ); + pMultiplexer->SetPos( &macAddress, timePosPerMile ); + return true; +} + +bool CVodHandler::StopStream( const uint8_t *mac ) +{ + CMultiplexer *pMultiplexer = GetMultiplexer( mac ); + if( NULL == pMultiplexer ) + return false; + CMacAddr macAddress; + macAddress.CopyValuesFromByteArray( mac ); + pMultiplexer->StopStream( &macAddress ); + return true; +} + +void CVodHandler::ToggleStatisticsPrint() +{ + statisticIsEnabled = !statisticIsEnabled; + if (statisticIsEnabled) + Start(); + else + Stop(); + valuesCleared = false; + ConsolePrintf( PRIO_HIGH, "Cyclic printing of statistic informations: %d\n", statisticIsEnabled ); +} + +void CVodHandler::NM_SendServerVersionGetRequest( const char *ip ) +{ + CMsgAddr *msgAddr = new CMsgAddr( ip, IPC_PORT_NUMBER_NM, IpcUdp_V2_0 ); + CMostMsgTx *mostMsg = new CMostMsgTx( msgAddr, + FBLOCK_NETWORK_MANAGER, + 0, + FUNC_NM_SERVERVERSION, + CMostMsg::OP_GET, + 0, + NULL, + 500, + NULL, + NULL ); + s_mainClass->mostIpcNm->SendMsg( mostMsg ); + mostMsg->RemoveReference(); +} + +void CVodHandler::ConnectToNetworkManager() +{ + do + { + NM_SendServerVersionGetRequest( "10.0.0.255" ); + NM_SendServerVersionGetRequest( "127.0.0.1" ); + sleep( 1 ); + if( !networkManagerFound ) + ConsolePrintf( PRIO_ERROR, YELLOW"No Network Manager found, retrying.."RESETCOLOR"\n" ); + } + while( !networkManagerFound ); +} + +void CVodHandler::GetConnectionListFromNetworkManager() +{ + if( !networkManagerFound ) + { + ConsolePrintf( PRIO_ERROR, + RED"GetConnectionListFromNetworkManager called without server found, aborting.."RESETCOLOR + "\n" ); + return; + } + CMsgAddr *msgAddr = new CMsgAddr( networkManagerIP, IPC_PORT_NUMBER_NM, IpcTcp_V2_0 ); + CMostMsgTx *mostMsg = new CMostMsgTx( msgAddr, + FBLOCK_NETWORK_MANAGER, + 0, + FUNC_NM_CONNECTION_LIST, + CMostMsg::OP_GET, + 0, + NULL, + 500, + NULL, + NULL ); + s_mainClass->mostIpcNm->SendMsg( mostMsg ); + mostMsg->RemoveReference(); +} + +bool CVodHandler::CreateMacIndependentStream(uint8_t cdevIndex, uint8_t streamIndex, uint8_t *outMac) +{ + volatile CAutoLock autoLock( &infoContainerMutex ); + if (NULL == outMac) + { + ConsolePrintf( PRIO_ERROR, RED"CreateMacIndependentStream NULL was passed to parameters"RESETCOLOR"\n" ); + return false; + } + if( !networkManagerFound ) + { + ConsolePrintf( PRIO_ERROR, RED"CreateMacIndependentStream called without"\ + " server found, aborting.."RESETCOLOR"\n" ); + return false; + } + if( NULL == infoContainer ) + { + ConsolePrintf( PRIO_ERROR, RED"CreateMacIndependentStream called without any"\ + " info stored, aborting.."RESETCOLOR"\n" ); + return false; + } + CMultiplexer *multiplexer = allMultiplexer[cdevIndex]; + if (NULL == multiplexer) + { + ConsolePrintf( PRIO_ERROR, RED"CreateMacIndependentStream with invalid CDEV index:%d"\ + RESETCOLOR"\n", cdevIndex ); + return false; + } + for( uint32_t i = 0; NULL != infoContainer && i < infoContainer->GetAllInfoAmount(); i++ ) + { + CConnectionInfo *info = infoContainer->GetInfo( i ); + if (NULL == info || NULL == info->deviceName || 0 != strcmp(info->deviceName, multiplexer->GetDriverName())) + continue; + + outMac[0] = 0x2A; + outMac[1] = 0x11; + outMac[2] = 0x11; + outMac[3] = 0x11; + outMac[4] = (uint8_t)(i & 0xFF); + outMac[5] = streamIndex; + CMacAddr mac; + mac.CopyValuesFromByteArray(outMac); + CConnectionInfo *fake = infoContainer->GetInfo( &mac, streamIndex, info->mostInstance, false ); + if( NULL == fake ) + { + ConsolePrintf( PRIO_ERROR, RED"CreateMacIndependentStream failed to create"\ + " new Info object.."RESETCOLOR"\n" ); + return false; + } + fake->mostInstance = info->mostInstance; + fake->dataType = info->dataType; + fake->reservedBandwidth = info->reservedBandwidth; + fake->mostConnectionLabel = info->mostConnectionLabel; + fake->deviceType = 0xAFE; + fake->inSocketCreated = true; + fake->outSocketCreated = true; + fake->socketsConnected = true; + return true; + } + ConsolePrintf( PRIO_ERROR, RED"CreateMacIndependentStream failed to get"\ + " valid Multiplexer for CDEV index: %d"RESETCOLOR"\n", cdevIndex ); + return false; +} + +void CVodHandler::CreateSampleStreams( uint8_t amount ) +{ + volatile CAutoLock autoLock( &infoContainerMutex ); + if( !networkManagerFound ) + { + ConsolePrintf( PRIO_ERROR, RED"CreateSampleStreams called without"\ + " server found, aborting.."RESETCOLOR"\n" ); + return; + } + if( NULL == infoContainer ) + { + ConsolePrintf( PRIO_ERROR, RED"CreateSampleStreams called without any"\ + " info stored, aborting.."RESETCOLOR"\n" ); + return; + } + uint32_t cdevs = infoContainer->GetAmountOfCDevs(); + uint32_t cdevPos = 0; + static uint32_t pidNr = 0x40; + static int32_t lastFileCnt = -1; + for( uint8_t i = 0; i < amount; i++ ) + { + char fullPath[200]; + snprintf( fullPath, sizeof ( fullPath ), "%s/video/test-%d.ts", searchPath, ++lastFileCnt ); + + bool ignored; + CConnectionInfo *info = NULL; + do + { + ignored = false; + if( ( cdevPos % cdevs ) == 0 ) + ++pidNr; + info = infoContainer->GetConnectionInfoOfCdev( cdevPos ); + if( NULL == info ) + { + ConsolePrintf( PRIO_ERROR, RED"CreateSampleStreams failed to get"\ + " info.."RESETCOLOR"\n" ); + return; + } + + for (uint32_t i = 0; i < s_mainClass->ignoreCdevPatterns.Size(); i++) + { + char *pattern = s_mainClass->ignoreCdevPatterns[i]; + if ( NULL == pattern ) + continue; + if ( NULL != strstr( info->deviceName, pattern ) ) + { + if( ++cdevPos >= cdevs ) + cdevPos = 0; + ignored = true; + break; + } + } + } + while(ignored); + CMultiplexer *mp = GetMultiplexer(info->deviceName); + if( NULL == mp ) + { + ConsolePrintf( PRIO_ERROR, RED"CreateSampleStreams failed to get"\ + " valid Multiplexer for device name: %s"RESETCOLOR"\n", info->deviceName ); + return; + } + + //Create fake sink entry + + CMacAddr *macAddr = new CMacAddr( 0xAF,0xFE,0,0,cdevPos, pidNr ); + ConsolePrintf( PRIO_HIGH, "Will add file '%s' to Multiplexer with MAC: %s to CDEV: %s\n", + fullPath, macAddr->ToString(), info->deviceName ); + CConnectionInfo *fake = infoContainer->GetInfo( macAddr, 47, info->mostInstance, false ); + if( NULL == fake ) + { + ConsolePrintf( PRIO_ERROR, RED"CreateSampleStreams failed to create"\ + " new Info object.."RESETCOLOR"\n" ); + return; + } + fake->mostInstance = info->mostInstance; + fake->dataType = info->dataType; + fake->reservedBandwidth = info->reservedBandwidth; + fake->mostConnectionLabel = info->mostConnectionLabel; + fake->deviceType = 0xAFE; + fake->inSocketCreated = true; + fake->outSocketCreated = true; + fake->socketsConnected = true; + + char *ext = rindex(fullPath, '.'); + if (ext && !strcasecmp(ext, ".ts")) + mp->PlayStream( fake->macAddr, new CSourceFile( fullPath, true ) ); + else + mp->PlayStream( fake->macAddr, new CSourceFileConverted( fullPath, true ) ); + mp->SetRepetition( fake->macAddr, true ); + + if( ++cdevPos >= cdevs ) + cdevPos = 0; + } + ConsolePrintf( PRIO_HIGH, GREEN"CreateSampleStreams created %d sample streams.."\ + RESETCOLOR"\n", amount ); + infoContainer->PrintTable( false, false ); +} + +void CVodHandler::Run() +{ +#define THREAD_SLEEP_TIME_IN_SEC 1 + if( statisticIsEnabled ) + { + if( !valuesCleared ) + { + valuesCleared = true; + } + uint64_t allBytesWritten = 0; + + ConsolePrintfStart( PRIO_HIGH, "==============Statistic Start==============\n" ); + pthread_mutex_lock( &infoContainerMutex ); + for( uint32_t i = 0; NULL != infoContainer && i < infoContainer->GetAllInfoAmount(); i++ ) + { + bool success = false; + CMultiplexer::MultiplexStats_t stats; + CConnectionInfo *info = infoContainer->GetInfo( i ); + if( NULL == info || NULL == info->macAddr || ( ( EP_Isochron != info->dataType ) && + ( EP_Synchron == info->dataType && info->reservedBandwidth <= 4 ) ) ) + continue; + CConnectionInfo *remoteInfo = + infoContainer->GetRemoteConnectionInfoByMacAddress( info->macAddr->GetBytes() ); + if( NULL == remoteInfo || remoteInfo == info ) + continue; + CMultiplexer *multiplexer = GetMultiplexer( remoteInfo->deviceName ); + if( NULL == multiplexer ) + continue; + success = multiplexer->GetStatistics( info->macAddr, &stats ); + if( success ) + { + allBytesWritten += stats.bytesSent + stats.bytesStuffed; + ConsolePrintfContinue( "Device:%s, Stream %s, sent:%lld (kBit/s), stuffed:%lld (kBit/s),"\ + " read:%lld (kBit/s), PCR-error:%lld, Buf Ovfl-error:%lld, "\ + "Buf Unfl-error:%lld, TS-error:%lld\n", multiplexer->GetDriverName(), + info->macAddr->ToString(), ( 8 * stats.bytesSent / 1000 / THREAD_SLEEP_TIME_IN_SEC ), ( 8 * + stats.bytesStuffed / 1000 / THREAD_SLEEP_TIME_IN_SEC ), ( 8 * stats.bytesRead / 1000 / + THREAD_SLEEP_TIME_IN_SEC ), stats.errPcr, stats.errBufOverflow, stats.errBufUnderflow, + stats.errTs ); + } + } + for( uint32_t i = 0; NULL != infoContainer && i < infoContainer->GetAmountOfCDevs(); i++ ) + { + bool success = false; + CMultiplexer::MultiplexStats_t stats; + CConnectionInfo *info = infoContainer->GetConnectionInfoOfCdev( i ); + if( NULL == info ) + continue; + + CMultiplexer *multiplexer = GetMultiplexer( info->deviceName ); + if( NULL == multiplexer ) + continue; + success = multiplexer->GetStatistics( info->macAddr, &stats ); + if( success ) + { + allBytesWritten += stats.bytesSent + stats.bytesStuffed; + ConsolePrintfContinue( "Device:%s, Stream %s, sent:%lld (kBit/s), stuffed:%lld (kBit/s),"\ + " read:%lld (kBit/s), PCR-error:%lld, Buf Ovfl-error:%lld, "\ + "Buf Unfl-error:%lld, TS-error:%lld\n", multiplexer->GetDriverName(), + info->macAddr->ToString(), ( 8 * stats.bytesSent / 1000 / THREAD_SLEEP_TIME_IN_SEC ), ( 8 * + stats.bytesStuffed / 1000 / THREAD_SLEEP_TIME_IN_SEC ), ( 8 * stats.bytesRead / 1000 / + THREAD_SLEEP_TIME_IN_SEC ), stats.errPcr, stats.errBufOverflow, stats.errBufUnderflow, + stats.errTs ); + } + } + pthread_mutex_unlock( &infoContainerMutex ); + ConsolePrintfContinue( "Overall sent performance of all streams: %lld (kBit/s)\n", ( 8 * allBytesWritten / + 1000 / THREAD_SLEEP_TIME_IN_SEC ) ); + ConsolePrintfExit( "===============Statistic End===============\n" ); + } + usleep( THREAD_SLEEP_TIME_IN_SEC * 1000000 ); +} diff --git a/Src/VodHandler.h b/Src/VodHandler.h new file mode 100644 index 0000000..030c08e --- /dev/null +++ b/Src/VodHandler.h @@ -0,0 +1,193 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CVodHandler class. + */ +/*----------------------------------------------------------*/ +#ifndef VODHANDLER_H +#define VODHANDLER_H + +#include "Multiplexer.h" +#include "Source.h" +#include "Thread.h" +#include "ConnectionInfo.h" +#include "MostIpc.h" +#include "MacAddr.h" +#include "MsgAddr.h" +#include "MostMsg.h" + +typedef enum +{ + SelectFileType_Video = 0, + SelectFileType_Audio = 1, + SelectFileType_Radio = 2, + SelectFileType_Navigation = 3, + SelectFileType_Cluster = 4 +} SelectFileType_t; + +/*----------------------------------------------------------*/ +/*! \brief Structure holding the file name and the pointer to the CSourceFile object. + */ +/*----------------------------------------------------------*/ +typedef struct +{ + char fileName[128]; + CSource *sourceFile; +} BroadcastEntry_t; + +/*! + * \brief Applications main entry point and event handler, as it implements callbacks from CNetworkListener. + * It controls the Network via the CNetwork class. + * Isochronous TX connections will be automatically connected to the CMultiplexer class. + */ +class CVodHandler : public CThread +{ +private: + CSafeVector<BroadcastEntry_t *> broadcastFiles; + CSafeVector<CMultiplexer *> allMultiplexer; + CSafeVector<char *> ignoreCdevPatterns; + bool networkManagerFound; + char networkManagerIP[100]; + bool valuesCleared; + bool statisticIsEnabled; + char searchPath[64]; + CConnectionInfoContainer *infoContainer; + pthread_mutex_t infoContainerMutex; + + CMostIpc *mostIpcVod; + CMsgFilter *vodSelectFile_Set; + CMsgFilter *vodPlayMode_Set; + CMsgFilter *vodTimePostion_Set; + CMsgFilter *vodRepetition_Set; + CMsgFilter *vodServerVersion_Get; + CMsgFilter *vodMediaPid_Get; + CMsgFilter *vodFileList_Get; + CMsgFilter *vodIndependStream_SetGet; + CMsgFilter *vodFakeMode_Set; + + CMostIpc *mostIpcNm; + CMsgFilter *nmServerVersion_Status; + CMsgFilter *nmConnectionList_Status; + + CVodHandler(); + virtual ~CVodHandler(); + static const char *GetSubPath( SelectFileType_t fileType ); + static bool ExistsFile( const char *pFileName ); + CMultiplexer *GetMultiplexer( const char* deviceName ); + void RegisterBroadcastFiles( SelectFileType_t fileType ); + CMultiplexer *GetMultiplexer( const uint8_t *mac ); + void NM_SendServerVersionGetRequest( const char *ip ); + + static void OnVodSelectFile_Set( CMsgAddr *pAddr, CMostMsg *pMsg ); + static void OnVodPlayMode_Set( CMsgAddr *pAddr, CMostMsg *pMsg ); + static void OnVodTimePosition_Set( CMsgAddr *pAddr, CMostMsg *pMsg ); + static void OnVodRepetition_Set( CMsgAddr *pAddr, CMostMsg *pMsg ); + static void OnVodServerVersion_Get( CMsgAddr *pAddr, CMostMsg *pMsg ); + static void OnVodMediaPid_Get( CMsgAddr *pAddr, CMostMsg *pMsg ); + static void OnVodFileList_Get( CMsgAddr *pAddr, CMostMsg *pMsg ); + static void OnVodIndependentStream_SetGet( CMsgAddr *pAddr, CMostMsg *pMsg ); + static void OnFakeMode_Set( CMsgAddr *pAddr, CMostMsg *pMsg ); + + static void OnNmServerVersion_Status( CMsgAddr *pAddr, CMostMsg *pMsg ); + static void OnNmConnectionList_Status( CMsgAddr *pAddr, CMostMsg *pMsg ); +public: + + static CVodHandler *GetInstance(); + + static void DestroyInstance(); + + void ConnectToNetworkManager(); + void GetConnectionListFromNetworkManager(); + bool CreateMacIndependentStream(uint8_t cdevIndex, uint8_t streamIndex, uint8_t *outMac); + void CreateSampleStreams( uint8_t amount ); + + void SetSearchPath( const char *pSearchPath ); + void SetCdevIgnorePattern( const char *pIgnorePattern ); + + /*----------------------------------------------------------*/ + /*! \brief Streams a transport stream file via the specified multiplexer. + * \param mac - The MAC address of the sink. + * \param fileName - The full path to the transport stream file to play. + * \return true, if the stream is acceptable and the multiplexer exists. + * false, the stream can not be read or there is no multiplexer assigned with this connection. + * \note Make sure, the given sinkIndex points to a TX connection with a valid CMultiplexer object assigned. + */ + /*----------------------------------------------------------*/ + bool StreamFile( const uint8_t *mac, SelectFileType_t fileType, const char *fileName ); + + /*----------------------------------------------------------*/ + /*! \brief Set the pause state for a stream. + * \param mac - The MAC address of the sink. + * \param isPaused - If set to true, the video file will be paused. + * \return true, if the stream pause state could be toggled. false, otherwise. + * \note Make sure, the given sinkIndex points to a TX connection with a valid CMultiplexer object assigned. + * \note Make sure, that there is a file currently playing. Started with the StreamFile method. + */ + /*----------------------------------------------------------*/ + bool SetPause( const uint8_t *mac, bool isPaused ); + + /*----------------------------------------------------------*/ + /*! \brief Set the repetition state for a stream. + * \param mac - The MAC address of the sink. + * \param isRepeated - If set to true, the video file will be repeated. + * \return true, if the stream repetition state could be toggled. false, otherwise. + * \note Make sure, that there is a file currently playing. Started with the StreamFile method. + */ + /*----------------------------------------------------------*/ + bool SetRepetition( const uint8_t *mac, bool isRepeated ); + + /*----------------------------------------------------------*/ + /*! \brief Sets the time position of the given stream. + * \param mac - The MAC address of the sink. + * \return true, if the stream's time position could be adjusted to given value. false, otherwise. + * \note Make sure, that there is a file currently playing. Started with the StreamFile method. + */ + /*----------------------------------------------------------*/ + bool SetTimePos( const uint8_t *mac, uint16_t timePosPerMile ); + + /*----------------------------------------------------------*/ + /*! \brief Stops the playback of the given stream. + * \param mac - The MAC address of the sink. + * \return true, if the stream was stopped. false, otherwise. + * \note Make sure, that there is a file currently playing. Started with the StreamFile method. + */ + /*----------------------------------------------------------*/ + bool StopStream( const uint8_t *mac ); + + /*----------------------------------------------------------*/ + /*! \brief Toggle on / off the printing of Multiplexer Statistics + */ + /*----------------------------------------------------------*/ + void ToggleStatisticsPrint(); + + /*----------------------------------------------------------*/ + /*! \brief Thread method performing the cyclic Multiplexer Statistic prints. + * \warning Never call this method directly. Use ToggleStatisticsPrint instead! + */ + /*----------------------------------------------------------*/ + void Run(); +}; + +#endif //VODHANDLER_H |