前言:最近自己需要寫程序分析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流處理文件有一點點小問題,但不影響學習二進制文件結構化讀取。