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

{                                                       }

{       File : MPEGPSBitrateAnalyzer.h                  }

{       Created by Tsviatko Jongov                      }

{       http://tsviatko.jongov.com                      }

{                                                       }

{       Program stream bitrate analyzer.                }

{                                                       }

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

   

#ifndef __MPEGPSBITRATEANALYZER_H__

#define __MPEGPSBITRATEANALYZER_H__

   

#include <vector>

#include "MPEGPSUtils.h"

#include "Buffer.h"

#include "Thread.h"

   

struct MPEGPSClock

{

    __int64 FilePosition;  //File position in bytes

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

};

   

class CMPEGPSBitrateAnalyzer : public CThread

{

protected:

    FILE * m_FileStream;

    CBuffer * m_Buffer;

    __int64 m_FilePosition;

    __int64 m_FileSize;

    pack_header m_pack_header;

   

    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<MPEGPSClock*> m_ClockList;

protected:

    HRESULT Process()

    {

        unsigned char * P;

        unsigned long Len;

        __int64 Clck;

        __int64 FP;

       

        P = m_Buffer->Head();

        Len = m_Buffer->Len();

       

        while (Len > 50)

        {

            if (*((DWORD *)(P)) == PACK_START_CODE_SWP)

            {

                //parse clock

                if (ParsePackHeader(P, Len, &m_pack_header))

                {

                    Clck = GetSCRInt64(&m_pack_header);

                    FP = m_FilePosition + (P - m_Buffer->Head() + 6);

                    

                    AddClock(FP, Clck);

                   

                    P += 10;

                    Len -= 10;

                }

            }

           

            P++;

            Len--;

        }

       

        m_FilePosition += m_Buffer->Len() - Len;

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

        m_Buffer->Compact();

       

        return S_OK;

    }

   

    HRESULT ClearClockList()

    {

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

        {

            delete m_ClockList[i];

        }

       

        m_ClockList.clear();

       

        return true;

    }

   

    HRESULT AddClock(__int64 FilePosition, __int64 Clock)

    {

        EnterCriticalSection(&m_ClockListCriticalSection);

        MPEGPSClock * Clk = new MPEGPSClock;

        Clk->FilePosition = FilePosition;

        Clk->Clock = Clock;

        m_ClockList.push_back(Clk);

        LeaveCriticalSection(&m_ClockListCriticalSection);

        return S_OK;

    }

   

    int ThreadFunc(void)

    {

        unsigned long Read;

        __int64 ReadSoFar;

       

        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;

               

                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();

            }

           

            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 (Process() != S_OK)

                {

                    OutputDebugString(L"tsv: Warning - invalid program 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);

            }

           

            Sleep(1);

        }

       

        return 0;

    }

   

public:

    CMPEGPSBitrateAnalyzer():

        m_Progress(0.0),

        m_FileStream(NULL)

    {

        SetThreadPriority(THREAD_PRIORITY_NORMAL);

       

        m_Buffer = new CBuffer(2 * 1024 * 1024);

        m_Buffer->Empty();

       

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

       

        InitializeCriticalSection(&m_ClockListCriticalSection);

        InitializeCriticalSection(&m_ProgressCriticalSection);

       

        ResumeThread();

    }

   

    virtual ~CMPEGPSBitrateAnalyzer()

    {

        SetEvent(m_ExitEvent);

        WaitFor();

       

        CloseHandle(m_ExitEvent);

       

        delete m_Buffer;

       

        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 (!IsPS(FileName))

        {

            return false;

        }

       

        memset(m_FileName, 0, MAX_PATH);

        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()

    {

        return 1;

    }

   

    unsigned long GetClocksCount(unsigned long ClockListIndex)

    {

        if (ClockListIndex != 0)

        {

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

            return E_FAIL;

        }

       

        unsigned long Cnt = 0;

        EnterCriticalSection(&m_ClockListCriticalSection);

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

        LeaveCriticalSection(&m_ClockListCriticalSection);

       

        return Cnt;

    }

   

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

    {

        if (ClockListIndex != 0)

        {

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

            return E_FAIL;

        }

       

        HRESULT hr = S_OK;

       

        EnterCriticalSection(&m_ClockListCriticalSection);

        if (ClockItemIndex >= m_ClockList.size())

        {

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

            hr = E_FAIL;

        }

       

        if (hr == S_OK)

        {

            *FilePosition = m_ClockList[ClockItemIndex]->FilePosition;

            *Clock = m_ClockList[ClockItemIndex]->Clock;

        }

       

        LeaveCriticalSection(&m_ClockListCriticalSection);

       

        return hr;

    }

   

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

    {

        if (ClockListIndex != 0)

        {

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

            return E_FAIL;

        }

       

        HRESULT hr = E_FAIL;

        EnterCriticalSection(&m_ClockListCriticalSection);

       

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

        {

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

            {

                *FilePosition = m_ClockList[i]->FilePosition;

                *Clock = m_ClockList[i]->Clock;

                *ClockItemIndex = i;

                hr = S_OK;

                break;

            }

        }

       

        LeaveCriticalSection(&m_ClockListCriticalSection);

       

        return hr;

    }

};

   

#endif // __MPEGPSBITRATEANALYZER_H__