Converting bit field to byte array

2019-08-06 05:34发布

问题:

I have some mpeg ts bitfields, for example transport stream package:

struct ts_package_header_s {
    unsigned int continuity_counter :4;
    unsigned int adaptation_field_control :2;
    unsigned int transport_scrambling_control :2;
    unsigned int PID :13;
    unsigned int transport_priority :1;
    unsigned int payload_unit_start_indicator :1;
    unsigned int transport_error_indicator :1;
    unsigned int sync_byte :8;
};

struct ts_package_s {
    struct ts_package_header_s ts_header;
    unsigned char ts_body[TS_BODY];
};

union ts_package_u {
    struct ts_package_s ts_package;
    unsigned char bytes[TS_PACKAGE];
};

In my source code I initialize header struct:

pat_package_header.sync_byte = 0x47;
pat_package_header.transport_error_indicator = 0;
pat_package_header.payload_unit_start_indicator = 1;
pat_package_header.transport_priority = 0;
pat_package_header.PID = PAT_PID;
pat_package_header.transport_scrambling_control = 0;
pat_package_header.adaptation_field_control = 1;
pat_package_header.continuity_counter = 0;

And than I make ts_packag union

union ts_package_u package;
package.ts_package.ts_header = pat_package_header;

Than I fill ts_body array. When I write this package to file. I get backwards array:

10 00 40 47 XX XX XX.. instead of 47 40 10 00 XX XX XX.. 

I tried to cast my struct to char* instead of using union, but got same result.

Where is fault? Thanks.

回答1:

It's dangerous to use try and serialise structs like this directly to disk where the format must be known across different architectures, or use different compilers.

Compilers differ in how they store bitfields and the underlying endianess of your architecture also changes how the data is stored

For example, it could be that a compiler chooses to align bitfields on byte, word or some other boundary. It's a compiler decision. It may also choose to save the bits in any order, which usually depends on the endianess of your machine.

In order to safely write this header to disk, you need to serialise the data yourself. The header is 32-bit and Big Endian according to Wikipedia.

So for example:

#include <stdio.h>

#define TS_BODY 1024
#define PAT_PID 0x40

struct ts_package_header_s {
    unsigned int continuity_counter :4;
    unsigned int adaptation_field_control :2;
    unsigned int transport_scrambling_control :2;
    unsigned int PID :13;
    unsigned int transport_priority :1;
    unsigned int payload_unit_start_indicator :1;
    unsigned int transport_error_indicator :1;
    unsigned int sync_byte :8;
};

struct ts_package_s {
    struct ts_package_header_s ts_header;
    unsigned char ts_body[TS_BODY];
};

static void write_ts( struct ts_package_s pat_package )
{
    FILE* f = fopen( "test.ts", "wb+" );
    unsigned int header = 0;

    if( f == NULL )
        return;

    header = pat_package.ts_header.sync_byte << 24;
    header |= ( pat_package.ts_header.transport_error_indicator << 23 );
    header |= ( pat_package.ts_header.payload_unit_start_indicator << 22 );
    header |= ( pat_package.ts_header.transport_priority << 21 );
    header |= ( pat_package.ts_header.PID << 8 );
    header |= ( pat_package.ts_header.transport_scrambling_control << 6 );
    header |= ( pat_package.ts_header.adaptation_field_control << 4 );
    header |= ( pat_package.ts_header.continuity_counter );

    /* Write the 32-bit header as big-endian */
    unsigned char byte = header >> 24;
    fwrite( &byte, 1, 1, f );

    byte = ( header >> 16 ) & 0xFF;
    fwrite( &byte, 1, 1, f );

    byte = ( header >> 8 ) & 0xFF;
    fwrite( &byte, 1, 1, f );

    byte = header & 0xFF;
    fwrite( &byte, 1, 1, f );

    fclose( f );
}

int main( int argc, char* argv[] )
{
    struct ts_package_s pat_package;
    pat_package.ts_header.sync_byte = 0x47;
    pat_package.ts_header.transport_error_indicator = 0;
    pat_package.ts_header.payload_unit_start_indicator = 1;
    pat_package.ts_header.transport_priority = 0;
    pat_package.ts_header.PID = PAT_PID;
    pat_package.ts_header.transport_scrambling_control = 0;
    pat_package.ts_header.adaptation_field_control = 1;
    pat_package.ts_header.continuity_counter = 0;

    write_ts( pat_package );

    return 0;
}

Writes a file with the following header:

0x47 0x40 0x01 0x10

which appears to be correct according to the values you're using.