/*******************************************************}

{                                                       }

{       File : MPEGTSBitrateAnalyzer.h                  }

{       Created by Tsviatko Jongov                      }

{       http://tsviatko.jongov.com                      }

{                                                       }

{       Transport stream bitrate analyzer.              }

{                                                       }

{*******************************************************/

   

#ifndef __MPEGTSBITRATEANALYZER_H__

#define __MPEGTSBITRATEANALYZER_H__

   

#include <vector>

#include "MPEGTSUtils.h"

#include "Buffer.h"

#include "Thread.h"

#include "transport_packet_class.h"

   

struct MPEGTSClock

{

    __int64 FilePosition;  //File position in bytes

    __int64 Clock;         //Clock in 27 MHz units (27 000 000 = 1 second)

};

   

class CMPEGTSClockList

{

public:

    std::vector<MPEGTSClock*> m_ClockList;

    WORD m_PID;

   

    CMPEGTSClockList(WORD PID)

    {

        m_PID = PID;

    }

   

    virtual ~CMPEGTSClockList()

    {

        ClearList();

    }

   

    HRESULT ClearList()

    {

        for (unsigned long i = 0; i < m_ClockList.size(); i++)

        {

            delete m_ClockList[i];

        }

       

        m_ClockList.clear();

       

        return S_OK;

    }

   

    HRESULT AddClock(__int64 FilePosition, __int64 Clock)

    {

        MPEGTSClock * Clk = new MPEGTSClock;

        Clk->FilePosition = FilePosition;

        Clk->Clock = Clock;

        m_ClockList.push_back(Clk);

       

        return S_OK;

    }

   

    unsigned long GetClocksCount()

    {

        return (unsigned long)m_ClockList.size();

    }

   

    HRESULT GetClock(unsigned long Index, __int64 * FilePosition, __int64 * Clock)

    {

        if (Index >= m_ClockList.size())

        {

            OutputDebugString(L"tsv: Error in CMPEGTSClockList.GetClock - invalid index.");

            return E_FAIL;

        }

       

        *FilePosition = m_ClockList[Index]->FilePosition;

        *Clock = m_ClockList[Index]->Clock;

       

        return S_OK;

    }

};

   

class CMPEGTSBitrateAnalyzer : public CThread

{

protected:

    FILE * m_FileStream;

    unsigned long m_TSPacketLen;

    CBuffer * m_Buffer;

    Ctransport_packet_class * m_transport_packet_class;

    __int64 m_FilePosition;

    __int64 m_FileSize;

   

    HANDLE m_ExitEvent;

    char m_FileName[MAX_PATH];

    bool m_FileToLoad;

    bool m_Analyzing;

    double m_Progress;

    CRITICAL_SECTION m_ProgressCriticalSection;

    CRITICAL_SECTION m_ClockListCriticalSection;

    bool m_StopAnalyze;

   

    std::vector<CMPEGTSClockList*> m_ClockList;

protected:

    bool PIDExists(WORD PID, unsigned long * ListIndex)

    {

        bool res = false;

        *ListIndex = -1;

        for (unsigned long i = 0; i < m_ClockList.size(); i++)

        {

            if (m_ClockList[i]->m_PID == PID)

            {

                res = true;

                *ListIndex = i;

                break;

            }

        }

        return res;

    }

   

    HRESULT ClearClockList()

    {

        for (unsigned long i = 0; i < m_ClockList.size(); i++)

        {

            delete m_ClockList[i];

        }

       

        m_ClockList.clear();

       

        return true;

    }

   

    HRESULT AddClock(WORD PID, __int64 FilePosition, __int64 Clock)

    {

        unsigned long ListIndex;

       

        EnterCriticalSection(&m_ClockListCriticalSection);

       

        if (!PIDExists(PID, &ListIndex))

        {

            CMPEGTSClockList * MPEGTSClockList = new CMPEGTSClockList(PID);

            MPEGTSClockList->AddClock(FilePosition, Clock);

            m_ClockList.push_back(MPEGTSClockList);

        }

        else

        {

            m_ClockList[ListIndex]->AddClock(FilePosition, Clock);

        }

       

        LeaveCriticalSection(&m_ClockListCriticalSection);

       

        return true;

    }

   

    HRESULT SendDataObj(void * Data, unsigned long Size)

