C++解析EIT表中的P-F信息

一、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表包含的數據量非常非常大,需要考慮解析的效率以及內存分配問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章