/*******************************************************}
{
}
{ 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__