    {

        __int64 Clck;

        unsigned long ClkPos;

       

        m_transport_packet_class->SetData(Data, Size);

       

        if (m_transport_packet_class->m_transport_packet.adaptation_field_control & 0x02)

        {

            if (m_transport_packet_class->m_transport_packet.adaptation_fld.PCR_flag)

            {

                Clck = m_transport_packet_class->m_transport_packet.adaptation_fld.program_clock_reference_base * 300;

                Clck += m_transport_packet_class->m_transport_packet.adaptation_fld.program_clock_reference_extension;

               

                m_transport_packet_class->GetPCRPosition(&ClkPos);

               

                AddClock((WORD)m_transport_packet_class->m_transport_packet.PID, m_FilePosition + ClkPos, Clck);

            }

        }

       

        return S_OK;

    }

   

protected:

   

    HRESULT Process()

    {

        unsigned char * p;

        unsigned long Len;

       

        p = m_Buffer->Head();

        Len = m_Buffer->Len();

       

        while (Len > m_TSPacketLen)

        {

            if ( (p[0] == 0x47) && (p[m_TSPacketLen] == 0x47) )

            {

                SendDataObj(p, m_TSPacketLen);

                p += m_TSPacketLen;

                Len -= m_TSPacketLen;

                m_FilePosition += m_TSPacketLen;

                continue;

            }

           

            p++;

            Len--;

            m_TSPacketLen++;

        }

       

        m_Buffer->m_First = m_Buffer->Len() - Len;

        m_Buffer->Compact();

       

        return S_OK;

    }

   

    int ThreadFunc(void)

    {

        unsigned long Read;

        __int64 ReadSoFar = 0;

        for (;;)

        {

            if (WaitForSingleObject(m_ExitEvent, 0) == WAIT_OBJECT_0)

            {

                break;

            }

           

            if (m_StopAnalyze)

            {

                m_FileToLoad = false;

                if (m_FileStream)

                {

                    fclose(m_FileStream);

                    m_FileStream = NULL;

                }

               

                m_StopAnalyze = false;

                m_Analyzing = false;

                m_Buffer->Empty();

            }

           

            if (m_FileToLoad)

            {

                m_Analyzing = true;

                m_FileToLoad = false;

               

                ReadSoFar = 0;

                m_FileSize = 0;

               

                if (m_FileStream)

                {

                    fclose(m_FileStream);

                    m_FileStream = NULL;

                }

               

                if (fopen_s(&m_FileStream, m_FileName, "rb") == 0)

                {

                    _fseeki64(m_FileStream, 0, SEEK_END);

                    m_FileSize = _ftelli64(m_FileStream);

                    _fseeki64(m_FileStream, 0, SEEK_SET);

                }

               

                m_FilePosition = 0;

                ClearClockList();

               

                m_TSPacketLen = 0;

            }

           

            if (m_FileStream)

            {

                Read = (unsigned long)fread(m_Buffer->Tail(), 1, 1024 * 1024, m_FileStream);

                if (Read == 0)

                {

                    if (m_FileStream)

                    {

                        fclose(m_FileStream);

                        m_FileStream = NULL;

                    }

                   

                    m_Analyzing = false;

                   

                    EnterCriticalSection(&m_ProgressCriticalSection);

                    m_Progress = 100.0;

                    LeaveCriticalSection(&m_ProgressCriticalSection);

                    

                    continue;

                }

                else

                {

                    m_Buffer->m_Last += Read;

                }

               

                if (m_TSPacketLen == 0)

                {

                    if (!GetTSStreamType(m_Buffer->Head(), Read, &m_TSPacketLen))

                    {

                        OutputDebugString(L"tsv: Warning - invalid transport stream.");

                        if (m_FileStream)

                        {

                            fclose(m_FileStream);

                            m_FileStream = NULL;

                        }

                        m_Analyzing = false;

                        continue;

                    }

                }

               

                ReadSoFar += Read;

               

                EnterCriticalSection(&m_ProgressCriticalSection);

                m_Progress = 100 * ((double)ReadSoFar / m_FileSize);

                LeaveCriticalSection(&m_ProgressCriticalSection);

                

                Process();

            }

           

            Sleep(1);

        }

       

        return S_OK;

    }

   

public:

    CMPEGTSBitrateAnalyzer() :

      m_Progress(0.0),

      m_FileStream(NULL)

    {

        SetThreadPriority(THREAD_PRIORITY_NORMAL);

       

        m_Buffer = new CBuffer(1024 * 1024);

       

        m_ExitEvent = CreateEvent(NULL, false, false, NULL);

       

        InitializeCriticalSection(&m_ClockListCriticalSection);

        InitializeCriticalSection(&m_ProgressCriticalSection);

       

        m_transport_packet_class = new Ctransport_packet_class();

       

        ResumeThread();

    }

   

    virtual ~CMPEGTSBitrateAnalyzer()

