/* * 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 2 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 . * * 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 #include #include #include #include #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; CSafeVectorm_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_