diff options
Diffstat (limited to 'Src/Multiplexer')
-rw-r--r-- | Src/Multiplexer/Multiplexer.h | 476 | ||||
-rw-r--r-- | Src/Multiplexer/RingBuffer.h | 187 | ||||
-rw-r--r-- | Src/Multiplexer/Source.h | 408 | ||||
-rw-r--r-- | Src/Multiplexer/SourceFile.cpp | 176 | ||||
-rw-r--r-- | Src/Multiplexer/SourceFile.h | 77 | ||||
-rw-r--r-- | Src/Multiplexer/SourceFileConverted.cpp | 188 | ||||
-rw-r--r-- | Src/Multiplexer/SourceFileConverted.h | 79 | ||||
-rw-r--r-- | Src/Multiplexer/Stream.cpp | 236 | ||||
-rw-r--r-- | Src/Multiplexer/Stream.h | 250 | ||||
-rw-r--r-- | Src/Multiplexer/StreamList.cpp | 246 | ||||
-rw-r--r-- | Src/Multiplexer/StreamList.h | 179 | ||||
-rw-r--r-- | Src/Multiplexer/ThreadReadHdd.cpp | 23 | ||||
-rw-r--r-- | Src/Multiplexer/ThreadReadHdd.h | 171 | ||||
-rw-r--r-- | Src/Multiplexer/ThreadWriteNetwork.cpp | 134 | ||||
-rw-r--r-- | Src/Multiplexer/ThreadWriteNetwork.h | 97 | ||||
-rw-r--r-- | Src/Multiplexer/TsPacket.h | 661 | ||||
-rw-r--r-- | Src/Multiplexer/udp-stream.c | 127 | ||||
-rw-r--r-- | Src/Multiplexer/udp-stream.h | 71 |
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, ¶ms); + if (ret != 0) { + ConsolePrintf( PRIO_ERROR, "ThreadReadHdd_setschedparam(SCHED_FIFO) failed\n"); + } + // Now verify the change in thread priority + int policy = 0; + ret = pthread_getschedparam(this_thread, &policy, ¶ms); + if (ret != 0) { + ConsolePrintf( PRIO_ERROR, "ThreadReadHdd_getschedparam() failed\n"); + } + + while (that->m_bRunning) + { + if ( !that->m_StreamList->ReadHdd() ) // buffer full? + usleep(50000); // we have 300ms buffer size, so it's save to sleep 50ms + } + pthread_exit(0); + } + + + + + /*----------------------------------------------------------*/ + /*! \brief start reading thread + */ + /*----------------------------------------------------------*/ + void Start() + { + if ( m_bRunning ) + return; + + if ( NULL == m_StreamList ) + return; + + struct sched_param param; + pthread_attr_t tattr; + pthread_attr_init(&tattr); + pthread_attr_getschedparam(&tattr, ¶m); + pthread_attr_setschedpolicy(&tattr, SCHED_RR); + param.sched_priority = sched_get_priority_max(SCHED_RR); + pthread_attr_setschedparam(&tattr, ¶m); + m_bRunning = true; + if ( 0 != pthread_create(&m_hThread, &tattr, Run, this) ) + ConsolePrintf( PRIO_ERROR, RED"! failed to create thread"RESETCOLOR"\n"); + } + + + + + /*----------------------------------------------------------*/ + /*! \brief stop reading thread + */ + /*----------------------------------------------------------*/ + void Stop() + { + if ( m_bRunning ) + { + m_bRunning = false; + void *pVal; + pthread_join(m_hThread, &pVal); + m_hThread = 0; + } + } + +public: + /*----------------------------------------------------------*/ + /*! \brief add a stream list to read thread + * \param StreamList - stream list to add for reading + * \return true, if stream list was added successfully + */ + /*----------------------------------------------------------*/ + void AddStreamList(CStreamList *streamList) + { + Stop(); + m_StreamList = streamList; + Start(); + } + + + + + /*----------------------------------------------------------*/ + /*! \brief remove a stream list from read thread + */ + /*----------------------------------------------------------*/ + void RemoveStreamList(CStreamList *streamList) + { + Stop(); + m_StreamList = NULL; + } +}; + + + + + + +#endif + diff --git a/Src/Multiplexer/ThreadWriteNetwork.cpp b/Src/Multiplexer/ThreadWriteNetwork.cpp new file mode 100644 index 0000000..5723a6c --- /dev/null +++ b/Src/Multiplexer/ThreadWriteNetwork.cpp @@ -0,0 +1,134 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include <stdint.h> +#include "Console.h" +#include "ThreadWriteNetwork.h" +#include "Multiplexer.h" + + + + + +CThreadWriteNetwork::CThreadWriteNetwork() : + m_hThread(0), m_bRunning(false), m_Multiplexer(NULL) +{ +} + + +void *CThreadWriteNetwork::Run(void *pInst) { + assert(NULL != pInst); + CThreadWriteNetwork *that = (CThreadWriteNetwork *)pInst; + + /* set thread to max priority: */ + int ret; + pthread_t this_thread = pthread_self(); + struct sched_param params; + params.sched_priority = sched_get_priority_max(SCHED_FIFO); + ret = pthread_setschedparam(this_thread, SCHED_FIFO, ¶ms); + if (ret != 0) { + ConsolePrintf( PRIO_ERROR, RED"CThreadWriteNetwork setschedparam(SCHED_FIFO) failed"RESETCOLOR"\n"); + } + // Now verify the change in thread priority + int policy = 0; + ret = pthread_getschedparam(this_thread, &policy, ¶ms); + if (ret != 0) { + ConsolePrintf( PRIO_ERROR, RED"CThreadWriteNetwork getschedparam() failed"RESETCOLOR"\n"); + } + bool errorPrinted = false; + WriteResult_t result; + bool shallWait; + while (that->m_bRunning) + { + shallWait = true; + result = that->m_Multiplexer->WriteToDriver(); + switch (result) + { + case WriteResult_Failed: + if (!errorPrinted) + { + errorPrinted = true; + ConsolePrintf( PRIO_ERROR, RED"failed to write driver"RESETCOLOR"\n"); + } + break; + case WriteResult_WouldBlock: + break; + case WriteResult_Success: + default: + shallWait = false; + break; + } + if ( that->m_bRunning && shallWait ) + usleep(1000); + } + pthread_exit(0); +} + + + + +void CThreadWriteNetwork::Start() +{ + if ( m_bRunning ) + return; + + if ( NULL == m_Multiplexer ) + return; + + m_bRunning = true; + if ( 0 != pthread_create(&m_hThread, NULL, Run, this) ) + ConsolePrintf( PRIO_ERROR, RED"! failed to create thread"RESETCOLOR"\n"); +} + + + + +void CThreadWriteNetwork::Stop() +{ + if ( m_bRunning ) + { + m_bRunning = false; + void *pVal; + pthread_join(m_hThread, &pVal); + m_hThread = 0; + } +} + + + + +void CThreadWriteNetwork::AddMultiplexer(CMultiplexer *multiplexer) +{ + Stop(); + m_Multiplexer = multiplexer; + Start(); +} + + + + +void CThreadWriteNetwork::RemoveMultiplexer(CMultiplexer *multiplexer) +{ + Stop(); + m_Multiplexer = NULL; +} diff --git a/Src/Multiplexer/ThreadWriteNetwork.h b/Src/Multiplexer/ThreadWriteNetwork.h new file mode 100644 index 0000000..c0fda28 --- /dev/null +++ b/Src/Multiplexer/ThreadWriteNetwork.h @@ -0,0 +1,97 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the CThreadWriteNetwork class. + */ +/*----------------------------------------------------------*/ +#ifndef THREADWRITENETWORK_H +#define THREADWRITENETWORK_H + +#include "SafeVector.h" +#include <pthread.h> + +class CMultiplexer; + + + + +/*----------------------------------------------------------*/ +/*! \brief class creating a thread to call stream's sending + * function. + */ +/*----------------------------------------------------------*/ +class CThreadWriteNetwork +{ + pthread_t m_hThread; + bool m_bRunning; + CMultiplexer *m_Multiplexer; + + /*----------------------------------------------------------*/ + /*! \brief thread reading from all stream lists + */ + /*----------------------------------------------------------*/ + static void *Run(void *pInst); + +public: + CThreadWriteNetwork(); + + + /*----------------------------------------------------------*/ + /*! \brief start reading thread + */ + /*----------------------------------------------------------*/ + void Start(); + + + + + /*----------------------------------------------------------*/ + /*! \brief stop reading thread + */ + /*----------------------------------------------------------*/ + void Stop(); + + + + +public: + /*----------------------------------------------------------*/ + /*! \brief add a multiplexer to send thread + * \param multiplexer - multiplexer to add to sending thread + */ + /*----------------------------------------------------------*/ + void AddMultiplexer(CMultiplexer *multiplexer); + + + + + /*----------------------------------------------------------*/ + /*! \brief remove a multiplexer from send thread + */ + /*----------------------------------------------------------*/ + void RemoveMultiplexer(CMultiplexer *multiplexer); +}; + +#endif diff --git a/Src/Multiplexer/TsPacket.h b/Src/Multiplexer/TsPacket.h new file mode 100644 index 0000000..fe68289 --- /dev/null +++ b/Src/Multiplexer/TsPacket.h @@ -0,0 +1,661 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*----------------------------------------------------------*/ +/*! \file + * \brief This file contains static helper C-functions to parse TS packets. + */ +/*----------------------------------------------------------*/ +#ifndef TSPACKET_H +#define TSPACKET_H + +//#define TS_PATTERN_CHECK + +#include <stdint.h> +#include <stdbool.h> +#include <string.h> + +/*----------------------------------------------------------*/ +/*! + * \brief Handles a transport stream packet of 188 bytes + */ +/*----------------------------------------------------------*/ +class CTsPacket +{ +private: + static const uint32_t ADAPTION_FIELD_CONTROL__RESERVED = (0x00 << 4); + static const uint32_t ADAPTION_FIELD_CONTROL__PAYLOAD_ONLY = (0x01 << 4); + static const uint32_t ADAPTION_FIELD_CONTROL__NO_PAYLOAD = (0x02 << 4); + static const uint32_t ADAPTION_FIELD_CONTROL__BOTH = (0x03 << 4); + static const uint32_t program_stream_map = 0xBC; + static const uint32_t padding_stream = 0xBE; + static const uint32_t private_stream_2 = 0xBF; + static const uint32_t ECM = 0xF0; + static const uint32_t EMM = 0xF1; + static const uint32_t DSMCC_stream = 0xF2; + static const uint32_t H222_stream = 0xF8; + static const uint32_t program_stream_directory = 0xFF; + static const uint32_t PROGRAM_ASSOSICIATION_SECTION = 0x00; + static const uint32_t PROGRAM_MAP_SECTION = 0x02; + static const uint32_t ISO_IEC_11172_AUDIO = 0x03; + static const uint32_t ISO_IEC_13818_2_Video = 0x02; + static const uint32_t ISO_IEC_13818_3_AUDIO = 0x04; + static const uint32_t ISO_IEC_13818_1_RESERVED = 0x1B; + static const uint8_t PCR_FLAG = 0x10; + +public: + static const uint32_t PID_INVALID = 0xFFFF; + static const uint32_t PCR_INVALID = 0xFFFFFFFF; + static const uint32_t PCR_TICKS_PER_SEC = 45000; + static const uint8_t TS_PACKET_LEN = 188; + + + + /*----------------------------------------------------------*/ + /*! \brief extract continuity counter + * + * \param pTs - uint8_t array containing TS packet + */ + /*----------------------------------------------------------*/ + static uint32_t GetContinuityCounter(uint8_t *pTs) + { + return (pTs[3] & 0xF); + } + + + + + /*----------------------------------------------------------*/ + /*! \brief get array index where ES payload + * + * \param pTs - uint8_t array containing TS packet + */ + /*----------------------------------------------------------*/ + static uint32_t GetElementaryPayloadStart(uint8_t *pTs) + { + uint32_t nRet = GetPayloadStart(pTs); // index to TS payload + + if (GetHasPesHeader(pTs)) { + nRet += 3 + 1 + 2 + 1 + 1; // packet start(3) + stream_id(1) + PES_packet_length(2) + PES_scampling...(1) + PTS_DTS_flags...(1) + nRet += (pTs[nRet] & 0xFF) + 1; // PES_header_data_length + } + + return nRet; + } + + + + + /*----------------------------------------------------------*/ + /*! \brief check if there is a discontinuity in the stream + * + * \param pTs - uint8_t array containing TS packet + * \param nLastDiscontinuityCounter - preceding counter + */ + /*----------------------------------------------------------*/ + static bool GetHasDiscontinuity(uint8_t *pTs, + uint32_t nLastDiscontinuityCounter) + { + nLastDiscontinuityCounter = (nLastDiscontinuityCounter + 1) & 0xF; + return (pTs[3] & 0xF) != nLastDiscontinuityCounter; + } + + + + + /*----------------------------------------------------------*/ + /*! \brief check if a TS packet has the given PID + * + * \param pTs - uint8_t array containing TS packet + * \param nPid - PID to check for + * + * \return true if PID is equal + */ + /*----------------------------------------------------------*/ + static bool GetHasPid(uint8_t *pTs, uint32_t nPid) + { + return nPid == GetPid(pTs); + } + + + + + /*----------------------------------------------------------*/ + /*! \brief check if a TS packet contains a PAT + * + * \param pTs - uint8_t array containing TS packet + * + * \return true if PAT is contained + */ + /*----------------------------------------------------------*/ + static bool GetIsPat(uint8_t *pTs) + { + if (!GetHasPid(pTs, 0)) + return false; + + uint32_t nPat = GetPayloadStart(pTs); // start of PSI + + nPat += 1 + (0xFF & pTs[nPat]); // pouint32_ter_field + + return (pTs[nPat] == PROGRAM_ASSOSICIATION_SECTION); // table_id + } + + + + + /*----------------------------------------------------------*/ + /*! \brief gets the PMT PID from the n-th program of a PAT + * packet + * + * \param pTs - uint8_t array containing TS packet + * \param nPrg - number of program + * + * \return true if PAT is contained + */ + /*----------------------------------------------------------*/ + static uint32_t GetPmtPidFromPat(uint8_t *pTs, uint32_t nPrg) + { + uint32_t nPat = GetPayloadStart(pTs); // PSI + nPat += 1 + (0xFF & pTs[nPat]); // pouint32_ter_field + + return ((0x1F & pTs[nPat + 10 + nPrg * 4]) << 8) + // program_map_PID (high uint8_t *) + (0xFF & pTs[nPat + 10 + nPrg * 4 + 1]); // program_map_PID (low uint8_t *) + } + + + + + /*----------------------------------------------------------*/ + /*! \brief gets the audio PID from a PMT packet + * + * \param pTs - uint8_t array containing TS packet + * + * \return PID of 1st audio channel + */ + /*----------------------------------------------------------*/ + static uint32_t GetAudioPidFromPmt(uint8_t *pTs) + { + uint32_t nPmt = GetPayloadStart(pTs); // PSI + nPmt += 1 + (0xFF & pTs[nPmt]); // pouint32_ter_field + + if (PROGRAM_MAP_SECTION != pTs[nPmt]) // check table_id + return PID_INVALID; + + uint32_t section_length = ((0x0F & pTs[nPmt + 1]) << 8) + + (0xFF & pTs[nPmt + 2]); + uint32_t program_info_length = ((0x0F & pTs[nPmt + 10]) << 8) + + (0xFF & pTs[nPmt + 11]); + + uint32_t n = nPmt + 12 + program_info_length; + + while (n < section_length + nPmt + 2) { // run through all streams + uint32_t stream_type = pTs[n]; + uint32_t elementary_pid = ((0x1F & pTs[n + 1]) << 8) + (0xFF & pTs[n + 2]); + uint32_t ES_info_length = ((0x0F & pTs[n + 3]) << 8) + (0xFF & pTs[n + 4]); + + if (ISO_IEC_11172_AUDIO == stream_type + || ISO_IEC_13818_3_AUDIO == stream_type) { + return elementary_pid; + } + + n += 5 + ES_info_length; // switch to next stream + } + + return PID_INVALID; // no audio stream found + } + + + + + /*----------------------------------------------------------*/ + /*! \brief gets the audio PID from a PMT packet + * + * \param pTs - uint8_t array containing TS packet + * + * \return PID of 1st audio channel + */ + /*----------------------------------------------------------*/ + static uint32_t GetVideoPidFromPmt(uint8_t *pTs) + { + uint32_t nPmt = GetPayloadStart(pTs); // PSI + nPmt += 1 + (0xFF & pTs[nPmt]); // pouint32_ter_field + + if (PROGRAM_MAP_SECTION != pTs[nPmt]) // check table_id + return PID_INVALID; + + uint32_t section_length = ((0x0F & pTs[nPmt + 1]) << 8) + + (0xFF & pTs[nPmt + 2]); + uint32_t program_info_length = ((0x0F & pTs[nPmt + 10]) << 8) + + (0xFF & pTs[nPmt + 11]); + + uint32_t n = nPmt + 12 + program_info_length; + + while (n < section_length + nPmt + 2) { // run through all streams + uint32_t stream_type = pTs[n]; + uint32_t elementary_pid = ((0x1F & pTs[n + 1]) << 8) + (0xFF & pTs[n + 2]); + uint32_t ES_info_length = ((0x0F & pTs[n + 3]) << 8) + (0xFF & pTs[n + 4]); + + if (ISO_IEC_13818_2_Video == stream_type + || ISO_IEC_13818_1_RESERVED == stream_type) { + return elementary_pid; + } + + n += 5 + ES_info_length; // switch to next stream + } + + return PID_INVALID; // no audio stream found + } + + + + + /*----------------------------------------------------------*/ + /*! \brief checks if TS packet is the start of an I frame + * + * \param pTs - uint8_t array containing TS packet + */ + /*----------------------------------------------------------*/ + static bool GetIsStartOfIFrame(uint8_t *pTs) + { + uint32_t n = GetPayloadStart(pTs); + + for (; n < 188 - 4; n++) // search for IDR (instantaneous decoder refresh) + if (0 == pTs[n] && 0 == pTs[n + 1] && 1 == pTs[n + 2] + && (5 == (pTs[n + 3] & 0x1F))) + return true; + + return false; + } + + + + + /*----------------------------------------------------------*/ + /*! \brief check if TS packet contains a PES header + * + * \param pTs - uint8_t array containing TS packet + */ + /*----------------------------------------------------------*/ + static bool GetHasPesHeader(uint8_t *pTs) + { + if (!GetIsPayloadUnitStart(pTs)) + return false; + + uint32_t nPes = GetPayloadStart(pTs); + + return !(0 != pTs[nPes ] || + 0 != pTs[nPes + 1] || + 1 != pTs[nPes + 2] || // is it a PES Header? + program_stream_map == pTs[nPes + 3] || + padding_stream == pTs[nPes + 3] || + private_stream_2 == pTs[nPes + 3] || + ECM == pTs[nPes + 3] || + EMM == pTs[nPes + 3] || + program_stream_directory == pTs[nPes + 3] || + DSMCC_stream == pTs[nPes + 3] || + H222_stream == pTs[nPes + 3] || + 0 == (0x80 & pTs[nPes + 7])); + } + + + + + /*----------------------------------------------------------*/ + /*! \brief check if TS packet contains a PTS + * + * \param pTs - uint8_t array containing TS packet + */ + /*----------------------------------------------------------*/ + static bool GetHasPts(uint8_t *pTs) + { + if (!GetHasPesHeader(pTs)) + return false; + + uint32_t nPes = GetPayloadStart(pTs); + + return (0 != ((pTs[nPes + 7] & 0x80))); // PTS_DTS_flags is 0x2 or 0x3 + } + + + + + /*----------------------------------------------------------*/ + /*! \brief get PCR from a packet if available + * + * \param pTs - uint8_t array containing TS packet + * + * \return PCR or PCR_INVALID if packet contains no PCR + */ + /*----------------------------------------------------------*/ + static uint32_t GetPcr(uint8_t *pTs) + { + switch ((pTs[3] & 0x30)) { // check if packet contains adaption field + case ADAPTION_FIELD_CONTROL__PAYLOAD_ONLY: + case ADAPTION_FIELD_CONTROL__RESERVED: + return PCR_INVALID; + default: + break; + } + + if (0 == pTs[4]) // 0 == adaption_field_length ? + return PCR_INVALID; + + if (PCR_FLAG != (pTs[5] & PCR_FLAG)) // 1 == PCR_flag ? + return PCR_INVALID; + + return (pTs[6] << 24) || (pTs[7] << 16) || (pTs[8] << 8) || (pTs[9]); + } + + + + + /*----------------------------------------------------------*/ + /*! \brief check if a TS packet has sync uint8_t * + * + * \param pTs - uint8_t array containing TS packet + */ + /*----------------------------------------------------------*/ + static bool GetHasSyncByte(uint8_t *pTs) + { + return 0x47 == pTs[0]; + } + + + + + /*----------------------------------------------------------*/ + /*! \brief check if TS packet starts a new payload unit + * + * \param pTs - uint8_t array containing TS packet + */ + /*----------------------------------------------------------*/ + static bool GetIsPayloadUnitStart(uint8_t *pTs) + { + return (0 != (pTs[1] & 0x40)); + } + + + + + /*----------------------------------------------------------*/ + /*! \brief get arry index of payload start in packet + * + * \param pTs - uint8_t array containing TS packet + */ + /*----------------------------------------------------------*/ + static uint32_t GetPayloadStart(uint8_t *pTs) + { + switch ((pTs[3] & 0x30)) { + case ADAPTION_FIELD_CONTROL__PAYLOAD_ONLY: + return 4; + + case ADAPTION_FIELD_CONTROL__BOTH: + return 4 + 1 + (pTs[4] & 0xFF); + + case ADAPTION_FIELD_CONTROL__RESERVED: + case ADAPTION_FIELD_CONTROL__NO_PAYLOAD: + return 188; + } + + return TS_PACKET_LEN; + } + + + + + /*----------------------------------------------------------*/ + /*! \brief get PES packet length from packet + * + * \param pTs - uint8_t array containing TS packet + * \return length of PES packet in uint8_t *s + */ + /*----------------------------------------------------------*/ + static uint32_t GetPesPacketLength(uint8_t *pTs) + { + uint32_t nPes = GetPayloadStart(pTs); + return ((pTs[nPes + 4] & 0xFF) << 8) + (pTs[nPes + 5] & 0xFF); + } + + + + + /*----------------------------------------------------------*/ + /*! \brief extract PID from a TS packet + * + * \param pTs - uint8_t array containing TS packet + */ + /*----------------------------------------------------------*/ + static uint32_t GetPid(uint8_t *pTs) + { + return (((uint32_t)pTs[1] & 0x1F) << 8) + ((uint32_t)pTs[2] & 0xFF); + } + + + + + /*----------------------------------------------------------*/ + /*! \brief extract PTS from a TS packet in 45 kHz units + * + * \param pTs - uint8_t array containing TS packet + */ + /*----------------------------------------------------------*/ + static uint32_t GetPts(uint8_t *pTs) + { + uint32_t nPes = GetPayloadStart(pTs); + + return ((pTs[nPes + 9] & 0x0E) << 28) + + ((pTs[nPes + 10] & 0xFF) << 21) + + ((pTs[nPes + 11] & 0xFE) << 13) + + ((pTs[nPes + 12] & 0xFF) << 6) + + ((pTs[nPes + 13] & 0xFF) >> 2); + } + + + + + /*----------------------------------------------------------*/ + /*! \brief calculate CRC from a memory area + * + * \param pSrc - start of memory area + * \param nLen - length of memory area in bytes + */ + /*----------------------------------------------------------*/ + static uint32_t GetCrc32(uint8_t *pSrc, int nLen) + { + static const uint32_t CrcTable[256] = { + 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, + 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, + 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, + 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, + 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, + 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, + 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, + 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, + 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, + 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, + 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, + 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, + 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, + 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, + 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, + 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, + 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, + 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, + 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, + 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, + 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, + 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, + 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, + 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9, + 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, + 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, + 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, + 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, + 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, + 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, + 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, + 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, + 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, + 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, + 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, + 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, + 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, + 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, + 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 + }; + + uint32_t nCrc = 0xFFFFFFFF; + + while (nLen--) + nCrc = (nCrc << 8) ^ CrcTable[((nCrc >> 24) ^ *pSrc++) & 0xFF ]; + + return nCrc; + } + + + + + /*----------------------------------------------------------*/ + /*! \brief calculate a PMT packet for a program containing + * one video and one audio stream + * + * \param pTs - pointer to TS packet to store result + * \param nAPid - PID of audio + * \param nAPid - PID of video + * \param nAPid - PID of PMT + */ + /*----------------------------------------------------------*/ + static void CreatePmt(uint8_t *pTs, uint32_t nAPid, uint32_t nVPid, + uint32_t nPPid) + { + if (NULL == pTs) + return; + + pTs[0] = 0x47; // packet start + + pTs[1] = (1 << 6) | // Payload Unit start indicator, PID = 0; + (nPPid >> 8); // PMT PID (HB) + + pTs[2] = nPPid & 0xFF; // PMT PID (LB) + + pTs[3] = (1 << 4); // no adaption field + + pTs[4] = 0; // PointerField: new section + + uint8_t *pPmt = pTs + 5; // pointer to PMT section + + pPmt[0] = 0x02; // Table ID: Program Map Section + + uint16_t wSectionLength = + 12 - 3 + // section until program info length + 5 + // video entry + 5 + // audio entry + 4; // CRC + + pPmt[1] = (1 << 7) | // section syntax indicator + (3 << 4) | // reserved + (wSectionLength >> 8); // section syntax 1, section length + pPmt[2] = wSectionLength & 0xFF; + + pPmt[3] = (nPPid >> 8); // program number (HB) + pPmt[4] = nPPid & 0xFF; // program number (LB) + + pPmt[5] = (3 << 6) | 1; // version, current/next + + pPmt[6] = 0; // section number + pPmt[7] = 0; // last section number + + pPmt[8] = (7 << 5) | // reserved + (nVPid >> 8); // PCR PID, assuming video has PCR + + pPmt[9] = nVPid & 0xFF; // PCR PID (LB) + + pPmt[10] = (0xF << 4) | // reserved + (0); // program info length + + pPmt[11] = 0; + + // video + pPmt[12] = 27; // stream type = video + + pPmt[13] = (7 << 5) | // reserved + (nVPid >> 8); // ES PID (HB) + pPmt[14] = nVPid & 0xFF; // ES PID (LB) + + pPmt[15] = (0xF << 4) | // reserved + (0); // ES Info length + pPmt[16] = 0; // ES Info length + + // audio + pPmt[17] = 3; // stream type = audio + + pPmt[18] = (7 << 5) | // reserved + (nAPid >> 8); // ES PID + + pPmt[19] = nAPid & 0xFF; + + pPmt[20] = (0xF << 4) | // reserved + (0); // ES Info + + pPmt[21] = 0; + + // CRC + uint32_t nCrc = GetCrc32(pPmt, + wSectionLength + 3 - 4);// Crc for entire pmt section + pPmt[wSectionLength + 3 - 4 + 0] = (nCrc >> 24) & 0xFF; + pPmt[wSectionLength + 3 - 4 + 1] = (nCrc >> 16) & 0xFF; + pPmt[wSectionLength + 3 - 4 + 2] = (nCrc >> 8) & 0xFF; + pPmt[wSectionLength + 3 - 4 + 3] = (nCrc) & 0xFF; + } + + + + /*----------------------------------------------------------*/ + /*! \brief calculate a stuffing packet + * + * \param pTs - pointer to TS packet to store result + * \param nPatternCount - Actual pattern packet count, shall be incremented each time. + */ + /*----------------------------------------------------------*/ + static void CreateStuffing(uint8_t *pTs, uint32_t nPatternCount) + { + pTs[0] = 0x47; + pTs[1] = 0x1F; + pTs[2] = 0xFF; + pTs[3] = 0x00; +#ifdef TS_PATTERN_CHECK + memcpy(&pTs[4], &nPatternCount, sizeof(nPatternCount)); + uint8_t startByte = (uint8_t)(nPatternCount & 0xFF); + for (uint8_t i = 8; i < 188; i++) { + pTs[i] = startByte + i; + } +#endif + } + +}; + +#endif //TSPACKET_H diff --git a/Src/Multiplexer/udp-stream.c b/Src/Multiplexer/udp-stream.c new file mode 100644 index 0000000..c830789 --- /dev/null +++ b/Src/Multiplexer/udp-stream.c @@ -0,0 +1,127 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +#include <stdio.h> +#include <stdint.h> +#include <stdbool.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <errno.h> +#include "Console.h" + +/*----------------------------------------------------------*/ +/*! \brief Private structure used by UdpStream component. + */ +/*----------------------------------------------------------*/ +typedef struct localVar_tag +{ + ///Socket handle. + int sock; + ///Target port. + int port; + ///Target IP address as zero terminated string. + char targetIpAddress[32]; + ///SocketAddress structure to address remote node. + struct sockaddr_in destination; + ///Helper variable to suppress a lot of error messages. + bool errorMode; +} localVar_t; + +static localVar_t udpLocal; + +bool UdpStream_Init( const char *targetIp, int targetPort ) +{ + if( ( NULL == targetIp ) || ( 0 == targetPort ) ) + { + return false; + } + ConsolePrintf( PRIO_HIGH, "Sending UDP stream to IP:'%s', port:%d\n", targetIp, targetPort ); + strcpy( udpLocal.targetIpAddress, targetIp ); + udpLocal.port = targetPort; + udpLocal.errorMode = false; + + memset( &udpLocal.destination, 0, sizeof( udpLocal.destination ) ); + + /* Default send address */ + udpLocal.destination.sin_family = AF_INET; + udpLocal.destination.sin_addr.s_addr = inet_addr( udpLocal.targetIpAddress ); + udpLocal.destination.sin_port = htons( udpLocal.port ); + + if( ( udpLocal.sock = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP ) ) < 0 ) + { + ConsolePrintf( PRIO_ERROR, RED"Failed to create socket"RESETCOLOR"\n" ); + return false; + } +#if defined (SO_REUSEADDR) && defined (SO_REUSEPORT) + int one = 1; + setsockopt(udpLocal.sock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &one, sizeof(one)); +#endif + + /* set default destination */ + setsockopt( udpLocal.sock, IPPROTO_IP, IP_MULTICAST_IF, &udpLocal.destination, sizeof( udpLocal.destination ) ); + + // this call is what allows broadcast packets to be sent: + int enableSocketFlag = 1; + if( setsockopt( udpLocal.sock, SOL_SOCKET, SO_BROADCAST, &enableSocketFlag, sizeof( enableSocketFlag ) ) < 0 ) + { + ConsolePrintf( PRIO_ERROR, RED"setsockopt (SO_BROADCAST) failed"RESETCOLOR"\n" ); + return false; + } + int ttl = 5; + if (!(setsockopt( udpLocal.sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)))) { + ConsolePrintf( PRIO_HIGH, "TTL set successfully to %d\n", ttl ); + } else { + ConsolePrintf( PRIO_ERROR, "Error setting TTL: %s\n", strerror(errno)); + } + + return true; +} + +void UdpStream_Close() +{ + close( udpLocal.sock ); +} + +bool UdpStream_Send( const uint8_t *data, uint32_t length ) +{ + int is_length = sendto( udpLocal.sock, data, length, + 0, ( struct sockaddr * )&udpLocal.destination, sizeof( udpLocal.destination ) ); + if( is_length != length ) + { + if( !udpLocal.errorMode ) + { + udpLocal.errorMode = true; + ConsolePrintf( PRIO_ERROR, RED"Send failed; shall len = %d; is length = %d"RESETCOLOR"\n", length, is_length ); + } + return false; + } + else + udpLocal.errorMode = false; + + return true; +} diff --git a/Src/Multiplexer/udp-stream.h b/Src/Multiplexer/udp-stream.h new file mode 100644 index 0000000..df6c5db --- /dev/null +++ b/Src/Multiplexer/udp-stream.h @@ -0,0 +1,71 @@ +/* + * Video On Demand Samples + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + + /*----------------------------------------------------------*/ +/*! \file + * \brief This file contains the a simple C-API to send a UDP stream. + */ +/*----------------------------------------------------------*/ +#ifndef UDP_STREAM_H +#define UDP_STREAM_H + +#include <stdint.h> +#include <stdbool.h> + + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*----------------------------------------------------------*/ +/*! \brief Initializies the UDP socket. + * \note Do not call any other function of this component, before calling this function. + * \param targetIp - IP address of remote node as zero terminated string. + * \param targetPort - Port number of remote node. + * \return True, if successful, false otherwise. + */ +/*----------------------------------------------------------*/ +bool UdpStream_Init( const char *targetIp, int targetPort ); + +/*----------------------------------------------------------*/ +/*! \brief Deinitializies the UDP socket. + * \note Do not call any other function of this component, after calling this function. + */ +/*----------------------------------------------------------*/ +void UdpStream_Close(); + +/*----------------------------------------------------------*/ +/*! \brief Send the given Data via the UDP socket. + * \param data - Byte array to be sent. + * \param length - The length of the Byte array. + * \return True, if successful, false otherwise. + */ +/*----------------------------------------------------------*/ +bool UdpStream_Send( const uint8_t *data, uint32_t length ); + +#ifdef __cplusplus +} +#endif + +#endif //UDP_STREAM_H |