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流处理文件有一点点小问题,但不影响学习二进制文件结构化读取。

发布了13 篇原创文章 · 获赞 17 · 访问量 1431

猜你喜欢

转载自blog.csdn.net/LucifeR_Shun/article/details/104486894
今日推荐