TS流解析-提取PSI信息等
代碼加註釋如下:
- #include <iostream>
- #include <fstream>
- using namespace std;
- struct programs //封裝節目信息的結構體
- {
- int programID;//節目編號
- int pmtPID;//所屬PMT的pid
- int videoPID;//視頻pid
- int audioPID1;//音頻pid
- int 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分組,查找PAT
- myFile.seekg(0,ios::end);//定位到文件尾部
- int totalBytes=myFile.tellg();//獲取尾部距離首部的偏移量,即TS文件字節總數totalBytes
- int packageCount=(totalBytes-startPos)/packageLen;//確定進行遍歷的循環次數 即總TS包數
- int curPack=0;
- while (curPack<packageCount)//遍歷分組
- {
- myFile.seekg(startPos+curPack*packageLen);//定位到第curPack個分組的首字節
- myFile.read((char *)buffer,packageLen);//讀出當前分組,保存到緩存buffer中,讀一段分組長度188或204
- pID=((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或204
- pID=((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);//執行程序:解析PMT
- a++;
- }
- }
- if(a==prog_count)
- {
- break;
- }
- curPack++;
- }
- cout<<endl;
- cout<<"TS流相關信息:流中第一個TS分組起始位置"<<startPos<<","<<"TS分組長度"<<packageLen<<","<<"節目數"<<program<<","<<"空包數"<<nullpack<<endl;
- cout<<"所有節目相關PID信息"<<endl;
- delete[]buffer;
- myFile.close();
- }
- //查找並解析PAT
- bool FindAndParsePAT(unsigned char *buffer,int pID,int curPack)
- {
- //3.根據pid值是否爲0確認PAT分組,並從中讀PMT的PID
- int 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;
- }
- //查找並解析PMT
- bool 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; //規定此字段之後此分段的字節數,包括CRC
- int section_number;
- int last_section_number; //8位字段,值總爲0x00
- int i=0;
- if (((buffer[3])<<2)/64==1)
- //判斷adaption_field_control '01',無調整字段,僅含有有效負載
- //2位字段。用於指示本傳送流分組首部是否跟隨有調整字段和/或有效負載。
- /*00 爲ISO/IEC未來使用保留
- 01 無調整字段,僅含有效負載
- 10 僅含調整字段,無有效負載
- 11 調整字段後爲有效負載
- */
- {
- adaption_field_length=0;
- }
- else if (((buffer[3])<<2)/64==2)
- //判斷adaption_field_control'10',僅含有調整字段,無有效負載
- {
- return true;
- }
- else if (((buffer[3])<<2)/64==3)
- //判斷adaption_field_control'11',調整字段後爲有效負載
- {
- adaption_field_length=buffer[4];//獲得自適應字段長度
- }
- i=adaption_field_length;//指針指向有效負載部分
- /*
- 如果傳輸流分組帶有一個 PSI部分的第一個字節,payload_unit_start_indicator值應被置'1',表明傳輸流分組的第一個字
- 節帶有 pointer_field。如果傳輸流分組不帶有一個 PSI部分的第一個字節payload_unit_start_indicator值應被置'0',表明在有
- 效負載中沒有pointer_field。空分組的payload_unit_start_indicator應置'0'。
- */
- payload_unit_start_indicator=( (buffer[1])<<1 )/128;
- //1bit,1表示有pointer-field
- //0表示無
- if(payload_unit_start_indicator) //判斷payload_unit_start_indicator
- {
- pointer_field=buffer[i+4];
- //8位,其值爲在此字段之後到傳輸流分組有效負載的第一個字段的第一個字節之間的字節數。
- }
- else
- {
- pointer_field=-1;
- }
- i=i+pointer_field+1+4;//確定淨荷在當前分組中的偏移量,頭的字節長度
- if(buffer[i]==2) //判斷table_id,PMT==0x02
- {
- section_length=buffer[i+1]%16*256+buffer[i+2];
- //section_length,12bit,表明在section_length字段之後此分段的字節數,包括CRC
- }
- section_number=buffer[i+6];
- last_section_number=buffer[i+7];
- for(int m=0;m<(section_length-57)/5;m++)
- //視頻或音頻流數目N的計算方法是N=(section_length--57)/5
- //section_length後面佔用9字節+一堆從tsr.exe中看到的字節共57,CRC佔用4字節
- {
- if(buffer[i+23+m*5]==0x02)
- {
- cout<<"視頻PID:"<<buffer[i+24+m*5]%32*256+buffer[i+25+m*5]<<"\t";
- }
- if(buffer[i+23+m*(5+16)]==0x04)
- {
- cout<<"音頻PID:"<<buffer[i+24+m*(5+16)]%32*256+buffer[i+25+m*(5+16)]<<"\t";
- }
- }
- cout<<endl;
- //your code here
- return true;
- }