C++ 二進制文件結構化讀取 涉及位域大小端 使用TS文件爲例子

前言:最近自己需要寫程序分析TS流,結果剛剛上手就失敗了,TS文件二進制讀取總是有問題,打印出來的結果和文件中的內容不一樣。一開始以爲是自己程序的問題,後來搜索了很久也沒有找到一個合適的結果,後來偶然看到了位域,突然想起之前對RTP操作的時候也有這種問題。那次的代碼是用的別人的,只是大致看了看。哎~


二進制文件操作網上很多教程,不再敘述。直接上代碼:

cpp文件,簡單的打開文件,讀文件到結構體中,打印結構體。

#include <iostream>
#include <stdio.h>
#include <bitset>

#include "TSpacket.h"

using namespace std;


int main()
{
    FILE* tsfile = fopen("test.ts","rb");
    if(tsfile == NULL) {cout<<"open file err!"<<endl;return -1;}

    
    TS_packet_header* tsbit = (TS_packet_header*)calloc(sizeof(TS_packet_header),1);

    fread(tsbit,sizeof(TS_packet_header),1,tsfile);

    fclose(tsfile);

    cout<<sizeof(*tsbit)<<endl;

    cout << "sync_byte:\t\t\t" << bitset<8>(tsbit->sync_byte) << endl;
    cout << "transport_error_indicator:\t" << bitset<1>(tsbit->transport_error_indicator) << endl;
    cout << "payload_unit_start_indicator:\t" << bitset<1>(tsbit->payload_unit_start_indicator) << endl;
    cout << "transport_priority:\t\t" << bitset<1>(tsbit->transport_priority) << endl;
    cout << "PID:\t\t\t\t" << bitset<13>(tsbit->PID) << endl;
    cout << "transport_scrambling_control:\t" << bitset<2>(tsbit->transport_scrambling_control) << endl;
    cout << "adaption_field_control:\t\t" << bitset<2>(tsbit->adaption_field_control) << endl;
    cout << "continuity_counter:\t\t" << bitset<4>(tsbit->continuity_counter) << endl;
}

注意下  bitset<n>(a)  這個函數可以把 a 按二進制輸出 n 位,例如 bitset<4>(3) 輸出 0011

頭文件是TS的頭的結構體(例子) TSpacket.h

#pragma once

typedef struct TS_packet_header
{
    unsigned sync_byte                        : 8;
    unsigned transport_error_indicator        : 1;
    unsigned payload_unit_start_indicator    : 1;
    unsigned transport_priority                : 1;
    unsigned PID                            : 13;
    unsigned transport_scrambling_control    : 2;
    unsigned adaption_field_control            : 2;
    unsigned continuity_counter                : 4;
} TS_packet_header;

這裏用到了位域這個名詞, 變量 : n 表示 這個變量佔幾個bits 

資源見我的上傳:鏈接地址 (順便說一句,這個是linux的,但可以自行移植,沒啥改的)

(雖然資源被改成了最終的樣子,但內容比較簡單,照着文章看一遍就明白了 )

若是現在這個模樣,打印到屏幕上的必然和你用二進制查看工具看到的不一樣,但是你會發現,第一個 sync_byte 準確無誤的輸出了。

原因在於:機器存放數據的方式和你想的不一樣。

第一個字節沒問題,第二個字節中理應前三個bit放那三個變量的值,後五個bit放PID的前5個bit,然而他是反着來的,變成了:前五個bit放的是PID的前5個bit,然後是transport_priority 然後是payload_unit_start_indicator 然後是transport_error_indicator  。所以錯了,那麼在每個字節裏手動交換下位置不就可以了嗎?請看:

typedef struct TS_packet_header
{
    //1B
    unsigned int sync_byte                      : 8;
    //2B
    unsigned int PID_1                          : 5;
    unsigned int transport_priority             : 1;
    unsigned int payload_unit_start_indicator   : 1;
    unsigned int transport_error_indicator      : 1;
    //3B 手動交換導致PID裂開了
    unsigned int PID_2                          : 8;
    //4B
    unsigned int continuity_counter             : 4;
    unsigned int adaption_field_control         : 2;
    unsigned int transport_scrambling_control   : 2;

} TS_packet_header;

這個PID跨越了兩個字節,這樣交換位置導致PID直接分裂了,而且這個結構體也變得“難以理解” 。雖然是正確的,使用下面的打印語句。 

    cout << "sync_byte:\t\t\t" << bitset<8>(tsbit->sync_byte) << endl;
    cout << "transport_error_indicator:\t" << bitset<1>(tsbit->transport_error_indicator) << endl;
    cout << "payload_unit_start_indicator:\t" << bitset<1>(tsbit->payload_unit_start_indicator) << endl;
    cout << "transport_priority:\t\t" << bitset<1>(tsbit->transport_priority) << endl;
    cout << "PID:\t\t\t\t" << bitset<5>(tsbit->PID_1) << bitset<8>(tsbit->PID_2) << endl;
    cout << "transport_scrambling_control:\t" << bitset<2>(tsbit->transport_scrambling_control) << endl;
    cout << "adaption_field_control:\t\t" << bitset<2>(tsbit->adaption_field_control) << endl;
    cout << "continuity_counter:\t\t" << bitset<4>(tsbit->continuity_counter) << endl;

然後就有了個交換函數,專門處理這個問題。具體看資源,不再敘述。

ps:交換函數是抄來的,但年代久遠,我找不到原作者,原理也挺簡單就是個位運算。資源404可能被下架了。這個TS流處理文件有一點點小問題,但不影響學習二進制文件結構化讀取。

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