    {

        SetEvent(m_ExitEvent);

        WaitFor();

       

        CloseHandle(m_ExitEvent);

       

        delete m_Buffer;

        delete m_transport_packet_class;

       

        ClearClockList();

       

        DeleteCriticalSection(&m_ClockListCriticalSection);

        DeleteCriticalSection(&m_ProgressCriticalSection);

    }

   

    HRESULT AnalyzeFile(char * FileName)

    {

        m_StopAnalyze = false;

       

        if (m_Analyzing)

        {

            m_StopAnalyze = true;

           

            for (;;)

            {

                if (!m_StopAnalyze)

                {

                    break;

                }

               

                Sleep(1);

            }

        }

       

       

        if (!IsTS(FileName, &m_TSPacketLen))

        {

            return E_FAIL;

        }

       

        memset(&m_FileName, 0, strlen(m_FileName));

        memcpy(m_FileName, FileName, strlen(FileName));

        m_FileToLoad = true;

       

        return S_OK;

    }

   

    HRESULT GetProgress(double * PercentProgress)

    {

        EnterCriticalSection(&m_ProgressCriticalSection);

        *PercentProgress = m_Progress;

        LeaveCriticalSection(&m_ProgressCriticalSection);

       

        return S_OK;

    }

   

    HRESULT StopAnalyze()

    {

        m_StopAnalyze = true;

       

        for (;;)

        {

       

            if (!m_StopAnalyze)

            {

                break;

            }

           

            Sleep(1);

        }

       

        return S_OK;

    }

   

    unsigned long GetClockListsCount()

    {

        EnterCriticalSection(&m_ClockListCriticalSection);

        unsigned long Count = (unsigned long)m_ClockList.size();

        LeaveCriticalSection(&m_ClockListCriticalSection);

        return Count;

    }

   

    unsigned long GetClocksCount(unsigned long ClockListIndex)

    {

        EnterCriticalSection(&m_ClockListCriticalSection);

        if (ClockListIndex >= m_ClockList.size())

        {

            OutputDebugString(L"tsv: Error in TMPEGTSBitrateAnalyzer.GetClocksCount - invalid clock list index");

            return E_FAIL;

        }

       

        unsigned long Count = (unsigned long)m_ClockList[ClockListIndex]->m_ClockList.size();

        LeaveCriticalSection(&m_ClockListCriticalSection);

       

        return Count;

    }

   

    HRESULT GetClockItem(unsigned long ClockListIndex, unsigned long ClockItemIndex, __int64 * FilePosition, __int64 * Clock)

    {

        EnterCriticalSection(&m_ClockListCriticalSection);

        if (ClockListIndex >= m_ClockList.size())

        {

            OutputDebugString(L"tsv: Error in TMPEGTSBitrateAnalyzer.GetClockItem - invalid clock list index.");

            return E_FAIL;

        }

       

        if (ClockItemIndex >= m_ClockList[ClockListIndex]->m_ClockList.size())

        {

            OutputDebugString(L"tsv: Error in TMPEGTSBitrateAnalyzer.GetClockItem - invalid clock item index.");

            return E_FAIL;

        }

       

        *FilePosition = m_ClockList[ClockListIndex]->m_ClockList[ClockItemIndex]->FilePosition;

        *Clock = m_ClockList[ClockListIndex]->m_ClockList[ClockItemIndex]->Clock;

       

        LeaveCriticalSection(&m_ClockListCriticalSection);

       

        return S_OK;

    }

   

    HRESULT GetNearestNextClockItem(unsigned long ClockListIndex, __int64 * FilePosition, __int64 * Clock, __int64 * ClockItemIndex)

    {

        EnterCriticalSection(&m_ClockListCriticalSection);

        if (ClockListIndex >= m_ClockList.size())

        {

            OutputDebugString(L"tsv: Error in TMPEGTSBitrateAnalyzer.GetNearestNextClockItem - invalid clock list index.");

            return E_FAIL;

        }

       

        HRESULT hr = E_FAIL;

        for (unsigned long i = 0; i < m_ClockList[ClockListIndex]->m_ClockList.size(); i++)

        {

            if ( m_ClockList[ClockListIndex]->m_ClockList[i]->FilePosition >= *FilePosition )

            {

                *FilePosition = m_ClockList[ClockListIndex]->m_ClockList[i]->FilePosition;

                *Clock = m_ClockList[ClockListIndex]->m_ClockList[i]->Clock;

                *ClockItemIndex = i;

               

                hr = S_OK;

                break;

            }

        }

       

        LeaveCriticalSection(&m_ClockListCriticalSection);

       

        return hr;

    }

};

   

#endif // __MPEGTSBITRATEANALYZER_H__