summaryrefslogtreecommitdiffstats
path: root/Src/Multiplexer
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 /Src/Multiplexer
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>
Diffstat (limited to 'Src/Multiplexer')
-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
18 files changed, 3786 insertions, 0 deletions
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