一、EIT表簡介
EIT按時間順序提供每一個節目業務中包含的節目段的信息。
二、TS包頭結構
其中EIT表的PID爲0X12;
要根據這個PID值從TS碼流中過濾出所有屬於EIT的ts包;
除了PID,TS包頭中還需要考慮的參數有:
syn_byte=0x47(標識正確的包的起始位置)
transport_error_indicator=0(標識當前傳輸的ts包無明顯錯誤)
payload_unit_start_indicator(很重要的一個參數,爲1標識該包爲一個段的起始ts包,且ts包有效負載第一位爲指針pointer_field;爲0,則標識有效負載中沒有指針。)
adaptation_field_control(當其值爲00或10時,標識continuity_counter不連續,該ts包及該段中之前的ts要被丟棄)
三、EIT表結構
篩選出PID==0x12的ts包,並按一定規則拼接爲段後,我們要分析EIT短信息。
因爲我們分析的是P/F信息,因此只取 table_id=0x4E的EIT段。其中需要考慮的參數有:
section_length(標識從下一個字節起,該section包含的字節數)
service_id(標識節目號)
version_number(版本號,隨時間更新)
current_next_indicator(爲1,標識當前表有效;爲0,標識下一個子表纔有用)
section_number(標識EIT表中包含的section個數,因爲我們分析的P/F信息,只包含section_number爲0和爲1的段,因此該參數不用特意分析考慮,只需最後直接輸出)
start_time(標識該節目的起始時間)
duration_time(標識該節目的持續時間)
descriptor_loop_length(接下來的循環包含的總的字節數)
discriptor有很多種,我們只分析包含名稱信息的短節目段描述符,即選descriptor_tag==0X4D。該描述符的結構如下:
其中descriptor_length標識下一字節開始,該描述符的長度;
event_name_length標識名稱信息佔用的字節數;event_name_char標識節目名稱信息。
四、碼流分析儀中的EIT結構顯示:
通過C++分析完一段數據後,可以比照碼流分析儀中顯示的時間、名稱信息來看看自己的程序是否正確。
五、代碼段
1、ts_packet類
//ts_packet類
#include<iostream>
#include <cstring>
class Ts_Packet
{
private:
unsigned sync_byte;
unsigned transport_error_indicator;
unsigned payload_unit_start_indicator;
unsigned PID;
unsigned adaptation_field_control;
//unsigned continuity_counter;
unsigned offset;
unsigned char payload[184];
public:
Ts_Packet();
Ts_Packet(unsigned char tsHeadbuff[188] );
~Ts_Packet(){};
unsigned get_sync_byte();
unsigned get_transport_error_indicator();
unsigned get_payload_unit_start_indicator();
unsigned get_PID();
unsigned get_adaptation_field_control();
//unsigned get_continuity_counter();
unsigned get_offset();
unsigned char *get_payload();
void print_tsheader();
};
2、ts_packet類的成員函數實現
#include"a_Ts_Packet.h"
Ts_Packet::Ts_Packet()
{
sync_byte = 0;
transport_error_indicator =0;
payload_unit_start_indicator =0;
PID =0;
adaptation_field_control=0;
offset=0;
memset(payload, 0, 184);
};
Ts_Packet::Ts_Packet(unsigned char tsHeadbuff[188])
{
sync_byte = tsHeadbuff[0];
transport_error_indicator = tsHeadbuff[1] >> 7;
payload_unit_start_indicator = ((tsHeadbuff[1] >> 6) & 0x01);
PID = ((tsHeadbuff[1] & 0x1F) << 8 ) | tsHeadbuff[2];
adaptation_field_control=(tsHeadbuff[3]&0x30)>>4;
offset=tsHeadbuff[5];
for(int i=0;i<184;i++)
{
payload[i]=tsHeadbuff[4+i];
}
}
unsigned Ts_Packet::get_sync_byte(){return sync_byte;}
unsigned Ts_Packet::get_transport_error_indicator(){return transport_error_indicator;};
unsigned Ts_Packet::get_payload_unit_start_indicator(){return payload_unit_start_indicator;};
unsigned Ts_Packet::get_PID(){return PID;};
unsigned Ts_Packet::get_offset(){return offset;};
unsigned char*Ts_Packet::get_payload(){return payload;};
unsigned Ts_Packet::get_adaptation_field_control(){return adaptation_field_control;};
void Ts_Packet::print_tsheader()
{
printf("%02x ",sync_byte);
printf("%02x ",transport_error_indicator);
printf("%02x ",payload_unit_start_indicator);
printf("%02x ",PID);
}
3、EIT_Section類
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
struct DATE1
{
int aa;
int bb;
int cc;
};
class EIT_Section
{
private:
unsigned table_id;
unsigned section_length:12;
unsigned service_id;
unsigned version_number;
unsigned current_next_indicator;
unsigned section_number;
unsigned char*time_buff;
unsigned descriptor_loop_length;
unsigned char *descriptor;
public:
EIT_Section();
EIT_Section(unsigned char *buff);
unsigned get_table_id();
unsigned get_section_length();
unsigned get_service_id();
unsigned get_version_number();
unsigned get_current_next_indicator();
unsigned get_section_number();
unsigned char*get_time_buff();
unsigned get_descriptor_loop_length();
unsigned char *get_descriptor();
void prinf_EIT();
int MJD_Date(__int32 date);
void getTime(unsigned char *buff);
};
class Descriptor
{
private:
unsigned descriptor_tag :8;
unsigned descriptor_length :8;
unsigned event_name_length :8;
unsigned char*event_name_char;
public:
Descriptor();
Descriptor(unsigned char*descriptor_buff);
unsigned get_descriptor_tag();
unsigned get_descriptor_length();
unsigned get_event_name_length();
unsigned char*get_event_name();
void print();
void getName(unsigned char *buff,int length);
void parse_name();
};
4、EIT_Section類中成員函數的具體實現
#include"a_EIT_Section2.h"
EIT_Section::EIT_Section()
{
table_id=0;
section_length=0;
service_id=0;
version_number=0;
current_next_indicator=0;
section_number=0;
time_buff=0;
descriptor_loop_length=0;
descriptor=0;
}
EIT_Section::EIT_Section(unsigned char*buff)
{
table_id=buff[0];
section_length=((buff[1]&0x0f)<<8)|buff[2];//結果顯示不對!必須是兩個括號!!!兩個數相與也需要括起來!!
service_id=(buff[3]<<8)|buff[4];
version_number=(buff[5]&0x3e)>>1;
current_next_indicator=buff[5]&0x01;
section_number=buff[6];
time_buff=buff+16;
descriptor_loop_length=((buff[24]&0x0f)<<8)|buff[25];
descriptor=buff+26;
}
unsigned EIT_Section::get_table_id(){return table_id;}
unsigned EIT_Section::get_section_length(){return section_length;}
unsigned EIT_Section::get_service_id(){return service_id;}
unsigned EIT_Section::get_version_number(){return version_number;}
unsigned EIT_Section::get_current_next_indicator(){return current_next_indicator;}
unsigned EIT_Section::get_section_number(){return section_number;}
unsigned EIT_Section::get_descriptor_loop_length(){return descriptor_loop_length;}
unsigned char *EIT_Section::get_time_buff(){return time_buff;};
unsigned char *EIT_Section::get_descriptor(){return descriptor;}
int EIT_Section::MJD_Date(__int32 date)
{
__int32 year, month, day;
year = (int)((date - 15078.2) / 365.25);
month = (int)((date - 14956.1 - (int)(year*365.25)) / 30.6001);
day = (int)(date - 14956 - (int)(year*365.25) - (int)(month*30.6001));
if (month == 14 || month == 15)
{
year ++;
month = month - 1 - 12;
}
else
month--;
year += 1900;
return ((year & 0xffff) << 16 | (month & 0xff) << 8 | (day & 0xff));
};
void EIT_Section::getTime(unsigned char *buff)
{
DATE1 date, time1, time2;
int num= (buff[0] << 8) + buff[1];
int temp= MJD_Date(num);
date.aa= temp>> 16 & 0xffff;
date.bb = temp >> 8 & 0xff;
date.cc = temp & 0xff;
//獲得開始時間
time1.aa = ((buff[2] >> 4) & 0x0F) * 10 + (buff[2] & 0x0F);
time1.bb = ((buff[3] >> 4) & 0x0F) * 10 + (buff[3] & 0x0F);
time1.cc = ((buff[4] >> 4) & 0x0F) * 10 + (buff[4] & 0x0F);
//獲得結束時間
time2.aa = ((buff[5] >> 4) & 0x0F) * 10 + (buff[5] & 0x0F);
time2.bb = ((buff[6] >> 4) & 0x0F) * 10 + (buff[6] & 0x0F);
time2.cc = ((buff[7] >> 4) & 0x0F) * 10 + (buff[7] & 0x0F);
int t = 0;
time2.cc = time2.cc + time1.cc;
if (time2.cc >= 60)
{
time2.cc -= 60;
t = 1;
}
time2.bb = time2.bb + time1.bb + t;
t = 0;
if (time2.bb >= 60)
{
time2.bb -= 60;
t = 1;
}
time2.aa = (time2.aa + time1.aa + t) % 24;
printf(" 日期: %02d/%02d/%02d\n", date.aa, date.bb, date.cc);
printf(" 開始時間: %02d:%02d:%02d\n", time1.aa, time1.bb, time1.cc);
printf(" 結束時間: %02d:%02d:%02d\n", time2.aa, time2.bb, time2.cc);
};
void EIT_Section::prinf_EIT()
{
cout<<"The service_id is ";
printf("%x \n",service_id);
cout<<"Its ";
if (0==section_number)cout<<"P information is:";
else cout<<"F information is:";
cout<<endl;
}
Descriptor::Descriptor()
{
descriptor_tag=0;
descriptor_length=0;
event_name_length=0;
event_name_char=0;
}
Descriptor::Descriptor(unsigned char*descriptor_buff)
{
descriptor_tag=descriptor_buff[0];
descriptor_length=descriptor_buff[1];
event_name_length=descriptor_buff[5];
event_name_char=descriptor_buff+6;
}
unsigned Descriptor::get_descriptor_tag(){return descriptor_tag;}
unsigned Descriptor::get_descriptor_length(){return descriptor_length;}
unsigned Descriptor::get_event_name_length(){return event_name_length;}
unsigned char*Descriptor::get_event_name(){return event_name_char;}
void Descriptor::print()
{
printf("%02x ",descriptor_tag);
printf("%d ",descriptor_length);
cout<<endl;
}
void Descriptor::getName(unsigned char *buff, int length)
{
string NAME;
for (int i = 0; i < length; i++)
{
if (buff[i] > 32 && buff[i]<126)
NAME.push_back(buff[i]);
}
printf(" %s", NAME.c_str());
printf("\n");
}
5、main函數
#include<fstream>
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<queue>
#include"a_Ts_Packet.h"
#include"a_EIT_Section2.h"
static queue<unsigned char*>ts_queue; //隊列ts_queue存放ts包; 隊列1 !!
static queue<unsigned char*>eit_queue; //存放EIT段信息,隊列2
static int JudgmentPacketLength(FILE *pfTsFile, int iTsPosition, int iLength) ;
int ParseTsLength(FILE *pfTsFile, int *piTsPosition) ;
int main()
{
//打開文件
errno_t err;
FILE *pfTsFile = NULL; //pfTsFile爲指針
char *filename="E:\\DAS.ts";
err = fopen_s(&pfTsFile,filename, "rb");
if (err != 0)
{
perror("fopen .ts error!");
return -1;
}
//從碼流中找出正確的ts包起始位置,即連續找相差188或204的三個0X47
int LEN_ts = 0; //單個TS包的長度
int iTsPosition = 0;
LEN_ts = ParseTsLength(pfTsFile, &iTsPosition);
//確定該ts碼流中包含的ts包的個數
fseek(pfTsFile,0l,2);
int len_ts=(ftell(pfTsFile))/LEN_ts; //ts流中包含的ts包的個數
fseek(pfTsFile,0l,iTsPosition);
unsigned char *ts; //數組ts用來存放從ts流中截取的LEN_ts字節的數據;
//分析ts包,找出跟EIT表相關的ts包壓入隊列1中,等待下一步分析
for(int j=0;j<len_ts;j++) //從ts流中進行處理的TS包的個數
{
ts=new unsigned char [LEN_ts];
fread(ts,1,LEN_ts,pfTsFile);
Ts_Packet ts_packet(ts);//ts壓入隊列之前,先對其進行判斷是否爲0x12
if((0x12==ts_packet.get_PID())&&(0x0==ts_packet.get_transport_error_indicator()))//篩選EIT包
ts_queue.push(ts);
}
//開始拼段,拼好段後找出跟P/F信息相關的段壓入隊列2中
unsigned char *sectionBuff;
int pp=0;
while(!ts_queue.empty())
{
sectionBuff=new unsigned char[4096];
unsigned len_section=0;
int Begin=0;
int ts_indicator=0;
while((ts_indicator<=len_section)&&(!ts_queue.empty()))
{
Ts_Packet ts_pakt(ts_queue.front());
unsigned char*pointer_tsbuff=ts_pakt.get_payload();
if(1==ts_pakt.get_payload_unit_start_indicator())
{
memset(sectionBuff, 0, 4096);
ts_indicator=0;
Begin=1;
if((ts_pakt.get_adaptation_field_control()&0x1) == 0x1)//找到正確的段的起始位置!從中讀取出段長!
{
int adjust_byte=pointer_tsbuff[0];
pointer_tsbuff=pointer_tsbuff+1+adjust_byte;//
for(int i=0;i<LEN_ts-5-adjust_byte;i++)
sectionBuff[i]=pointer_tsbuff[i];
len_section=((sectionBuff[1]&0x0f)<<8)|sectionBuff[2];
ts_indicator=LEN_ts-5-adjust_byte;
}
}
else
{
if(1==Begin&&(ts_pakt.get_adaptation_field_control()&0x1) == 0x1)
{
for(int i=0;i<LEN_ts-4;i++)
sectionBuff[i+ts_indicator]=pointer_tsbuff[i];
ts_indicator=ts_indicator+LEN_ts-4;
}
else
{
ts_indicator=0;
memset(sectionBuff,0, 4096);
Begin=0;
}
}
ts_queue.pop();
if(ts_indicator>=len_section)
{
unsigned char *temp=new unsigned char[ts_indicator+1];
memcpy(temp,sectionBuff,ts_indicator);
if(0x4e==temp[0])
{
eit_queue.push(temp);
pp++;
}
}
}
}
//分析段信息,解析出時間、名稱信息
int len_sectionnn=eit_queue.size();
unsigned *sum=new unsigned [pp];
int sum_pointer=0;
for(int y=0;y<pp;y++)
{
unsigned char*show=eit_queue.front();
EIT_Section eit_section(show);
if(eit_section.get_section_length()<25)
goto number0;
sum[sum_pointer]=(eit_section.get_service_id()<<8)+eit_section.get_section_number();
for(int r=0;r<sum_pointer;r++)
{
if(sum[sum_pointer]==sum[r])
goto number0;
}
eit_section.prinf_EIT();
cout<<" Time information:\n";
eit_section.getTime(eit_section.get_time_buff());
int LEN=eit_section.get_descriptor_loop_length(); // 當有完整的段數據時用這個;刪除上面一句;
unsigned char*descriptor_buff;
descriptor_buff=eit_section.get_descriptor();
while(LEN>0)
{
Descriptor descriptor(descriptor_buff);
int tem=descriptor.get_descriptor_length();
if(0x4d==descriptor.get_descriptor_tag())
{
cout<<" Name information:\n";
descriptor.getName(descriptor.get_event_name(), descriptor.get_event_name_length()); //函數調用
printf("\n");
}
descriptor_buff=descriptor_buff+tem+2;
LEN=LEN-tem-2;
}
number0:eit_queue.pop();
sum_pointer++;
}
return 0;
}
選取碼流是測試碼流,每個僅僅五六分鐘,因此要考慮的因素比較少。如果是一個很完整的數據量大的碼流,按這個程序來處理是有些問題。首先要重新考慮version_number版本更新的問題,其次EIT表包含的數據量非常非常大,需要考慮解析的效率以及內存分配問題。