C++ 二进制文件结构化读取 涉及位域大小端 使用TS文件为例子

Pascall ·
更新时间:2024-11-13
· 958 次阅读

前言:最近自己需要写程序分析TS流,结果刚刚上手就失败了,TS文件二进制读取总是有问题,打印出来的结果和文件中的内容不一样。一开始以为是自己程序的问题,后来搜索了很久也没有找到一个合适的结果,后来偶然看到了位域,突然想起之前对RTP操作的时候也有这种问题。那次的代码是用的别人的,只是大致看了看。哎~

二进制文件操作网上很多教程,不再叙述。直接上代码:

cpp文件,简单的打开文件,读文件到结构体中,打印结构体。

#include #include #include #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(tsbit->sync_byte) << endl; cout << "transport_error_indicator:\t" << bitset(tsbit->transport_error_indicator) << endl; cout << "payload_unit_start_indicator:\t" << bitset(tsbit->payload_unit_start_indicator) << endl; cout << "transport_priority:\t\t" << bitset(tsbit->transport_priority) << endl; cout << "PID:\t\t\t\t" << bitset(tsbit->PID) << endl; cout << "transport_scrambling_control:\t" << bitset(tsbit->transport_scrambling_control) << endl; cout << "adaption_field_control:\t\t" << bitset(tsbit->adaption_field_control) << endl; cout << "continuity_counter:\t\t" << bitset(tsbit->continuity_counter) << endl; }

注意下  bitset(a)  这个函数可以把 a 按二进制输出 n 位,例如 bitset(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(tsbit->sync_byte) << endl; cout << "transport_error_indicator:\t" << bitset(tsbit->transport_error_indicator) << endl; cout << "payload_unit_start_indicator:\t" << bitset(tsbit->payload_unit_start_indicator) << endl; cout << "transport_priority:\t\t" << bitset(tsbit->transport_priority) << endl; cout << "PID:\t\t\t\t" << bitset(tsbit->PID_1) << bitset(tsbit->PID_2) << endl; cout << "transport_scrambling_control:\t" << bitset(tsbit->transport_scrambling_control) << endl; cout << "adaption_field_control:\t\t" << bitset(tsbit->adaption_field_control) << endl; cout << "continuity_counter:\t\t" << bitset(tsbit->continuity_counter) << endl;

然后就有了个交换函数,专门处理这个问题。具体看资源,不再叙述。

ps:交换函数是抄来的,但年代久远,我找不到原作者,原理也挺简单就是个位运算。资源404可能被下架了。这个TS流处理文件有一点点小问题,但不影响学习二进制文件结构化读取。


作者:LucifeR_Shun



c+ ts 二进制 ts文件 C++ 进制

需要 登录 后方可回复, 如果你还没有账号请 注册新账号