#include <iostream>#include <fstream>using namespace std;struct programs //封装节目信息的结构体{int programID;//节目编号int pmtPID;//所属PMT的pidint videoPID;//视频pidint audioPID1;//音频pidint audioPID2;//音频pid}myProg[20];bool FindAndParsePAT(unsigned char *buffer,int pID,int curPack);//传入BUF和PID的值bool FindAndParsePMT(unsigned char *buffer,int pID,int curPack);int program=0;int prog_count=0;void main(){unsigned char *buffer=new unsigned char[500];int startPos=0;//第一个TS分组在流中的位置序号int packageLen=0;//分组长度int pmtCount=-1;//PMT表序号int pID=0;int nullpack=0;//0.以二进制方式打开TS文件ifstream myFile("test.ts",ios::binary|ios::in);//1.读入文件的前500个字节,找同步头、确定包长myFile.read((char *)buffer,500);for(int i=0;i<500;i++){ //判断有无压缩if(buffer[i]==0x47&&buffer[i+188]==0x47){startPos=i;//第一个TS分组在流中的位置序号packageLen=188;//分组长度break;}else if(buffer[i]==0x47&&buffer[i+204]==0x47){startPos=i;packageLen=204;break;}}//2.遍历流中的TS分组,查找PATmyFile.seekg(0,ios::end);//定位到文件尾部int totalBytes=myFile.tellg();//获取尾部距离首部的偏移量,即TS文件字节总数totalBytesint packageCount=(totalBytes-startPos)/packageLen;//确定进行遍历的循环次数即总TS包数int curPack=0;while (curPack<packageCount)//遍历分组{myFile.seekg(startPos+curPack*packageLen);//定位到第curPack个分组的首字节myFile.read((char *)buffer,packageLen);//读出当前分组,保存到缓存buffer中,读一段分组长度188或204pID=((buffer[1]&31)<<8)+buffer[2];//解析出当前分组的pid(13位=第2个字节的后5位+第3个字节全8位)if(pID==0x1fff) //检查空包数{nullpack++;}if(FindAndParsePAT(buffer,pID,curPack))//执行程序:解析PAT 有效break; //表明只要解析一个PAT就行curPack++;}curPack=0;int a=0;while (curPack<packageCount){myFile.seekg(startPos+curPack*packageLen);//定位到第curPack个分组的首字节myFile.read((char *)buffer,packageLen);//读出当前分组,保存到缓存buffer中,读一段分组长度188或204pID=((buffer[1]&31)<<8)+buffer[2];//解析出当前分组的pid(13位=第2个字节的后5位+第3个字节全8位)for(int k=0;k<prog_count;k++){if(pID==myProg[k].pmtPID)//根据PAT表内容确定如何查PMT表{cout<<"第"<<k+1<<"套节目:"<<endl;FindAndParsePMT(buffer,pID,curPack);//执行程序:解析PMTa++;}}if(a==prog_count){break;}curPack++;}cout<<endl;cout<<"TS流相关信息:流中第一个TS分组起始位置"<<startPos<<","<<"TS分组长度"<<packageLen<<","<<"节目数"<<program<<","<<"空包数"<<nullpack<<endl;cout<<"所有节目相关PID信息"<<endl;delete[]buffer;myFile.close();}//查找并解析PATbool FindAndParsePAT(unsigned char *buffer,int pID,int curPack){//3.根据pid值是否为0确认PAT分组,并从中读PMT的PIDint adapLen=0;//TS分组适配字段长度int offset=0;//实际净荷在当前分组中的偏移量if(pID==0){int payload_unit_start = (buffer[1]>>6) & 0X01;//净荷单元起始指示int adaptation_field_control = (buffer[3]>>4) & 0X03;//自适应字段控制//3.1 确定净荷起始位置(4字节固定首部+适配字段长度,adaption_field_control)if(adaptation_field_control==0x01)//无调整字段,仅净荷{adapLen=0;//TS分组适配字段长度为0}else if(adaptation_field_control==0x11)//有调整字段和净荷{adapLen=buffer[4];//自适应字段长度}else//无有效载荷,查找下一个分组{curPack++;//continue;}offset=4+adapLen;//确定净荷在当前分组中的偏移量,头的字节长度//3.2 确定PAT首部在净荷中的偏移量(如payload_unit_start_indicator为1,//则净荷首字节为偏移指针,指示PAT首部与其之间的偏移值)if(payload_unit_start==0x01)//如果净荷单元起始指示为1{offset+=buffer[offset]+1;//pointer_field字段长为1字节}//3.3 开始解析PAT表int tableID=buffer[offset];//从净荷起始if(tableID==0)//进入节目关联表PAT{int section_len=((buffer[offset+1]&0x0F)<<8)+buffer[offset+2];//code here:初始化 int transport_stream_idd=(buffer[offset+3]<<8)+buffer[offset+4];//code here:初始化int current_next_indicator=buffer[offset+5]&0x01;//code here:初始化if (current_next_indicator)//当前PAT有效{prog_count=(section_len-9)/4-1;for(int i=0;i<prog_count;i++){myProg[i].programID=(buffer[offset+12+i*4]<<8)+buffer[offset+12+i*4+1];//用2个字节表示节目号cout<<"节目号"<<myProg[i].programID<<" ";myProg[i].pmtPID=(buffer[offset+14+i*4]&0x1F<<8)+buffer[offset+14+i*4+1];//用13位表示映射表cout<<"映射表ID"<<myProg[i].pmtPID<<"\n";program++;}//your code here 读出PAT包中存储的有关节目PMT的信息,确定节目数以及每路节目的PMT表pid,存储到myProg中return true;}}}return false;}//查找并解析PMTbool FindAndParsePMT(unsigned char *buffer,int pID,int curPack){//PMT 标志位int payload_unit_start_indicator; //1比特标志位,用来指示传送流分组带有PES分组或PSI数据时的情况int adaption_field_length; //自适应字段长度。
int pointer_field; //int section_length; //规定此字段之后此分段的字节数,包括CRCint section_number;int last_section_number; //8位字段,值总为0x00int i=0;if (((buffer[3])<<2)/64==1)//判断adaption_field_control '01',无调整字段,仅含有有效负载//2位字段。
用于指示本传送流分组首部是否跟随有调整字段和/或有效负载。