TS 碼流率計算總結
——By 風波邪人
1 transport_rate計算公式
其中, ,PCR字段編碼在MPEG-2 TS包的自適應字段(Adaptation field)的6個Byte中,其中6 bits為預留位,42 bits為有效位,其在TS包中的編碼位置見下圖,PCR分兩部分編碼,一個以STC頻率的1/300(90KHz)為單位,稱為PCR_base,共33bit;另一個以STC頻率(27MHz)為單位,稱為PCR_ext,共9bit。
i指包含PCR_base 字段的最后一個bit的字節,PCR(i)表示這個字節到達TS系統目標解碼器(T-STD)的時間,具體定義請參見參考文獻1。通過PCR值不但可以獲得正確的解碼時間, 還可以計算TS速率等與時間有關的信息。這里的I’-I’’意思是連個包含PCR的數據包之間的字節差值。
圖1 PCR字段編碼在TS包中的位置
2 TS數據包分析
圖2 Transport Packet
圖3 TS包結構
TS流數據包是由:包頭+自適應區(可選)+報數據(凈荷)組成,Sync-byte同步字節為0x47。這里要用到pf(有效載荷標識), 因為它有效時 Table 2-6 Transport Stream adaptation field才有效,我們要抓取的PCR數據就在adaptation field中,接着往下看Table 2-6就會發現,只有PCR_flag == 1 時,我們要找的program_clock_reference_base 和 program_clock_reference_extension才有效。故,由此一連串的因果關系可以很明顯的看出抓取PCR的過程了。
圖4 Transport Stream Adaptation Field
圖5 TS數據包內容
3 利用程序抓取PCR數據
在抓取PCR數據之前做一項工作,就是用程序讀取 視頻文件內容,在能正確讀取文件內容的基礎上才能抓取PCR數據。我是先根據TS包的結構特點和PCR數據存在的幾個條件一起考慮寫了一個簡單的程序,提取出了PCR數據。通過一個小程序將得到的PCR數據處理成PCR_base和PCR_ext部分,在通過 公式將PCR(i)得到,從而得出transport_rate。
代碼如下:
#include <iostream>
#include <iomanip>
using namespace std;
#define _FORMAT 0
#if _FORMAT == 1
#define FORMAT dec
#else
#define FORMAT hex
#endif
int PCR_OK = 0, OPCR_OK = 0, SP_CD_OK = 0;
// TS數據包
class TS{ //Byte
private:
int sync_byte; //8bit
short ei; //1bit
short pusi; //1bit
short tpr; //1bit
int PID; //13bit
short scr_flags; //2bit
short af; //1bit
short pf; //1bit
short cc; //4bit
int adpt_flength; //8bit
short flag; //1bit
unsigned long PCR[2]; //48bit
unsigned long OPCR[2]; //48bit
int splice_countdown; //8bit
unsigned char buf_data[179]; //179byte
public:
void display(int i);
int undecoder(FILE * fp);
};
//數據顯示
void TS::display(int i)
{
cout<<setw(3)<<setiosflags(ios::right)<<dec<<i;
cout<<"----"<<endl;
cout<<"Item "<<"Info"<<endl;
//cout.setf(ios::showbase);
cout.setf(ios::hex);
cout<<"sync_byte : "<<FORMAT<<sync_byte<<endl;
cout<<"ei : "<<ei<<endl;
cout<<"pusi : "<<pusi<<endl;
cout<<"tpr : "<<tpr<<endl;
cout<<"PID : "<<PID<<endl;
cout<<"scr_flags : "<<scr_flags<<endl;
cout<<"af : "<<af<<endl;
cout<<"pf : "<<pf<<endl;
cout<<"cc : "<<cc<<endl;
cout<<"adpt_flength : "<<adpt_flength<<endl;
//output flag
cout<<"flag : "<<flag;
cout<<" binary :";
int j;
//output flag binary
for(j = 0; j < 8; j++)
{
if(!j)
cout<<dec<<((flag>>(7-j)) & 0x01);
else if(j%4 )
cout<<dec<<((flag>>(7-j)) & 0x01);
else
{
cout<<'_';cout<<dec<<((flag>>(7-j)) & 0x01);
}
}
cout<<endl;
if(PCR_OK)
{
PCR_OK = 0;
//output PCR
cout<<"PCR : "<<FORMAT<<PCR[0]<<PCR[1]<<endl;
}
if(OPCR_OK)
{
OPCR_OK = 0;
//output OPCR
cout<<"OPCR : "<<FORMAT<<OPCR[0]<<OPCR[1]<<endl;
}
//output splice_countdown
if(SP_CD_OK)
{
SP_CD_OK = 0;
cout<<"splice_countdown: "<<FORMAT<<splice_countdown<<endl;
}
}
int TS::undecoder(FILE * fp)
{
unsigned char buf[6];
int z=0;
unsigned int ch, ch1;
ch = fgetc(fp);
sync_byte = ch;
ch = fgetc(fp);
ei = (ch & 0x80)>>7 ; //transport_error_indicator
pusi = (ch & 0x40)>>6; //payload_unit_start_indicator
tpr = (ch & 0x20)>>5; //transport_priority
ch1 = fgetc(fp);
PID = ((ch & 0x1f)<<8) | ch1; //PID
ch = fgetc(fp);
scr_flags = (ch & 0xc0)>>6; //transport_scrambling_control
af = (ch & 0x20)>>5; //adaptation_field_control
pf = (ch & 0x10)>>4;
cc = (ch & 0x0f); //continuity_counter
ch = fgetc(fp);
adpt_flength = ch; //adaptation_field_length
ch = fgetc(fp);
flag = ch; //adaptation_field_flag
//-------6B
//adaptation field 有效
//自適應區長度不為0
if(af && adpt_flength )
{
// 當 PCR_flag 有效時,讀 PCR值到緩存 , 6B
if( flag &0x10)
{
for(z=0;z<6;z++)
{
buf[z] = fgetc(fp); //讀取PCR數據
}
PCR[0] = (buf[0]<<16) | (buf[1]<<8) | buf[2];
PCR[1] = (buf[3]<<16) | (buf[4]<<8) | buf[5];
PCR_OK = 1; //PCR數據讀取完畢標志
}
else
fseek(fp, 6, 1); //移動文件指針
// 當 OPCR_flag 有效時,讀 OPCR值到緩存 ,6B
if(flag & 0x08)
{
for(z=0;z<6;z++)
{
buf[z] = fgetc(fp);
}
OPCR[0] = (buf[0]<<16) | (buf[1]<<8) | buf[2];
OPCR[1] = (buf[3]<<16) | (buf[4]<<8) | buf[5];
OPCR_OK = 1;
}
else
fseek(fp, 6, 1);
if(flag & 0x04) // 1B
{
ch = fgetc(fp); //讀取splice_countdown 數據
splice_countdown = ch;
SP_CD_OK = 1;
}
else
fseek(fp, 1, 1);
//文件指針移到下一個TS包起始位置
fseek(fp, 0xbb - 0x12, 1); //0 top position, 1 current position, 2 button position
}
else
fseek(fp, 0xbb - 0x05, 1); //0 top position, 1 current position, 2 button position
return 1;
}
void compute()
{
long pcr_in1,pcr_in2,pcr_base_H, pcr_base_L, pcr_base,pcr_base_header,pcr_ext;
while(1)
{
cout.setf(ios::hex);
cout<<"input 0 end"<<endl;
cin>>hex>>pcr_in1;
if(pcr_in1 == 0)
return ;
cin>>pcr_in2;
pcr_base_H = pcr_in1;
pcr_base_L = pcr_in2 >>15;
if(pcr_base_H & 0x800000)
pcr_base_header = 1;
else
pcr_base_header = 0;
pcr_base = (pcr_base_H<<9) | pcr_base_L;
pcr_ext = pcr_in2 & 0x0001ff;
cout<<"PCR_BASE:"<<hex<<pcr_base_header<<hex<<pcr_base<<endl;
cout<<"PCR_EXT:"<<hex<<pcr_ext<<endl;
}
}
void find()
{
TS ts;
FILE * fp;
fp = fopen("ysgq.trp","rb");
if(fp != NULL)
cout<<"FILE OPEN SUCCESS"<<endl;
else
{
cout<<"FILE OPEN FAIL"<<endl;
exit(0);
}
int count = 0;
for(int c = 0; !feof(fp) ; c++) //讀取TS流文件
{
if(ts.undecoder(fp)) //解碼成功
{
if(PCR_OK || OPCR_OK) //找到PCR
{
ts.display(c); //打印該包的信息
count ++;
if(count == 10)
break;
}
else
;
}
else
{
cout<<"Read file fail!"<<endl;
break;
}
}
fclose(fp);
}
int main()
{
// 數據類型顯示
#if _FORMAT == 0
cout<<"hex"<<endl;
#endif
#if _FORMAT == 1
cout<<"dec"<<endl;
#endif
//===================================
int sel;
while(1)
{
cout<<"0:compute \n1:find"<<endl;
cin>>sel;
switch(sel)
{
case 0:compute();break; //計算PCR(i)_base , PCR(i)_ext
case 1:find();break; //尋找PCR
default:break;
}
}
//====================================
return 0;
}
在啟動程序之后,會有一個菜單選項,0表示:計算PCR(i)值,加入用程序抓取到的PCR數據為A45D3456FE41,則先輸入A45D34,按回車鍵再輸入56FE41,回車即可得到PCR_base和PCR_ext。通過計算器求得PCR(i)。
4 利用公式計算transport_rate。
在以上工作做好之后利用公式計算是很容易的事情。