summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Gromm <christian.gromm@microchip.com>2016-12-08 16:46:07 +0100
committerChristian Gromm <christian.gromm@microchip.com>2016-12-08 16:46:07 +0100
commit0a28683a1ff9f3975c8b68b15017cf342bab5f3d (patch)
tree2f350b709db4d45532fc0624cfce22ec11e43ccf
parent18c9e9fc0000861257ab06f6a033612b0c567674 (diff)
src: vod-server: import sources
This patch adds the source tree of the Video-On-Demand server and its configuration scripts. Change-Id: I6db8c43d46c7450baf117a541bd7153496dbcd4c Signed-off-by: Christian Gromm <christian.gromm@microchip.com>
-rw-r--r--.dep.inc5
-rw-r--r--Makefile128
-rw-r--r--Src/AutoLock.h57
-rw-r--r--Src/ConnectionInfo.cpp712
-rw-r--r--Src/ConnectionInfo.h316
-rw-r--r--Src/Console.c209
-rw-r--r--Src/Console.h113
-rw-r--r--Src/DoxyGenStartPage.h109
-rw-r--r--Src/IP/MostIpc.cpp510
-rw-r--r--Src/IP/MostIpc.h164
-rw-r--r--Src/IP/MostMsg.cpp405
-rw-r--r--Src/IP/MostMsg.h184
-rw-r--r--Src/IP/MostMsgTx.cpp86
-rw-r--r--Src/IP/MostMsgTx.h96
-rw-r--r--Src/IP/MsgAddr.cpp71
-rw-r--r--Src/IP/MsgAddr.h73
-rw-r--r--Src/IP/MsgFilter.cpp47
-rw-r--r--Src/IP/MsgFilter.h72
-rw-r--r--Src/MacAddr.cpp165
-rw-r--r--Src/MacAddr.h153
-rw-r--r--Src/Main.cpp160
-rw-r--r--Src/Multiplexer/Multiplexer.h476
-rw-r--r--Src/Multiplexer/RingBuffer.h187
-rw-r--r--Src/Multiplexer/Source.h408
-rw-r--r--Src/Multiplexer/SourceFile.cpp176
-rw-r--r--Src/Multiplexer/SourceFile.h77
-rw-r--r--Src/Multiplexer/SourceFileConverted.cpp188
-rw-r--r--Src/Multiplexer/SourceFileConverted.h79
-rw-r--r--Src/Multiplexer/Stream.cpp236
-rw-r--r--Src/Multiplexer/Stream.h250
-rw-r--r--Src/Multiplexer/StreamList.cpp246
-rw-r--r--Src/Multiplexer/StreamList.h179
-rw-r--r--Src/Multiplexer/ThreadReadHdd.cpp23
-rw-r--r--Src/Multiplexer/ThreadReadHdd.h171
-rw-r--r--Src/Multiplexer/ThreadWriteNetwork.cpp134
-rw-r--r--Src/Multiplexer/ThreadWriteNetwork.h97
-rw-r--r--Src/Multiplexer/TsPacket.h661
-rw-r--r--Src/Multiplexer/udp-stream.c127
-rw-r--r--Src/Multiplexer/udp-stream.h71
-rw-r--r--Src/SafeVector.h192
-rw-r--r--Src/Thread.cpp129
-rw-r--r--Src/Thread.h88
-rw-r--r--Src/Types.h230
-rw-r--r--Src/VodHandler.cpp1128
-rw-r--r--Src/VodHandler.h193
-rw-r--r--buildX86/Makefile-Release.mk189
-rw-r--r--buildX86/Makefile-impl.mk133
-rw-r--r--buildX86/Makefile-variables.mk27
-rw-r--r--buildX86/Package-Release.bash76
-rw-r--r--buildX86/project.xml27
-rw-r--r--config-d-amp.xml313
-rw-r--r--config-d.xml3341
-rw-r--r--i2c-slim-amplifier-v2.3.script92
-rw-r--r--i2c-uda1388-v2.3.script29
-rw-r--r--i2c-uda1388.script52
-rw-r--r--nbproject/private/Makefile-variables.mk0
-rwxr-xr-xstartDemo-d.sh23
57 files changed, 13883 insertions, 0 deletions
diff --git a/.dep.inc b/.dep.inc
new file mode 100644
index 0000000..4560e55
--- /dev/null
+++ b/.dep.inc
@@ -0,0 +1,5 @@
+# This code depends on make tool being used
+DEPFILES=$(wildcard $(addsuffix .d, ${OBJECTFILES}))
+ifneq (${DEPFILES},)
+include ${DEPFILES}
+endif
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..d92f5af
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,128 @@
+#
+# There exist several targets which are by default empty and which can be
+# used for execution of your targets. These targets are usually executed
+# before and after some main targets. They are:
+#
+# .build-pre: called before 'build' target
+# .build-post: called after 'build' target
+# .clean-pre: called before 'clean' target
+# .clean-post: called after 'clean' target
+# .clobber-pre: called before 'clobber' target
+# .clobber-post: called after 'clobber' target
+# .all-pre: called before 'all' target
+# .all-post: called after 'all' target
+# .help-pre: called before 'help' target
+# .help-post: called after 'help' target
+#
+# Targets beginning with '.' are not intended to be called on their own.
+#
+# Main targets can be executed directly, and they are:
+#
+# build build a specific configuration
+# clean remove built files from a configuration
+# clobber remove all built files
+# all build all configurations
+# help print help mesage
+#
+# Targets .build-impl, .clean-impl, .clobber-impl, .all-impl, and
+# .help-impl are implemented in nbproject/makefile-impl.mk.
+#
+# Available make variables:
+#
+# CND_BASEDIR base directory for relative paths
+# CND_DISTDIR default top distribution directory (build artifacts)
+# CND_BUILDDIR default top build directory (object files, ...)
+# CONF name of current configuration
+# CND_PLATFORM_${CONF} platform name (current configuration)
+# CND_ARTIFACT_DIR_${CONF} directory of build artifact (current configuration)
+# CND_ARTIFACT_NAME_${CONF} name of build artifact (current configuration)
+# CND_ARTIFACT_PATH_${CONF} path to build artifact (current configuration)
+# CND_PACKAGE_DIR_${CONF} directory of package (current configuration)
+# CND_PACKAGE_NAME_${CONF} name of package (current configuration)
+# CND_PACKAGE_PATH_${CONF} path to package (current configuration)
+#
+# NOCDDL
+
+
+# Environment
+MKDIR=mkdir
+CP=cp
+CCADMIN=CCadmin
+
+
+# build
+build: .build-post
+
+.build-pre:
+# Add your pre 'build' code here...
+
+.build-post: .build-impl
+# Add your post 'build' code here...
+
+
+# clean
+clean: .clean-post
+
+.clean-pre:
+# Add your pre 'clean' code here...
+
+.clean-post: .clean-impl
+# Add your post 'clean' code here...
+
+
+# clobber
+clobber: .clobber-post
+
+.clobber-pre:
+# Add your pre 'clobber' code here...
+
+.clobber-post: .clobber-impl
+# Add your post 'clobber' code here...
+
+
+# all
+all: .all-post
+
+.all-pre:
+# Add your pre 'all' code here...
+
+.all-post: .all-impl
+# Add your post 'all' code here...
+
+
+# build tests
+build-tests: .build-tests-post
+
+.build-tests-pre:
+# Add your pre 'build-tests' code here...
+
+.build-tests-post: .build-tests-impl
+# Add your post 'build-tests' code here...
+
+
+# run tests
+test: .test-post
+
+.test-pre: build-tests
+# Add your pre 'test' code here...
+
+.test-post: .test-impl
+# Add your post 'test' code here...
+
+
+# help
+help: .help-post
+
+.help-pre:
+# Add your pre 'help' code here...
+
+.help-post: .help-impl
+# Add your post 'help' code here...
+
+
+
+# include project implementation makefile
+include buildX86/Makefile-impl.mk
+
+# include project make variables
+include buildX86/Makefile-variables.mk
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, &params);
+ 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, &params);
+ 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, &param);
+ pthread_attr_setschedpolicy(&tattr, SCHED_RR);
+ param.sched_priority = sched_get_priority_max(SCHED_RR);
+ pthread_attr_setschedparam(&tattr, &param);
+ 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, &params);
+ 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, &params);
+ 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, &params );
+ 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, &params );
+ 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, &param );
+ pthread_attr_setschedpolicy( &tattr, SCHED_RR );
+ if( m_maxPrio )
+ {
+ param.sched_priority = sched_get_priority_max( SCHED_RR );
+ pthread_attr_setschedparam( &tattr, &param );
+ // 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
diff --git a/buildX86/Makefile-Release.mk b/buildX86/Makefile-Release.mk
new file mode 100644
index 0000000..792bb9c
--- /dev/null
+++ b/buildX86/Makefile-Release.mk
@@ -0,0 +1,189 @@
+#
+# Generated Makefile - do not edit!
+#
+# Edit the Makefile in the project folder instead (../Makefile). Each target
+# has a -pre and a -post target defined where you can add customized code.
+#
+# This makefile implements configuration specific macros and targets.
+
+
+# Environment
+MKDIR=mkdir
+CP=cp
+GREP=grep
+NM=nm
+CCADMIN=CCadmin
+RANLIB=ranlib
+CC=${CROSS_COMPILE}gcc
+CCC=${CROSS_COMPILE}g++
+CXX=${CROSS_COMPILE}g++
+FC=${CROSS_COMPILE}gfortran
+AS=${CROSS_COMPILE}as
+
+# Macros
+CND_PLATFORM=GNU-Linux-x86
+CND_DLIB_EXT=so
+CND_CONF=Release
+CND_DISTDIR=dist
+CND_BUILDDIR=build
+
+# Include project Makefile
+include Makefile
+
+# Object Directory
+OBJECTDIR=${CND_BUILDDIR}/${CND_CONF}/${CND_PLATFORM}
+
+# Object Files
+OBJECTFILES= \
+${OBJECTDIR}/Src/Console.o \
+ ${OBJECTDIR}/Src/IP/MostIpc.o \
+ ${OBJECTDIR}/Src/IP/MostMsg.o \
+ ${OBJECTDIR}/Src/IP/MostMsgTx.o \
+ ${OBJECTDIR}/Src/IP/MsgAddr.o \
+ ${OBJECTDIR}/Src/IP/MsgFilter.o \
+ ${OBJECTDIR}/Src/MacAddr.o \
+ ${OBJECTDIR}/Src/Main.o \
+ ${OBJECTDIR}/Src/ConnectionInfo.o \
+ ${OBJECTDIR}/Src/VodHandler.o \
+ ${OBJECTDIR}/Src/Multiplexer/Stream.o \
+ ${OBJECTDIR}/Src/Multiplexer/StreamList.o \
+ ${OBJECTDIR}/Src/Multiplexer/ThreadReadHdd.o \
+ ${OBJECTDIR}/Src/Multiplexer/ThreadWriteNetwork.o \
+ ${OBJECTDIR}/Src/Multiplexer/SourceFile.o \
+ ${OBJECTDIR}/Src/Multiplexer/SourceFileConverted.o \
+ ${OBJECTDIR}/Src/Multiplexer/udp-stream.o \
+ ${OBJECTDIR}/Src/Thread.o \
+
+# Include Path
+C_INCLUDE=-Imnsl -ISrc -ISrc/IP -ISrc/Multiplexer ${INCLUDE_PATH}
+
+# C Compiler Flags
+CFLAGS=-c -Wall -O3 -MMD -MP -DNDEBUG ${PROJECT_C_FLAGS}
+
+# CC Compiler Flags
+CCFLAGS=-Wall -O3 -MMD -MP -DNDEBUG ${PROJECT_C_FLAGS}
+CXXFLAGS=${CCFLAGS}
+
+# Fortran Compiler Flags
+FFLAGS=
+
+# Assembler Flags
+ASFLAGS=
+
+# Link Libraries and Options
+LDLIBSOPTIONS=-lpthread
+
+# Build Targets
+.build-conf: ${BUILD_SUBPROJECTS}
+ "${MAKE}" -f buildX86/Makefile-${CND_CONF}.mk VideoOnDemand
+
+VideoOnDemand: ${OBJECTFILES}
+ ${MKDIR} -p ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}
+ ${LINK.cc} -o VideoOnDemand ${OBJECTFILES} ${LDLIBSOPTIONS}
+
+${OBJECTDIR}/Src/Console.o: Src/Console.c
+ ${MKDIR} -p ${OBJECTDIR}/Src
+ ${RM} "$@.d"
+ ${CC} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/Console.o Src/Console.c
+
+${OBJECTDIR}/Src/IP/MostIpc.o: Src/IP/MostIpc.cpp
+ ${MKDIR} -p ${OBJECTDIR}/Src/IP
+ ${RM} "$@.d"
+ ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/IP/MostIpc.o Src/IP/MostIpc.cpp
+
+${OBJECTDIR}/Src/IP/MostMsg.o: Src/IP/MostMsg.cpp
+ ${MKDIR} -p ${OBJECTDIR}/Src/IP
+ ${RM} "$@.d"
+ ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/IP/MostMsg.o Src/IP/MostMsg.cpp
+
+${OBJECTDIR}/Src/IP/MostMsgTx.o: Src/IP/MostMsgTx.cpp
+ ${MKDIR} -p ${OBJECTDIR}/Src/IP
+ ${RM} "$@.d"
+ ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/IP/MostMsgTx.o Src/IP/MostMsgTx.cpp
+
+${OBJECTDIR}/Src/IP/MsgAddr.o: Src/IP/MsgAddr.cpp
+ ${MKDIR} -p ${OBJECTDIR}/Src/IP
+ ${RM} "$@.d"
+ ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/IP/MsgAddr.o Src/IP/MsgAddr.cpp
+
+${OBJECTDIR}/Src/IP/MsgFilter.o: Src/IP/MsgFilter.cpp
+ ${MKDIR} -p ${OBJECTDIR}/Src/IP
+ ${RM} "$@.d"
+ ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/IP/MsgFilter.o Src/IP/MsgFilter.cpp
+
+${OBJECTDIR}/Src/MacAddr.o: Src/MacAddr.cpp
+ ${MKDIR} -p ${OBJECTDIR}/Src
+ ${RM} "$@.d"
+ ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/MacAddr.o Src/MacAddr.cpp
+
+${OBJECTDIR}/Src/Main.o: Src/Main.cpp
+ ${MKDIR} -p ${OBJECTDIR}/Src
+ ${RM} "$@.d"
+ ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/Main.o Src/Main.cpp
+
+${OBJECTDIR}/Src/ConnectionInfo.o: Src/ConnectionInfo.cpp
+ ${MKDIR} -p ${OBJECTDIR}/Src
+ ${RM} "$@.d"
+ ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/ConnectionInfo.o Src/ConnectionInfo.cpp
+
+${OBJECTDIR}/Src/VodHandler.o: Src/VodHandler.cpp
+ ${MKDIR} -p ${OBJECTDIR}/Src
+ ${RM} "$@.d"
+ ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/VodHandler.o Src/VodHandler.cpp
+
+${OBJECTDIR}/Src/Multiplexer/Stream.o: Src/Multiplexer/Stream.cpp
+ ${MKDIR} -p ${OBJECTDIR}/Src/Multiplexer
+ ${RM} "$@.d"
+ ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/Multiplexer/Stream.o Src/Multiplexer/Stream.cpp
+
+${OBJECTDIR}/Src/Multiplexer/StreamList.o: Src/Multiplexer/StreamList.cpp
+ ${MKDIR} -p ${OBJECTDIR}/Src/Multiplexer
+ ${RM} "$@.d"
+ ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/Multiplexer/StreamList.o Src/Multiplexer/StreamList.cpp
+
+${OBJECTDIR}/Src/Multiplexer/ThreadReadHdd.o: Src/Multiplexer/ThreadReadHdd.cpp
+ ${MKDIR} -p ${OBJECTDIR}/Src/Multiplexer
+ ${RM} "$@.d"
+ ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/Multiplexer/ThreadReadHdd.o Src/Multiplexer/ThreadReadHdd.cpp
+
+${OBJECTDIR}/Src/Multiplexer/ThreadWriteNetwork.o: Src/Multiplexer/ThreadWriteNetwork.cpp
+ ${MKDIR} -p ${OBJECTDIR}/Src/Multiplexer
+ ${RM} "$@.d"
+ ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/Multiplexer/ThreadWriteNetwork.o Src/Multiplexer/ThreadWriteNetwork.cpp
+
+${OBJECTDIR}/Src/Multiplexer/SourceFile.o: Src/Multiplexer/SourceFile.cpp
+ ${MKDIR} -p ${OBJECTDIR}/Src/Multiplexer
+ ${RM} "$@.d"
+ ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/Multiplexer/SourceFile.o Src/Multiplexer/SourceFile.cpp
+
+${OBJECTDIR}/Src/Multiplexer/SourceFileConverted.o: Src/Multiplexer/SourceFileConverted.cpp
+ ${MKDIR} -p ${OBJECTDIR}/Src/Multiplexer
+ ${RM} "$@.d"
+ ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/Multiplexer/SourceFileConverted.o Src/Multiplexer/SourceFileConverted.cpp
+
+${OBJECTDIR}/Src/Multiplexer/udp-stream.o: Src/Multiplexer/udp-stream.c
+ ${MKDIR} -p ${OBJECTDIR}/Src/Multiplexer
+ ${RM} "$@.d"
+ ${CC} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/Multiplexer/udp-stream.o Src/Multiplexer/udp-stream.c
+
+${OBJECTDIR}/Src/Thread.o: Src/Thread.cpp
+ ${MKDIR} -p ${OBJECTDIR}/Src
+ ${RM} "$@.d"
+ ${CXX} ${CFLAGS} ${C_INCLUDE} -MF "$@.d" -o ${OBJECTDIR}/Src/Thread.o Src/Thread.cpp
+
+
+# Subprojects
+.build-subprojects:
+
+# Clean Targets
+.clean-conf: ${CLEAN_SUBPROJECTS}
+ ${RM} -r ${CND_BUILDDIR}/${CND_CONF}
+ ${RM} VideoOnDemand
+
+# Subprojects
+.clean-subprojects:
+
+# Enable dependency checking
+.dep.inc: .depcheck-impl
+
+include .dep.inc
diff --git a/buildX86/Makefile-impl.mk b/buildX86/Makefile-impl.mk
new file mode 100644
index 0000000..5f9d8f4
--- /dev/null
+++ b/buildX86/Makefile-impl.mk
@@ -0,0 +1,133 @@
+#
+# Generated Makefile - do not edit!
+#
+# Edit the Makefile in the project folder instead (../Makefile). Each target
+# has a pre- and a post- target defined where you can add customization code.
+#
+# This makefile implements macros and targets common to all configurations.
+#
+# NOCDDL
+
+
+# Building and Cleaning subprojects are done by default, but can be controlled with the SUB
+# macro. If SUB=no, subprojects will not be built or cleaned. The following macro
+# statements set BUILD_SUB-CONF and CLEAN_SUB-CONF to .build-reqprojects-conf
+# and .clean-reqprojects-conf unless SUB has the value 'no'
+SUB_no=NO
+SUBPROJECTS=${SUB_${SUB}}
+BUILD_SUBPROJECTS_=.build-subprojects
+BUILD_SUBPROJECTS_NO=
+BUILD_SUBPROJECTS=${BUILD_SUBPROJECTS_${SUBPROJECTS}}
+CLEAN_SUBPROJECTS_=.clean-subprojects
+CLEAN_SUBPROJECTS_NO=
+CLEAN_SUBPROJECTS=${CLEAN_SUBPROJECTS_${SUBPROJECTS}}
+
+
+# Project Name
+PROJECTNAME=VideoOnDemand
+
+# Active Configuration
+DEFAULTCONF=Release
+CONF=${DEFAULTCONF}
+
+# All Configurations
+ALLCONFS=Release
+
+
+# build
+.build-impl: .build-pre .validate-impl .depcheck-impl
+ @#echo "=> Running $@... Configuration=$(CONF)"
+ "${MAKE}" -f buildX86/Makefile-${CONF}.mk QMAKE=${QMAKE} SUBPROJECTS=${SUBPROJECTS} .build-conf
+
+
+# clean
+.clean-impl: .clean-pre .validate-impl .depcheck-impl
+ @#echo "=> Running $@... Configuration=$(CONF)"
+ "${MAKE}" -f buildX86/Makefile-${CONF}.mk QMAKE=${QMAKE} SUBPROJECTS=${SUBPROJECTS} .clean-conf
+
+
+# clobber
+.clobber-impl: .clobber-pre .depcheck-impl
+ @#echo "=> Running $@..."
+ for CONF in ${ALLCONFS}; \
+ do \
+ "${MAKE}" -f buildX86/Makefile-$${CONF}.mk QMAKE=${QMAKE} SUBPROJECTS=${SUBPROJECTS} .clean-conf; \
+ done
+
+# all
+.all-impl: .all-pre .depcheck-impl
+ @#echo "=> Running $@..."
+ for CONF in ${ALLCONFS}; \
+ do \
+ "${MAKE}" -f buildX86/Makefile-$${CONF}.mk QMAKE=${QMAKE} SUBPROJECTS=${SUBPROJECTS} .build-conf; \
+ done
+
+# build tests
+.build-tests-impl: .build-impl .build-tests-pre
+ @#echo "=> Running $@... Configuration=$(CONF)"
+ "${MAKE}" -f buildX86/Makefile-${CONF}.mk SUBPROJECTS=${SUBPROJECTS} .build-tests-conf
+
+# run tests
+.test-impl: .build-tests-impl .test-pre
+ @#echo "=> Running $@... Configuration=$(CONF)"
+ "${MAKE}" -f buildX86/Makefile-${CONF}.mk SUBPROJECTS=${SUBPROJECTS} .test-conf
+
+# dependency checking support
+.depcheck-impl:
+ @echo "# This code depends on make tool being used" >.dep.inc
+ @if [ -n "${MAKE_VERSION}" ]; then \
+ echo "DEPFILES=\$$(wildcard \$$(addsuffix .d, \$${OBJECTFILES}))" >>.dep.inc; \
+ echo "ifneq (\$${DEPFILES},)" >>.dep.inc; \
+ echo "include \$${DEPFILES}" >>.dep.inc; \
+ echo "endif" >>.dep.inc; \
+ else \
+ echo ".KEEP_STATE:" >>.dep.inc; \
+ echo ".KEEP_STATE_FILE:.make.state.\$${CONF}" >>.dep.inc; \
+ fi
+
+# configuration validation
+.validate-impl:
+ @if [ ! -f buildX86/Makefile-${CONF}.mk ]; \
+ then \
+ echo ""; \
+ echo "Error: can not find the makefile for configuration '${CONF}' in project ${PROJECTNAME}"; \
+ echo "See 'make help' for details."; \
+ echo "Current directory: " `pwd`; \
+ echo ""; \
+ fi
+ @if [ ! -f buildX86/Makefile-${CONF}.mk ]; \
+ then \
+ exit 1; \
+ fi
+
+
+# help
+.help-impl: .help-pre
+ @echo "This makefile supports the following configurations:"
+ @echo " ${ALLCONFS}"
+ @echo ""
+ @echo "and the following targets:"
+ @echo " build (default target)"
+ @echo " clean"
+ @echo " clobber"
+ @echo " all"
+ @echo " help"
+ @echo ""
+ @echo "Makefile Usage:"
+ @echo " make [CONF=<CONFIGURATION>] [SUB=no] build"
+ @echo " make [CONF=<CONFIGURATION>] [SUB=no] clean"
+ @echo " make [SUB=no] clobber"
+ @echo " make [SUB=no] all"
+ @echo " make help"
+ @echo ""
+ @echo "Target 'build' will build a specific configuration and, unless 'SUB=no',"
+ @echo " also build subprojects."
+ @echo "Target 'clean' will clean a specific configuration and, unless 'SUB=no',"
+ @echo " also clean subprojects."
+ @echo "Target 'clobber' will remove all built files from all configurations and,"
+ @echo " unless 'SUB=no', also from subprojects."
+ @echo "Target 'all' will will build all configurations and, unless 'SUB=no',"
+ @echo " also build subprojects."
+ @echo "Target 'help' prints this message."
+ @echo ""
+
diff --git a/buildX86/Makefile-variables.mk b/buildX86/Makefile-variables.mk
new file mode 100644
index 0000000..92eaac7
--- /dev/null
+++ b/buildX86/Makefile-variables.mk
@@ -0,0 +1,27 @@
+#
+# Generated - do not edit!
+#
+# NOCDDL
+#
+CND_BASEDIR=`pwd`
+CND_BUILDDIR=build
+CND_DISTDIR=dist
+# Release configuration
+CND_PLATFORM_Release=GNU-Linux-x86
+CND_ARTIFACT_DIR_Release=dist/Release/GNU-Linux-x86
+CND_ARTIFACT_NAME_Release=tmp
+CND_ARTIFACT_PATH_Release=dist/Release/GNU-Linux-x86/tmp
+CND_PACKAGE_DIR_Release=dist/Release/GNU-Linux-x86/package
+CND_PACKAGE_NAME_Release=tmp.tar
+CND_PACKAGE_PATH_Release=dist/Release/GNU-Linux-x86/package/tmp.tar
+#
+# include compiler specific variables
+#
+# dmake command
+ROOT:sh = test -f nbproject/private/Makefile-variables.mk || \
+ (mkdir -p nbproject/private && touch nbproject/private/Makefile-variables.mk)
+#
+# gmake command
+.PHONY: $(shell test -f nbproject/private/Makefile-variables.mk || (mkdir -p nbproject/private && touch nbproject/private/Makefile-variables.mk))
+#
+include nbproject/private/Makefile-variables.mk
diff --git a/buildX86/Package-Release.bash b/buildX86/Package-Release.bash
new file mode 100644
index 0000000..fb38dd2
--- /dev/null
+++ b/buildX86/Package-Release.bash
@@ -0,0 +1,76 @@
+#!/bin/bash -x
+
+#
+# Generated - do not edit!
+#
+
+# Macros
+TOP=`pwd`
+CND_PLATFORM=GNU-Linux-x86
+CND_CONF=Release
+CND_DISTDIR=dist
+CND_BUILDDIR=build
+CND_DLIB_EXT=so
+NBTMPDIR=${CND_BUILDDIR}/${CND_CONF}/${CND_PLATFORM}/tmp-packaging
+TMPDIRNAME=tmp-packaging
+OUTPUT_PATH=${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/tmp
+OUTPUT_BASENAME=tmp
+PACKAGE_TOP_DIR=tmp/
+
+# Functions
+function checkReturnCode
+{
+ rc=$?
+ if [ $rc != 0 ]
+ then
+ exit $rc
+ fi
+}
+function makeDirectory
+# $1 directory path
+# $2 permission (optional)
+{
+ mkdir -p "$1"
+ checkReturnCode
+ if [ "$2" != "" ]
+ then
+ chmod $2 "$1"
+ checkReturnCode
+ fi
+}
+function copyFileToTmpDir
+# $1 from-file path
+# $2 to-file path
+# $3 permission
+{
+ cp "$1" "$2"
+ checkReturnCode
+ if [ "$3" != "" ]
+ then
+ chmod $3 "$2"
+ checkReturnCode
+ fi
+}
+
+# Setup
+cd "${TOP}"
+mkdir -p ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package
+rm -rf ${NBTMPDIR}
+mkdir -p ${NBTMPDIR}
+
+# Copy files and create directories and links
+cd "${TOP}"
+makeDirectory "${NBTMPDIR}/tmp/bin"
+copyFileToTmpDir "${OUTPUT_PATH}" "${NBTMPDIR}/${PACKAGE_TOP_DIR}bin/${OUTPUT_BASENAME}" 0755
+
+
+# Generate tar file
+cd "${TOP}"
+rm -f ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package/tmp.tar
+cd ${NBTMPDIR}
+tar -vcf ../../../../${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package/tmp.tar *
+checkReturnCode
+
+# Cleanup
+cd "${TOP}"
+rm -rf ${NBTMPDIR}
diff --git a/buildX86/project.xml b/buildX86/project.xml
new file mode 100644
index 0000000..f4d9328
--- /dev/null
+++ b/buildX86/project.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://www.netbeans.org/ns/project/1">
+ <type>org.netbeans.modules.cnd.makeproject</type>
+ <configuration>
+ <data xmlns="http://www.netbeans.org/ns/make-project/1">
+ <name>app</name>
+ <c-extensions>c</c-extensions>
+ <cpp-extensions>cpp</cpp-extensions>
+ <header-extensions>h</header-extensions>
+ <sourceEncoding>UTF-8</sourceEncoding>
+ <make-dep-projects/>
+ <sourceRootList>
+ <sourceRootElem>mnsl</sourceRootElem>
+ <sourceRootElem>Src</sourceRootElem>
+ </sourceRootList>
+ <confList>
+ <confElem>
+ <name>Release</name>
+ <type>1</type>
+ </confElem>
+ </confList>
+ <formatting>
+ <project-formatting-style>false</project-formatting-style>
+ </formatting>
+ </data>
+ </configuration>
+</project>
diff --git a/config-d-amp.xml b/config-d-amp.xml
new file mode 100644
index 0000000..2103e8c
--- /dev/null
+++ b/config-d-amp.xml
@@ -0,0 +1,313 @@
+<?xml version="1.0"?>
+<root>
+ <network_manager>
+
+ <local_connection>
+ <device_api_ver>3</device_api_ver>
+ <port_id>USB</port_id>
+ <timing_master>1</timing_master>
+ <async_bandwidth>32</async_bandwidth>
+ <endpoint>
+ <data_type>CTRL</data_type>
+ <dir>IN</dir>
+ <channel_addr_hex>8f</channel_addr_hex>
+ </endpoint>
+ <endpoint>
+ <data_type>CTRL</data_type>
+ <dir>OUT</dir>
+ <channel_addr_hex>f</channel_addr_hex>
+ </endpoint>
+ </local_connection>
+
+ <!-- Telematics device with OS81118 D-Rev -->
+ <device>
+ <device_type_hex>311</device_type_hex>
+ <device_api_ver>3</device_api_ver>
+ <channel>
+ <channel_id>1</channel_id>
+ <socket>
+ <port_id>USB</port_id>
+ <data_type>ISOC</data_type>
+ <dir>IN</dir>
+ <channel_addr_hex>1</channel_addr_hex>
+ <subbuffer_size>188</subbuffer_size>
+ <number_of_buffers>32</number_of_buffers>
+ <buffer_size>7520</buffer_size>
+ <packets_per_xact>2</packets_per_xact>
+ </socket>
+ <socket>
+ <port_id>MOST</port_id>
+ <data_type>ISOC</data_type>
+ <dir>OUT</dir>
+ <blockwidth>80</blockwidth>
+ <subbuffer_size>188</subbuffer_size>
+ </socket>
+ </channel>
+ <channel>
+ <!-- 2nd Channel: Sync HMI Audio TX (splitted in two mono channels (1/2) -->
+ <channel_id>2</channel_id>
+ <socket>
+ <port_id>USB</port_id>
+ <data_type>SYNC</data_type>
+ <dir>IN</dir>
+ <channel_addr_hex>4</channel_addr_hex>
+ <number_of_buffers>16</number_of_buffers>
+ <buffer_size>512</buffer_size>
+ <packets_per_xact>128</packets_per_xact>
+ <subbuffer_size>4</subbuffer_size>
+ <offset>0</offset>
+ </socket>
+ <socket>
+ <port_id>MOST</port_id>
+ <data_type>SYNC</data_type>
+ <dir>OUT</dir>
+ <blockwidth>2</blockwidth>
+ </socket>
+ </channel>
+ <channel>
+ <!-- 3rd Channel: Sync HMI Audio TX (splitted in two mono channels (2/2) -->
+ <channel_id>3</channel_id>
+ <socket>
+ <port_id>USB</port_id>
+ <data_type>SYNC</data_type>
+ <dir>IN</dir>
+ <channel_addr_hex>4</channel_addr_hex>
+ <offset>2</offset>
+ </socket>
+ <socket>
+ <port_id>MOST</port_id>
+ <data_type>SYNC</data_type>
+ <dir>OUT</dir>
+ <blockwidth>2</blockwidth>
+ </socket>
+ </channel>
+ </device>
+
+ <!-- HMI with OS81118 USB -->
+ <device>
+ <device_type_hex>332</device_type_hex>
+ <device_api_ver>3</device_api_ver>
+ <channel>
+ <!-- 1st Channel: Iosc RX -->
+ <channel_id>1</channel_id>
+ <socket>
+ <port_id>MOST</port_id>
+ <data_type>ISOC</data_type>
+ <dir>IN</dir>
+ <blockwidth>80</blockwidth>
+ <subbuffer_size>188</subbuffer_size>
+ </socket>
+ <socket>
+ <port_id>USB</port_id>
+ <data_type>ISOC</data_type>
+ <dir>OUT</dir>
+ <buffer_size>7520</buffer_size>
+ <packets_per_xact>2</packets_per_xact>
+ <channel_addr_hex>83</channel_addr_hex>
+ <subbuffer_size>188</subbuffer_size>
+ </socket>
+ </channel>
+ <channel>
+ <!-- 2nd Channel: Sync HMI Audio TX (splitted in two mono channels (1/2) -->
+ <channel_id>2</channel_id>
+ <socket>
+ <port_id>USB</port_id>
+ <data_type>SYNC</data_type>
+ <dir>IN</dir>
+ <channel_addr_hex>4</channel_addr_hex>
+ <number_of_buffers>16</number_of_buffers>
+ <buffer_size>512</buffer_size>
+ <packets_per_xact>128</packets_per_xact>
+ <subbuffer_size>4</subbuffer_size>
+ <offset>0</offset>
+ </socket>
+ <socket>
+ <port_id>MOST</port_id>
+ <data_type>SYNC</data_type>
+ <dir>OUT</dir>
+ <blockwidth>2</blockwidth>
+ </socket>
+ </channel>
+ <channel>
+ <!-- 3rd Channel: Sync HMI Audio TX (splitted in two mono channels (2/2) -->
+ <channel_id>3</channel_id>
+ <socket>
+ <port_id>USB</port_id>
+ <data_type>SYNC</data_type>
+ <dir>IN</dir>
+ <channel_addr_hex>4</channel_addr_hex>
+ <offset>2</offset>
+ </socket>
+ <socket>
+ <port_id>MOST</port_id>
+ <data_type>SYNC</data_type>
+ <dir>OUT</dir>
+ <blockwidth>2</blockwidth>
+ </socket>
+ </channel>
+ </device>
+
+ <!-- Slim Amplifier with OS81118 D-Rev -->
+ <device>
+ <device_type_hex>370</device_type_hex>
+ <device_api_ver>3</device_api_ver>
+ <script>i2c-slim-amplifier-v2.3.script</script>
+ <channel>
+ <channel_id>1</channel_id>
+ <socket>
+ <port_id>MOST</port_id>
+ <data_type>SYNC</data_type>
+ <dir>IN</dir>
+ <blockwidth>2</blockwidth>
+ </socket>
+ <socket>
+ <port_id>I2S</port_id>
+ <data_type>SYNC</data_type>
+ <dir>OUT</dir>
+ <blockwidth>4</blockwidth>
+ <subbuffer_size>4</subbuffer_size>
+ <i2s_option>DualOut</i2s_option>
+ <i2s_clock_dir>Output</i2s_clock_dir>
+ <i2s_clock_delay>1</i2s_clock_delay>
+ <i2s_fs_speed>64</i2s_fs_speed>
+ <i2s_align>Left16</i2s_align>
+ <i2s_pin>SRXA0</i2s_pin>
+ <offset>0</offset>
+ </socket>
+ </channel>
+ <channel>
+ <channel_id>2</channel_id>
+ <socket>
+ <port_id>MOST</port_id>
+ <data_type>SYNC</data_type>
+ <dir>IN</dir>
+ <blockwidth>2</blockwidth>
+ </socket>
+ <socket>
+ <port_id>I2S</port_id>
+ <data_type>SYNC</data_type>
+ <dir>OUT</dir>
+ <blockwidth>4</blockwidth>
+ <subbuffer_size>4</subbuffer_size>
+ <i2s_option>DualOut</i2s_option>
+ <i2s_clock_dir>Output</i2s_clock_dir>
+ <i2s_clock_delay>1</i2s_clock_delay>
+ <i2s_fs_speed>64</i2s_fs_speed>
+ <i2s_align>Left16</i2s_align>
+ <i2s_pin>SRXA0</i2s_pin>
+ <offset>2</offset>
+ </socket>
+ </channel>
+ </device>
+
+ <!-- Audio demo board with OS81118 D-Rev -->
+ <device>
+ <device_type_hex>324</device_type_hex>
+ <device_api_ver>3</device_api_ver>
+ <script>i2c-uda1388-v2.3.script</script>
+ <channel>
+ <channel_id>1</channel_id>
+ <socket>
+ <port_id>MOST</port_id>
+ <data_type>SYNC</data_type>
+ <dir>IN</dir>
+ <blockwidth>2</blockwidth>
+ </socket>
+ <socket>
+ <port_id>I2S</port_id>
+ <data_type>SYNC</data_type>
+ <dir>OUT</dir>
+ <blockwidth>4</blockwidth>
+ <subbuffer_size>4</subbuffer_size>
+ <i2s_option>DualOut</i2s_option>
+ <i2s_clock_dir>Output</i2s_clock_dir>
+ <i2s_clock_delay>1</i2s_clock_delay>
+ <i2s_fs_speed>64</i2s_fs_speed>
+ <i2s_align>Left16</i2s_align>
+ <i2s_pin>SRXA1</i2s_pin>
+ <offset>0</offset>
+ </socket>
+ </channel>
+ <channel>
+ <channel_id>2</channel_id>
+ <socket>
+ <port_id>MOST</port_id>
+ <data_type>SYNC</data_type>
+ <dir>IN</dir>
+ <blockwidth>2</blockwidth>
+ </socket>
+ <socket>
+ <port_id>I2S</port_id>
+ <data_type>SYNC</data_type>
+ <dir>OUT</dir>
+ <blockwidth>4</blockwidth>
+ <subbuffer_size>4</subbuffer_size>
+ <i2s_option>DualOut</i2s_option>
+ <i2s_clock_dir>Output</i2s_clock_dir>
+ <i2s_clock_delay>1</i2s_clock_delay>
+ <i2s_fs_speed>64</i2s_fs_speed>
+ <i2s_align>Left16</i2s_align>
+ <i2s_pin>SRXA1</i2s_pin>
+ <offset>2</offset>
+ </socket>
+ </channel>
+ </device>
+
+ <!-- Routing Telematics to HMI -->
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>1</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+
+ <!-- Routing Audio of HMI 1 to amplifier -->
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>370</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>324</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+
+ <!-- Routing Audio of HMI 2 to amplifier -->
+ <route>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>370</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>324</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ </network_manager>
+</root>
diff --git a/config-d.xml b/config-d.xml
new file mode 100644
index 0000000..6ce58f1
--- /dev/null
+++ b/config-d.xml
@@ -0,0 +1,3341 @@
+<?xml version="1.0"?>
+<root>
+ <network_manager>
+
+ <local_connection>
+ <device_api_ver>3</device_api_ver>
+ <port_id>USB</port_id>
+ <timing_master>1</timing_master>
+ <async_bandwidth>32</async_bandwidth>
+ <endpoint>
+ <data_type>CTRL</data_type>
+ <dir>IN</dir>
+ <channel_addr_hex>8f</channel_addr_hex>
+ </endpoint>
+ <endpoint>
+ <data_type>CTRL</data_type>
+ <dir>OUT</dir>
+ <channel_addr_hex>f</channel_addr_hex>
+ </endpoint>
+ </local_connection>
+
+ <local_connection>
+ <device_api_ver>3</device_api_ver>
+ <port_id>MLB</port_id>
+ <timing_master>1</timing_master>
+ <async_bandwidth>32</async_bandwidth>
+ <endpoint>
+ <data_type>CTRL</data_type>
+ <dir>IN</dir>
+ <channel_addr_hex>2</channel_addr_hex>
+ </endpoint>
+ <endpoint>
+ <data_type>CTRL</data_type>
+ <dir>OUT</dir>
+ <channel_addr_hex>4</channel_addr_hex>
+ </endpoint>
+ </local_connection>
+
+ <!-- Server device (USB )-->
+ <device>
+ <device_type_hex>311</device_type_hex>
+ <device_api_ver>3</device_api_ver>
+ <channel>
+ <channel_id>1</channel_id>
+ <socket>
+ <port_id>USB</port_id>
+ <data_type>ISOC</data_type>
+ <dir>IN</dir>
+ <channel_addr_hex>1</channel_addr_hex>
+ <subbuffer_size>188</subbuffer_size>
+ <number_of_buffers>32</number_of_buffers>
+ <buffer_size>7520</buffer_size>
+ <packets_per_xact>2</packets_per_xact>
+ </socket>
+ <socket>
+ <port_id>MOST</port_id>
+ <data_type>ISOC</data_type>
+ <dir>OUT</dir>
+ <blockwidth>85</blockwidth>
+ <subbuffer_size>188</subbuffer_size>
+ </socket>
+ </channel>
+ <channel>
+ <channel_id>2</channel_id>
+ <socket>
+ <port_id>USB</port_id>
+ <data_type>ISOC</data_type>
+ <dir>IN</dir>
+ <channel_addr_hex>2</channel_addr_hex>
+ <subbuffer_size>188</subbuffer_size>
+ <number_of_buffers>32</number_of_buffers>
+ <buffer_size>7520</buffer_size>
+ <packets_per_xact>2</packets_per_xact>
+ </socket>
+ <socket>
+ <port_id>MOST</port_id>
+ <data_type>ISOC</data_type>
+ <dir>OUT</dir>
+ <blockwidth>85</blockwidth>
+ <subbuffer_size>188</subbuffer_size>
+ </socket>
+ </channel>
+ <channel>
+ <channel_id>3</channel_id>
+ <socket>
+ <port_id>USB</port_id>
+ <data_type>ISOC</data_type>
+ <dir>IN</dir>
+ <channel_addr_hex>3</channel_addr_hex>
+ <subbuffer_size>188</subbuffer_size>
+ <number_of_buffers>64</number_of_buffers>
+ <buffer_size>7520</buffer_size>
+ <packets_per_xact>2</packets_per_xact>
+ </socket>
+ <socket>
+ <port_id>MOST</port_id>
+ <data_type>ISOC</data_type>
+ <dir>OUT</dir>
+ <blockwidth>85</blockwidth>
+ <subbuffer_size>188</subbuffer_size>
+ </socket>
+ </channel>
+ <channel>
+ <channel_id>4</channel_id>
+ <socket>
+ <port_id>USB</port_id>
+ <data_type>ISOC</data_type>
+ <dir>IN</dir>
+ <channel_addr_hex>4</channel_addr_hex>
+ <subbuffer_size>188</subbuffer_size>
+ <number_of_buffers>64</number_of_buffers>
+ <buffer_size>7520</buffer_size>
+ <packets_per_xact>2</packets_per_xact>
+ </socket>
+ <socket>
+ <port_id>MOST</port_id>
+ <data_type>ISOC</data_type>
+ <dir>OUT</dir>
+ <blockwidth>85</blockwidth>
+ <subbuffer_size>188</subbuffer_size>
+ </socket>
+ </channel>
+ </device>
+
+ <!-- Server device (MLB) -->
+ <device>
+ <device_type_hex>313</device_type_hex>
+ <device_api_ver>3</device_api_ver>
+ <channel>
+ <channel_id>1</channel_id>
+ <socket>
+ <port_id>MLB</port_id>
+ <data_type>ISOC</data_type>
+ <dir>IN</dir>
+ <channel_addr_hex>a</channel_addr_hex>
+ <blockwidth>84</blockwidth>
+ <subbuffer_size>188</subbuffer_size>
+ <number_of_buffers>32</number_of_buffers>
+ <buffer_size>7520</buffer_size>
+ </socket>
+ <socket>
+ <port_id>MOST</port_id>
+ <data_type>ISOC</data_type>
+ <dir>OUT</dir>
+ <blockwidth>85</blockwidth>
+ <subbuffer_size>188</subbuffer_size>
+ </socket>
+ </channel>
+ <channel>
+ <channel_id>2</channel_id>
+ <socket>
+ <port_id>MLB</port_id>
+ <data_type>ISOC</data_type>
+ <dir>IN</dir>
+ <channel_addr_hex>c</channel_addr_hex>
+ <blockwidth>84</blockwidth>
+ <subbuffer_size>188</subbuffer_size>
+ <number_of_buffers>32</number_of_buffers>
+ <buffer_size>7520</buffer_size>
+ </socket>
+ <socket>
+ <port_id>MOST</port_id>
+ <data_type>ISOC</data_type>
+ <dir>OUT</dir>
+ <blockwidth>85</blockwidth>
+ <subbuffer_size>188</subbuffer_size>
+ </socket>
+ </channel>
+ <channel>
+ <channel_id>3</channel_id>
+ <socket>
+ <port_id>MLB</port_id>
+ <data_type>ISOC</data_type>
+ <dir>IN</dir>
+ <channel_addr_hex>e</channel_addr_hex>
+ <blockwidth>84</blockwidth>
+ <subbuffer_size>188</subbuffer_size>
+ <number_of_buffers>32</number_of_buffers>
+ <buffer_size>7520</buffer_size>
+ </socket>
+ <socket>
+ <port_id>MOST</port_id>
+ <data_type>ISOC</data_type>
+ <dir>OUT</dir>
+ <blockwidth>85</blockwidth>
+ <subbuffer_size>188</subbuffer_size>
+ </socket>
+ </channel>
+ <channel>
+ <channel_id>4</channel_id>
+ <socket>
+ <port_id>MLB</port_id>
+ <data_type>ISOC</data_type>
+ <dir>IN</dir>
+ <channel_addr_hex>10</channel_addr_hex>
+ <blockwidth>84</blockwidth>
+ <subbuffer_size>188</subbuffer_size>
+ <number_of_buffers>32</number_of_buffers>
+ <buffer_size>7520</buffer_size>
+ </socket>
+ <socket>
+ <port_id>MOST</port_id>
+ <data_type>ISOC</data_type>
+ <dir>OUT</dir>
+ <blockwidth>85</blockwidth>
+ <subbuffer_size>188</subbuffer_size>
+ </socket>
+ </channel>
+ </device>
+
+ <!-- Seat with OS81118 USB D-Rev -->
+ <device>
+ <device_type_hex>332</device_type_hex>
+ <device_api_ver>3</device_api_ver>
+ <channel>
+ <channel_id>1</channel_id>
+ <socket>
+ <port_id>MOST</port_id>
+ <data_type>ISOC</data_type>
+ <dir>IN</dir>
+ <blockwidth>85</blockwidth>
+ <subbuffer_size>188</subbuffer_size>
+ </socket>
+ <socket>
+ <port_id>USB</port_id>
+ <data_type>ISOC</data_type>
+ <dir>OUT</dir>
+ <buffer_size>7520</buffer_size>
+ <packets_per_xact>2</packets_per_xact>
+ <channel_addr_hex>83</channel_addr_hex>
+ <subbuffer_size>188</subbuffer_size>
+ </socket>
+ </channel>
+ <channel>
+ <channel_id>2</channel_id>
+ <socket>
+ <port_id>MOST</port_id>
+ <data_type>ISOC</data_type>
+ <dir>IN</dir>
+ <blockwidth>85</blockwidth>
+ <subbuffer_size>188</subbuffer_size>
+ </socket>
+ <socket>
+ <port_id>USB</port_id>
+ <data_type>ISOC</data_type>
+ <dir>OUT</dir>
+ <buffer_size>7520</buffer_size>
+ <packets_per_xact>2</packets_per_xact>
+ <channel_addr_hex>81</channel_addr_hex>
+ <subbuffer_size>188</subbuffer_size>
+ </socket>
+ </channel>
+ <channel>
+ <channel_id>3</channel_id>
+ <socket>
+ <port_id>MOST</port_id>
+ <data_type>ISOC</data_type>
+ <dir>IN</dir>
+ <blockwidth>85</blockwidth>
+ <subbuffer_size>188</subbuffer_size>
+ </socket>
+ <socket>
+ <port_id>USB</port_id>
+ <data_type>ISOC</data_type>
+ <dir>OUT</dir>
+ <buffer_size>7520</buffer_size>
+ <packets_per_xact>2</packets_per_xact>
+ <channel_addr_hex>82</channel_addr_hex>
+ <subbuffer_size>188</subbuffer_size>
+ </socket>
+ </channel>
+ </device>
+
+ <!-- VOD Routings -->
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>1</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>2</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>3</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>4</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>5</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>6</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>7</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>8</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>9</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>10</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>11</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>12</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>13</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>14</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>15</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>16</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>17</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>18</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>19</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>20</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>21</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>22</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>23</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>24</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>25</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>26</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>27</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>28</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>29</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>30</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>31</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>32</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>33</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>34</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>35</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>36</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>37</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>38</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>39</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>40</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>41</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>42</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>43</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>44</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>45</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>46</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>47</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>48</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>49</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>50</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>51</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>52</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>53</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>54</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>55</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>56</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>57</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>58</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>59</instance>
+ <channel_id>1</channel_id>
+ </terminal>
+ </route>
+
+ <!-- TV Station 1 Routings -->
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>0</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>1</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>2</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>3</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>4</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>5</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>6</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>7</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>8</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>9</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>10</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>11</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>12</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>13</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>14</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>15</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>16</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>17</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>18</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>19</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>20</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>21</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>22</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>23</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>24</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>25</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>26</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>27</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>28</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>29</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>30</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>31</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>32</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>33</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>34</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>35</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>36</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>37</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>38</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>39</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>40</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>41</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>42</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>43</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>44</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>45</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>46</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>47</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>48</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>49</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>50</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>51</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>52</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>53</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>54</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>55</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>56</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>57</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>58</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>59</instance>
+ <channel_id>2</channel_id>
+ </terminal>
+ </route>
+
+
+ <!-- TV Station 2 Routings -->
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>0</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>1</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>2</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>3</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>4</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>5</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>6</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>7</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>8</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>9</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>10</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>11</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>12</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>13</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>14</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>15</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>16</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>17</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>18</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>19</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>20</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>21</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>22</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>23</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>24</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>25</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>26</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>27</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>28</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>29</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>30</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>31</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>32</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>33</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>34</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>35</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>36</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>37</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>38</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>39</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>40</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>41</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>42</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>43</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>44</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>45</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>46</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>47</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>48</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>49</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>50</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>51</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>52</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>53</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>54</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>55</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>56</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>57</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>58</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ <route>
+ <terminal>
+ <device_type_hex>311</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>313</device_type_hex>
+ <instance>0</instance>
+ <channel_id>4</channel_id>
+ </terminal>
+ <terminal>
+ <device_type_hex>332</device_type_hex>
+ <instance>59</instance>
+ <channel_id>3</channel_id>
+ </terminal>
+ </route>
+ </network_manager>
+</root>
diff --git a/i2c-slim-amplifier-v2.3.script b/i2c-slim-amplifier-v2.3.script
new file mode 100644
index 0000000..02f4bcc
--- /dev/null
+++ b/i2c-slim-amplifier-v2.3.script
@@ -0,0 +1,92 @@
+<script>
+ <action>
+ <name>I2CPortCreate (1/1)</name>
+ <type>SEND_MCM</type>
+ <fblock_id_hex>0</fblock_id_hex>
+ <function_id_hex>6C1</function_id_hex>
+ <op_type_request_hex>2</op_type_request_hex>
+ <op_type_response_hex>C</op_type_response_hex>
+ <payload_hex>00 00 01 01</payload_hex>
+ </action>
+ <action>
+ <name>TAS5717 Setup (1/8)</name>
+ <type>SEND_MCM</type>
+ <fblock_id_hex>0</fblock_id_hex>
+ <function_id_hex>6C4</function_id_hex>
+ <op_type_request_hex>2</op_type_request_hex>
+ <op_type_response_hex>C</op_type_response_hex>
+ <payload_hex> 0F 00 00 00 2A 02 03 E8 1B 80</payload_hex>
+ </action>
+ <action>
+ <name>TAS5717 Setup (2/8)</name>
+ <type>SEND_MCM</type>
+ <fblock_id_hex>0</fblock_id_hex>
+ <function_id_hex>6C4</function_id_hex>
+ <op_type_request_hex>2</op_type_request_hex>
+ <op_type_response_hex>C</op_type_response_hex>
+ <payload_hex> 0F 00 00 00 2A 02 03 E8 11 B8</payload_hex>
+ </action>
+ <action>
+ <name>TAS5717 Setup (3/8)</name>
+ <type>SEND_MCM</type>
+ <fblock_id_hex>0</fblock_id_hex>
+ <function_id_hex>6C4</function_id_hex>
+ <op_type_request_hex>2</op_type_request_hex>
+ <op_type_response_hex>C</op_type_response_hex>
+ <payload_hex> 0F 00 00 00 2A 02 03 E8 12 60</payload_hex>
+ </action>
+ <action>
+ <name>TAS5717 Setup (4/8)</name>
+ <type>SEND_MCM</type>
+ <fblock_id_hex>0</fblock_id_hex>
+ <function_id_hex>6C4</function_id_hex>
+ <op_type_request_hex>2</op_type_request_hex>
+ <op_type_response_hex>C</op_type_response_hex>
+ <payload_hex> 0F 00 00 00 2A 02 03 E8 13 A0</payload_hex>
+ </action>
+ <action>
+ <name>TAS5717 Setup (5/8)</name>
+ <type>SEND_MCM</type>
+ <fblock_id_hex>0</fblock_id_hex>
+ <function_id_hex>6C4</function_id_hex>
+ <op_type_request_hex>2</op_type_request_hex>
+ <op_type_response_hex>C</op_type_response_hex>
+ <payload_hex> 0F 00 00 00 2A 02 03 E8 14 48</payload_hex>
+ </action>
+ <action>
+ <name>TAS5717 Setup (6/8)</name>
+ <type>SEND_MCM</type>
+ <fblock_id_hex>0</fblock_id_hex>
+ <function_id_hex>6C4</function_id_hex>
+ <op_type_request_hex>2</op_type_request_hex>
+ <op_type_response_hex>C</op_type_response_hex>
+ <payload_hex> 0F 00 00 00 2A 05 03 E8 20 00 89 77 72</payload_hex>
+ </action>
+ <action>
+ <name>TAS5717 Setup (7/8)</name>
+ <type>SEND_MCM</type>
+ <fblock_id_hex>0</fblock_id_hex>
+ <function_id_hex>6C4</function_id_hex>
+ <op_type_request_hex>2</op_type_request_hex>
+ <op_type_response_hex>C</op_type_response_hex>
+ <payload_hex> 0F 00 00 00 2A 02 03 E8 06 00</payload_hex>
+ </action>
+ <action>
+ <name>TAS5717 Setup (8/8)</name>
+ <type>SEND_MCM</type>
+ <fblock_id_hex>0</fblock_id_hex>
+ <function_id_hex>6C4</function_id_hex>
+ <op_type_request_hex>2</op_type_request_hex>
+ <op_type_response_hex>C</op_type_response_hex>
+ <payload_hex> 0F 00 00 00 2A 02 03 E8 05 00</payload_hex>
+ </action>
+ <action>
+ <name>TAS5717 Master Volume (1/1)</name>
+ <type>SEND_MCM</type>
+ <fblock_id_hex>0</fblock_id_hex>
+ <function_id_hex>6C4</function_id_hex>
+ <op_type_request_hex>2</op_type_request_hex>
+ <op_type_response_hex>C</op_type_response_hex>
+ <payload_hex> 0F 00 00 00 2A 03 03 E8 07 01 50</payload_hex>
+ </action>
+</script>
diff --git a/i2c-uda1388-v2.3.script b/i2c-uda1388-v2.3.script
new file mode 100644
index 0000000..f327f59
--- /dev/null
+++ b/i2c-uda1388-v2.3.script
@@ -0,0 +1,29 @@
+<script>
+ <action>
+ <name>I2CPortCreate (1/1)</name>
+ <type>SEND_MCM</type>
+ <fblock_id_hex>0</fblock_id_hex>
+ <function_id_hex>6C1</function_id_hex>
+ <op_type_request_hex>2</op_type_request_hex>
+ <op_type_response_hex>c</op_type_response_hex>
+ <payload_hex>00 00 01 01</payload_hex>
+ </action>
+ <action>
+ <name>Uda1380Setup (1/2)</name>
+ <type>SEND_MCM</type>
+ <fblock_id_hex>0</fblock_id_hex>
+ <function_id_hex>6C4</function_id_hex>
+ <op_type_request_hex>2</op_type_request_hex>
+ <op_type_response_hex>c</op_type_response_hex>
+ <payload_hex>0F 00 02 0A 18 03 03 E8 00 0f 02 01 00 00 02 a5 df 03 3f 3f 04 02 02 10 00 00 11 00 00 12 00 00 13 00 00 14 00 00</payload_hex>
+ </action>
+ <action>
+ <name>Uda1380Setup (2/2)</name>
+ <type>SEND_MCM</type>
+ <fblock_id_hex>0</fblock_id_hex>
+ <function_id_hex>6C4</function_id_hex>
+ <op_type_request_hex>2</op_type_request_hex>
+ <op_type_response_hex>c</op_type_response_hex>
+ <payload_hex>0F 00 02 04 18 03 03 E8 20 00 00 21 00 00 22 00 00 23 00 00</payload_hex>
+ </action>
+</script> \ No newline at end of file
diff --git a/i2c-uda1388.script b/i2c-uda1388.script
new file mode 100644
index 0000000..8ea5b40
--- /dev/null
+++ b/i2c-uda1388.script
@@ -0,0 +1,52 @@
+<script>
+ <action>
+ <name>Set I2C speed to 400kbps (1/2)</name>
+ <type>SEND_MCM</type>
+ <fblock_id_hex>0</fblock_id_hex>
+ <function_id_hex>E1D</function_id_hex>
+ <op_type_request_hex>2</op_type_request_hex>
+ <op_type_response_hex>C</op_type_response_hex>
+ <payload_hex>01 00 01</payload_hex>
+ </action>
+ <action>
+ <name>Set I2C speed to 400kbps (2/2)</name>
+ <type>PAUSE</type>
+ <pause_ms>25</pause_ms>
+ </action>
+ <action>
+ <name>Uda1380Setup (1/2)</name>
+ <type>SEND_MCM</type>
+ <fblock_id_hex>0</fblock_id_hex>
+ <function_id_hex>E1E</function_id_hex>
+ <op_type_request_hex>2</op_type_request_hex>
+ <op_type_response_hex>C</op_type_response_hex>
+ <payload_hex>18 03 0a 00 0f 02 01 00 00 02 a5 df 03 3f 3f 04 02 02 10 00 00 11 00 00 12 00 00 13 00 00 14 00 00</payload_hex>
+ </action>
+ <action>
+ <name>Uda1380Setup (2/2)</name>
+ <type>SEND_MCM</type>
+ <fblock_id_hex>0</fblock_id_hex>
+ <function_id_hex>E1E</function_id_hex>
+ <op_type_request_hex>2</op_type_request_hex>
+ <op_type_response_hex>C</op_type_response_hex>
+ <payload_hex>18 03 04 20 00 00 21 00 00 22 00 00 23 00 00</payload_hex>
+ </action>
+ <action>
+ <name>Uda1380Setup (1/2)</name>
+ <type>SEND_MCM</type>
+ <fblock_id_hex>0</fblock_id_hex>
+ <function_id_hex>E1E</function_id_hex>
+ <op_type_request_hex>2</op_type_request_hex>
+ <op_type_response_hex>C</op_type_response_hex>
+ <payload_hex>1a 03 0a 00 0f 02 01 00 00 02 a5 df 03 3f 3f 04 02 02 10 00 00 11 00 00 12 00 00 13 00 00 14 00 00</payload_hex>
+ </action>
+ <action>
+ <name>Uda1380Setup (2/2)</name>
+ <type>SEND_MCM</type>
+ <fblock_id_hex>0</fblock_id_hex>
+ <function_id_hex>E1E</function_id_hex>
+ <op_type_request_hex>2</op_type_request_hex>
+ <op_type_response_hex>C</op_type_response_hex>
+ <payload_hex>1a 03 04 20 00 00 21 00 00 22 00 00 23 00 00</payload_hex>
+ </action>
+</script>
diff --git a/nbproject/private/Makefile-variables.mk b/nbproject/private/Makefile-variables.mk
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/nbproject/private/Makefile-variables.mk
diff --git a/startDemo-d.sh b/startDemo-d.sh
new file mode 100755
index 0000000..131988c
--- /dev/null
+++ b/startDemo-d.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+if [ ! "$(whoami)" = "root" ]
+then
+ echo Warning you are not root, expect problems!
+fi
+
+
+./loadDriver-d.sh &
+
+sleep 3
+
+if [ -e ../../NetworkManager/Server/NetworkManager ]
+then
+../../NetworkManager/Server/NetworkManager config-d.xml &
+else
+./NetworkManager config-d.xml &
+fi
+
+sleep 5
+
+./VideoOnDemand -i ca14 -i ca16 -i ep03 -i ep04 -v &
+