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。
在以上工作做好之后利用公式计算是很容易的事情。