summaryrefslogtreecommitdiffstats
path: root/Src/Multiplexer/Multiplexer.h
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Multiplexer/Multiplexer.h')
-rw-r--r--Src/Multiplexer/Multiplexer.h476
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