前言:最近自己需要写程序分析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