diff options
Diffstat (limited to 'Src/Multiplexer/Multiplexer.h')
-rw-r--r-- | Src/Multiplexer/Multiplexer.h | 476 |
1 files changed, 476 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 |